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.digiplex.internal.communication;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.openhab.binding.digiplex.internal.communication.events.AreaEvent;
17 import org.openhab.binding.digiplex.internal.communication.events.AreaEventType;
18 import org.openhab.binding.digiplex.internal.communication.events.GenericEvent;
19 import org.openhab.binding.digiplex.internal.communication.events.SpecialAlarmEvent;
20 import org.openhab.binding.digiplex.internal.communication.events.SpecialAlarmType;
21 import org.openhab.binding.digiplex.internal.communication.events.TroubleEvent;
22 import org.openhab.binding.digiplex.internal.communication.events.TroubleStatus;
23 import org.openhab.binding.digiplex.internal.communication.events.TroubleType;
24 import org.openhab.binding.digiplex.internal.communication.events.ZoneEvent;
25 import org.openhab.binding.digiplex.internal.communication.events.ZoneEventType;
26 import org.openhab.binding.digiplex.internal.communication.events.ZoneStatusEvent;
29 * Resolves serial messages to appropriate classes
31 * @author Robert Michalak - Initial contribution
35 public class DigiplexResponseResolver {
37 private static final String OK = "&ok";
38 // TODO: handle failures
39 private static final String FAIL = "&fail";
41 public static DigiplexResponse resolveResponse(String message) {
42 if (message.length() < 4) { // sanity check: try to filter out malformed responses
43 return new UnknownResponse(message);
47 String commandType = message.substring(0, 2);
48 switch (commandType) {
49 case "CO": // communication status
50 if (message.contains(FAIL)) {
51 return CommunicationStatus.FAILURE;
53 return CommunicationStatus.OK;
55 case "ZL": // zone label
56 zoneNo = Integer.valueOf(message.substring(2, 5));
57 if (message.contains(FAIL)) {
58 return ZoneLabelResponse.failure(zoneNo);
60 return ZoneLabelResponse.success(zoneNo, message.substring(5).trim());
62 case "AL": // area label
63 areaNo = Integer.valueOf(message.substring(2, 5));
64 if (message.contains(FAIL)) {
65 return AreaLabelResponse.failure(areaNo);
67 return AreaLabelResponse.success(areaNo, message.substring(5).trim());
69 case "RZ": // zone status
70 zoneNo = Integer.valueOf(message.substring(2, 5));
71 if (message.contains(FAIL)) {
72 return ZoneStatusResponse.failure(zoneNo);
74 return ZoneStatusResponse.success(zoneNo, // zone number
75 ZoneStatus.fromMessage(message.charAt(5)), // status
76 toBoolean(message.charAt(6)), // alarm
77 toBoolean(message.charAt(7)), // fire alarm
78 toBoolean(message.charAt(8)), // supervision lost
79 toBoolean(message.charAt(9))); // battery low
81 case "RA": // area status
82 areaNo = Integer.valueOf(message.substring(2, 5));
83 if (message.contains(FAIL)) {
84 return AreaStatusResponse.failure(areaNo);
86 return AreaStatusResponse.success(areaNo, // zone number
87 AreaStatus.fromMessage(message.charAt(5)), // status
88 toBoolean(message.charAt(6)), // zone in memory
89 toBoolean(message.charAt(7)), // trouble
90 !toBoolean(message.charAt(8)), // ready (note ! in front)
91 toBoolean(message.charAt(9)), // in programming
92 toBoolean(message.charAt(10)), // in alarm
93 toBoolean(message.charAt(11))); // strobe
95 case "AA": // area arm
96 case "AQ": // area quick arm
97 case "AD": // area disarm
98 areaNo = Integer.valueOf(message.substring(2, 5));
99 if (message.contains(FAIL)) {
100 return AreaArmDisarmResponse.failure(areaNo, ArmDisarmType.fromMessage(commandType));
102 return AreaArmDisarmResponse.success(areaNo, ArmDisarmType.fromMessage(commandType));
104 case "UL": // user label
105 case "PG": // PGM events
107 if (message.startsWith("G")) {
108 return resolveSystemEvent(message);
110 return new UnknownResponse(message);
115 private static boolean toBoolean(char value) {
119 private static DigiplexResponse resolveSystemEvent(String message) {
120 int eventGroup = Integer.parseInt(message.substring(1, 4));
121 int eventNumber = Integer.parseInt(message.substring(5, 8));
122 int areaNumber = Integer.parseInt(message.substring(9, 12));
123 switch (eventGroup) {
125 return new ZoneStatusEvent(eventNumber, ZoneStatus.CLOSED, areaNumber);
127 return new ZoneStatusEvent(eventNumber, ZoneStatus.OPEN, areaNumber);
129 return new ZoneStatusEvent(eventNumber, ZoneStatus.TAMPERED, areaNumber);
131 return new ZoneStatusEvent(eventNumber, ZoneStatus.FIRE_LOOP_TROUBLE, areaNumber);
133 return new ZoneEvent(eventNumber, ZoneEventType.TX_DELAY_ZONE_ALARM, areaNumber);
140 return new AreaEvent(AreaEventType.DISARMED, areaNumber);
142 return new ZoneEvent(eventNumber, ZoneEventType.BYPASSED, areaNumber);
144 return new ZoneEvent(eventNumber, ZoneEventType.ALARM, areaNumber);
146 return new ZoneEvent(eventNumber, ZoneEventType.FIRE_ALARM, areaNumber);
148 return new ZoneEvent(eventNumber, ZoneEventType.ALARM_RESTORE, areaNumber);
150 return new ZoneEvent(eventNumber, ZoneEventType.FIRE_ALARM_RESTORE, areaNumber);
152 return new SpecialAlarmEvent(areaNumber, SpecialAlarmType.fromMessage(eventNumber));
154 return new ZoneEvent(eventNumber, ZoneEventType.SHUTDOWN, areaNumber);
156 return new ZoneEvent(eventNumber, ZoneEventType.TAMPER, areaNumber);
158 return new ZoneEvent(eventNumber, ZoneEventType.TAMPER_RESTORE, areaNumber);
160 return new TroubleEvent(TroubleType.fromEventNumber(eventNumber), TroubleStatus.TROUBLE_STARTED,
163 return new TroubleEvent(TroubleType.fromEventNumber(eventNumber), TroubleStatus.TROUBLE_RESTORED,
166 return new ZoneEvent(eventNumber, ZoneEventType.LOW_BATTERY, areaNumber);
168 return new ZoneEvent(eventNumber, ZoneEventType.SUPERVISION_TROUBLE, areaNumber);
170 return new ZoneEvent(eventNumber, ZoneEventType.LOW_BATTERY_RESTORE, areaNumber);
172 return new ZoneEvent(eventNumber, ZoneEventType.SUPERVISION_TROUBLE_RESTORE, areaNumber);
174 return new ZoneEvent(eventNumber, ZoneEventType.INTELLIZONE_TRIGGERED, areaNumber);
176 switch (eventNumber) {
178 return new AreaEvent(AreaEventType.ARMED, areaNumber);
180 return new AreaEvent(AreaEventType.ARMED_FORCE, areaNumber);
182 return new AreaEvent(AreaEventType.ARMED_STAY, areaNumber);
184 return new AreaEvent(AreaEventType.ARMED_INSTANT, areaNumber);
186 return new AreaEvent(AreaEventType.ALARM_STROBE, areaNumber);
188 return new AreaEvent(AreaEventType.ALARM_SILENT, areaNumber);
190 return new AreaEvent(AreaEventType.ALARM_AUDIBLE, areaNumber);
192 return new AreaEvent(AreaEventType.ALARM_FIRE, areaNumber);
196 switch (eventNumber) {
198 return new AreaEvent(AreaEventType.READY, areaNumber);
200 return new AreaEvent(AreaEventType.EXIT_DELAY, areaNumber);
202 return new AreaEvent(AreaEventType.ENTRY_DELAY, areaNumber);
204 return new AreaEvent(AreaEventType.SYSTEM_IN_TROUBLE, areaNumber);
206 return new AreaEvent(AreaEventType.ALARM_IN_MEMORY, areaNumber);
208 return new AreaEvent(AreaEventType.ZONES_BYPASSED, areaNumber);
211 return new GenericEvent(eventGroup, eventNumber, areaNumber);