2 * Copyright (c) 2010-2021 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.dscalarm.internal.handler;
15 import static org.openhab.binding.dscalarm.internal.DSCAlarmBindingConstants.*;
17 import java.text.ParseException;
18 import java.text.SimpleDateFormat;
19 import java.util.Date;
20 import java.util.EventObject;
21 import java.util.List;
23 import org.openhab.binding.dscalarm.internal.DSCAlarmCode;
24 import org.openhab.binding.dscalarm.internal.DSCAlarmEvent;
25 import org.openhab.binding.dscalarm.internal.DSCAlarmMessage;
26 import org.openhab.binding.dscalarm.internal.DSCAlarmMessage.DSCAlarmMessageInfoType;
27 import org.openhab.core.library.types.DateTimeType;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.StringType;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * This is a class for handling a Panel type Thing.
41 * @author Russell Stephens - Initial Contribution
43 public class PanelThingHandler extends DSCAlarmBaseThingHandler {
45 private static final int PANEL_COMMAND_POLL = 0;
46 private static final int PANEL_COMMAND_STATUS_REPORT = 1;
47 private static final int PANEL_COMMAND_LABELS_REQUEST = 2;
48 private static final int PANEL_COMMAND_DUMP_ZONE_TIMERS = 8;
49 private static final int PANEL_COMMAND_SET_TIME_DATE = 10;
50 private static final int PANEL_COMMAND_CODE_SEND = 200;
52 private final Logger logger = LoggerFactory.getLogger(PanelThingHandler.class);
59 public PanelThingHandler(Thing thing) {
61 setDSCAlarmThingType(DSCAlarmThingType.PANEL);
65 public void updateChannel(ChannelUID channelUID, int state, String description) {
66 logger.debug("updateChannel(): Panel Channel UID: {}", channelUID);
73 if (channelUID != null) {
74 switch (channelUID.getId()) {
76 updateState(channelUID, new StringType(description));
78 case PANEL_SYSTEM_ERROR:
79 updateState(channelUID, new StringType(description));
83 SimpleDateFormat sdfReceived = new SimpleDateFormat("hhmmMMddyy");
86 date = sdfReceived.parse(description);
87 } catch (ParseException e) {
88 logger.warn("updateChannel(): Parse Exception occurred while trying to parse date string: {}. ",
93 SimpleDateFormat sdfUpdate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
94 String systemTime = sdfUpdate.format(date);
95 updateState(channelUID, new DateTimeType(systemTime));
99 case PANEL_TIME_STAMP:
100 boolState = state != 0;
101 onOffType = boolState ? OnOffType.ON : OnOffType.OFF;
102 updateState(channelUID, onOffType);
104 case PANEL_TIME_BROADCAST:
105 boolState = state != 0;
106 onOffType = boolState ? OnOffType.ON : OnOffType.OFF;
107 updateState(channelUID, onOffType);
110 updateState(channelUID, new DecimalType(state));
112 case PANEL_TROUBLE_MESSAGE:
113 updateState(channelUID, new StringType(description));
115 case PANEL_TROUBLE_LED:
116 boolState = state != 0;
117 onOffType = boolState ? OnOffType.ON : OnOffType.OFF;
118 updateState(channelUID, onOffType);
120 case PANEL_SERVICE_REQUIRED:
121 trouble = state != 0;
122 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
123 updateState(channelUID, onOffType);
125 case PANEL_AC_TROUBLE:
126 trouble = state != 0;
127 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
128 updateState(channelUID, onOffType);
130 case PANEL_TELEPHONE_TROUBLE:
131 trouble = state != 0;
132 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
133 updateState(channelUID, onOffType);
135 case PANEL_FTC_TROUBLE:
136 trouble = state != 0;
137 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
138 updateState(channelUID, onOffType);
140 case PANEL_ZONE_FAULT:
141 trouble = state != 0;
142 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
143 updateState(channelUID, onOffType);
145 case PANEL_ZONE_TAMPER:
146 trouble = state != 0;
147 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
148 updateState(channelUID, onOffType);
150 case PANEL_ZONE_LOW_BATTERY:
151 trouble = state != 0;
152 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
153 updateState(channelUID, onOffType);
155 case PANEL_TIME_LOSS:
156 trouble = state != 0;
157 onOffType = trouble ? OnOffType.ON : OnOffType.OFF;
158 updateState(channelUID, onOffType);
160 case PANEL_FIRE_KEY_ALARM:
161 trigger = state != 0;
162 onOffType = trigger ? OnOffType.ON : OnOffType.OFF;
163 updateState(channelUID, onOffType);
165 case PANEL_PANIC_KEY_ALARM:
166 trigger = state != 0;
167 onOffType = trigger ? OnOffType.ON : OnOffType.OFF;
168 updateState(channelUID, onOffType);
170 case PANEL_AUX_KEY_ALARM:
171 trigger = state != 0;
172 onOffType = trigger ? OnOffType.ON : OnOffType.OFF;
173 updateState(channelUID, onOffType);
175 case PANEL_AUX_INPUT_ALARM:
176 trigger = state != 0;
177 onOffType = trigger ? OnOffType.ON : OnOffType.OFF;
178 updateState(channelUID, onOffType);
181 logger.debug("updateChannel(): Panel Channel not updated - {}.", channelUID);
188 public void handleCommand(ChannelUID channelUID, Command command) {
189 logger.debug("handleCommand(): Command Received - {} {}.", channelUID, command);
191 if (command instanceof RefreshType) {
195 if (dscAlarmBridgeHandler != null && dscAlarmBridgeHandler.isConnected()) {
198 switch (channelUID.getId()) {
200 cmd = Integer.parseInt(command.toString());
201 handlePanelCommand(cmd);
202 updateState(channelUID, new StringType(String.valueOf(-1)));
204 case PANEL_TIME_STAMP:
205 if (command instanceof OnOffType) {
206 cmd = command == OnOffType.ON ? 1 : 0;
207 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.TimeStampControl, String.valueOf(cmd));
208 updateState(channelUID, (OnOffType) command);
211 case PANEL_TIME_BROADCAST:
212 if (command instanceof OnOffType) {
213 cmd = command == OnOffType.ON ? 1 : 0;
214 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.TimeDateBroadcastControl, String.valueOf(cmd));
215 updateState(channelUID, (OnOffType) command);
225 * Method to handle PANEL_COMMAND
229 private void handlePanelCommand(int cmd) {
231 case PANEL_COMMAND_POLL:
232 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.Poll);
234 case PANEL_COMMAND_STATUS_REPORT:
235 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.StatusReport);
237 case PANEL_COMMAND_LABELS_REQUEST:
238 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.LabelsRequest);
240 case PANEL_COMMAND_DUMP_ZONE_TIMERS:
241 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.DumpZoneTimers);
243 case PANEL_COMMAND_SET_TIME_DATE:
244 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.SetTimeDate);
246 case PANEL_COMMAND_CODE_SEND:
247 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.CodeSend, getUserCode());
255 * Method to set the time stamp state.
259 private void setTimeStampState(String timeStamp) {
261 ChannelUID channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME_STAMP);
263 boolean isTimeStamp = timeStamp != "";
265 if ((timeStamp == "" && !isTimeStamp) || (timeStamp != "" && isTimeStamp)) {
266 logger.debug("setTimeStampState(): Already Set: {}", timeStamp);
268 } else if (timeStamp != "") {
272 updateChannel(channelUID, state, "");
276 * Method to set Channel PANEL_SYSTEM_ERROR.
279 * @param dscAlarmMessage
281 private void panelSystemError(DSCAlarmMessage dscAlarmMessage) {
282 ChannelUID channelUID = new ChannelUID(getThing().getUID(), PANEL_SYSTEM_ERROR);
283 int systemErrorCode = 0;
284 String systemErrorDescription = "";
286 if (dscAlarmMessage != null) {
287 systemErrorCode = Integer.parseInt(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA));
288 switch (systemErrorCode) {
290 systemErrorDescription = "Receive Buffer Overrun";
293 systemErrorDescription = "Receive Buffer Overflow";
296 systemErrorDescription = "Transmit Buffer Overflow";
299 systemErrorDescription = "Keybus Transmit Buffer Overrun";
302 systemErrorDescription = "Keybus Transmit Time Timeout";
305 systemErrorDescription = "Keybus Transmit Mode Timeout";
308 systemErrorDescription = "Keybus Transmit Keystring Timeout";
311 systemErrorDescription = "Keybus Interface Not Functioning";
314 systemErrorDescription = "Keybus Busy - Attempting to Disarm or Arm with user code";
317 systemErrorDescription = "Keybus Busy – Lockout";
320 systemErrorDescription = "Keybus Busy – Installers Mode";
323 systemErrorDescription = "Keybus Busy - General Busy";
326 systemErrorDescription = "API Command Syntax Error";
329 systemErrorDescription = "API Command Partition Error - Requested Partition is out of bounds";
332 systemErrorDescription = "API Command Not Supported";
335 systemErrorDescription = "API System Not Armed - Sent in response to a disarm command";
338 systemErrorDescription = "API System Not Ready to Arm - System is either not-secure, in exit-delay, or already armed";
341 systemErrorDescription = "API Command Invalid Length";
344 systemErrorDescription = "API User Code not Required";
347 systemErrorDescription = "API Invalid Characters in Command - No alpha characters are allowed except for checksum";
350 systemErrorDescription = "API Virtual Keypad is Disabled";
353 systemErrorDescription = "API Not Valid Parameter";
356 systemErrorDescription = "API Keypad Does Not Come Out of Blank Mode";
359 systemErrorDescription = "API IT-100 is Already in Thermostat Menu";
362 systemErrorDescription = "API IT-100 is NOT in Thermostat Menu";
365 systemErrorDescription = "API No Response From Thermostat or Escort Module";
369 systemErrorDescription = "No Error";
375 String errorMessage = String.format("%03d", systemErrorCode) + ": " + systemErrorDescription;
376 channelUID = new ChannelUID(getThing().getUID(), PANEL_SYSTEM_ERROR);
377 updateState(channelUID, new StringType(errorMessage));
381 * Handle Verbose Trouble Status events for the EyezOn Envisalink 3/2DS DSC Alarm Interface.
385 private void verboseTroubleStatusHandler(EventObject event) {
386 DSCAlarmEvent dscAlarmEvent = (DSCAlarmEvent) event;
387 DSCAlarmMessage dscAlarmMessage = dscAlarmEvent.getDSCAlarmMessage();
388 String[] channelTypes = { PANEL_SERVICE_REQUIRED, PANEL_AC_TROUBLE, PANEL_TELEPHONE_TROUBLE, PANEL_FTC_TROUBLE,
389 PANEL_ZONE_FAULT, PANEL_ZONE_TAMPER, PANEL_ZONE_LOW_BATTERY, PANEL_TIME_LOSS };
392 ChannelUID channelUID = null;
394 int bitField = Integer.decode("0x" + dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA));
395 int[] masks = { 1, 2, 4, 8, 16, 32, 64, 128 };
396 int[] bits = new int[8];
398 for (int i = 0; i < 8; i++) {
399 bits[i] = bitField & masks[i];
401 channel = channelTypes[i];
404 channelUID = new ChannelUID(getThing().getUID(), channel);
405 updateChannel(channelUID, bits[i] != 0 ? 1 : 0, "");
411 * Restores all partitions that are in alarm after special panel alarm conditions have been restored.
413 * @param dscAlarmCode
415 private void restorePartitionsInAlarm(DSCAlarmCode dscAlarmCode) {
416 logger.debug("restorePartitionsInAlarm(): DSC Alarm Code: {}!", dscAlarmCode.toString());
418 ChannelUID channelUID = null;
420 if (dscAlarmCode == DSCAlarmCode.FireKeyRestored || dscAlarmCode == DSCAlarmCode.AuxiliaryKeyRestored
421 || dscAlarmCode == DSCAlarmCode.PanicKeyRestored
422 || dscAlarmCode == DSCAlarmCode.AuxiliaryInputAlarmRestored) {
423 List<Thing> things = dscAlarmBridgeHandler.getThing().getThings();
424 for (Thing thg : things) {
425 if (thg.getThingTypeUID().equals(PARTITION_THING_TYPE)) {
426 DSCAlarmBaseThingHandler handler = (DSCAlarmBaseThingHandler) thg.getHandler();
427 if (handler != null) {
428 channelUID = new ChannelUID(thg.getUID(), PARTITION_IN_ALARM);
429 handler.updateChannel(channelUID, 0, "");
431 logger.debug("restorePartitionsInAlarm(): Partition In Alarm Restored: {}!", thg.getUID());
439 public void dscAlarmEventReceived(EventObject event, Thing thing) {
441 DSCAlarmEvent dscAlarmEvent = (DSCAlarmEvent) event;
442 DSCAlarmMessage dscAlarmMessage = dscAlarmEvent.getDSCAlarmMessage();
443 String dscAlarmMessageData = dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA);
444 setTimeStampState(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.TIME_STAMP));
446 if (getThing() == thing) {
447 ChannelUID channelUID = null;
448 DSCAlarmCode dscAlarmCode = DSCAlarmCode
449 .getDSCAlarmCodeValue(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.CODE));
450 logger.debug("dscAlarmEventRecieved(): Thing - {} Command - {}", thing.getUID(), dscAlarmCode);
454 switch (dscAlarmCode) {
455 case CommandAcknowledge: /* 500 */
457 case SystemError: /* 502 */
458 int errorCode = Integer.parseInt(dscAlarmMessageData);
460 if (errorCode == 23 || errorCode == 24) {
461 List<Thing> things = dscAlarmBridgeHandler.getThing().getThings();
462 for (Thing thg : things) {
463 if (thg.getThingTypeUID().equals(PARTITION_THING_TYPE)) {
464 DSCAlarmBaseThingHandler handler = (DSCAlarmBaseThingHandler) thg.getHandler();
465 if (handler != null) {
466 channelUID = new ChannelUID(thg.getUID(), PARTITION_ARM_MODE);
467 handler.updateChannel(channelUID, 0, "");
473 panelSystemError(dscAlarmMessage);
475 case TimeDateBroadcast: /* 550 */
476 channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME);
477 String panelTime = dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA);
478 updateChannel(channelUID, state, panelTime);
480 channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME_BROADCAST);
481 updateChannel(channelUID, 1, "");
483 case FireKeyAlarm: /* 621 */
485 case FireKeyRestored: /* 622 */
486 channelUID = new ChannelUID(getThing().getUID(), PANEL_FIRE_KEY_ALARM);
487 updateChannel(channelUID, state, "");
488 restorePartitionsInAlarm(dscAlarmCode);
490 case AuxiliaryKeyAlarm: /* 623 */
492 case AuxiliaryKeyRestored: /* 624 */
493 channelUID = new ChannelUID(getThing().getUID(), PANEL_AUX_KEY_ALARM);
494 updateChannel(channelUID, state, "");
495 restorePartitionsInAlarm(dscAlarmCode);
497 case PanicKeyAlarm: /* 625 */
499 case PanicKeyRestored: /* 626 */
500 channelUID = new ChannelUID(getThing().getUID(), PANEL_PANIC_KEY_ALARM);
501 updateChannel(channelUID, state, "");
502 restorePartitionsInAlarm(dscAlarmCode);
504 case AuxiliaryInputAlarm: /* 631 */
506 case AuxiliaryInputAlarmRestored: /* 632 */
507 channelUID = new ChannelUID(getThing().getUID(), PANEL_AUX_INPUT_ALARM);
508 updateChannel(channelUID, state, "");
509 restorePartitionsInAlarm(dscAlarmCode);
511 case TroubleLEDOn: /* 840 */
512 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_LED);
513 updateChannel(channelUID, 1, "");
515 case TroubleLEDOff: /* 841 */
516 channelUID = new ChannelUID(getThing().getUID(), PANEL_SERVICE_REQUIRED);
517 updateChannel(channelUID, 0, "");
519 channelUID = new ChannelUID(getThing().getUID(), PANEL_AC_TROUBLE);
520 updateChannel(channelUID, 0, "");
522 channelUID = new ChannelUID(getThing().getUID(), PANEL_TELEPHONE_TROUBLE);
523 updateChannel(channelUID, 0, "");
525 channelUID = new ChannelUID(getThing().getUID(), PANEL_FTC_TROUBLE);
526 updateChannel(channelUID, 0, "");
528 channelUID = new ChannelUID(getThing().getUID(), PANEL_ZONE_FAULT);
529 updateChannel(channelUID, 0, "");
531 channelUID = new ChannelUID(getThing().getUID(), PANEL_ZONE_TAMPER);
532 updateChannel(channelUID, 0, "");
534 channelUID = new ChannelUID(getThing().getUID(), PANEL_ZONE_LOW_BATTERY);
535 updateChannel(channelUID, 0, "");
537 channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME_LOSS);
538 updateChannel(channelUID, 0, "");
540 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_LED);
541 updateChannel(channelUID, 0, "");
543 case PanelBatteryTrouble: /* 800 */
544 case PanelACTrouble: /* 802 */
545 case SystemBellTrouble: /* 806 */
546 case TLMLine1Trouble: /* 810 */
547 case TLMLine2Trouble: /* 812 */
548 case FTCTrouble: /* 814 */
549 case GeneralDeviceLowBattery: /* 821 */
550 case WirelessKeyLowBatteryTrouble: /* 825 */
551 case HandheldKeypadLowBatteryTrouble: /* 827 */
552 case GeneralSystemTamper: /* 829 */
553 case HomeAutomationTrouble: /* 831 */
554 case KeybusFault: /* 896 */
555 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_MESSAGE);
556 updateChannel(channelUID, 0,
557 dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DESCRIPTION));
559 case PanelBatteryTroubleRestore: /* 801 */
560 case PanelACRestore: /* 803 */
561 case SystemBellTroubleRestore: /* 807 */
562 case TLMLine1TroubleRestore: /* 811 */
563 case TLMLine2TroubleRestore: /* 813 */
564 case GeneralDeviceLowBatteryRestore: /* 822 */
565 case WirelessKeyLowBatteryTroubleRestore: /* 826 */
566 case HandheldKeypadLowBatteryTroubleRestore: /* 828 */
567 case GeneralSystemTamperRestore: /* 830 */
568 case HomeAutomationTroubleRestore: /* 832 */
569 case KeybusFaultRestore: /* 897 */
570 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_MESSAGE);
571 updateChannel(channelUID, 0, "");
573 case VerboseTroubleStatus: /* 849 */
574 verboseTroubleStatusHandler(event);
576 case CodeRequired: /* 900 */
577 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.CodeSend, getUserCode());