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.time.Instant;
18 import java.time.ZoneId;
19 import java.time.ZonedDateTime;
20 import java.time.format.DateTimeFormatter;
21 import java.util.List;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.jablotron.internal.config.JablotronDeviceConfig;
28 import org.openhab.binding.jablotron.internal.model.JablotronControlResponse;
29 import org.openhab.binding.jablotron.internal.model.JablotronDataUpdateResponse;
30 import org.openhab.binding.jablotron.internal.model.JablotronDiscoveredService;
31 import org.openhab.binding.jablotron.internal.model.JablotronHistoryDataEvent;
32 import org.openhab.binding.jablotron.internal.model.JablotronService;
33 import org.openhab.binding.jablotron.internal.model.JablotronServiceData;
34 import org.openhab.binding.jablotron.internal.model.JablotronServiceDetail;
35 import org.openhab.binding.jablotron.internal.model.JablotronServiceDetailSegment;
36 import org.openhab.core.cache.ExpiringCache;
37 import org.openhab.core.library.types.DateTimeType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.thing.Bridge;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.ThingStatusInfo;
44 import org.openhab.core.thing.binding.BaseThingHandler;
45 import org.openhab.core.types.State;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import com.google.gson.Gson;
52 * The {@link JablotronAlarmHandler} is responsible for handling commands, which are
53 * sent to one of the channels.
55 * @author Ondrej Pecta - Initial contribution
58 public abstract class JablotronAlarmHandler extends BaseThingHandler {
60 private final Logger logger = LoggerFactory.getLogger(getClass());
62 protected final Gson gson = new Gson();
64 protected JablotronDeviceConfig thingConfig = new JablotronDeviceConfig();
66 private String lastWarningTime = "";
68 protected String alarmName = "";
70 private boolean inService = false;
72 protected @Nullable ScheduledFuture<?> future = null;
74 protected @Nullable ExpiringCache<JablotronDataUpdateResponse> dataCache;
75 protected ExpiringCache<List<JablotronHistoryDataEvent>> eventCache;
77 public JablotronAlarmHandler(Thing thing, String alarmName) {
79 this.alarmName = alarmName;
80 eventCache = new ExpiringCache<>(CACHE_TIMEOUT_MS, this::sendGetEventHistory);
84 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
85 super.bridgeStatusChanged(bridgeStatusInfo);
86 if (ThingStatus.OFFLINE == bridgeStatusInfo.getStatus()
87 || ThingStatus.UNINITIALIZED == bridgeStatusInfo.getStatus()) {
90 if (ThingStatus.ONLINE == bridgeStatusInfo.getStatus()) {
96 public void dispose() {
102 public void initialize() {
103 logger.debug("Initializing the alarm: {}", getThing().getUID());
104 thingConfig = getConfigAs(JablotronDeviceConfig.class);
105 future = scheduler.scheduleWithFixedDelay(this::updateAlarmStatus, 1, thingConfig.getRefresh(),
107 updateStatus(ThingStatus.ONLINE);
110 public boolean isInService() {
114 public String getAlarmName() {
118 protected abstract void updateSegmentStatus(JablotronServiceDetailSegment segment);
120 protected void updateSegmentStatus(String segmentName, @Nullable JablotronDataUpdateResponse dataUpdate) {
121 if (dataUpdate == null || !dataUpdate.isStatus()) {
124 List<JablotronServiceData> serviceData = dataUpdate.getData().getServiceData();
125 for (JablotronServiceData data : serviceData) {
126 if (!thingConfig.getServiceId().equals(data.getServiceId())) {
129 List<JablotronService> services = data.getData();
130 for (JablotronService service : services) {
131 JablotronServiceDetail detail = service.getData();
132 for (JablotronServiceDetailSegment segment : detail.getSegments()) {
133 if (segmentName.toUpperCase().equals(segment.getSegmentId())) {
134 updateSegmentStatus(segment);
141 private void cleanup() {
142 logger.debug("doing cleanup...");
143 ScheduledFuture<?> localFuture = future;
144 if (localFuture != null) {
145 localFuture.cancel(true);
149 protected State getCheckTime() {
150 ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
151 return new DateTimeType(zdt);
154 protected synchronized @Nullable JablotronDataUpdateResponse sendGetStatusRequest() {
155 JablotronBridgeHandler handler = getBridgeHandler();
156 if (handler != null) {
157 return handler.sendGetStatusRequest(getThing());
162 protected synchronized boolean updateAlarmStatus() {
163 logger.debug("Updating status of alarm: {}", getThing().getUID());
164 JablotronDataUpdateResponse dataUpdate = sendGetStatusRequest();
165 if (dataUpdate == null) {
169 if (dataUpdate.isStatus()) {
170 updateState(CHANNEL_LAST_CHECK_TIME, getCheckTime());
171 List<JablotronServiceData> serviceData = dataUpdate.getData().getServiceData();
172 for (JablotronServiceData data : serviceData) {
173 if (!thingConfig.getServiceId().equals(data.getServiceId())) {
176 List<JablotronService> services = data.getData();
177 for (JablotronService service : services) {
178 JablotronServiceDetail detail = service.getData();
179 for (JablotronServiceDetailSegment segment : detail.getSegments()) {
180 updateSegmentStatus(segment);
186 logger.debug("Error during alarm status update: {}", dataUpdate.getErrorMessage());
189 List<JablotronHistoryDataEvent> events = sendGetEventHistory();
190 if (events != null && !events.isEmpty()) {
191 JablotronHistoryDataEvent event = events.get(0);
192 updateLastEvent(event);
198 protected @Nullable List<JablotronHistoryDataEvent> sendGetEventHistory() {
199 return sendGetEventHistory(alarmName);
202 private @Nullable List<JablotronHistoryDataEvent> sendGetEventHistory(String alarm) {
203 JablotronBridgeHandler handler = getBridgeHandler();
204 if (handler != null) {
205 return handler.sendGetEventHistory(getThing(), alarm);
210 protected void updateLastEvent(JablotronHistoryDataEvent event) {
211 updateState(CHANNEL_LAST_EVENT_TIME, new DateTimeType(getZonedDateTime(event.getDate())));
212 updateState(CHANNEL_LAST_EVENT, new StringType(event.getEventText()));
213 updateState(CHANNEL_LAST_EVENT_CLASS, new StringType(event.getIconType()));
214 updateState(CHANNEL_LAST_EVENT_INVOKER, new StringType(event.getInvokerName()));
216 // oasis does not have sections
217 if (getThing().getChannel(CHANNEL_LAST_EVENT_SECTION) != null) {
218 updateState(CHANNEL_LAST_EVENT_SECTION, new StringType(event.getSectionName()));
222 protected void updateEventChannel(String channel) {
223 List<JablotronHistoryDataEvent> events = eventCache.getValue();
224 if (events != null && !events.isEmpty()) {
225 JablotronHistoryDataEvent event = events.get(0);
227 case CHANNEL_LAST_EVENT_TIME:
228 updateState(CHANNEL_LAST_EVENT_TIME, new DateTimeType(getZonedDateTime(event.getDate())));
230 case CHANNEL_LAST_EVENT:
231 updateState(CHANNEL_LAST_EVENT, new StringType(event.getEventText()));
233 case CHANNEL_LAST_EVENT_CLASS:
234 updateState(CHANNEL_LAST_EVENT_CLASS, new StringType(event.getIconType()));
236 case CHANNEL_LAST_EVENT_INVOKER:
237 updateState(CHANNEL_LAST_EVENT_INVOKER, new StringType(event.getInvokerName()));
239 case CHANNEL_LAST_EVENT_SECTION:
240 updateState(CHANNEL_LAST_EVENT_SECTION, new StringType(event.getSectionName()));
246 public ZonedDateTime getZonedDateTime(String date) {
247 return ZonedDateTime.parse(date.substring(0, 22) + ":" + date.substring(22, 24),
248 DateTimeFormatter.ISO_DATE_TIME);
251 protected @Nullable JablotronControlResponse sendUserCode(String section, String key, String status, String code) {
252 JablotronBridgeHandler handler = getBridgeHandler();
253 if (handler != null) {
254 return handler.sendUserCode(getThing(), section, key, status, code);
259 protected @Nullable JablotronBridgeHandler getBridgeHandler() {
260 Bridge br = getBridge();
261 if (br != null && br.getHandler() != null) {
262 return (JablotronBridgeHandler) br.getHandler();
267 public void setStatus(ThingStatus status, ThingStatusDetail detail, String message) {
268 updateStatus(status, detail, message);
271 public void triggerAlarm(JablotronDiscoveredService service) {
272 if (!service.getWarningTime().equals(lastWarningTime)) {
273 logger.debug("Service id: {} is triggering an alarm: {}", thing.getUID().getId(), service.getWarning());
274 lastWarningTime = service.getWarningTime();
275 triggerChannel(CHANNEL_ALARM, service.getWarning());
279 public void setInService(boolean inService) {
280 this.inService = inService;