]> git.basschouten.com Git - openhab-addons.git/blob
fb255805ed0b582a79af27015a46826640844576
[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.alarmdecoder.internal.handler;
14
15 import static org.openhab.binding.alarmdecoder.internal.AlarmDecoderBindingConstants.*;
16
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
19
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;
38
39 /**
40  * The {@link KeypadHandler} is responsible for handling keypad messages.
41  *
42  * @author Bob Adair - Initial contribution
43  * @author Bill Forsyth - Initial contribution
44  */
45 @NonNullByDefault
46 public class KeypadHandler extends ADThingHandler {
47
48     private static final Pattern VALID_COMMAND_PATTERN = Pattern.compile(ADCommand.KEYPAD_COMMAND_REGEX);
49
50     private final Logger logger = LoggerFactory.getLogger(KeypadHandler.class);
51
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;
58
59     public KeypadHandler(Thing thing) {
60         super(thing);
61     }
62
63     @Override
64     public void initialize() {
65         config = getConfigAs(KeypadConfig.class);
66
67         try {
68             addressMaskLong = Long.parseLong(config.addressMask, 16);
69         } catch (NumberFormatException e) {
70             logger.debug("Number format exception parsing addressMask parameter: {}", e.getMessage());
71             addressMaskLong = -1;
72         }
73
74         if (addressMaskLong < 0) {
75             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid addressMask setting");
76             return;
77         }
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);
80         if (singleAddress) {
81             ADAddress device = ADAddress.getDevice(addressMaskLong);
82             if (device != null) {
83                 sendingAddress = device.deviceNum();
84             }
85         }
86
87         try {
88             intCommandMap = new IntCommandMap(config.commandMapping);
89         } catch (IllegalArgumentException e) {
90             logger.warn("Invalid commmandMapping parameter supplied. Error: {}.", e.getMessage());
91             intCommandMap = null;
92         }
93
94         logger.debug("Keypad handler initializing for address mask {}", config.addressMask);
95
96         initDeviceState();
97
98         logger.trace("Keypad handler finished initializing");
99     }
100
101     @Override
102     public void initChannelState() {
103         previousMessage = null;
104     }
105
106     @Override
107     public void notifyPanelReady() {
108         // Do nothing
109     }
110
111     @Override
112     public void handleCommand(ChannelUID channelUID, Command command) {
113         IntCommandMap intCommandMap = this.intCommandMap;
114
115         if (channelUID.getId().equals(CHANNEL_KP_COMMAND)) {
116             if (command instanceof StringType commandString) {
117                 String cmd = commandString.toString();
118                 handleKeypadCommand(cmd);
119             }
120         } else if (channelUID.getId().equals(CHANNEL_KP_INTCOMMAND)) {
121             if (command instanceof Number numberCommand) {
122                 int icmd = numberCommand.intValue();
123                 if (intCommandMap != null) {
124                     String cmd = intCommandMap.getCommand(icmd);
125                     if (cmd != null) {
126                         handleKeypadCommand(cmd);
127                     }
128                 }
129             }
130         }
131     }
132
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.");
138                 return;
139             }
140
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);
145                 return;
146             }
147
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);
157
158             if (singleAddress) {
159                 sendCommand(ADCommand.addressedMessage(sendingAddress, cmd)); // Send from keypad address
160             } else {
161                 sendCommand(new ADCommand(cmd)); // Send from AD address
162             }
163         }
164     }
165
166     @Override
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)) {
172             return;
173         }
174         KeypadMessage kpMsg = (KeypadMessage) msg;
175
176         long msgAddressMask = kpMsg.getLongAddressMask();
177
178         if (!(((addressMaskLong & msgAddressMask) != 0) || addressMaskLong == 0 || msgAddressMask == 0)) {
179             return;
180         }
181         logger.trace("Keypad handler for address mask {} received update: {}", config.addressMask, kpMsg);
182
183         if (kpMsg.equals(previousMessage)) {
184             return; // ignore repeated messages
185         }
186
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.");
192                 if (singleAddress) {
193                     sendCommand(ADCommand.addressedMessage(sendingAddress, "*")); // Send from keypad address
194                 } else {
195                     sendCommand(new ADCommand("*")); // send from AD address
196                 }
197             }
198         }
199
200         updateState(CHANNEL_KP_ZONE, new DecimalType(kpMsg.getZone()));
201         updateState(CHANNEL_KP_TEXT, new StringType(kpMsg.alphaMessage));
202
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)));
208
209         updateState(CHANNEL_KP_BEEPS, new DecimalType(kpMsg.nbeeps));
210
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)));
221
222         previousMessage = kpMsg;
223     }
224 }