mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[zwavejs] Add notification channel (#19328)
* Add notification channel * Add documentation * Fix value being updated correctly * Update bundles/org.openhab.binding.zwavejs/src/main/java/org/openhab/binding/zwavejs/internal/type/ZwaveJSTypeGeneratorImpl.java Signed-off-by: Leo Siepel <leosiepel@gmail.com> Signed-off-by: lsiepel <leosiepel@gmail.com>
This commit is contained in:
@@ -57,6 +57,14 @@ Z-Wave nodes can have multiple channels corresponding to their capabilities.
|
||||
The channels can be linked to items in openHAB to control and monitor the device.
|
||||
These channels are dynamically added to the Thing during node initialization; therefore, there is no list of possible channels in this documentation.
|
||||
|
||||
### Notification-Virtual Channel
|
||||
|
||||
The binding automatically adds a `notification-virtual` channel for endpoints that have both a Notification (Alarm) and a Door Lock command class.
|
||||
This virtual channel is designed to provide a unified state update when a notification event occurs.
|
||||
It provides the event payload in JSON as a string value.
|
||||
A common scenario is using this JSON to determine the userId that unlocked a door lock.
|
||||
This channel is added automatically; no manual configuration is required.
|
||||
|
||||
## Channel Configuration and Inversion
|
||||
|
||||
Channels in the `zwavejs` binding support an **inversion** configuration option.
|
||||
|
||||
+5
@@ -91,4 +91,9 @@ public class BindingConstants {
|
||||
public static final String VIRTUAL_ROLLERSHUTTER_PROPERTY = "virtual";
|
||||
public static final String VIRTUAL_ROLLERSHUTTER_CHANNEL_ID = String.format("%s-%s",
|
||||
VIRTUAL_COMMAND_CLASS_ROLLERSHUTTER, VIRTUAL_ROLLERSHUTTER_PROPERTY);
|
||||
|
||||
public static final String VIRTUAL_COMMAND_CLASS_NOTIFICATION = "notification";
|
||||
public static final String VIRTUAL_NOTIFICATION_PROPERTY = "virtual";
|
||||
public static final String VIRTUAL_NOTIFICATION_CHANNEL_ID = String.format("%s-%s",
|
||||
VIRTUAL_COMMAND_CLASS_NOTIFICATION, VIRTUAL_NOTIFICATION_PROPERTY);
|
||||
}
|
||||
|
||||
+1
-1
@@ -299,7 +299,7 @@ public class ZWaveJSClient implements WebSocketListener {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Error invoking event listener on websockettext: {}", e.toString());
|
||||
logger.debug("Error invoking event listener on websockettext: {}", e.toString(), e);
|
||||
} else {
|
||||
logger.warn("Error invoking event listener on websockettext");
|
||||
}
|
||||
|
||||
+6
@@ -24,4 +24,10 @@ public class Args {
|
||||
public Object prevValue;
|
||||
public String propertyName;
|
||||
public Object propertyKey;
|
||||
|
||||
public int event;
|
||||
public int type;
|
||||
public String label;
|
||||
public String eventLabel;
|
||||
public Object parameters;
|
||||
}
|
||||
|
||||
+2
@@ -21,6 +21,8 @@ public class Event {
|
||||
public int nodeId;
|
||||
public Args args;
|
||||
public Node node;
|
||||
public int ccId;
|
||||
public int endpointIndex;
|
||||
|
||||
enum Source {
|
||||
driver,
|
||||
|
||||
+8
@@ -160,6 +160,14 @@ public abstract class BaseMetadata {
|
||||
this.factor = 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given property name is either the first occurrence of its mapped value
|
||||
* in the {@code CHANNEL_ID_PROPERTY_NAME_REPLACEMENTS} map or if it is not mapped at all.
|
||||
*
|
||||
* @param propertyName the name of the property to check; may be {@code null}.
|
||||
* @return {@code true} if the property name is the first occurrence of its mapped value,
|
||||
* not mapped, or {@code null}; {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isFirstOrNotMapped(@Nullable String propertyName) {
|
||||
if (propertyName == null || propertyName.contains("unknown")) {
|
||||
return true;
|
||||
|
||||
+24
@@ -12,6 +12,9 @@
|
||||
*/
|
||||
package org.openhab.binding.zwavejs.internal.handler;
|
||||
|
||||
import static org.openhab.binding.zwavejs.internal.BindingConstants.VIRTUAL_COMMAND_CLASS_NOTIFICATION;
|
||||
import static org.openhab.binding.zwavejs.internal.BindingConstants.VIRTUAL_NOTIFICATION_PROPERTY;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -56,6 +59,8 @@ import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link ZwaveJSBridgeHandler} is responsible for handling communication between the
|
||||
* {@link ZwaveJSNodeHandler} 's and the {@link ZWaveJSClient} This handler also manages node discovery
|
||||
@@ -166,6 +171,12 @@ public class ZwaveJSBridgeHandler extends BaseBridgeHandler implements ZwaveEven
|
||||
String eventType = eventMsg.event.event;
|
||||
ZwaveNodeListener nodeListener = nodeListeners.get(eventMsg.event.nodeId);
|
||||
switch (eventType) {
|
||||
case "notification":
|
||||
if (nodeListener != null) {
|
||||
Event event = normalizeNotificationEvent(eventMsg.event);
|
||||
nodeListener.onNodeStateChanged(event);
|
||||
}
|
||||
break;
|
||||
case "value updated":
|
||||
case "value notification":
|
||||
if (nodeListener != null) {
|
||||
@@ -200,6 +211,19 @@ public class ZwaveJSBridgeHandler extends BaseBridgeHandler implements ZwaveEven
|
||||
}
|
||||
}
|
||||
|
||||
public static Event normalizeNotificationEvent(Event event) {
|
||||
Event normalizedEvent = new Event();
|
||||
normalizedEvent.event = event.event;
|
||||
normalizedEvent.args = new Args();
|
||||
normalizedEvent.args.commandClass = event.ccId;
|
||||
normalizedEvent.args.commandClassName = VIRTUAL_COMMAND_CLASS_NOTIFICATION;
|
||||
normalizedEvent.args.propertyName = VIRTUAL_NOTIFICATION_PROPERTY;
|
||||
normalizedEvent.args.endpoint = event.endpointIndex;
|
||||
normalizedEvent.args.newValue = new Gson().toJson(event.args);
|
||||
normalizedEvent.nodeId = event.nodeId;
|
||||
return normalizedEvent;
|
||||
}
|
||||
|
||||
private @Nullable Event createEventFromMessageId(String messageId, @Nullable Object value) {
|
||||
// Example messageId: getvalue|0|51|Color Switch|2|currentColor|44|2466
|
||||
String[] parts = messageId.split("\\|");
|
||||
|
||||
+73
-18
@@ -13,6 +13,8 @@
|
||||
package org.openhab.binding.zwavejs.internal.type;
|
||||
|
||||
import static org.openhab.binding.zwavejs.internal.BindingConstants.*;
|
||||
import static org.openhab.binding.zwavejs.internal.CommandClassConstants.COMMAND_CLASS_ALARM;
|
||||
import static org.openhab.binding.zwavejs.internal.CommandClassConstants.COMMAND_CLASS_DOOR_LOCK;
|
||||
import static org.openhab.binding.zwavejs.internal.CommandClassConstants.COMMAND_CLASS_SWITCH_COLOR;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -24,6 +26,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -82,7 +85,6 @@ import org.slf4j.LoggerFactory;
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class ZwaveJSTypeGeneratorImpl implements ZwaveJSTypeGenerator {
|
||||
|
||||
private static final Object CHANNEL_TYPE_VERSION = "6"; // when static configuration is changed, the version must be
|
||||
// changed as well to force new channel type generation
|
||||
private static final Map<String, SemanticTag> ITEM_TYPES_TO_PROPERTY_TAGS = new HashMap<>();
|
||||
@@ -182,6 +184,9 @@ public class ZwaveJSTypeGeneratorImpl implements ZwaveJSTypeGenerator {
|
||||
// add a color temperature channel if necessary
|
||||
addColorTemperatureChannel(thingUID, node, result);
|
||||
|
||||
// add raw notification channel if necessary
|
||||
addRawNotificationChannel(thingUID, node, result);
|
||||
|
||||
logger.debug("Node {}. Generated {} channels and {} configDescriptions with URI {}", node.nodeId,
|
||||
result.channels.size(), configDescriptions.size(), uri);
|
||||
|
||||
@@ -191,6 +196,67 @@ public class ZwaveJSTypeGeneratorImpl implements ZwaveJSTypeGenerator {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addRawNotificationChannel(ThingUID thingUID, Node node, ZwaveJSTypeGeneratorResult result) {
|
||||
// loop all channels to find endpoints with a notification CC or a door lock CC
|
||||
Map<Integer, List<Integer>> grouped = result.channels.values().stream()
|
||||
.map(channel -> channel.getConfiguration().as(ZwaveJSChannelConfiguration.class))
|
||||
.filter(config -> COMMAND_CLASS_ALARM == config.commandClassId
|
||||
|| COMMAND_CLASS_DOOR_LOCK == config.commandClassId)
|
||||
.collect(Collectors.groupingBy(config -> config.endpoint,
|
||||
Collectors.mapping(config -> config.commandClassId, Collectors.toList())));
|
||||
|
||||
// 2. Find endpoints with both Notification and Door Lock CCs
|
||||
List<Integer> endpoints = grouped.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().contains(COMMAND_CLASS_ALARM)
|
||||
&& entry.getValue().contains(COMMAND_CLASS_DOOR_LOCK))
|
||||
.map(Map.Entry::getKey).toList();
|
||||
|
||||
for (Integer endpoint : endpoints) {
|
||||
createRawNotificationChannel(thingUID, node, result, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void createRawNotificationChannel(ThingUID thingUID, Node node, ZwaveJSTypeGeneratorResult result,
|
||||
int endpoint) {
|
||||
Value value = new Value();
|
||||
value.endpoint = endpoint;
|
||||
value.commandClass = COMMAND_CLASS_ALARM;
|
||||
value.commandClassName = VIRTUAL_COMMAND_CLASS_NOTIFICATION;
|
||||
value.propertyKey = VIRTUAL_NOTIFICATION_PROPERTY;
|
||||
value.property = VIRTUAL_NOTIFICATION_PROPERTY;
|
||||
value.metadata = new Metadata();
|
||||
value.metadata.type = MetadataType.STRING;
|
||||
value.metadata.writeable = false;
|
||||
value.metadata.label = "Raw Notification";
|
||||
value.metadata.description = "Notification channel that updates on alarm events";
|
||||
value.metadata.readable = true;
|
||||
|
||||
ChannelMetadata details = new ChannelMetadata(node.nodeId, value);
|
||||
if (!result.channels.containsKey(details.id)) {
|
||||
logger.trace("Node {} building channel with Id: {}", details.nodeId, details.id);
|
||||
logger.trace(" >> {}", details);
|
||||
|
||||
ChannelTypeUID channelTypeUID = generateChannelTypeUID(details);
|
||||
ChannelType type = getOrGenerate(channelTypeUID, details);
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
Configuration config = buildChannelConfiguration(details);
|
||||
|
||||
Channel channel = ChannelBuilder.create(new ChannelUID(thingUID, details.id), CoreItemFactory.STRING)
|
||||
.withType(type.getUID()) //
|
||||
.withDefaultTags(type.getTags()) //
|
||||
.withKind(type.getKind()) //
|
||||
.withLabel(details.label) //
|
||||
.withDescription(Objects.requireNonNull(details.description)) //
|
||||
.withAutoUpdatePolicy(type.getAutoUpdatePolicy()) //
|
||||
.withConfiguration(config) //
|
||||
.build();
|
||||
|
||||
result.channels.put(details.id, channel);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigDescriptionParameter createConfigDescription(ConfigMetadata details) {
|
||||
logger.trace("Node {}. createConfigDescriptions with Id: {}", details.nodeId, details.id);
|
||||
|
||||
@@ -482,23 +548,12 @@ public class ZwaveJSTypeGeneratorImpl implements ZwaveJSTypeGenerator {
|
||||
|| keyWords.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
label = label != null ? label.toLowerCase() : "";
|
||||
description = description != null ? description.toLowerCase() : "";
|
||||
for (String keyWord : keyWords) {
|
||||
String keyWordLowercase = keyWord.toLowerCase();
|
||||
if (label.contains(keyWordLowercase) || description.contains(keyWordLowercase)) {
|
||||
return true;
|
||||
}
|
||||
if (label.endsWith(keyWordLowercase.stripTrailing())
|
||||
|| description.endsWith(keyWordLowercase.stripTrailing())) {
|
||||
return true;
|
||||
}
|
||||
if (label.startsWith(keyWordLowercase.stripLeading())
|
||||
|| description.startsWith(keyWordLowercase.stripLeading())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
String labelLower = label != null ? label.toLowerCase() : "";
|
||||
String descLower = description != null ? description.toLowerCase() : "";
|
||||
return keyWords.stream().map(String::toLowerCase)
|
||||
.anyMatch(kw -> labelLower.contains(kw) || descLower.contains(kw)
|
||||
|| labelLower.endsWith(kw.stripTrailing()) || descLower.endsWith(kw.stripTrailing())
|
||||
|| labelLower.startsWith(kw.stripLeading()) || descLower.startsWith(kw.stripLeading()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+128
@@ -19,13 +19,22 @@ import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.zwavejs.internal.DataUtil;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.Event;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.Node;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.Result;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.State;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.Status;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.messages.EventMessage;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.messages.ResultMessage;
|
||||
import org.openhab.binding.zwavejs.internal.api.dto.messages.VersionMessage;
|
||||
import org.openhab.binding.zwavejs.internal.discovery.NodeDiscoveryService;
|
||||
import org.openhab.binding.zwavejs.internal.handler.mock.ZwaveJSBridgeHandlerMock;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
@@ -86,4 +95,123 @@ public class ZwaveJSBridgeHandlerTest {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnEventWithVersionMessage() {
|
||||
final Bridge thing = ZwaveJSBridgeHandlerMock.mockBridge("localhost");
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
final ZwaveJSBridgeHandler handler = ZwaveJSBridgeHandlerMock.createAndInitHandler(callback, thing);
|
||||
|
||||
VersionMessage versionMessage = new VersionMessage();
|
||||
versionMessage.driverVersion = "1.0.0";
|
||||
versionMessage.serverVersion = "1.2.3";
|
||||
versionMessage.minSchemaVersion = 1;
|
||||
versionMessage.maxSchemaVersion = 3;
|
||||
versionMessage.homeId = 12345;
|
||||
|
||||
handler.onEvent(versionMessage);
|
||||
|
||||
try {
|
||||
verify(thing).setProperties(argThat(properties -> properties.containsKey("driverVersion")
|
||||
&& properties.get("driverVersion").equals("1.0.0") && properties.containsKey("serverVersion")
|
||||
&& properties.get("serverVersion").equals("1.2.3") && properties.containsKey("minSchemaVersion")
|
||||
&& properties.get("minSchemaVersion").equals("1") && properties.containsKey("maxSchemaVersion")
|
||||
&& properties.get("maxSchemaVersion").equals("3") && properties.containsKey("homeId")
|
||||
&& properties.get("homeId").equals("12345")));
|
||||
} finally {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnEventWithResultMessageGetValue() {
|
||||
final Bridge thing = ZwaveJSBridgeHandlerMock.mockBridge("localhost");
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
final ZwaveJSBridgeHandlerMock handler = ZwaveJSBridgeHandlerMock.createAndInitHandler(callback, thing);
|
||||
|
||||
ZwaveNodeListener nodeListener = mock(ZwaveNodeListener.class);
|
||||
when(nodeListener.getId()).thenReturn(3);
|
||||
handler.registerNodeListener(nodeListener);
|
||||
|
||||
ResultMessage resultMessage = new ResultMessage();
|
||||
resultMessage.messageId = "getvalue|1|2|CommandClass|propertyKey|propertyName|3";
|
||||
resultMessage.result = new Result();
|
||||
resultMessage.result.value = "testValue";
|
||||
|
||||
handler.onEvent(resultMessage);
|
||||
|
||||
try {
|
||||
verify(nodeListener).onNodeStateChanged(any(Event.class));
|
||||
} finally {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnEventWithResultMessageStateUpdate() {
|
||||
final Bridge thing = ZwaveJSBridgeHandlerMock.mockBridge("localhost");
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
final ZwaveJSBridgeHandler handler = ZwaveJSBridgeHandlerMock.createAndInitHandler(callback, thing);
|
||||
|
||||
ResultMessage resultMessage = new ResultMessage();
|
||||
resultMessage.result = new Result();
|
||||
resultMessage.result.state = new State();
|
||||
resultMessage.result.state.nodes = List.of(new Node() {
|
||||
{
|
||||
nodeId = 1;
|
||||
status = Status.ALIVE;
|
||||
}
|
||||
});
|
||||
|
||||
handler.onEvent(resultMessage);
|
||||
|
||||
try {
|
||||
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
|
||||
} finally {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnEventWithEventMessageNodeAdded() {
|
||||
final Bridge thing = ZwaveJSBridgeHandlerMock.mockBridge("localhost");
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
final ZwaveJSBridgeHandlerMock handler = ZwaveJSBridgeHandlerMock.createAndInitHandler(callback, thing);
|
||||
final NodeDiscoveryService discoveryService = mock(NodeDiscoveryService.class);
|
||||
handler.registerDiscoveryListener(discoveryService);
|
||||
|
||||
EventMessage eventMessage = new EventMessage();
|
||||
eventMessage.event = new Event();
|
||||
eventMessage.event.event = "node added";
|
||||
eventMessage.event.node = new Node();
|
||||
eventMessage.event.node.nodeId = 5;
|
||||
eventMessage.event.node.status = Status.ALIVE;
|
||||
|
||||
handler.onEvent(eventMessage);
|
||||
|
||||
try {
|
||||
verify(discoveryService).addNodeDiscovery(eq(eventMessage.event.node));
|
||||
} finally {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnEventWithUnhandledEventType() {
|
||||
final Bridge thing = ZwaveJSBridgeHandlerMock.mockBridge("localhost");
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
final ZwaveJSBridgeHandler handler = ZwaveJSBridgeHandlerMock.createAndInitHandler(callback, thing);
|
||||
|
||||
EventMessage eventMessage = new EventMessage();
|
||||
eventMessage.event = new Event();
|
||||
eventMessage.event.event = "unhandled event";
|
||||
|
||||
handler.onEvent(eventMessage);
|
||||
|
||||
try {
|
||||
// No specific verification, just ensuring no exceptions are thrown
|
||||
} finally {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -36,6 +36,7 @@ import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
@@ -45,6 +46,8 @@ import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Leo Siepel - Initial contribution
|
||||
*/
|
||||
@@ -400,6 +403,29 @@ public class ZwaveJSNodeHandlerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNode186NotificationEventUpdate() throws IOException {
|
||||
final Thing thing = ZwaveJSNodeHandlerMock.mockThing(186);
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
final ZwaveJSNodeHandlerMock handler = ZwaveJSNodeHandlerMock.createAndInitHandler(callback, thing,
|
||||
"store_4.json");
|
||||
|
||||
EventMessage eventMessage = DataUtil.fromJson("event_node_186_notification.json", EventMessage.class);
|
||||
|
||||
handler.onNodeStateChanged(ZwaveJSBridgeHandler.normalizeNotificationEvent(eventMessage.event));
|
||||
|
||||
ChannelUID channelIdNotification = new ChannelUID("zwavejs:test-bridge:test-thing:notification-virtual");
|
||||
try {
|
||||
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
|
||||
verify(callback).statusUpdated(argThat(arg -> arg.getUID().equals(thing.getUID())),
|
||||
argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
|
||||
verify(callback, times(1)).stateUpdated(eq(channelIdNotification),
|
||||
eq(new StringType(new Gson().toJson(eventMessage.event.args))));
|
||||
} finally {
|
||||
handler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private ZwaveJSNodeHandlerMock arrangeHandleCommandTest(int nodeId) {
|
||||
final Thing thing = ZwaveJSNodeHandlerMock.mockThing(nodeId);
|
||||
final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
|
||||
|
||||
+17
@@ -356,6 +356,23 @@ public class ZwaveJSTypeGeneratorTest {
|
||||
assertNotNull(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNode186NotificationChannelDetection() throws IOException {
|
||||
Channel channel = getChannel("store_4.json", 186, "notification-virtual");
|
||||
ChannelType type = channelTypeProvider.getChannelType(Objects.requireNonNull(channel.getChannelTypeUID()),
|
||||
null);
|
||||
Configuration configuration = channel.getConfiguration();
|
||||
|
||||
assertNotNull(type);
|
||||
assertEquals("zwavejs:test-bridge:test-thing:notification-virtual", channel.getUID().getAsString());
|
||||
assertEquals("String", type.getItemType());
|
||||
assertEquals("Raw Notification", channel.getLabel());
|
||||
assertNull(configuration.get(BindingConstants.CONFIG_CHANNEL_WRITE_PROPERTY_STR));
|
||||
|
||||
StateDescription statePattern = type.getState();
|
||||
assertNull(statePattern);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenCTNode186WeirdType() throws IOException {
|
||||
Channel channel = getChannel("store_4.json", 186, "door-lock-inside-handles-can-open-door");
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "event",
|
||||
"event": {
|
||||
"source": "node",
|
||||
"event": "notification",
|
||||
"nodeId": 186,
|
||||
"endpointIndex": 0,
|
||||
"ccId": 113,
|
||||
"args": {
|
||||
"type": 6,
|
||||
"event": 5,
|
||||
"label": "Access Control",
|
||||
"eventLabel": "Keypad lock operation",
|
||||
"parameters": {
|
||||
"userId": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user