]> git.basschouten.com Git - openhab-addons.git/blob
14a063d481e5bf1d082dbcbcf522e0ee06170b42
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.digiplex.internal.communication;
14
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;
27
28 /**
29  * Resolves serial messages to appropriate classes
30  *
31  * @author Robert Michalak - Initial contribution
32  *
33  */
34 @NonNullByDefault
35 public class DigiplexResponseResolver {
36
37     private static final String OK = "&ok";
38     // TODO: handle failures
39     private static final String FAIL = "&fail";
40
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);
44         }
45
46         int zoneNo, areaNo;
47         String commandType = message.substring(0, 2);
48         switch (commandType) {
49             case "CO": // communication status
50                 if (message.contains(FAIL)) {
51                     return CommunicationStatus.FAILURE;
52                 } else {
53                     return CommunicationStatus.OK;
54                 }
55             case "ZL": // zone label
56                 zoneNo = Integer.valueOf(message.substring(2, 5));
57                 if (message.contains(FAIL)) {
58                     return ZoneLabelResponse.failure(zoneNo);
59                 } else {
60                     return ZoneLabelResponse.success(zoneNo, message.substring(5).trim());
61                 }
62             case "AL": // area label
63                 areaNo = Integer.valueOf(message.substring(2, 5));
64                 if (message.contains(FAIL)) {
65                     return AreaLabelResponse.failure(areaNo);
66                 } else {
67                     return AreaLabelResponse.success(areaNo, message.substring(5).trim());
68                 }
69             case "RZ": // zone status
70                 zoneNo = Integer.valueOf(message.substring(2, 5));
71                 if (message.contains(FAIL)) {
72                     return ZoneStatusResponse.failure(zoneNo);
73                 } else {
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
80                 }
81             case "RA": // area status
82                 areaNo = Integer.valueOf(message.substring(2, 5));
83                 if (message.contains(FAIL)) {
84                     return AreaStatusResponse.failure(areaNo);
85                 } else {
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
94                 }
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));
101                 } else {
102                     return AreaArmDisarmResponse.success(areaNo, ArmDisarmType.fromMessage(commandType));
103                 }
104             case "UL": // user label
105             case "PG": // PGM events
106             default:
107                 if (message.startsWith("G")) {
108                     return resolveSystemEvent(message);
109                 } else {
110                     return new UnknownResponse(message);
111                 }
112         }
113     }
114
115     private static boolean toBoolean(char value) {
116         if (value == 'O') {
117             return false;
118         } else {
119             return true;
120         }
121     }
122
123     private static DigiplexResponse resolveSystemEvent(String message) {
124         int eventGroup = Integer.parseInt(message.substring(1, 4));
125         int eventNumber = Integer.parseInt(message.substring(5, 8));
126         int areaNumber = Integer.parseInt(message.substring(9, 12));
127         switch (eventGroup) {
128             case 0:
129                 return new ZoneStatusEvent(eventNumber, ZoneStatus.CLOSED, areaNumber);
130             case 1:
131                 return new ZoneStatusEvent(eventNumber, ZoneStatus.OPEN, areaNumber);
132             case 2:
133                 return new ZoneStatusEvent(eventNumber, ZoneStatus.TAMPERED, areaNumber);
134             case 3:
135                 return new ZoneStatusEvent(eventNumber, ZoneStatus.FIRE_LOOP_TROUBLE, areaNumber);
136             case 8:
137                 return new ZoneEvent(eventNumber, ZoneEventType.TX_DELAY_ZONE_ALARM, areaNumber);
138             case 13:
139             case 14:
140             case 15:
141             case 16:
142             case 17:
143             case 18:
144                 return new AreaEvent(AreaEventType.DISARMED, areaNumber);
145             case 23:
146                 return new ZoneEvent(eventNumber, ZoneEventType.BYPASSED, areaNumber);
147             case 24:
148                 return new ZoneEvent(eventNumber, ZoneEventType.ALARM, areaNumber);
149             case 25:
150                 return new ZoneEvent(eventNumber, ZoneEventType.FIRE_ALARM, areaNumber);
151             case 26:
152                 return new ZoneEvent(eventNumber, ZoneEventType.ALARM_RESTORE, areaNumber);
153             case 27:
154                 return new ZoneEvent(eventNumber, ZoneEventType.FIRE_ALARM_RESTORE, areaNumber);
155             case 30:
156                 return new SpecialAlarmEvent(areaNumber, SpecialAlarmType.fromMessage(eventNumber));
157             case 32:
158                 return new ZoneEvent(eventNumber, ZoneEventType.SHUTDOWN, areaNumber);
159             case 33:
160                 return new ZoneEvent(eventNumber, ZoneEventType.TAMPER, areaNumber);
161             case 34:
162                 return new ZoneEvent(eventNumber, ZoneEventType.TAMPER_RESTORE, areaNumber);
163             case 36:
164                 return new TroubleEvent(TroubleType.fromEventNumber(eventNumber), TroubleStatus.TROUBLE_STARTED,
165                         areaNumber);
166             case 37:
167                 return new TroubleEvent(TroubleType.fromEventNumber(eventNumber), TroubleStatus.TROUBLE_RESTORED,
168                         areaNumber);
169             case 41:
170                 return new ZoneEvent(eventNumber, ZoneEventType.LOW_BATTERY, areaNumber);
171             case 42:
172                 return new ZoneEvent(eventNumber, ZoneEventType.SUPERVISION_TROUBLE, areaNumber);
173             case 43:
174                 return new ZoneEvent(eventNumber, ZoneEventType.LOW_BATTERY_RESTORE, areaNumber);
175             case 44:
176                 return new ZoneEvent(eventNumber, ZoneEventType.SUPERVISION_TROUBLE_RESTORE, areaNumber);
177             case 55:
178                 return new ZoneEvent(eventNumber, ZoneEventType.INTELLIZONE_TRIGGERED, areaNumber);
179             case 64:
180                 switch (eventNumber) {
181                     case 0:
182                         return new AreaEvent(AreaEventType.ARMED, areaNumber);
183                     case 1:
184                         return new AreaEvent(AreaEventType.ARMED_FORCE, areaNumber);
185                     case 2:
186                         return new AreaEvent(AreaEventType.ARMED_STAY, areaNumber);
187                     case 3:
188                         return new AreaEvent(AreaEventType.ARMED_INSTANT, areaNumber);
189                     case 4:
190                         return new AreaEvent(AreaEventType.ALARM_STROBE, areaNumber);
191                     case 5:
192                         return new AreaEvent(AreaEventType.ALARM_SILENT, areaNumber);
193                     case 6:
194                         return new AreaEvent(AreaEventType.ALARM_AUDIBLE, areaNumber);
195                     case 7:
196                         return new AreaEvent(AreaEventType.ALARM_FIRE, areaNumber);
197                 }
198                 break;
199             case 65:
200                 switch (eventNumber) {
201                     case 0:
202                         return new AreaEvent(AreaEventType.READY, areaNumber);
203                     case 1:
204                         return new AreaEvent(AreaEventType.EXIT_DELAY, areaNumber);
205                     case 2:
206                         return new AreaEvent(AreaEventType.ENTRY_DELAY, areaNumber);
207                     case 3:
208                         return new AreaEvent(AreaEventType.SYSTEM_IN_TROUBLE, areaNumber);
209                     case 4:
210                         return new AreaEvent(AreaEventType.ALARM_IN_MEMORY, areaNumber);
211                     case 5:
212                         return new AreaEvent(AreaEventType.ZONES_BYPASSED, areaNumber);
213                 }
214         }
215         return new GenericEvent(eventGroup, eventNumber, areaNumber);
216     }
217 }