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.alarmdecoder.internal.handler;
15 import static org.openhab.binding.alarmdecoder.internal.AlarmDecoderBindingConstants.*;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.alarmdecoder.internal.config.KeypadConfig;
23 import org.openhab.binding.alarmdecoder.internal.protocol.ADAddress;
24 import org.openhab.binding.alarmdecoder.internal.protocol.ADCommand;
25 import org.openhab.binding.alarmdecoder.internal.protocol.ADMessage;
26 import org.openhab.binding.alarmdecoder.internal.protocol.IntCommandMap;
27 import org.openhab.binding.alarmdecoder.internal.protocol.KeypadMessage;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.StringType;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.types.Command;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link KeypadHandler} is responsible for handling keypad messages.
42 * @author Bob Adair - Initial contribution
43 * @author Bill Forsyth - Initial contribution
46 public class KeypadHandler extends ADThingHandler {
48 private static final Pattern VALID_COMMAND_PATTERN = Pattern.compile(ADCommand.KEYPAD_COMMAND_REGEX);
50 private final Logger logger = LoggerFactory.getLogger(KeypadHandler.class);
52 private KeypadConfig config = new KeypadConfig();
53 private boolean singleAddress;
54 private int sendingAddress;
55 private @Nullable IntCommandMap intCommandMap;
56 private @Nullable KeypadMessage previousMessage;
57 private long addressMaskLong = 0;
59 public KeypadHandler(Thing thing) {
64 public void initialize() {
65 config = getConfigAs(KeypadConfig.class);
68 addressMaskLong = Long.parseLong(config.addressMask, 16);
69 } catch (NumberFormatException e) {
70 logger.debug("Number format exception parsing addressMask parameter: {}", e.getMessage());
74 if (addressMaskLong < 0) {
75 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid addressMask setting");
78 // If 1 and only 1 device is set in the addressMask parameter, use that device number as the sending address
79 singleAddress = ADAddress.singleAddress(addressMaskLong);
81 ADAddress device = ADAddress.getDevice(addressMaskLong);
83 sendingAddress = device.deviceNum();
88 intCommandMap = new IntCommandMap(config.commandMapping);
89 } catch (IllegalArgumentException e) {
90 logger.warn("Invalid commmandMapping parameter supplied. Error: {}.", e.getMessage());
94 logger.debug("Keypad handler initializing for address mask {}", config.addressMask);
98 logger.trace("Keypad handler finished initializing");
102 public void initChannelState() {
103 previousMessage = null;
107 public void notifyPanelReady() {
112 public void handleCommand(ChannelUID channelUID, Command command) {
113 IntCommandMap intCommandMap = this.intCommandMap;
115 if (channelUID.getId().equals(CHANNEL_KP_COMMAND)) {
116 if (command instanceof StringType) {
117 String cmd = ((StringType) command).toString();
118 handleKeypadCommand(cmd);
120 } else if (channelUID.getId().equals(CHANNEL_KP_INTCOMMAND)) {
121 if (command instanceof Number) {
122 int icmd = ((Number) command).intValue();
123 if (intCommandMap != null) {
124 String cmd = intCommandMap.getCommand(icmd);
126 handleKeypadCommand(cmd);
133 private void handleKeypadCommand(String command) {
134 String cmd = command;
135 if (cmd.length() > 0) {
136 if (!config.sendCommands) {
137 logger.info("Sending keypad commands is disabled. Enable using the sendCommands keypad parameter.");
141 // check that received command is valid
142 Matcher matcher = VALID_COMMAND_PATTERN.matcher(cmd);
143 if (!matcher.matches()) {
144 logger.info("Invalid characters in command. Ignoring command: {}", cmd);
148 // Replace A-H in command string with special key strings
149 cmd = cmd.replace("A", ADCommand.SPECIAL_KEY_1);
150 cmd = cmd.replace("B", ADCommand.SPECIAL_KEY_2);
151 cmd = cmd.replace("C", ADCommand.SPECIAL_KEY_3);
152 cmd = cmd.replace("D", ADCommand.SPECIAL_KEY_4);
153 cmd = cmd.replace("E", ADCommand.SPECIAL_KEY_5);
154 cmd = cmd.replace("F", ADCommand.SPECIAL_KEY_6);
155 cmd = cmd.replace("G", ADCommand.SPECIAL_KEY_7);
156 cmd = cmd.replace("H", ADCommand.SPECIAL_KEY_8);
159 sendCommand(ADCommand.addressedMessage(sendingAddress, cmd)); // Send from keypad address
161 sendCommand(new ADCommand(cmd)); // Send from AD address
167 public void handleUpdate(ADMessage msg) {
168 // This will ignore a received message unless it is a KeypadMessage and either this handler's address mask is 0
169 // (all), the message's address mask is 0 (all), or any bits in this handler's address mask match bits set in
170 // the message's address mask.
171 if (!(msg instanceof KeypadMessage)) {
174 KeypadMessage kpMsg = (KeypadMessage) msg;
176 long msgAddressMask = kpMsg.getLongAddressMask();
178 if (!(((addressMaskLong & msgAddressMask) != 0) || addressMaskLong == 0 || msgAddressMask == 0)) {
181 logger.trace("Keypad handler for address mask {} received update: {}", config.addressMask, kpMsg);
183 if (kpMsg.equals(previousMessage)) {
184 return; // ignore repeated messages
187 if (config.sendStar) {
188 if (kpMsg.alphaMessage.contains("Hit * for faults") || kpMsg.alphaMessage.contains("Press * to show faults")
189 || kpMsg.alphaMessage.contains("Press * Key")
190 || kpMsg.alphaMessage.contains("Press * to show faults")) {
191 logger.debug("Sending * command to show faults.");
193 sendCommand(ADCommand.addressedMessage(sendingAddress, "*")); // Send from keypad address
195 sendCommand(new ADCommand("*")); // send from AD address
200 updateState(CHANNEL_KP_ZONE, new DecimalType(kpMsg.getZone()));
201 updateState(CHANNEL_KP_TEXT, new StringType(kpMsg.alphaMessage));
203 updateState(CHANNEL_KP_READY, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_READY)));
204 updateState(CHANNEL_KP_ARMEDAWAY, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_ARMEDAWAY)));
205 updateState(CHANNEL_KP_ARMEDHOME, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_ARMEDHOME)));
206 updateState(CHANNEL_KP_BACKLIGHT, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_BACKLIGHT)));
207 updateState(CHANNEL_KP_PRORGAM, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_PRORGAM)));
209 updateState(CHANNEL_KP_BEEPS, new DecimalType(kpMsg.nbeeps));
211 updateState(CHANNEL_KP_BYPASSED, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_BYPASSED)));
212 updateState(CHANNEL_KP_ACPOWER, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_ACPOWER)));
213 updateState(CHANNEL_KP_CHIME, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_CHIME)));
214 updateState(CHANNEL_KP_ALARMOCCURRED, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_ALARMOCCURRED)));
215 updateState(CHANNEL_KP_ALARM, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_ALARM)));
216 updateState(CHANNEL_KP_LOWBAT, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_LOWBAT)));
217 updateState(CHANNEL_KP_DELAYOFF, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_DELAYOFF)));
218 updateState(CHANNEL_KP_FIRE, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_FIRE)));
219 updateState(CHANNEL_KP_SYSFAULT, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_SYSFAULT)));
220 updateState(CHANNEL_KP_PERIMETER, OnOffType.from(kpMsg.getStatus(KeypadMessage.BIT_PERIMETER)));
222 previousMessage = kpMsg;