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.powermax.internal.message;
15 import java.math.BigInteger;
16 import java.util.ArrayList;
17 import java.util.List;
19 import org.openhab.binding.powermax.internal.state.PowermaxArmMode;
20 import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings;
21 import org.openhab.binding.powermax.internal.state.PowermaxState;
22 import org.openhab.binding.powermax.internal.state.PowermaxZoneSettings;
25 * A class for STATUS message handling
27 * @author Laurent Garnier - Initial contribution
29 public class PowermaxStatusMessage extends PowermaxBaseMessage {
31 private static byte[] zoneBytes(byte zones1, byte zones9, byte zones17, byte zones25) {
32 return new byte[] { zones25, zones17, zones9, zones1 };
35 private static boolean[] zoneBits(byte[] zoneBytes) {
36 boolean[] zones = new boolean[32];
37 char[] binary = new BigInteger(zoneBytes).toString(2).toCharArray();
38 int len = binary.length - 1;
40 for (int i = len; i >= 0; i--) {
41 zones[len - i + 1] = (binary[i] == '1');
47 private static String zoneList(byte[] zoneBytes) {
48 boolean[] zones = zoneBits(zoneBytes);
49 List<String> names = new ArrayList<>();
51 for (int i = 1; i < zones.length; i++) {
53 names.add(String.format("Zone %d", i));
57 return String.join(", ", names);
64 * the received message as a buffer of bytes
66 public PowermaxStatusMessage(byte[] message) {
71 protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) {
72 if (commManager == null) {
76 PowermaxPanelSettings panelSettings = commManager.getPanelSettings();
77 PowermaxState updatedState = commManager.createNewState();
79 byte[] message = getRawData();
80 byte eventType = message[3];
81 String eventTypeStr = PowermaxMessageConstants.getMessageTypeString(eventType & 0x000000FF);
83 debug("Event type", eventType, eventTypeStr);
85 if (eventType == 0x02) {
86 byte[] zoneStatusBytes = zoneBytes(message[4], message[5], message[6], message[7]);
87 byte[] batteryStatusBytes = zoneBytes(message[8], message[9], message[10], message[11]);
89 boolean[] zoneStatus = zoneBits(zoneStatusBytes);
90 boolean[] batteryStatus = zoneBits(batteryStatusBytes);
92 String zoneStatusStr = zoneList(zoneStatusBytes);
93 String batteryStatusStr = zoneList(batteryStatusBytes);
95 for (int i = 1; i <= panelSettings.getNbZones(); i++) {
96 updatedState.setSensorTripped(i, zoneStatus[i]);
97 updatedState.setSensorLowBattery(i, batteryStatus[i]);
100 debug("Zone status", zoneStatusBytes, zoneStatusStr);
101 debug("Battery status", batteryStatusBytes, batteryStatusStr);
102 } else if (eventType == 0x04) {
103 byte sysStatus = message[4];
104 byte sysFlags = message[5];
105 byte eventZone = message[6];
106 byte zoneEType = message[7];
107 int x10Status = (message[10] & 0x000000FF) | ((message[11] << 8) & 0x0000FF00);
109 String eventZoneStr = PowermaxMessageConstants.getZoneOrUserString(eventZone & 0x000000FF);
110 String zoneETypeStr = PowermaxMessageConstants.getZoneEventString(zoneEType & 0x000000FF);
112 if (zoneEType == 0x03) {
113 updatedState.setSensorTripped(eventZone, Boolean.TRUE);
114 updatedState.setSensorLastTripped(eventZone, System.currentTimeMillis());
115 } else if (zoneEType == 0x04) {
116 updatedState.setSensorTripped(eventZone, Boolean.FALSE);
117 } else if (zoneEType == 0x05) {
118 PowermaxZoneSettings zone = panelSettings.getZoneSettings(eventZone);
119 if ((zone != null) && zone.getSensorType().equalsIgnoreCase("unknown")) {
120 zone.setSensorType("Motion");
122 updatedState.setSensorTripped(eventZone, Boolean.TRUE);
123 updatedState.setSensorLastTripped(eventZone, System.currentTimeMillis());
127 for (int i = 0; i < panelSettings.getNbPGMX10Devices(); i++) {
128 updatedState.setPGMX10DeviceStatus(i, ((x10Status >> i) & 0x1) > 0);
131 String sysStatusStr = "";
132 if ((sysFlags & 0x1) == 1) {
133 sysStatusStr = sysStatusStr + "Ready, ";
134 updatedState.setReady(true);
136 sysStatusStr = sysStatusStr + "Not ready, ";
137 updatedState.setReady(false);
139 if (((sysFlags >> 1) & 0x1) == 1) {
140 sysStatusStr = sysStatusStr + "Alert in memory, ";
141 updatedState.setAlertInMemory(true);
143 updatedState.setAlertInMemory(false);
145 if (((sysFlags >> 2) & 0x1) == 1) {
146 sysStatusStr = sysStatusStr + "Trouble, ";
147 updatedState.setTrouble(true);
149 updatedState.setTrouble(false);
151 if (((sysFlags >> 3) & 0x1) == 1) {
152 sysStatusStr = sysStatusStr + "Bypass on, ";
153 updatedState.setBypass(true);
155 updatedState.setBypass(false);
156 for (int i = 1; i <= panelSettings.getNbZones(); i++) {
157 updatedState.setSensorBypassed(i, false);
160 if (((sysFlags >> 4) & 0x1) == 1) {
161 sysStatusStr = sysStatusStr + "Last 10 seconds, ";
163 if (((sysFlags >> 5) & 0x1) == 1) {
164 sysStatusStr = sysStatusStr + zoneETypeStr;
165 if (eventZone == 0xFF) {
166 sysStatusStr = sysStatusStr + " from Panel, ";
167 } else if (eventZone > 0) {
168 sysStatusStr = sysStatusStr + String.format(" in Zone %d, ", eventZone);
170 sysStatusStr = sysStatusStr + ", ";
173 if (((sysFlags >> 6) & 0x1) == 1) {
174 sysStatusStr = sysStatusStr + "Status changed, ";
176 if (((sysFlags >> 7) & 0x1) == 1) {
177 sysStatusStr = sysStatusStr + "Alarm event, ";
178 updatedState.setAlarmActive(true);
180 updatedState.setAlarmActive(false);
182 sysStatusStr = sysStatusStr.substring(0, sysStatusStr.length() - 2);
185 PowermaxArmMode armMode = PowermaxArmMode.fromCode(sysStatus & 0x000000FF);
186 statusStr = armMode.getName();
187 } catch (IllegalArgumentException e) {
188 statusStr = "UNKNOWN";
190 updatedState.setArmMode(statusStr);
191 updatedState.setStatusStr(statusStr + ", " + sysStatusStr);
193 debug("System status", sysStatus, statusStr);
194 debug("System flags", sysFlags, sysStatusStr);
195 debug("Event zone", eventZone, eventZoneStr);
196 debug("Zone event type", zoneEType, zoneETypeStr);
197 debug("X10 status", x10Status);
199 for (int i = 1; i <= panelSettings.getNbZones(); i++) {
200 PowermaxZoneSettings zone = panelSettings.getZoneSettings(i);
202 // mode: armed or not: 4=armed home; 5=armed away
203 int mode = sysStatus & 0x0000000F;
204 // Zone is shown as armed if
205 // the sensor type always triggers an alarm
206 // or the system is armed away (mode = 5)
207 // or the system is armed home (mode = 4) and the zone is not interior(-follow)
208 boolean armed = (!zone.getType().equalsIgnoreCase("Non-Alarm") && (zone.isAlwaysInAlarm()
209 || (mode == 0x5) || ((mode == 0x4) && !zone.getType().equalsIgnoreCase("Interior-Follow")
210 && !zone.getType().equalsIgnoreCase("Interior"))));
211 updatedState.setSensorArmed(i, armed);
214 } else if (eventType == 0x06) {
215 byte[] zoneBypassBytes = zoneBytes(message[8], message[9], message[10], message[11]);
216 boolean[] zoneBypass = zoneBits(zoneBypassBytes);
217 String zoneBypassStr = zoneList(zoneBypassBytes);
219 for (int i = 1; i <= panelSettings.getNbZones(); i++) {
220 updatedState.setSensorBypassed(i, zoneBypass[i]);
223 debug("Zone bypass", zoneBypassBytes, zoneBypassStr);