diff --git a/bundles/org.openhab.binding.nuki/README.md b/bundles/org.openhab.binding.nuki/README.md index 7ab814fbfaf27..d42225f0d1088 100644 --- a/bundles/org.openhab.binding.nuki/README.md +++ b/bundles/org.openhab.binding.nuki/README.md @@ -57,7 +57,7 @@ connected to is configured and online. ### Nuki Smart Lock -The following configuration options are available: +This is a common thing for all Nuki smart lock products - Nuki Smart Lock 1.0/2.0/3.0 (Pro) and Nuki Smart Door. The following configuration options are available: | Parameter | Description | Comment | |-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| @@ -106,14 +106,16 @@ Unfortunately the Nuki Bridge is not reporting any transition states (e.g. for L ##### Supported doorSensorState values - | State | Name | - |--------|--------------------------| - | 0 | Unavailable | - | 1 | Deactivated | - | 2 | Closed | - | 3 | Open | - | 4 | Unknown | - | 5 | Calibrating | + | State | Name | + |-------|---------------------| + | 1 | Deactivated | + | 2 | Closed | + | 3 | Open | + | 4 | Door state unknonwn | + | 5 | Calibrating | + | 16 | Uncalibrated | + | 240 | Removed | + | 255 | Unknown | ### Nuki Opener @@ -175,7 +177,7 @@ A manual setup through files could look like this: ``` Bridge nuki:bridge:NB1 [ ip="192.168.0.50", port=8080, apiToken="myS3cr3t!", manageCallbacks=true ] { - Thing smartlock SL1 [ nukiId="12AB89EF", unlatch=false ] + Thing smartlock SL1 [ nukiId="12AB89EF", deviceType=0, unlatch=false ] } ``` diff --git a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/configuration/NukiDeviceConfiguration.java b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/configuration/NukiDeviceConfiguration.java index 9fb002b2cfac9..8f1ed0b8084be 100644 --- a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/configuration/NukiDeviceConfiguration.java +++ b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/configuration/NukiDeviceConfiguration.java @@ -22,4 +22,5 @@ @NonNullByDefault public class NukiDeviceConfiguration { public String nukiId = ""; + public int deviceType; } diff --git a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/constants/NukiBindingConstants.java b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/constants/NukiBindingConstants.java index b1e2ae72e8f72..87edfb9d5fcc2 100644 --- a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/constants/NukiBindingConstants.java +++ b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/constants/NukiBindingConstants.java @@ -49,7 +49,8 @@ public class NukiBindingConstants { // Device Types public static final int DEVICE_SMART_LOCK = 0; public static final int DEVICE_OPENER = 2; - public static final Set SUPPORTED_DEVICES = Set.of(DEVICE_OPENER, DEVICE_SMART_LOCK); + public static final int DEVICE_SMART_DOOR = 3; + public static final int DEVICE_SMART_LOCK_3 = 4; // Properties public static final String PROPERTY_WIFI_FIRMWARE_VERSION = "wifiFirmwareVersion"; @@ -59,6 +60,7 @@ public class NukiBindingConstants { public static final String PROPERTY_NAME = "name"; public static final String PROPERTY_NUKI_ID = "nukiId"; public static final String PROPERTY_BRIDGE_ID = "bridgeId"; + public static final String PROPERTY_DEVICE_TYPE = "deviceType"; // List of all Smart Lock Channel ids public static final String CHANNEL_SMARTLOCK_LOCK = "lock"; diff --git a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/dataexchange/NukiHttpClient.java b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/dataexchange/NukiHttpClient.java index 65e618a54a4fc..bd9e4bbb9090d 100644 --- a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/dataexchange/NukiHttpClient.java +++ b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/dataexchange/NukiHttpClient.java @@ -163,8 +163,8 @@ public BridgeLockStateResponse getBridgeLockState(String nukiId, int deviceType) } } - public BridgeLockActionResponse getSmartLockAction(String nukiId, SmartLockAction action) { - return getBridgeLockAction(nukiId, action.getAction(), NukiBindingConstants.DEVICE_SMART_LOCK); + public BridgeLockActionResponse getSmartLockAction(String nukiId, SmartLockAction action, int deviceType) { + return getBridgeLockAction(nukiId, action.getAction(), deviceType); } public BridgeLockActionResponse getOpenerAction(String nukiId, OpenerAction action) { diff --git a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/discovery/NukiDeviceDiscoveryService.java b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/discovery/NukiDeviceDiscoveryService.java index 4c92417bb2a2c..cfaa93f61ad47 100644 --- a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/discovery/NukiDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/discovery/NukiDeviceDiscoveryService.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.nuki.internal.discovery; +import java.util.Optional; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -56,27 +57,41 @@ protected void startScan() { scheduler.execute(() -> { bridgeHandler.withHttpClient(client -> { BridgeListResponse list = client.getList(); - list.getDevices().stream() - .filter(device -> NukiBindingConstants.SUPPORTED_DEVICES.contains(device.getDeviceType())) - .map(device -> createDiscoveryResult(device, bridgeHandler)).forEach(this::thingDiscovered); + list.getDevices().stream().map(device -> createDiscoveryResult(device, bridgeHandler)) + .flatMap(Optional::stream).forEach(this::thingDiscovered); }); }); } - private DiscoveryResult createDiscoveryResult(BridgeApiListDeviceDto device, NukiBridgeHandler bridgeHandler) { - return DiscoveryResultBuilder.create(getUid(device.getNukiId(), device.getDeviceType(), bridgeHandler)) - .withBridge(bridgeHandler.getThing().getUID()).withLabel(device.getName()) - .withRepresentationProperty(NukiBindingConstants.PROPERTY_NUKI_ID) - .withProperty(NukiBindingConstants.PROPERTY_NAME, device.getName()) - .withProperty(NukiBindingConstants.PROPERTY_NUKI_ID, device.getNukiId()) - .withProperty(NukiBindingConstants.PROPERTY_FIRMWARE_VERSION, device.getFirmwareVersion()).build(); + private Optional createDiscoveryResult(BridgeApiListDeviceDto device, + NukiBridgeHandler bridgeHandler) { + ThingUID uid = getUid(device.getNukiId(), device.getDeviceType(), bridgeHandler); + if (uid == null) { + logger.warn("Failed to create UID for device '{}' - deviceType '{}' is not supported", device, + device.getDeviceType()); + return Optional.empty(); + } else { + return Optional.of(DiscoveryResultBuilder.create(uid).withBridge(bridgeHandler.getThing().getUID()) + .withLabel(device.getName()).withRepresentationProperty(NukiBindingConstants.PROPERTY_NUKI_ID) + .withProperty(NukiBindingConstants.PROPERTY_NAME, device.getName()) + .withProperty(NukiBindingConstants.PROPERTY_NUKI_ID, device.getNukiId()) + .withProperty(NukiBindingConstants.PROPERTY_DEVICE_TYPE, device.getDeviceType()) + .withProperty(NukiBindingConstants.PROPERTY_FIRMWARE_VERSION, device.getFirmwareVersion()).build()); + } } + @Nullable private ThingUID getUid(String nukiId, int deviceType, NukiBridgeHandler bridgeHandler) { - if (deviceType == NukiBindingConstants.DEVICE_OPENER) { - return new ThingUID(NukiBindingConstants.THING_TYPE_OPENER, bridgeHandler.getThing().getUID(), nukiId); - } else { - return new ThingUID(NukiBindingConstants.THING_TYPE_SMARTLOCK, bridgeHandler.getThing().getUID(), nukiId); + switch (deviceType) { + case NukiBindingConstants.DEVICE_OPENER: + return new ThingUID(NukiBindingConstants.THING_TYPE_OPENER, bridgeHandler.getThing().getUID(), nukiId); + case NukiBindingConstants.DEVICE_SMART_LOCK: + case NukiBindingConstants.DEVICE_SMART_DOOR: + case NukiBindingConstants.DEVICE_SMART_LOCK_3: + return new ThingUID(NukiBindingConstants.THING_TYPE_SMARTLOCK, bridgeHandler.getThing().getUID(), + nukiId); + default: + return null; } } diff --git a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/handler/NukiSmartLockHandler.java b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/handler/NukiSmartLockHandler.java index 9e3d81fb85215..50caba669c554 100644 --- a/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/handler/NukiSmartLockHandler.java +++ b/bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/handler/NukiSmartLockHandler.java @@ -61,7 +61,7 @@ public void refreshState(BridgeApiDeviceStateDto state) { @Override protected int getDeviceType() { - return NukiBindingConstants.DEVICE_SMART_LOCK; + return this.configuration.deviceType; } @Override @@ -79,7 +79,7 @@ protected boolean doHandleCommand(ChannelUID channelUID, Command command) { withHttpClient(client -> { BridgeLockActionResponse bridgeLockActionResponse = client - .getSmartLockAction(configuration.nukiId, action); + .getSmartLockAction(configuration.nukiId, action, getDeviceType()); handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString()); }); @@ -93,7 +93,7 @@ protected boolean doHandleCommand(ChannelUID channelUID, Command command) { if (action != null) { withHttpClient(client -> { BridgeLockActionResponse bridgeLockActionResponse = client - .getSmartLockAction(configuration.nukiId, action); + .getSmartLockAction(configuration.nukiId, action, getDeviceType()); handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString()); }); } diff --git a/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/i18n/nuki.properties b/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/i18n/nuki.properties index bbe646563e2eb..5cc074d649b0b 100644 --- a/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/i18n/nuki.properties +++ b/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/i18n/nuki.properties @@ -26,6 +26,8 @@ thing-type.config.nuki.bridge.secureToken.label = Secure Token thing-type.config.nuki.bridge.secureToken.description = Use hashed token when communicating with bridge. This increases security and prevents sniffing of access token and replay attacks, since communication with bridge is not encrypted. For this feature to work, both device running openHAB and Nuki Bridge must have synchronized time. When disabled, token is sent in plain text with each bridge request. It is recommended that this is turned on unless there are problems with synchronizing time between openHAB and Nuki Bridge. thing-type.config.nuki.opener.nukiId.label = Nuki ID thing-type.config.nuki.opener.nukiId.description = The decimal string that identifies the Nuki Opener. +thing-type.config.nuki.smartlock.deviceType.label = Device Type +thing-type.config.nuki.smartlock.deviceType.description = Numeric device type as specified by bridge HTTP API - 0 = Nuki Smart Lock 1.0/2.0, 3 = Nuki Smart Door, 4 = Nuki Smart Lock 3.0 (Pro). Sent with each API request. Its purpose is not documented, seems to only be used for distinguishing between opener and smartlock actions. There does not seem to be any (documented or observable) differences between different smart lock device types. thing-type.config.nuki.smartlock.nukiId.label = Nuki ID thing-type.config.nuki.smartlock.nukiId.description = The decimal string that identifies the Nuki Smart Lock. thing-type.config.nuki.smartlock.unlatch.label = Unlatch @@ -61,12 +63,14 @@ channel-type.nuki.smartLockBatteryCharging.state.option.OFF = Battery is not cha channel-type.nuki.smartLockBatteryCharging.state.option.ON = Battery is charging channel-type.nuki.smartlockDoorState.label = Door State channel-type.nuki.smartlockDoorState.description = Use this channel to display the current state of the door sensor -channel-type.nuki.smartlockDoorState.state.option.0 = Unavailable channel-type.nuki.smartlockDoorState.state.option.1 = Deactivated channel-type.nuki.smartlockDoorState.state.option.2 = Closed channel-type.nuki.smartlockDoorState.state.option.3 = Open -channel-type.nuki.smartlockDoorState.state.option.4 = Unknown +channel-type.nuki.smartlockDoorState.state.option.4 = Door state unknown channel-type.nuki.smartlockDoorState.state.option.5 = Calibrating +channel-type.nuki.smartlockDoorState.state.option.16 = Uncalibrated +channel-type.nuki.smartlockDoorState.state.option.240 = Removed +channel-type.nuki.smartlockDoorState.state.option.255 = Unknown channel-type.nuki.smartlockLock.label = Lock channel-type.nuki.smartlockLock.description = Use this channel with a Switch Item to unlock and lock the door. Configure "Unlatch" to true if your Nuki Smart Lock is mounted on a door lock with a knob on the outside. channel-type.nuki.smartlockLock.state.option.OFF = Unlocks the door diff --git a/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/thing/thing-types.xml index 64fd2d76fed35..d4359a162ef39 100644 --- a/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/thing/thing-types.xml @@ -92,6 +92,23 @@ The decimal string that identifies the Nuki Smart Lock. + + + 0 + + + + + + + Numeric device type as specified by bridge HTTP API - 0 = Nuki Smart Lock 1.0/2.0, 3 = Nuki Smart Door, + 4 = Nuki Smart Lock 3.0 (Pro). + Sent with each API request. Its purpose is not documented, seems to only be used for + distinguishing between opener and smartlock actions. + There does not seem to be any (documented or observable) + differences between different smart lock device types. + + @@ -191,12 +208,14 @@ Door - - + + + + veto