2 * Copyright (c) 2010-2024 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.verisure.internal.handler;
15 import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*;
17 import java.math.BigDecimal;
18 import java.time.ZoneId;
19 import java.time.ZonedDateTime;
20 import java.util.ArrayList;
21 import java.util.concurrent.TimeUnit;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.verisure.internal.DeviceStatusListener;
26 import org.openhab.binding.verisure.internal.VerisureSession;
27 import org.openhab.binding.verisure.internal.VerisureThingConfiguration;
28 import org.openhab.binding.verisure.internal.dto.VerisureThingDTO;
29 import org.openhab.core.library.types.DateTimeType;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.types.StringType;
32 import org.openhab.core.thing.Bridge;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusInfo;
37 import org.openhab.core.thing.binding.BaseThingHandler;
38 import org.openhab.core.thing.binding.BridgeHandler;
39 import org.openhab.core.types.Command;
40 import org.openhab.core.types.RefreshType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 import com.google.gson.Gson;
47 * Base class and handler for some of the different thing types that Verisure provides.
49 * @author Jarle Hjortland - Initial contribution
50 * @author Jan Gustafsson - Further development
54 public abstract class VerisureThingHandler<T extends VerisureThingDTO> extends BaseThingHandler
55 implements DeviceStatusListener<T> {
57 protected final Logger logger = LoggerFactory.getLogger(VerisureThingHandler.class);
58 protected final Gson gson = new Gson();
59 protected VerisureThingConfiguration config = new VerisureThingConfiguration();
61 public VerisureThingHandler(Thing thing) {
66 public void handleCommand(ChannelUID channelUID, Command command) {
67 logger.debug("VerisureThingHandler handleCommand, channel: {}, command: {}", channelUID, command);
68 if (command instanceof RefreshType) {
69 Bridge bridge = getBridge();
71 BridgeHandler bridgeHandler = bridge.getHandler();
72 if (bridgeHandler != null) {
73 bridgeHandler.handleCommand(channelUID, command);
74 String deviceId = config.getDeviceId();
75 VerisureSession session = getSession();
76 if (session != null) {
78 T thing = session.getVerisureThing(deviceId, getVerisureThingClass());
82 logger.trace("Thing is null!");
85 logger.debug("Session is null!");
88 logger.debug("BridgeHandler is null!");
91 logger.warn("Bridge is null!");
94 logger.warn("Unknown command! {}", command);
99 public void initialize() {
101 config = getConfigAs(VerisureThingConfiguration.class);
102 // Set status to UNKNOWN and let background task set correct status
103 updateStatus(ThingStatus.UNKNOWN);
104 Bridge bridge = getBridge();
105 if (bridge != null) {
106 this.bridgeStatusChanged(bridge.getStatusInfo());
111 public void dispose() {
112 logger.debug("dispose on thing: {}", thing);
113 VerisureSession session = getSession();
114 if (session != null) {
115 session.unregisterDeviceStatusListener(this);
116 session.removeVerisureThingHandler(config.getDeviceId());
121 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
122 if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
123 VerisureSession session = getSession();
124 if (session != null) {
125 String deviceId = config.getDeviceId();
127 T thing = session.getVerisureThing(deviceId, getVerisureThingClass());
131 logger.warn("Please check that you have configured correct deviceId for thing!");
133 session.registerDeviceStatusListener(this);
134 session.setVerisureThingHandler(this, config.getDeviceId());
137 super.bridgeStatusChanged(bridgeStatusInfo);
141 public void onDeviceStateChanged(T thing) {
142 String deviceId = thing.getDeviceId();
143 // Make sure device id is normalized
144 if (config.getDeviceId().equalsIgnoreCase((VerisureThingConfiguration.normalizeDeviceId(deviceId)))) {
149 public abstract void update(T thing);
151 public abstract void updateTriggerChannel(String event);
153 protected void updateInstallationChannels(T thing) {
154 BigDecimal siteId = thing.getSiteId();
155 updateState(CHANNEL_INSTALLATION_ID, new DecimalType(siteId.longValue()));
156 updateState(CHANNEL_INSTALLATION_NAME, new StringType(thing.getSiteName()));
159 protected void updateTriggerChannel(ArrayList<Event> newEvents) {
160 VerisureSession session = getSession();
162 for (Event newEvent : newEvents) {
163 String deviceId = newEvent.getDeviceId();
164 String eventType = newEvent.getEventType();
165 String eventCategory = newEvent.getEventCategory();
166 logger.debug("Trigger event type {}, event category {} for thing {}", eventType, eventCategory, deviceId);
167 if (session != null && eventType != null && deviceId != null) {
168 String deviceIdTransformed = VerisureThingConfiguration.normalizeDeviceId(deviceId);
170 T thing = session.getVerisureThing(deviceIdTransformed);
172 logger.debug("Trigger event {} on deviceId {} on thing {}", eventType, deviceIdTransformed, thing);
173 VerisureThingHandler<?> vth = session.getVerisureThinghandler(deviceIdTransformed);
175 logger.debug("No VerisureThingHandler found for thing {}", thing);
178 String eventTranslation = "UNKNOWN_EVENT_TYPE";
181 eventTranslation = TRIGGER_EVENT_INSTRUSION;
184 eventTranslation = TRIGGER_EVENT_FIRE;
187 eventTranslation = TRIGGER_EVENT_BATTERY_LOW;
190 eventTranslation = TRIGGER_EVENT_BATTERY_RESTORED;
194 eventTranslation = TRIGGER_EVENT_COM_TEST;
197 eventTranslation = TRIGGER_EVENT_COM_FAILURE;
200 eventTranslation = TRIGGER_EVENT_COM_RESTORED;
203 eventTranslation = TRIGGER_EVENT_SABOTAGE_ALARM;
206 eventTranslation = TRIGGER_EVENT_SABOTAGE_RESTORED;
211 eventTranslation = TRIGGER_EVENT_ARM;
217 eventTranslation = TRIGGER_EVENT_DISARM;
223 eventTranslation = TRIGGER_EVENT_LOCK;
226 eventTranslation = TRIGGER_EVENT_LOCK_FAILURE;
232 eventTranslation = TRIGGER_EVENT_UNLOCK;
235 eventTranslation = TRIGGER_EVENT_WATER;
238 eventTranslation = TRIGGER_EVENT_MICE;
240 case "DOORWINDOW_STATE_CHANGE_OPENED":
241 eventTranslation = TRIGGER_EVENT_DOORWINDOW_OPENED;
243 case "DOORWINDOW_STATE_CHANGE_CLOSED":
244 eventTranslation = TRIGGER_EVENT_DOORWINDOW_CLOSED;
246 case "LOCATION_HOME":
247 eventTranslation = TRIGGER_EVENT_LOCATION_HOME;
249 case "LOCATION_AWAY":
250 eventTranslation = TRIGGER_EVENT_LOCATION_AWAY;
253 logger.debug("Unhandled event type: {}, event category: {}", eventType, eventCategory);
255 logger.debug("Schedule vth {} and event {} with delay {}", vth, eventTranslation, delay);
256 scheduler.schedule(new EventTrigger(vth, eventTranslation), delay, TimeUnit.MILLISECONDS);
257 delay = delay + config.getEventTriggerDelay();
259 logger.debug("Thing is null!");
265 protected void updateTimeStamp(@Nullable String lastUpdatedTimeStamp) {
266 updateTimeStamp(lastUpdatedTimeStamp, CHANNEL_TIMESTAMP);
269 protected void updateTimeStamp(@Nullable String lastUpdatedTimeStamp, ChannelUID cuid) {
270 if (lastUpdatedTimeStamp != null) {
272 logger.trace("Parsing date {} for channel {}", lastUpdatedTimeStamp, cuid);
273 ZonedDateTime zdt = ZonedDateTime.parse(lastUpdatedTimeStamp);
274 ZonedDateTime zdtLocal = zdt.withZoneSameInstant(ZoneId.systemDefault());
275 logger.trace("Parsing datetime successful. Using date. {}", new DateTimeType(zdtLocal));
276 updateState(cuid, new DateTimeType(zdtLocal));
277 } catch (IllegalArgumentException e) {
278 logger.warn("Parsing date failed: {}.", e.getMessage(), e);
281 logger.debug("Timestamp is null!");
285 protected void updateTimeStamp(@Nullable String lastUpdatedTimeStamp, String channel) {
286 ChannelUID cuid = new ChannelUID(getThing().getUID(), channel);
287 updateTimeStamp(lastUpdatedTimeStamp, cuid);
290 protected @Nullable VerisureSession getSession() {
291 Bridge bridge = getBridge();
292 if (bridge != null) {
293 VerisureBridgeHandler vbh = (VerisureBridgeHandler) bridge.getHandler();
295 return vbh.getSession();
301 protected void scheduleImmediateRefresh(int refreshDelay) {
302 logger.debug("scheduleImmediateRefresh on thing: {}", thing);
303 Bridge bridge = getBridge();
304 if (bridge != null && bridge.getHandler() != null) {
305 VerisureBridgeHandler vbh = (VerisureBridgeHandler) bridge.getHandler();
307 vbh.scheduleImmediateRefresh(refreshDelay);
312 private class EventTrigger implements Runnable {
313 private @Nullable VerisureThingHandler<?> vth;
314 private @Nullable String event;
316 public EventTrigger(@Nullable VerisureThingHandler<?> vth, @Nullable String event) {
323 logger.debug("Trigger Event {} on {} at time {}", event, vth, ZonedDateTime.now());
324 String localEvent = event;
325 if (vth != null && localEvent != null) {
326 vth.updateTriggerChannel(localEvent);
331 protected class Event {
332 private @Nullable String deviceId;
333 private @Nullable String eventType;
334 private @Nullable String eventCategory;
336 public Event(@Nullable String deviceId, @Nullable String eventType, @Nullable String eventCategory) {
337 this.deviceId = deviceId;
338 this.eventType = eventType;
339 this.eventCategory = eventCategory;
342 public @Nullable String getDeviceId() {
346 public @Nullable String getEventType() {
350 public @Nullable String getEventCategory() {
351 return eventCategory;