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.caddx.internal.handler;
15 import java.util.Collection;
16 import java.util.HashMap;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.caddx.internal.CaddxBindingConstants;
22 import org.openhab.binding.caddx.internal.CaddxEvent;
23 import org.openhab.binding.caddx.internal.CaddxMessage;
24 import org.openhab.binding.caddx.internal.CaddxMessageContext;
25 import org.openhab.binding.caddx.internal.CaddxMessageType;
26 import org.openhab.binding.caddx.internal.CaddxProperty;
27 import org.openhab.binding.caddx.internal.action.CaddxPanelActions;
28 import org.openhab.core.library.types.OnOffType;
29 import org.openhab.core.library.types.StringType;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.binding.ThingHandlerService;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * This is a class for handling a Panel type Thing.
42 * @author Georgios Moutsos - Initial contribution
45 public class ThingHandlerPanel extends CaddxBaseThingHandler {
46 private final Logger logger = LoggerFactory.getLogger(ThingHandlerPanel.class);
47 private @Nullable HashMap<String, String> panelLogMessagesMap = null;
48 private @Nullable String communicatorStackPointer = null;
49 private long lastRefreshTime = 0;
51 public ThingHandlerPanel(Thing thing) {
52 super(thing, CaddxThingType.PANEL);
56 public void initialize() {
59 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
60 if (bridgeHandler == null) {
64 String cmd = CaddxBindingConstants.PANEL_SYSTEM_STATUS_REQUEST;
66 bridgeHandler.sendCommand(CaddxMessageContext.COMMAND, cmd, data);
70 public void updateChannel(ChannelUID channelUID, String data) {
71 if (channelUID.getId().equals(CaddxBindingConstants.PANEL_FIRMWARE_VERSION)
72 || channelUID.getId().startsWith("panel_log_message_")) {
73 updateState(channelUID, new StringType(data));
75 // All Panel channels are OnOffType
78 onOffType = ("true".equals(data)) ? OnOffType.ON : OnOffType.OFF;
79 updateState(channelUID, onOffType);
84 public void handleCommand(ChannelUID channelUID, Command command) {
85 logger.debug("handleCommand(): Command Received - {} {}.", channelUID, command);
89 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
90 if (bridgeHandler == null) {
94 if (command instanceof RefreshType) {
95 if (CaddxBindingConstants.PANEL_LOG_MESSAGE_N_0.equals(channelUID.getId())) {
96 cmd = CaddxBindingConstants.PANEL_SYSTEM_STATUS_REQUEST;
98 } else if (System.currentTimeMillis() - lastRefreshTime > 2000) {
99 // Refresh only if 2 seconds have passed from the last refresh
100 cmd = CaddxBindingConstants.PANEL_SYSTEM_STATUS_REQUEST;
106 bridgeHandler.sendCommand(CaddxMessageContext.COMMAND, cmd, data);
107 lastRefreshTime = System.currentTimeMillis();
109 logger.debug("Unknown command {}", command);
114 public void caddxEventReceived(CaddxEvent event, Thing thing) {
115 logger.trace("caddxEventReceived(): Event Received - {}.", event);
117 if (getThing().equals(thing)) {
118 CaddxMessage message = event.getCaddxMessage();
119 CaddxMessageType mt = message.getCaddxMessageType();
120 ChannelUID channelUID = null;
122 for (CaddxProperty p : mt.properties) {
123 if (!p.getId().isEmpty()) {
124 String value = message.getPropertyById(p.getId());
125 channelUID = new ChannelUID(getThing().getUID(), p.getId());
126 updateChannel(channelUID, value);
127 logger.trace("Updating panel channel: {}", channelUID.getAsString());
131 // Log event messages have special handling
132 if (CaddxMessageType.SYSTEM_STATUS_MESSAGE.equals(mt)) {
133 handleSystemStatusMessage(message);
134 } else if (CaddxMessageType.LOG_EVENT_MESSAGE.equals(mt)) {
135 handleLogEventMessage(message);
136 } else if (CaddxMessageType.ZONES_SNAPSHOT_MESSAGE.equals(mt)) {
137 handleZonesSnapshotMessage(message);
140 updateStatus(ThingStatus.ONLINE);
145 * Gets the pointer into the panel's log messages ring buffer
146 * and sends the command for the retrieval of the last event_message
148 private void handleSystemStatusMessage(CaddxMessage message) {
149 // Get the bridge handler
150 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
151 if (bridgeHandler == null) {
155 String pointer = message.getPropertyById("panel_communicator_stack_pointer");
156 communicatorStackPointer = pointer;
158 // build map of log message channels to event numbers
159 HashMap<String, String> map = new HashMap<String, String>();
160 map.put(pointer, CaddxBindingConstants.PANEL_LOG_MESSAGE_N_0);
161 bridgeHandler.sendCommand(CaddxMessageContext.COMMAND, CaddxBindingConstants.PANEL_LOG_EVENT_REQUEST, pointer);
162 panelLogMessagesMap = map;
166 * This function handles the panel log messages.
167 * If the received event_number matches our communication stack pointer then this is the last panel message. The
168 * channel gets updated and the required log message requests are generated for the update of the other log message
171 private void handleLogEventMessage(CaddxMessage message) {
172 // Get the bridge handler
173 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
174 if (bridgeHandler == null) {
178 String eventNumberString = message.getPropertyById("panel_log_event_number");
179 String eventSizeString = message.getPropertyById("panel_log_event_size");
182 LogEventMessage logEventMessage = new LogEventMessage(message);
184 logger.trace("Log_event: {}", logEventMessage);
186 // get the channel id from the map
187 HashMap<String, String> logMap = panelLogMessagesMap;
188 if (logMap != null) {
189 String id = logMap.get(eventNumberString);
191 ChannelUID channelUID = new ChannelUID(getThing().getUID(), id);
192 updateChannel(channelUID, logEventMessage.toString());
196 if (communicatorStackPointer != null && eventNumberString.equals(communicatorStackPointer)) {
197 HashMap<String, String> map = new HashMap<String, String>();
199 int eventNumber = Integer.parseInt(eventNumberString);
200 int eventSize = Integer.parseInt(eventSizeString);
202 // Retrieve at maximum the 10 last log messages from the panel
203 int messagesToRetrieve = Math.min(eventSize, 10);
204 for (int i = 1; i < messagesToRetrieve; i++) {
206 if (eventNumber < 0) {
207 eventNumber = eventSize;
210 map.put(Integer.toString(eventNumber), "panel_log_message_n_" + i);
211 bridgeHandler.sendCommand(CaddxMessageContext.COMMAND, CaddxBindingConstants.PANEL_LOG_EVENT_REQUEST,
212 Integer.toString(eventNumber));
215 communicatorStackPointer = null;
216 panelLogMessagesMap = map;
220 private void handleZonesSnapshotMessage(CaddxMessage message) {
221 // Get the bridge handler
222 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
223 if (bridgeHandler == null) {
227 int zoneOffset = Integer.parseInt(message.getPropertyById("zone_offset"));
229 for (int i = 1; i <= 16; i++) {
230 int zoneNumber = zoneOffset * 16 + i;
232 String zoneFaulted = message.getPropertyById("zone_" + i + "_faulted");
233 String zoneBypassed = message.getPropertyById("zone_" + i + "_bypassed");
234 String zoneTrouble = message.getPropertyById("zone_" + i + "_trouble");
235 String zoneAlarmMemory = message.getPropertyById("zone_" + i + "_alarm_memory");
237 logger.debug("Flags for zone {}. faulted:{}, bypassed:{}, trouble:{}, alarm_memory:{}", zoneNumber,
238 zoneFaulted, zoneBypassed, zoneTrouble, zoneAlarmMemory);
241 Thing thing = bridgeHandler.findThing(CaddxThingType.ZONE, null, zoneNumber, null);
243 ChannelUID channelUID;
245 logger.debug("Thing found for zone {}.", zoneNumber);
247 channelUID = new ChannelUID(thing.getUID(), "zone_faulted");
248 updateChannel(channelUID, zoneFaulted);
249 channelUID = new ChannelUID(thing.getUID(), "zone_bypassed");
250 updateChannel(channelUID, zoneBypassed);
251 channelUID = new ChannelUID(thing.getUID(), "zone_trouble");
252 updateChannel(channelUID, zoneTrouble);
253 channelUID = new ChannelUID(thing.getUID(), "zone_alarm_memory");
254 updateChannel(channelUID, zoneAlarmMemory);
260 public Collection<Class<? extends ThingHandlerService>> getServices() {
261 return Set.of(CaddxPanelActions.class);
264 private void sendPrimaryCommand(String pin, String function) {
265 String cmd = CaddxBindingConstants.PARTITION_PRIMARY_COMMAND_WITH_PIN;
268 StringBuilder sb = new StringBuilder();
269 sb.append("0x").append(pin.charAt(1)).append(pin.charAt(0)).append(",0x").append(pin.charAt(3))
270 .append(pin.charAt(2)).append(",0x").append(pin.charAt(5)).append(pin.charAt(4)).append(",")
271 .append(function).append(",").append("255");
273 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
274 if (bridgeHandler == null) {
277 bridgeHandler.sendCommand(CaddxMessageContext.COMMAND, cmd, sb.toString());
280 private void sendSecondaryCommand(String function) {
281 String cmd = CaddxBindingConstants.PARTITION_SECONDARY_COMMAND;
284 StringBuilder sb = new StringBuilder();
285 sb.append(function).append(",").append("255");
287 CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
288 if (bridgeHandler == null) {
291 bridgeHandler.sendCommand(CaddxMessageContext.COMMAND, cmd, sb.toString());
294 public void turnOffAnySounderOrAlarm(String pin) {
295 sendPrimaryCommand(pin, "0");
298 public void disarm(String pin) {
299 sendPrimaryCommand(pin, "1");
302 public void armInAwayMode(String pin) {
303 sendPrimaryCommand(pin, "2");
306 public void armInStayMode(String pin) {
307 sendPrimaryCommand(pin, "3");
310 public void cancel(String pin) {
311 sendPrimaryCommand(pin, "4");
314 public void initiateAutoArm(String pin) {
315 sendPrimaryCommand(pin, "5");
318 public void startWalkTestMode(String pin) {
319 sendPrimaryCommand(pin, "6");
322 public void stopWalkTestMode(String pin) {
323 sendPrimaryCommand(pin, "7");
327 sendSecondaryCommand("0");
330 public void chime() {
331 sendSecondaryCommand("1");
335 sendSecondaryCommand("2");
338 public void bypassInteriors() {
339 sendSecondaryCommand("3");
342 public void firePanic() {
343 sendSecondaryCommand("4");
346 public void medicalPanic() {
347 sendSecondaryCommand("5");
350 public void policePanic() {
351 sendSecondaryCommand("6");
354 public void smokeDetectorReset() {
355 sendSecondaryCommand("7");
358 public void autoCallbackDownload() {
359 sendSecondaryCommand("8");
362 public void manualPickupDownload() {
363 sendSecondaryCommand("9");
366 public void enableSilentExit() {
367 sendSecondaryCommand("10");
370 public void performTest() {
371 sendSecondaryCommand("11");
374 public void groupBypass() {
375 sendSecondaryCommand("12");
378 public void auxiliaryFunction1() {
379 sendSecondaryCommand("13");
382 public void auxiliaryFunction2() {
383 sendSecondaryCommand("14");
386 public void startKeypadSounder() {
387 sendSecondaryCommand("15");