Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[alarmdecoder] Fix keypad address mask handling #8406

Merged
merged 2 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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