]> git.basschouten.com Git - openhab-addons.git/blob
384d00d24179ea480e47282c71eead221cc68401
[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         return value != 'O';
117     }
118
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) {
124             case 0:
125                 return new ZoneStatusEvent(eventNumber, ZoneStatus.CLOSED, areaNumber);
126             case 1:
127                 return new ZoneStatusEvent(eventNumber, ZoneStatus.OPEN, areaNumber);
128             case 2:
129                 return new ZoneStatusEvent(eventNumber, ZoneStatus.TAMPERED, areaNumber);
130             case 3:
131                 return new ZoneStatusEvent(eventNumber, ZoneStatus.FIRE_LOOP_TROUBLE, areaNumber);
132             case 8:
133                 return new ZoneEvent(eventNumber, ZoneEventType.TX_DELAY_ZONE_ALARM, areaNumber);
134             case 13:
135             case 14:
136             case 15:
137             case 16:
138             case 17:
139             case 18:
140                 return new AreaEvent(AreaEventType.DISARMED, areaNumber);
141             case 23:
142                 return new ZoneEvent(eventNumber, ZoneEventType.BYPASSED, areaNumber);
143             case 24:
144                 return new ZoneEvent(eventNumber, ZoneEventType.ALARM, areaNumber);
145             case 25:
146                 return new ZoneEvent(eventNumber, ZoneEventType.FIRE_ALARM, areaNumber);
147             case 26:
148                 return new ZoneEvent(eventNumber, ZoneEventType.ALARM_RESTORE, areaNumber);
149             case 27:
150                 return new ZoneEvent(eventNumber, ZoneEventType.FIRE_ALARM_RESTORE, areaNumber);
151             case 30:
152                 return new SpecialAlarmEvent(areaNumber, SpecialAlarmType.fromMessage(eventNumber));
153             case 32:
154                 return new ZoneEvent(eventNumber, ZoneEventType.SHUTDOWN, areaNumber);
155             case 33:
156                 return new ZoneEvent(eventNumber, ZoneEventType.TAMPER, areaNumber);
157             case 34:
158                 return new ZoneEvent(eventNumber, ZoneEventType.TAMPER_RESTORE, areaNumber);
159             case 36:
160                 return new TroubleEvent(TroubleType.fromEventNumber(eventNumber), TroubleStatus.TROUBLE_STARTED,
161                         areaNumber);
162             case 37:
163                 return new TroubleEvent(TroubleType.fromEventNumber(eventNumber), TroubleStatus.TROUBLE_RESTORED,
164                         areaNumber);
165             case 41:
166                 return new ZoneEvent(eventNumber, ZoneEventType.LOW_BATTERY, areaNumber);
167             case 42:
168                 return new ZoneEvent(eventNumber, ZoneEventType.SUPERVISION_TROUBLE, areaNumber);
169             case 43:
170                 return new ZoneEvent(eventNumber, ZoneEventType.LOW_BATTERY_RESTORE, areaNumber);
171             case 44:
172                 return new ZoneEvent(eventNumber, ZoneEventType.SUPERVISION_TROUBLE_RESTORE, areaNumber);
173             case 55:
174                 return new ZoneEvent(eventNumber, ZoneEventType.INTELLIZONE_TRIGGERED, areaNumber);
175             case 64:
176                 switch (eventNumber) {
177                     case 0:
178                         return new AreaEvent(AreaEventType.ARMED, areaNumber);
179                     case 1:
180                         return new AreaEvent(AreaEventType.ARMED_FORCE, areaNumber);
181                     case 2:
182                         return new AreaEvent(AreaEventType.ARMED_STAY, areaNumber);
183                     case 3:
184                         return new AreaEvent(AreaEventType.ARMED_INSTANT, areaNumber);
185                     case 4:
186                         return new AreaEvent(AreaEventType.ALARM_STROBE, areaNumber);
187                     case 5:
188                         return new AreaEvent(AreaEventType.ALARM_SILENT, areaNumber);
189                     case 6:
190                         return new AreaEvent(AreaEventType.ALARM_AUDIBLE, areaNumber);
191                     case 7:
192                         return new AreaEvent(AreaEventType.ALARM_FIRE, areaNumber);
193                 }
194                 break;
195             case 65:
196                 switch (eventNumber) {
197                     case 0:
198                         return new AreaEvent(AreaEventType.READY, areaNumber);
199                     case 1:
200                         return new AreaEvent(AreaEventType.EXIT_DELAY, areaNumber);
201                     case 2:
202                         return new AreaEvent(AreaEventType.ENTRY_DELAY, areaNumber);
203                     case 3:
204                         return new AreaEvent(AreaEventType.SYSTEM_IN_TROUBLE, areaNumber);
205                     case 4:
206                         return new AreaEvent(AreaEventType.ALARM_IN_MEMORY, areaNumber);
207                     case 5:
208                         return new AreaEvent(AreaEventType.ZONES_BYPASSED, areaNumber);
209                 }
210         }
211         return new GenericEvent(eventGroup, eventNumber, areaNumber);
212     }
213 }