2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.jablotron.internal.handler;
15 import static org.openhab.binding.jablotron.JablotronBindingConstants.*;
17 import java.util.List;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.jablotron.internal.model.JablotronHistoryDataEvent;
22 import org.openhab.binding.jablotron.internal.model.JablotronServiceDetailSegment;
23 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetPGResponse;
24 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetSectionsResponse;
25 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetThermoDevicesResponse;
26 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronSection;
27 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronState;
28 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronThermoDevice;
29 import org.openhab.core.cache.ExpiringCache;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.library.unit.SIUnits;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.binding.builder.ChannelBuilder;
38 import org.openhab.core.thing.binding.builder.ThingBuilder;
39 import org.openhab.core.thing.type.ChannelTypeUID;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.openhab.core.types.State;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link JablotronJa100FHandler} is responsible for handling commands, which are
48 * sent to one of the channels.
50 * @author Ondrej Pecta - Initial contribution
53 public class JablotronJa100FHandler extends JablotronAlarmHandler {
55 private final Logger logger = LoggerFactory.getLogger(JablotronJa100FHandler.class);
57 private ExpiringCache<JablotronGetSectionsResponse> sectionCache;
58 private ExpiringCache<JablotronGetPGResponse> pgCache;
60 public JablotronJa100FHandler(Thing thing, String alarmName) {
61 super(thing, alarmName);
62 sectionCache = new ExpiringCache<>(CACHE_TIMEOUT_MS, this::sendGetSections);
63 pgCache = new ExpiringCache<>(CACHE_TIMEOUT_MS, this::sendGetProgrammableGates);
67 public void handleCommand(ChannelUID channelUID, Command command) {
68 if (RefreshType.REFRESH.equals(command)) {
69 logger.debug("refreshing channel: {}", channelUID.getId());
70 updateChannel(channelUID.getId());
72 if (channelUID.getId().startsWith("sec-") && command instanceof StringType) {
73 if ("PARTIAL_ARM".equals(command.toString())) {
74 controlComponent(channelUID.getId(), "CONTROL-SECTION", "DISARM");
76 scheduler.execute(() -> controlComponent(channelUID.getId().toUpperCase(), "CONTROL-SECTION",
80 if (channelUID.getId().startsWith("pg-") && command instanceof OnOffType) {
82 () -> controlComponent(channelUID.getId().toUpperCase(), "CONTROL-PG", command.toString()));
87 private void updateChannel(String channel) {
88 if (channel.startsWith("sec-")) {
89 JablotronGetSectionsResponse sections = sectionCache.getValue();
90 if (sections != null) {
91 updateSectionState(channel, sections.getData().getStates());
93 } else if (channel.startsWith("pg-")) {
94 JablotronGetPGResponse pgs = pgCache.getValue();
96 updateSectionState(channel, pgs.getData().getStates());
98 } else if (CHANNEL_LAST_CHECK_TIME.equals(channel)) {
101 updateEventChannel(channel);
106 protected void updateSegmentStatus(JablotronServiceDetailSegment segment) {
110 private void controlComponent(String componentId, String action, String value) {
111 logger.debug("Controlling component: {} with action: {} and value: {}", componentId, action, value);
113 JablotronBridgeHandler handler = getBridgeHandler();
114 if (handler != null) {
115 JablotronGetSectionsResponse response;
117 response = handler.controlComponent(getThing(), thingConfig.getCode(), action, value, componentId);
118 } catch (SecurityException se) {
119 response = handler.controlComponent(getThing(), thingConfig.getCode(), action, value, componentId);
121 if (response != null) {
122 updateSectionState(response.getData().getStates());
124 logger.debug("null response/status received during the control of component: {}", componentId);
130 private void createPGChannel(String name, String label) {
131 ChannelTypeUID pgmStatus = new ChannelTypeUID(BINDING_ID, "pgm_state");
132 ThingBuilder thingBuilder = editThing();
133 Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), name), "Switch").withLabel(label)
134 .withType(pgmStatus).build();
135 thingBuilder.withChannel(channel);
136 updateThing(thingBuilder.build());
139 private void createStateChannel(String name, String label) {
140 ChannelTypeUID alarmStatus = new ChannelTypeUID(BINDING_ID, "ja100f_alarm_state");
141 ThingBuilder thingBuilder = editThing();
142 Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), name), "String").withLabel(label)
143 .withType(alarmStatus).build();
144 thingBuilder.withChannel(channel);
145 updateThing(thingBuilder.build());
148 private void createThermoChannel(String name, String label) {
149 ChannelTypeUID alarmStatus = new ChannelTypeUID(BINDING_ID, "temperature");
150 ThingBuilder thingBuilder = editThing();
151 Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), name), "Number").withLabel(label)
152 .withType(alarmStatus).build();
153 thingBuilder.withChannel(channel);
154 updateThing(thingBuilder.build());
157 private @Nullable JablotronGetSectionsResponse sendGetSections() {
158 JablotronBridgeHandler handler = getBridgeHandler();
159 if (handler != null) {
160 return handler.sendGetSections(getThing(), alarmName);
165 protected @Nullable JablotronGetPGResponse sendGetProgrammableGates() {
166 JablotronBridgeHandler handler = getBridgeHandler();
167 if (handler != null) {
168 return handler.sendGetProgrammableGates(getThing(), alarmName);
174 protected synchronized boolean updateAlarmStatus() {
175 JablotronBridgeHandler handler = getBridgeHandler();
176 if (handler != null) {
177 updateState(CHANNEL_LAST_CHECK_TIME, getCheckTime());
180 JablotronGetSectionsResponse response = handler.sendGetSections(getThing(), alarmName);
181 if (response != null) {
182 createSectionChannels(response.getData().getSections());
183 updateSectionState(response.getData().getStates());
187 JablotronGetPGResponse resp = handler.sendGetProgrammableGates(getThing(), alarmName);
189 createPGChannels(resp.getData().getProgrammableGates());
190 updateSectionState(resp.getData().getStates());
194 JablotronGetThermoDevicesResponse respThermo = handler.sendGetThermometers(getThing(), alarmName);
195 if (respThermo != null) {
196 createThermoDeviceChannels(respThermo.getData().getThermoDevices());
197 updateThermoState(respThermo.getData().getStates());
201 List<JablotronHistoryDataEvent> events = sendGetEventHistory();
202 if (events != null && !events.isEmpty()) {
203 JablotronHistoryDataEvent event = events.get(0);
204 updateLastEvent(event);
212 private void createPGChannels(List<JablotronSection> programmableGates) {
213 for (JablotronSection gate : programmableGates) {
214 String id = gate.getCloudComponentId().toLowerCase();
215 logger.trace("component id: {} with name: {}", id, gate.getName());
216 Channel channel = getThing().getChannel(id);
217 if (channel == null) {
218 logger.debug("Creating a new channel: {}", id);
219 createPGChannel(id, gate.getName());
224 private void createSectionChannels(List<JablotronSection> sections) {
225 for (JablotronSection section : sections) {
226 String id = section.getCloudComponentId().toLowerCase();
227 logger.trace("component id: {} with name: {}", id, section.getName());
228 Channel channel = getThing().getChannel(id);
229 if (channel == null) {
230 logger.debug("Creating a new channel: {}", id);
231 createStateChannel(id, section.getName());
236 private void createThermoDeviceChannels(List<JablotronThermoDevice> thermoDevices) {
237 for (JablotronThermoDevice device : thermoDevices) {
238 String id = device.getObjectDeviceId().toLowerCase();
239 logger.trace("object device id: {} with name: {}", id, device.getName());
240 Channel channel = getThing().getChannel(id);
241 if (channel == null) {
242 logger.debug("Creating a new channel: {}", id);
243 createThermoChannel(id, device.getName());
248 private void updateSectionState(String section, List<JablotronState> states) {
249 for (JablotronState state : states) {
250 String id = state.getCloudComponentId();
251 if (id.equals(section.toUpperCase())) {
252 updateSection(id, state);
258 private void updateSectionState(List<JablotronState> states) {
259 for (JablotronState state : states) {
260 String id = state.getCloudComponentId();
261 updateSection(id, state);
265 private void updateThermoState(List<JablotronState> states) {
266 for (JablotronState state : states) {
267 logger.debug("updating thermo state: {}", state.getObjectDeviceId());
268 String id = state.getObjectDeviceId();
269 updateSection(id, state);
273 private void updateSection(String id, JablotronState state) {
274 logger.debug("component id: {} with state: {}", id, state.getState());
277 if (id.startsWith("SEC-")) {
278 newState = new StringType(state.getState());
279 } else if (id.startsWith("PG-")) {
280 newState = "ON".equals(state.getState()) ? OnOffType.ON : OnOffType.OFF;
281 } else if (id.startsWith("THM-")) {
282 newState = new QuantityType<>(state.getTemperature(), SIUnits.CELSIUS);
284 logger.debug("unknown component id: {}", id);
287 Channel channel = getThing().getChannel(id.toLowerCase());
288 if (channel != null) {
289 updateState(channel.getUID(), newState);
291 logger.debug("The channel: {} still doesn't exist!", id);