diff --git a/org.openhab.binding.zigbee/ESH-INF/thing/xiaomi/lumiremoteb286acn01.xml b/org.openhab.binding.zigbee/ESH-INF/thing/xiaomi/lumiremoteb286acn01.xml
new file mode 100644
index 000000000..3ad0af807
--- /dev/null
+++ b/org.openhab.binding.zigbee/ESH-INF/thing/xiaomi/lumiremoteb286acn01.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+ Xiaomi Wall Switch
+ WallSwitch
+
+
+
+
+
+ 1
+ 0x0012
+ 85
+ 1
+ 0x0012
+ 85
+ 0
+ 0x0012
+ 85
+ 2
+
+
+
+
+
+
+ 2
+ 0x0012
+ 85
+ 1
+ 0x0012
+ 85
+ 0
+ 0x0012
+ 85
+ 2
+
+
+
+
+
+
+ 1
+ 0x00
+ 5
+ lumi.remote.b286acn01
+
+
+
+
+
+ Xiaomi
+ lumi.remote.b286acn01
+ END_DEVICE
+
+
+ zigbee_macaddress
+
+
+
+
+
+
+
+
diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButton.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButton.java
index c9bcc9e0f..3ee817005 100644
--- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButton.java
+++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButton.java
@@ -9,18 +9,17 @@
package org.openhab.binding.zigbee.internal.converter;
import static java.lang.Integer.*;
-import static java.lang.String.format;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import com.zsmartsystems.zigbee.zcl.*;
import org.eclipse.smarthome.core.thing.Channel;
import org.eclipse.smarthome.core.thing.CommonTriggerEvents;
import org.eclipse.smarthome.core.thing.ThingUID;
@@ -30,9 +29,6 @@
import com.zsmartsystems.zigbee.CommandResult;
import com.zsmartsystems.zigbee.ZigBeeEndpoint;
-import com.zsmartsystems.zigbee.zcl.ZclCluster;
-import com.zsmartsystems.zigbee.zcl.ZclCommand;
-import com.zsmartsystems.zigbee.zcl.ZclCommandListener;
/**
* Generic converter for buttons (e.g., from remote controls).
@@ -43,8 +39,9 @@
* As the configuration is done via channel properties, this converter is usable via static thing types only.
*
* @author Henning Sudbrock - initial contribution
+ * @author Thomas Weißschuh - support for attribute-based buttons
*/
-public class ZigBeeConverterGenericButton extends ZigBeeBaseChannelConverter implements ZclCommandListener {
+public class ZigBeeConverterGenericButton extends ZigBeeBaseChannelConverter implements ZclCommandListener, ZclAttributeListener {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -52,63 +49,49 @@ public class ZigBeeConverterGenericButton extends ZigBeeBaseChannelConverter imp
private static final String COMMAND = "command_id";
private static final String PARAM_NAME = "parameter_name";
private static final String PARAM_VALUE = "parameter_value";
+ private static final String ATTRIBUTE_ID = "attribute_id";
+ private static final String ATTRIBUTE_VALUE = "attribute_value";
- private Map handledCommands = new HashMap<>();
+ private Map handledEvents = new EnumMap<>(ButtonPressType.class);
private Set clientClusters = new HashSet<>();
+ private Set serverClusters = new HashSet<>();
@Override
public synchronized boolean initializeConverter() {
for (ButtonPressType buttonPressType : ButtonPressType.values()) {
- CommandSpec commandSpec = parseCommandSpec(buttonPressType);
- if (commandSpec != null) {
- handledCommands.put(buttonPressType, commandSpec);
+ EventSpec eventSpec = parseEventSpec(channel.getProperties(), buttonPressType);
+ if (eventSpec != null) {
+ handledEvents.put(buttonPressType, eventSpec);
}
}
- if (handledCommands.isEmpty()) {
+ if (handledEvents.isEmpty()) {
logger.error("{}: No command is specified for any of the possible button press types in channel {}",
endpoint.getIeeeAddress(), channel.getUID());
return false;
}
- for (CommandSpec commandSpec : handledCommands.values()) {
- int clusterId = commandSpec.getClusterId();
+ boolean allBindsSucceeded = true;
- if (clientClusters.stream().anyMatch(cluster -> cluster.getClusterId().intValue() == clusterId)) {
- // bind to each output cluster only once
- continue;
- }
-
- ZclCluster clientCluster = endpoint.getOutputCluster(clusterId);
- if (clientCluster == null) {
- logger.error("{}: Error opening client cluster {} on endpoint {}", endpoint.getIeeeAddress(), clusterId,
- endpoint.getEndpointId());
- return false;
- }
-
- try {
- CommandResult bindResponse = bind(clientCluster).get();
- if (!bindResponse.isSuccess()) {
- logger.error("{}: Error 0x{} setting client binding for cluster {}", endpoint.getIeeeAddress(),
- toHexString(bindResponse.getStatusCode()), clusterId);
- }
- } catch (InterruptedException | ExecutionException e) {
- logger.error(endpoint.getIeeeAddress() + ": Exception setting binding to cluster " + clusterId, e);
- }
-
- clientCluster.addCommandListener(this);
- clientClusters.add(clientCluster);
+ for (EventSpec eventSpec: handledEvents.values()) {
+ allBindsSucceeded &= eventSpec.bindCluster();
}
- return true;
+ return allBindsSucceeded;
}
+
@Override
public void disposeConverter() {
for (ZclCluster clientCluster : clientClusters) {
- logger.debug("{}: Closing cluster {}", endpoint.getIeeeAddress(), clientCluster.getClusterId());
+ logger.debug("{}: Closing client cluster {}", endpoint.getIeeeAddress(), clientCluster.getClusterId());
clientCluster.removeCommandListener(this);
}
+
+ for (ZclCluster serverCluster : serverClusters) {
+ logger.debug("{}: Closing server cluster {}", endpoint.getIeeeAddress(), serverCluster.getClusterId());
+ serverCluster.removeAttributeListener(this);
+ }
}
@Override
@@ -133,6 +116,16 @@ public void commandReceived(ZclCommand command) {
}
}
+ @Override
+ public void attributeUpdated(ZclAttribute attribute) {
+ ButtonPressType buttonPressType = getButtonPressType(attribute);
+ if (buttonPressType != null) {
+ logger.debug("{}: Matching ZigBee attribute for press type {} received: {}", endpoint.getIeeeAddress(),
+ buttonPressType, attribute);
+ thing.triggerChannel(channel.getUID(), getEvent(buttonPressType));
+ }
+ }
+
private String getEvent(ButtonPressType pressType) {
switch (pressType) {
case DOUBLE_PRESS:
@@ -147,27 +140,31 @@ private String getEvent(ButtonPressType pressType) {
}
}
+ private ButtonPressType getButtonPressType(ZclAttribute attribute) {
+ return getButtonPressType(cs -> cs.matches(attribute));
+ }
+
private ButtonPressType getButtonPressType(ZclCommand command) {
- for (Entry entry : handledCommands.entrySet()) {
- if (entry.getValue().matchesCommand(command)) {
+ return getButtonPressType(cs -> cs.matches(command));
+ }
+
+ private ButtonPressType getButtonPressType(Predicate predicate) {
+ for (Entry entry : handledEvents.entrySet()) {
+ if (predicate.test(entry.getValue())) {
return entry.getKey();
}
}
return null;
}
- private CommandSpec parseCommandSpec(ButtonPressType pressType) {
- String clusterProperty = channel.getProperties().get(getParameterName(CLUSTER, pressType));
- String commandProperty = channel.getProperties().get(getParameterName(COMMAND, pressType));
- String commandParameterName = channel.getProperties().get(getParameterName(PARAM_NAME, pressType));
- String commandParameterValue = channel.getProperties().get(getParameterName(PARAM_VALUE, pressType));
+ private EventSpec parseEventSpec(Map properties, ButtonPressType pressType) {
+ String clusterProperty = properties.get(getParameterName(CLUSTER, pressType));
- if (clusterProperty == null || commandProperty == null) {
+ if (clusterProperty == null) {
return null;
}
int clusterId;
- int commandId;
try {
clusterId = parseId(clusterProperty);
@@ -176,6 +173,59 @@ private CommandSpec parseCommandSpec(ButtonPressType pressType) {
return null;
}
+ boolean hasCommand = properties.containsKey(getParameterName(COMMAND, pressType));
+ boolean hasAttribute = properties.containsKey(getParameterName(ATTRIBUTE_ID, pressType));
+
+ if (hasCommand && hasAttribute) {
+ logger.warn("{}: Only one of command or attribute can be used", endpoint.getIeeeAddress());
+ return null;
+ }
+
+ if (hasCommand) {
+ return parseCommandSpec(clusterId, properties, pressType);
+ } else {
+ return parseAttributeReportSpec(clusterId, properties, pressType);
+ }
+ }
+
+ private AttributeReportSpec parseAttributeReportSpec(int clusterId, Map properties, ButtonPressType pressType) {
+ String attributeIdProperty = properties.get(getParameterName(ATTRIBUTE_ID, pressType));
+ String attributeValue = properties.get(getParameterName(ATTRIBUTE_VALUE, pressType));
+
+ if (attributeIdProperty == null) {
+ logger.warn("{}: Missing attribute id", endpoint.getIeeeAddress());
+ return null;
+ }
+
+ Integer attributeId;
+
+ try {
+ attributeId = parseId(attributeIdProperty);
+ } catch (NumberFormatException e) {
+ logger.warn("{}: Could not parse attribute property {}", endpoint.getIeeeAddress(), attributeIdProperty);
+ return null;
+ }
+
+ if (attributeValue == null) {
+ logger.warn("{}: No attribute value for attribute {} specified", endpoint.getIeeeAddress(), attributeId);
+ return null;
+ }
+
+ return new AttributeReportSpec(clusterId, attributeId, attributeValue);
+ }
+
+ private CommandSpec parseCommandSpec(int clusterId, Map properties, ButtonPressType pressType) {
+ String commandProperty = properties.get(getParameterName(COMMAND, pressType));
+ String commandParameterName = properties.get(getParameterName(PARAM_NAME, pressType));
+ String commandParameterValue = properties.get(getParameterName(PARAM_VALUE, pressType));
+
+ if (commandProperty == null) {
+ logger.warn("{}: Missing command", endpoint.getIeeeAddress());
+ return null;
+ }
+
+ Integer commandId;
+
try {
commandId = parseId(commandProperty);
} catch (NumberFormatException e) {
@@ -190,16 +240,17 @@ private CommandSpec parseCommandSpec(ButtonPressType pressType) {
return null;
}
+
return new CommandSpec(clusterId, commandId, commandParameterName, commandParameterValue);
}
- private String getParameterName(String parameterType, ButtonPressType buttonPressType) {
+ private static String getParameterName(String parameterType, ButtonPressType buttonPressType) {
return String.format("zigbee_%s_%s", buttonPressType, parameterType);
}
- private int parseId(String id) throws NumberFormatException {
+ private static int parseId(String id) throws NumberFormatException {
if (id.startsWith("0x")) {
- return parseInt(id.substring(2, id.length()), 16);
+ return parseInt(id.substring(2), 16);
} else {
return parseInt(id);
}
@@ -222,25 +273,101 @@ public String toString() {
}
}
- private class CommandSpec {
+ private abstract class EventSpec {
private final int clusterId;
- private final int commandId;
+
+ EventSpec(int clusterId) {
+ this.clusterId = clusterId;
+ }
+
+ int getClusterId() {
+ return clusterId;
+ }
+
+ abstract boolean matches(ZclCommand command);
+ abstract boolean matches(ZclAttribute attribute);
+ abstract boolean bindCluster();
+
+ boolean bindCluster(String clusterType, Collection existingClusters, int clusterId,
+ Function getClusterById,
+ Consumer registrationFunction
+ ) {
+ if (existingClusters.stream().anyMatch(c -> c.getClusterId().intValue() == clusterId)) {
+ // bind to each output cluster only once
+ return true;
+ }
+
+ ZclCluster cluster = getClusterById.apply(clusterId);
+ if (cluster == null) {
+ logger.error("{}: Error opening {} cluster {} on endpoint {}", endpoint.getIeeeAddress(), clusterType, clusterId,
+ endpoint.getEndpointId());
+ return false;
+ }
+
+ try {
+ CommandResult bindResponse = bind(cluster).get();
+ if (!bindResponse.isSuccess()) {
+ logger.error("{}: Error 0x{} setting {} binding for cluster {}", endpoint.getIeeeAddress(),
+ toHexString(bindResponse.getStatusCode()), clusterType, clusterId);
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ logger.error("{}: Exception setting {} binding to cluster {}", endpoint.getIeeeAddress(), clusterType, clusterId, e);
+ }
+
+ registrationFunction.accept(cluster);
+ existingClusters.add(cluster);
+ return true;
+ }
+ }
+
+ protected final class AttributeReportSpec extends EventSpec {
+ private final Integer attributeId;
+ private final String attributeValue;
+
+ AttributeReportSpec(int clusterId, Integer attributeId, String attributeValue) {
+ super(clusterId);
+ this.attributeId = attributeId;
+ this.attributeValue = attributeValue;
+ }
+
+ @Override
+ boolean matches(ZclCommand command) {
+ return false;
+ }
+
+ @Override
+ boolean matches(ZclAttribute attribute) {
+ if (attributeId == null) {
+ return false;
+ }
+ boolean attributeIdMatches = attribute.getId() == attributeId;
+ boolean attributeValueMatches = Objects.equals(
+ Objects.toString(attribute.getLastValue()),
+ attributeValue
+ );
+ return attributeIdMatches && attributeValueMatches;
+ }
+
+ @Override
+ boolean bindCluster() {
+ return bindCluster(
+ "server", serverClusters, getClusterId(), endpoint::getInputCluster,
+ cluster -> cluster.addAttributeListener(ZigBeeConverterGenericButton.this));
+ }
+ }
+
+ private final class CommandSpec extends EventSpec {
+ private final Integer commandId;
private final String commandParameterName;
private final String commandParameterValue;
- public CommandSpec(int clusterId, int commandId, String commandParameterName, String commandParameterValue) {
- this.clusterId = clusterId;
+ private CommandSpec(int clusterId, Integer commandId, String commandParameterName, String commandParameterValue) {
+ super(clusterId);
this.commandId = commandId;
this.commandParameterName = commandParameterName;
this.commandParameterValue = commandParameterValue;
}
- public boolean matchesCommand(ZclCommand command) {
- boolean commandIdMatches = command.getCommandId().intValue() == commandId;
- return commandIdMatches
- && (commandParameterName == null || commandParameterValue == null || matchesParameter(command));
- }
-
private boolean matchesParameter(ZclCommand command) {
String capitalizedParameterName = commandParameterName.substring(0, 1).toUpperCase()
+ commandParameterName.substring(1);
@@ -250,14 +377,31 @@ private boolean matchesParameter(ZclCommand command) {
return Objects.equals(result.toString(), commandParameterValue);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
- logger.warn(format("%s: Could not read parameter %s for command %s", endpoint.getIeeeAddress(),
- commandParameterName, command), e);
+ logger.warn("{}: Could not read parameter {} for command {}", endpoint.getIeeeAddress(),
+ commandParameterName, command, e);
return false;
}
}
- public int getClusterId() {
- return clusterId;
+ @Override
+ boolean matches(ZclCommand command) {
+ if (commandId == null) {
+ return false;
+ }
+ boolean commandIdMatches = command.getCommandId().intValue() == commandId;
+ return commandIdMatches
+ && (commandParameterName == null || commandParameterValue == null || matchesParameter(command));
+ }
+
+ @Override
+ boolean matches(ZclAttribute attribute) {
+ return false;
+ }
+
+ @Override
+ boolean bindCluster() {
+ return bindCluster("client", clientClusters, getClusterId(), endpoint::getOutputCluster,
+ cluster -> cluster.addCommandListener(ZigBeeConverterGenericButton.this));
}
}
}
diff --git a/org.openhab.binding.zigbee/src/main/resources/discovery.txt b/org.openhab.binding.zigbee/src/main/resources/discovery.txt
index f8fda708c..7e3b3f75d 100644
--- a/org.openhab.binding.zigbee/src/main/resources/discovery.txt
+++ b/org.openhab.binding.zigbee/src/main/resources/discovery.txt
@@ -5,5 +5,6 @@ bitron-video-902010-23,vendor=Bitron Home,modelId=902010/23
bitron-video-av2010-34,vendor=Bitron Video,modelId=AV2010/34
xiaomi_lumisensorht,modelId=lumi.sensor_ht
xiaomi_lumisensor-motion,modelId=lumi.sensor_motion
+xiaomi_lumiremoteb286acn01,modelId=lumi.remote.b286acn01
innr-rc-110,vendor=innr,modelId=RC 110
osram-switch-4x-eu,vendor=OSRAM,modelId=Switch 4x EU-LIGHTIFY
diff --git a/org.openhab.binding.zigbee/src/test/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButtonTest.java b/org.openhab.binding.zigbee/src/test/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButtonTest.java
index 4055a252c..2a2e1b677 100644
--- a/org.openhab.binding.zigbee/src/test/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButtonTest.java
+++ b/org.openhab.binding.zigbee/src/test/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterGenericButtonTest.java
@@ -3,10 +3,16 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import com.zsmartsystems.zigbee.zcl.ZclAttribute;
+import com.zsmartsystems.zigbee.zcl.ZclAttributeListener;
+import com.zsmartsystems.zigbee.zcl.ZclCommandListener;
+import com.zsmartsystems.zigbee.zcl.protocol.ZclClusterType;
+import com.zsmartsystems.zigbee.zcl.protocol.ZclDataType;
import org.eclipse.smarthome.core.thing.Channel;
import org.eclipse.smarthome.core.thing.CommonTriggerEvents;
import org.eclipse.smarthome.core.thing.ThingUID;
@@ -62,7 +68,7 @@ public void setup() {
}
@Test
- public void converterInitializationBindsToCorrectCluster() {
+ public void converterInitializationForCommandBindsToCorrectCluster() {
channelProperties.put("zigbee_shortpress_cluster_id", "0x0008");
channelProperties.put("zigbee_shortpress_command_id", "0x0017");
@@ -72,6 +78,22 @@ public void converterInitializationBindsToCorrectCluster() {
assertTrue(initResult);
verify(cluster, times(1)).addCommandListener(converter);
+ verify(cluster, times(0)).addAttributeListener(any(ZclAttributeListener.class));
+ }
+
+ @Test
+ public void converterInitializationForAttributeBindsToCorrectCluster() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "0x0008");
+ channelProperties.put("zigbee_shortpress_attribute_id", "0x0017");
+ channelProperties.put("zigbee_shortpress_attribute_value", "2");
+
+ ZclCluster cluster = mockCluster(8);
+
+ boolean initResult = converter.initializeConverter();
+
+ assertTrue(initResult);
+ verify(cluster, times(1)).addAttributeListener(converter);
+ verify(cluster, times(0)).addCommandListener(any(ZclCommandListener.class));
}
@Test
@@ -103,7 +125,7 @@ public void converterInitializationClusterIdIsMandatory() {
}
@Test
- public void converterInitializationCommandIdIsMandatory() {
+ public void converterInitializationCommandIdOrAttributeIsMandatory() {
channelProperties.put("zigbee_shortpress_cluster_id", "0x008");
mockCluster(8);
boolean initResult = converter.initializeConverter();
@@ -127,6 +149,16 @@ public void converterCannotInitializeWithUnparseableCommandId() {
assertFalse(initResult);
}
+ @Test
+ public void converterCannotInitializeWithUnparseableAttributeId() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "0x8");
+ channelProperties.put("zigbee_shortpress_attribute_id", "abc");
+ channelProperties.put("zigbee_shortpress_attribute_value", "abc");
+ mockCluster(8);
+ boolean initResult = converter.initializeConverter();
+ assertFalse(initResult);
+ }
+
@Test
public void converterCannotInitializeWithIncompleteParamSpecWithoutValue() {
channelProperties.put("zigbee_shortpress_cluster_id", "0x8");
@@ -147,6 +179,26 @@ public void converterCannotInitializeWithIncompleteParamSpecWithoutName() {
assertFalse(initResult);
}
+ @Test
+ public void converterCannotInitializeWithIncompleteAttributeSpec() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "0x8");
+ channelProperties.put("zigbee_shortpress_attribute_id", "1");
+ mockCluster(8);
+ boolean initResult = converter.initializeConverter();
+ assertFalse(initResult);
+ }
+
+ @Test
+ public void converterCannotInitializeWithCommandAndAttribute() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "0x0008");
+ channelProperties.put("zigbee_shortpress_command_id", "0xabc");
+ channelProperties.put("zigbee_shortpress_attribute_id", "1");
+ channelProperties.put("zigbee_shortpress_attribute_value", "2");
+ mockCluster(8);
+ boolean initResult = converter.initializeConverter();
+ assertFalse(initResult);
+ }
+
@Test
public void cannotInitializeConverterWithoutChannel() {
assertNull(converter.getChannel(mock(ThingUID.class), endpoint));
@@ -161,7 +213,22 @@ public void commandListenersAreRemovedOnDispose() {
converter.initializeConverter();
converter.disposeConverter();
- verify(cluster).removeCommandListener(converter);
+ verify(cluster, times(1)).removeCommandListener(converter);
+ verify(cluster, times(0)).removeAttributeListener(any(ZclAttributeListener.class));
+ }
+
+ @Test
+ public void attributeListenersAreRemovedOnDispose() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "0x0008");
+ channelProperties.put("zigbee_shortpress_attribute_id", "0x0017");
+ channelProperties.put("zigbee_shortpress_attribute_value", "2");
+ ZclCluster cluster = mockCluster(8);
+
+ converter.initializeConverter();
+ converter.disposeConverter();
+
+ verify(cluster, times(1)).removeAttributeListener(converter);
+ verify(cluster, times(0)).removeCommandListener(any(ZclCommandListener.class));
}
@Test
@@ -196,6 +263,27 @@ public void commandWithMatchingSpecifiedParamIsHandled() {
verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.DOUBLE_PRESSED);
}
+ @Test
+ public void attributeWithMatchingValueIsHandled() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "768");
+ channelProperties.put("zigbee_shortpress_attribute_id", "85");
+ channelProperties.put("zigbee_shortpress_attribute_value", "1");
+ mockCluster(768);
+ converter.initializeConverter();
+
+ ZclAttribute attribute = new ZclAttribute(
+ ZclClusterType.MULTISTATE_INPUT__BASIC, 85, "foo",
+ ZclDataType.UNSIGNED_16_BIT_INTEGER,
+ false, false, true, true);
+ attribute.updateValue(1);
+ converter.attributeUpdated(attribute);
+
+ verify(thingHandler, times(1)).triggerChannel(channel.getUID(), CommonTriggerEvents.SHORT_PRESSED);
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.LONG_PRESSED);
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.DOUBLE_PRESSED);
+ }
+
+
@Test
public void commandWithNonMatchingSpecifiedParamNameIsNotHandled() {
channelProperties.put("zigbee_shortpress_cluster_id", "768");
@@ -232,6 +320,27 @@ public void commandWithNonMatchingSpecifiedParamValueIsNotHandled() {
verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.DOUBLE_PRESSED);
}
+ @Test
+ public void attributeWithNonMatchingValueIsNotHandled() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "768");
+ channelProperties.put("zigbee_shortpress_attribute_id", "85");
+ channelProperties.put("zigbee_shortpress_attribute_value", "1");
+ mockCluster(768);
+ converter.initializeConverter();
+
+ ZclAttribute attribute = new ZclAttribute(
+ ZclClusterType.MULTISTATE_INPUT__BASIC, 85, "foo",
+ ZclDataType.UNSIGNED_16_BIT_INTEGER,
+ false, false, true, true);
+ attribute.updateValue(2);
+ converter.attributeUpdated(attribute);
+
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.SHORT_PRESSED);
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.LONG_PRESSED);
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.DOUBLE_PRESSED);
+ }
+
+
@Test
public void commandTypeIsCorrectlyDetected() {
channelProperties.put("zigbee_shortpress_cluster_id", "0x0008");
@@ -253,16 +362,42 @@ public void commandTypeIsCorrectlyDetected() {
verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.DOUBLE_PRESSED);
}
- private ZclCluster mockCluster(int clusterId) {
- IeeeAddress ieeeAddress = new IeeeAddress();
+ @Test
+ public void attributeTypeIsCorrectlyDetected() {
+ channelProperties.put("zigbee_shortpress_cluster_id", "0x0008");
+ channelProperties.put("zigbee_shortpress_attribute_id", "0x0017");
+ channelProperties.put("zigbee_shortpress_attribute_value", "0x01");
+ channelProperties.put("zigbee_longpress_cluster_id", "768");
+ channelProperties.put("zigbee_longpress_attribute_id", "0x0017");
+ channelProperties.put("zigbee_longpress_attribute_value", "2");
+
+ channelProperties.put("zigbee_doublepress_cluster_id", "0x0009");
+ channelProperties.put("zigbee_doublepress_attribute_id", "0x0017");
+ channelProperties.put("zigbee_doublepress_attribute_value", "0x03");
+
+ mockCluster(768);
+ converter.initializeConverter();
+
+ ZclAttribute attribute = new ZclAttribute(
+ ZclClusterType.MULTISTATE_INPUT__BASIC, 0x17, "foo",
+ ZclDataType.UNSIGNED_16_BIT_INTEGER,
+ false, false, true, true);
+ attribute.updateValue(2);
+ converter.attributeUpdated(attribute);
+
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.SHORT_PRESSED);
+ verify(thingHandler, times(1)).triggerChannel(channel.getUID(), CommonTriggerEvents.LONG_PRESSED);
+ verify(thingHandler, times(0)).triggerChannel(channel.getUID(), CommonTriggerEvents.DOUBLE_PRESSED);
+ }
+
+ private ZclCluster mockCluster(int clusterId) {
ZclCluster cluster = mock(ZclCluster.class);
when(cluster.getClusterId()).thenReturn(clusterId);
- // when(cluster.bind(ieeeAddress, ZigBeeProfileType.ZIGBEE_HOME_AUTOMATION.getKey()))
when(cluster.bind(ArgumentMatchers.any(IeeeAddress.class), ArgumentMatchers.anyInt()))
.thenReturn(CompletableFuture.completedFuture(new CommandResult(new ZigBeeCommand())));
when(endpoint.getOutputCluster(clusterId)).thenReturn(cluster);
+ when(endpoint.getInputCluster(clusterId)).thenReturn(cluster);
return cluster;
}
-
}