]> git.basschouten.com Git - openhab-addons.git/commitdiff
[nuki] Support for SmartLock 3.0 and SmartDoor (#12005)
authorJan Vybíral <jan.vybiral1@gmail.com>
Mon, 10 Jan 2022 08:36:28 +0000 (09:36 +0100)
committerGitHub <noreply@github.com>
Mon, 10 Jan 2022 08:36:28 +0000 (09:36 +0100)
Signed-off-by: Jan Vybíral <jan.vybiral1@gmail.com>
bundles/org.openhab.binding.nuki/README.md
bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/configuration/NukiDeviceConfiguration.java
bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/constants/NukiBindingConstants.java
bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/dataexchange/NukiHttpClient.java
bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/discovery/NukiDeviceDiscoveryService.java
bundles/org.openhab.binding.nuki/src/main/java/org/openhab/binding/nuki/internal/handler/NukiSmartLockHandler.java
bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/i18n/nuki.properties
bundles/org.openhab.binding.nuki/src/main/resources/OH-INF/thing/thing-types.xml

index 7ab814fbfaf27757c4c7cf8bc059b7e4ae470c21..d42225f0d108889ce5345d59e8e5e580fb422c73 100644 (file)
@@ -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 ]
 }
 ```
 
index b1e2ae72e8f72938ef834af52d6c9f69e936cabc..87edfb9d5fcc22f38a04b66e4e075567b5f1356a 100644 (file)
@@ -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<Integer> 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";
index 65e618a54a4fcebd8e09d88a1ed7114ba207128d..bd9e4bbb9090df220105f6b22c0c3cb9e6b14317 100644 (file)
@@ -163,8 +163,8 @@ public class NukiHttpClient {
         }
     }
 
-    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) {
index 4c92417bb2a2c7b93c3ba80ec89292365cebac66..cfaa93f61ad479e65b0ed685ec0e510fe3b6c2a2 100644 (file)
@@ -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 @@ public class NukiDeviceDiscoveryService extends AbstractDiscoveryService impleme
         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<DiscoveryResult> 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;
         }
     }
 
index 9e3d81fb8521594e62b6e52c1a02b57275997a80..50caba669c554f546be17e018ca6c77e3c6e2411 100644 (file)
@@ -61,7 +61,7 @@ public class NukiSmartLockHandler extends AbstractNukiDeviceHandler<NukiSmartLoc
 
     @Override
     protected int getDeviceType() {
-        return NukiBindingConstants.DEVICE_SMART_LOCK;
+        return this.configuration.deviceType;
     }
 
     @Override
@@ -79,7 +79,7 @@ public class NukiSmartLockHandler extends AbstractNukiDeviceHandler<NukiSmartLoc
 
                     withHttpClient(client -> {
                         BridgeLockActionResponse bridgeLockActionResponse = client
-                                .getSmartLockAction(configuration.nukiId, action);
+                                .getSmartLockAction(configuration.nukiId, action, getDeviceType());
                         handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString());
                     });
 
@@ -93,7 +93,7 @@ public class NukiSmartLockHandler extends AbstractNukiDeviceHandler<NukiSmartLoc
                     if (action != null) {
                         withHttpClient(client -> {
                             BridgeLockActionResponse bridgeLockActionResponse = client
-                                    .getSmartLockAction(configuration.nukiId, action);
+                                    .getSmartLockAction(configuration.nukiId, action, getDeviceType());
                             handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString());
                         });
                     }
index bbe646563e2ebcd9d8852062f623afb7e5f453ef..5cc074d649b0b6c58666f766099259c8fe2b92b8 100644 (file)
@@ -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
index 64fd2d76fed352ca8f627275ca84e3a5cc03c041..d4359a162ef39ce0f5c3d9af62fa627c998ba5e8 100644 (file)
                                <label>Nuki ID</label>
                                <description>The decimal string that identifies the Nuki Smart Lock.</description>
                        </parameter>
+                       <parameter name="deviceType" type="integer" required="true" readOnly="true">
+                               <label>Device Type</label>
+                               <default>0</default>
+                               <options>
+                                       <option value="0">Nuki Smart Lock 1.0/2.0</option>
+                                       <option value="3">Nuki Smart Door</option>
+                                       <option value="4">Nuki Smart Lock 3.0 (Pro)</option>
+                               </options>
+                               <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.
+                               </description>
+                       </parameter>
                </config-description>
        </thing-type>
 
                <category>Door</category>
                <state readOnly="true">
                        <options>
-                               <option value="0">Unavailable</option>
                                <option value="1">Deactivated</option>
                                <option value="2">Closed</option>
                                <option value="3">Open</option>
-                               <option value="4">Unknown</option>
+                               <option value="4">Door state unknown</option>
                                <option value="5">Calibrating</option>
+                               <option value="16">Uncalibrated</option>
+                               <option value="240">Removed</option>
+                               <option value="255">Unknown</option>
                        </options>
                </state>
                <autoUpdatePolicy>veto</autoUpdatePolicy>