Skip to content

Commit

Permalink
[alarmdecoder] Fix keypad address mask handling (openhab#8406)
Browse files Browse the repository at this point in the history
Signed-off-by: Bob Adair <bob.github@att.net>
  • Loading branch information
bobadair authored and CSchlipp committed Sep 12, 2020
1 parent 18f88ef commit 536a108
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 13 deletions.
18 changes: 17 additions & 1 deletion bundles/org.openhab.binding.alarmdecoder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,27 @@ Command strings containing invalid characters will be ignored.

Parameters:

* `addressMask` (required) Keypad address mask (0 = All addresses)
* `addressMask` (default = 0) String containing the mask in hex of addresses for which the keypad thing will receive messages (0 = all addresses).
* `sendCommands` (default = false) Allow keypad commands to be sent to the alarm system from openHAB. Enabling this means the alarm system will be only as secure as your openHAB system.
* `sendStar` (default = false) When disarmed/faulted, automatically send the * character to obtain zone fault information.
* `commandMapping` (optional) Comma separated list of key/value pairs mapping integers to command strings for `intcommand` channel.

Address masks

Each bit in the 4 bytes of the address mask represents a device address, ranging from device 0 to device 31.
The first byte (left to right) represents devices 0-7, the second 8-15, the third 16-23, and the fourth 24-31.
The mask itself is represented as a string containing a hexadecimal number.
For example, a mask of 03000000 would indicate devices 0 and 1 as follows:

```
Mask: 03000000
Bytes: 03 00 00 00
Bits: 00000011 00000000 00000000 00000000
-------- -------- -------- --------
Device# 111111 22221111 33222222
76543210 54321098 32109876 10987654
```

Thing config file example:

```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
@NonNullByDefault
public class KeypadConfig {
public int addressMask = 0;
public String addressMask = "0";
public boolean sendCommands = false;
public boolean sendStar = false;
public String commandMapping = DEFAULT_MAPPING;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.types.Command;
import org.openhab.binding.alarmdecoder.internal.config.KeypadConfig;
import org.openhab.binding.alarmdecoder.internal.protocol.ADAddress;
import org.openhab.binding.alarmdecoder.internal.protocol.ADCommand;
import org.openhab.binding.alarmdecoder.internal.protocol.ADMessage;
import org.openhab.binding.alarmdecoder.internal.protocol.IntCommandMap;
Expand All @@ -50,8 +51,10 @@ public class KeypadHandler extends ADThingHandler {

private KeypadConfig config = new KeypadConfig();
private boolean singleAddress;
private int sendingAddress;
private @Nullable IntCommandMap intCommandMap;
private @Nullable KeypadMessage previousMessage;
private long addressMaskLong = 0;

public KeypadHandler(Thing thing) {
super(thing);
Expand All @@ -61,11 +64,25 @@ public KeypadHandler(Thing thing) {
public void initialize() {
config = getConfigAs(KeypadConfig.class);

if (config.addressMask < 0) {
try {
addressMaskLong = Long.parseLong(config.addressMask, 16);
} catch (NumberFormatException e) {
logger.debug("Number format exception parsing addressMask parameter: {}", e.getMessage());
addressMaskLong = -1;
}

if (addressMaskLong < 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid addressMask setting");
return;
}
singleAddress = (Integer.bitCount(config.addressMask) == 1);
// If 1 and only 1 device is set in the addressMask parameter, use that device number as the sending address
singleAddress = ADAddress.singleAddress(addressMaskLong);
if (singleAddress) {
ADAddress device = ADAddress.getDevice(addressMaskLong);
if (device != null) {
sendingAddress = device.deviceNum();
}
}

try {
intCommandMap = new IntCommandMap(config.commandMapping);
Expand Down Expand Up @@ -139,9 +156,9 @@ private void handleKeypadCommand(String command) {
cmd = cmd.replace("H", ADCommand.SPECIAL_KEY_8);

if (singleAddress) {
sendCommand(ADCommand.addressedMessage(config.addressMask, cmd)); // send from keypad address
sendCommand(ADCommand.addressedMessage(sendingAddress, cmd)); // Send from keypad address
} else {
sendCommand(new ADCommand(cmd)); // send from AD address
sendCommand(new ADCommand(cmd)); // Send from AD address
}
}
}
Expand All @@ -156,9 +173,9 @@ public void handleUpdate(ADMessage msg) {
}
KeypadMessage kpMsg = (KeypadMessage) msg;

int addressMask = kpMsg.getIntAddressMask();
long msgAddressMask = kpMsg.getLongAddressMask();

if (!(((config.addressMask & addressMask) != 0) || config.addressMask == 0 || addressMask == 0)) {
if (!(((addressMaskLong & msgAddressMask) != 0) || addressMaskLong == 0 || msgAddressMask == 0)) {
return;
}
logger.trace("Keypad handler for address mask {} received update: {}", config.addressMask, kpMsg);
Expand All @@ -173,7 +190,7 @@ public void handleUpdate(ADMessage msg) {
|| kpMsg.alphaMessage.contains("Press * to show faults")) {
logger.debug("Sending * command to show faults.");
if (singleAddress) {
sendCommand(ADCommand.addressedMessage(config.addressMask, "*")); // send from keypad address
sendCommand(ADCommand.addressedMessage(sendingAddress, "*")); // Send from keypad address
} else {
sendCommand(new ADCommand("*")); // send from AD address
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.alarmdecoder.internal.protocol;

import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* Defines keypad device addresses used in an AD keypad address mask.
*
* @author Bob Adair - Initial contribution
*/
@NonNullByDefault
public enum ADAddress {
KEYPAD0(0x01000000, 0),
KEYPAD1(0x02000000, 1),
KEYPAD2(0x04000000, 2),
KEYPAD3(0x08000000, 3),
KEYPAD4(0x10000000, 4),
KEYPAD5(0x20000000, 5),
KEYPAD6(0x40000000, 6),
KEYPAD7(0x80000000, 7),

KEYPAD8(0x00010000, 8),
KEYPAD9(0x00020000, 9),
KEYPAD10(0x00040000, 10),
KEYPAD11(0x00080000, 11),
KEYPAD12(0x00100000, 12),
KEYPAD13(0x00200000, 13),
KEYPAD14(0x00400000, 14),
KEYPAD15(0x00800000, 15),

KEYPAD16(0x00000100, 16),
KEYPAD17(0x00000200, 17),
KEYPAD18(0x00000400, 18),
KEYPAD19(0x00000800, 19),
KEYPAD20(0x00001000, 20),
KEYPAD21(0x00002000, 21),
KEYPAD22(0x00004000, 22),
KEYPAD23(0x00008000, 23),

KEYPAD24(0x00000001, 24),
KEYPAD25(0x00000002, 25),
KEYPAD26(0x00000004, 26),
KEYPAD27(0x00000008, 27),
KEYPAD28(0x00000010, 28),
KEYPAD29(0x00000020, 29),
KEYPAD30(0x00000040, 30),
KEYPAD31(0x00000080, 31);

private final long mask;
private final int device;

ADAddress(long mask, int device) {
this.mask = mask;
this.device = device;
}

/** Returns the device bit mask **/
public long mask() {
return mask;
}

/** Returns the device number (0-31) **/
public int deviceNum() {
return device;
}

/**
* Returns the first device address found in addressMask or null if none are found
*
* @param addressMask
*/
public static @Nullable ADAddress getDevice(long addressMask) {
for (ADAddress address : ADAddress.values()) {
if ((address.mask() & addressMask) != 0) {
return address;
}
}
return null;
}

/**
* Returns a Collection of the device addresses found in addressMask.
* Returns an empty collection if none are found.
*
* @param addressMask
*/
public static Collection<ADAddress> getDevices(long addressMask) {
Collection<ADAddress> addressCollection = new ArrayList<>();
for (ADAddress address : ADAddress.values()) {
if ((address.mask() & addressMask) != 0) {
addressCollection.add(address);
}
}
return addressCollection;
}

/**
* Return true if 1 and only 1 address bit is set in addressMask
*/
public static boolean singleAddress(long addressMask) {
return (Long.bitCount(addressMask) == 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ public String getAddressMask() {
}

/**
* Returns an int containing the address mask of the message
* Returns a long containing the address mask of the message
*/
public int getIntAddressMask() {
return Integer.parseInt(getAddressMask(), 16);
public long getLongAddressMask() {
return Long.parseLong(getAddressMask(), 16);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@
<config-description>
<parameter name="addressMask" type="text">
<label>Address Mask</label>
<description>Receive and send messages from a specific keypad (0=any)</description>
<description>String containing the address mask in hex that the keypad thing will receive messages for. (0=any)</description>
<default>0</default>
</parameter>
<parameter name="sendCommands" type="boolean">
Expand Down

0 comments on commit 536a108

Please sign in to comment.