2 * Copyright (c) 2010-2022 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) {
260 if (timeStamp != null) {
261 ChannelUID channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME_STAMP);
262 int state = timeStamp.isEmpty() ? 0 : 1;
263 updateChannel(channelUID, state, "");
268 * Method to set Channel PANEL_SYSTEM_ERROR.
271 * @param dscAlarmMessage
273 private void panelSystemError(DSCAlarmMessage dscAlarmMessage) {
274 ChannelUID channelUID = new ChannelUID(getThing().getUID(), PANEL_SYSTEM_ERROR);
275 int systemErrorCode = 0;
276 String systemErrorDescription = "";
278 if (dscAlarmMessage != null) {
279 systemErrorCode = Integer.parseInt(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA));
280 switch (systemErrorCode) {
282 systemErrorDescription = "Receive Buffer Overrun";
285 systemErrorDescription = "Receive Buffer Overflow";
288 systemErrorDescription = "Transmit Buffer Overflow";
291 systemErrorDescription = "Keybus Transmit Buffer Overrun";
294 systemErrorDescription = "Keybus Transmit Time Timeout";
297 systemErrorDescription = "Keybus Transmit Mode Timeout";
300 systemErrorDescription = "Keybus Transmit Keystring Timeout";
303 systemErrorDescription = "Keybus Interface Not Functioning";
306 systemErrorDescription = "Keybus Busy - Attempting to Disarm or Arm with user code";
309 systemErrorDescription = "Keybus Busy – Lockout";
312 systemErrorDescription = "Keybus Busy – Installers Mode";
315 systemErrorDescription = "Keybus Busy - General Busy";
318 systemErrorDescription = "API Command Syntax Error";
321 systemErrorDescription = "API Command Partition Error - Requested Partition is out of bounds";
324 systemErrorDescription = "API Command Not Supported";
327 systemErrorDescription = "API System Not Armed - Sent in response to a disarm command";
330 systemErrorDescription = "API System Not Ready to Arm - System is either not-secure, in exit-delay, or already armed";
333 systemErrorDescription = "API Command Invalid Length";
336 systemErrorDescription = "API User Code not Required";
339 systemErrorDescription = "API Invalid Characters in Command - No alpha characters are allowed except for checksum";
342 systemErrorDescription = "API Virtual Keypad is Disabled";
345 systemErrorDescription = "API Not Valid Parameter";
348 systemErrorDescription = "API Keypad Does Not Come Out of Blank Mode";
351 systemErrorDescription = "API IT-100 is Already in Thermostat Menu";
354 systemErrorDescription = "API IT-100 is NOT in Thermostat Menu";
357 systemErrorDescription = "API No Response From Thermostat or Escort Module";
361 systemErrorDescription = "No Error";
367 String errorMessage = String.format("%03d", systemErrorCode) + ": " + systemErrorDescription;
368 channelUID = new ChannelUID(getThing().getUID(), PANEL_SYSTEM_ERROR);
369 updateState(channelUID, new StringType(errorMessage));
373 * Handle Verbose Trouble Status events for the EyezOn Envisalink 3/2DS DSC Alarm Interface.
377 private void verboseTroubleStatusHandler(EventObject event) {
378 DSCAlarmEvent dscAlarmEvent = (DSCAlarmEvent) event;
379 DSCAlarmMessage dscAlarmMessage = dscAlarmEvent.getDSCAlarmMessage();
380 String[] channelTypes = { PANEL_SERVICE_REQUIRED, PANEL_AC_TROUBLE, PANEL_TELEPHONE_TROUBLE, PANEL_FTC_TROUBLE,
381 PANEL_ZONE_FAULT, PANEL_ZONE_TAMPER, PANEL_ZONE_LOW_BATTERY, PANEL_TIME_LOSS };
384 ChannelUID channelUID = null;
387 int bitField = Integer.decode("0x" + dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA));
388 int[] masks = { 1, 2, 4, 8, 16, 32, 64, 128 };
389 int[] bits = new int[bitCount];
391 for (int i = 0; i < bitCount; i++) {
392 channelUID = new ChannelUID(getThing().getUID(), channelTypes[i]);
393 bits[i] = bitField & masks[i];
394 updateChannel(channelUID, bits[i] != 0 ? 1 : 0, "");
399 * Restores all partitions that are in alarm after special panel alarm conditions have been restored.
401 * @param dscAlarmCode
403 private void restorePartitionsInAlarm(DSCAlarmCode dscAlarmCode) {
404 logger.debug("restorePartitionsInAlarm(): DSC Alarm Code: {}!", dscAlarmCode.toString());
406 ChannelUID channelUID = null;
408 if (dscAlarmCode == DSCAlarmCode.FireKeyRestored || dscAlarmCode == DSCAlarmCode.AuxiliaryKeyRestored
409 || dscAlarmCode == DSCAlarmCode.PanicKeyRestored
410 || dscAlarmCode == DSCAlarmCode.AuxiliaryInputAlarmRestored) {
411 List<Thing> things = dscAlarmBridgeHandler.getThing().getThings();
412 for (Thing thg : things) {
413 if (thg.getThingTypeUID().equals(PARTITION_THING_TYPE)) {
414 DSCAlarmBaseThingHandler handler = (DSCAlarmBaseThingHandler) thg.getHandler();
415 if (handler != null) {
416 channelUID = new ChannelUID(thg.getUID(), PARTITION_IN_ALARM);
417 handler.updateChannel(channelUID, 0, "");
419 logger.debug("restorePartitionsInAlarm(): Partition In Alarm Restored: {}!", thg.getUID());
427 @SuppressWarnings("PMD.CompareObjectsWithEquals")
428 public void dscAlarmEventReceived(EventObject event, Thing thing) {
430 DSCAlarmEvent dscAlarmEvent = (DSCAlarmEvent) event;
431 DSCAlarmMessage dscAlarmMessage = dscAlarmEvent.getDSCAlarmMessage();
432 String dscAlarmMessageData = dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA);
433 setTimeStampState(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.TIME_STAMP));
435 if (getThing() == thing) {
436 ChannelUID channelUID = null;
437 DSCAlarmCode dscAlarmCode = DSCAlarmCode
438 .getDSCAlarmCodeValue(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.CODE));
439 logger.debug("dscAlarmEventRecieved(): Thing - {} Command - {}", thing.getUID(), dscAlarmCode);
443 switch (dscAlarmCode) {
444 case CommandAcknowledge: /* 500 */
446 case SystemError: /* 502 */
447 int errorCode = Integer.parseInt(dscAlarmMessageData);
449 if (errorCode == 23 || errorCode == 24) {
450 List<Thing> things = dscAlarmBridgeHandler.getThing().getThings();
451 for (Thing thg : things) {
452 if (thg.getThingTypeUID().equals(PARTITION_THING_TYPE)) {
453 DSCAlarmBaseThingHandler handler = (DSCAlarmBaseThingHandler) thg.getHandler();
454 if (handler != null) {
455 channelUID = new ChannelUID(thg.getUID(), PARTITION_ARM_MODE);
456 handler.updateChannel(channelUID, 0, "");
462 panelSystemError(dscAlarmMessage);
464 case TimeDateBroadcast: /* 550 */
465 channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME);
466 String panelTime = dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA);
467 updateChannel(channelUID, state, panelTime);
469 channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME_BROADCAST);
470 updateChannel(channelUID, 1, "");
472 case FireKeyAlarm: /* 621 */
474 case FireKeyRestored: /* 622 */
475 channelUID = new ChannelUID(getThing().getUID(), PANEL_FIRE_KEY_ALARM);
476 updateChannel(channelUID, state, "");
477 restorePartitionsInAlarm(dscAlarmCode);
479 case AuxiliaryKeyAlarm: /* 623 */
481 case AuxiliaryKeyRestored: /* 624 */
482 channelUID = new ChannelUID(getThing().getUID(), PANEL_AUX_KEY_ALARM);
483 updateChannel(channelUID, state, "");
484 restorePartitionsInAlarm(dscAlarmCode);
486 case PanicKeyAlarm: /* 625 */
488 case PanicKeyRestored: /* 626 */
489 channelUID = new ChannelUID(getThing().getUID(), PANEL_PANIC_KEY_ALARM);
490 updateChannel(channelUID, state, "");
491 restorePartitionsInAlarm(dscAlarmCode);
493 case AuxiliaryInputAlarm: /* 631 */
495 case AuxiliaryInputAlarmRestored: /* 632 */
496 channelUID = new ChannelUID(getThing().getUID(), PANEL_AUX_INPUT_ALARM);
497 updateChannel(channelUID, state, "");
498 restorePartitionsInAlarm(dscAlarmCode);
500 case TroubleLEDOn: /* 840 */
501 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_LED);
502 updateChannel(channelUID, 1, "");
504 case TroubleLEDOff: /* 841 */
505 channelUID = new ChannelUID(getThing().getUID(), PANEL_SERVICE_REQUIRED);
506 updateChannel(channelUID, 0, "");
508 channelUID = new ChannelUID(getThing().getUID(), PANEL_AC_TROUBLE);
509 updateChannel(channelUID, 0, "");
511 channelUID = new ChannelUID(getThing().getUID(), PANEL_TELEPHONE_TROUBLE);
512 updateChannel(channelUID, 0, "");
514 channelUID = new ChannelUID(getThing().getUID(), PANEL_FTC_TROUBLE);
515 updateChannel(channelUID, 0, "");
517 channelUID = new ChannelUID(getThing().getUID(), PANEL_ZONE_FAULT);
518 updateChannel(channelUID, 0, "");
520 channelUID = new ChannelUID(getThing().getUID(), PANEL_ZONE_TAMPER);
521 updateChannel(channelUID, 0, "");
523 channelUID = new ChannelUID(getThing().getUID(), PANEL_ZONE_LOW_BATTERY);
524 updateChannel(channelUID, 0, "");
526 channelUID = new ChannelUID(getThing().getUID(), PANEL_TIME_LOSS);
527 updateChannel(channelUID, 0, "");
529 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_LED);
530 updateChannel(channelUID, 0, "");
532 case PanelBatteryTrouble: /* 800 */
533 case PanelACTrouble: /* 802 */
534 case SystemBellTrouble: /* 806 */
535 case TLMLine1Trouble: /* 810 */
536 case TLMLine2Trouble: /* 812 */
537 case FTCTrouble: /* 814 */
538 case GeneralDeviceLowBattery: /* 821 */
539 case WirelessKeyLowBatteryTrouble: /* 825 */
540 case HandheldKeypadLowBatteryTrouble: /* 827 */
541 case GeneralSystemTamper: /* 829 */
542 case HomeAutomationTrouble: /* 831 */
543 case KeybusFault: /* 896 */
544 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_MESSAGE);
545 updateChannel(channelUID, 0,
546 dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DESCRIPTION));
548 case PanelBatteryTroubleRestore: /* 801 */
549 case PanelACRestore: /* 803 */
550 case SystemBellTroubleRestore: /* 807 */
551 case TLMLine1TroubleRestore: /* 811 */
552 case TLMLine2TroubleRestore: /* 813 */
553 case GeneralDeviceLowBatteryRestore: /* 822 */
554 case WirelessKeyLowBatteryTroubleRestore: /* 826 */
555 case HandheldKeypadLowBatteryTroubleRestore: /* 828 */
556 case GeneralSystemTamperRestore: /* 830 */
557 case HomeAutomationTroubleRestore: /* 832 */
558 case KeybusFaultRestore: /* 897 */
559 channelUID = new ChannelUID(getThing().getUID(), PANEL_TROUBLE_MESSAGE);
560 updateChannel(channelUID, 0, "");
562 case VerboseTroubleStatus: /* 849 */
563 verboseTroubleStatusHandler(event);
565 case CodeRequired: /* 900 */
566 dscAlarmBridgeHandler.sendCommand(DSCAlarmCode.CodeSend, getUserCode());