Nuki Smart Lock Integration in Home Assistant as Cover

I was always unhappy with the official NUKI integration for Home Assistant (slow and not very versatile). Inspired by the great work of @alexdelprete -
Nuki Card with callbacks (official integration not needed) I decided to try a slightly modified way.

My hardware is: Nuki Smart Lock 2, Nuki Bridge (hardware release 1), keypad

(NO door sensor, solution by @alexdelprete has to be modified slightly if you do not own one)

As mentioned multiple times in the thread link above it takes a few seconds for the smart lock to turn the key and also the bridge does not send the POST-rest-api-request to HA very fast.

So for me the Nuki Smart Lock is more like a cover than a lock.

A lock is either locked or unlocked and state changes are expected to happen “immediately”. A cover on the other hand has four states open-closing-closed-opening and the transitions (opening, closing) take some seconds. These fours states can easily be names locked - unlocking - unlocked - locking for the Nuki Smart Lock by defining a template cover. The state is defined by a variable input_text.nuki_state. On my dashboard I wanted to get a visual representation (different icons) for each of the four possible states even if they are visible only a few seconds (a UI should always show reaction to clicks and taps). This can be done with a custom button card.

So here is my modified package file (I only read the attributes of the map the POST-request gives back, no rest sensors are used for the first release):


########################
# Nuki Card v6.0-beta1 #
# modified by ww.      #
########################

#######################################################################################################################
###                                                                                                                 ###
### Automations                                                                                                     ###
###                                                                                                                 ###
#######################################################################################################################

automation:
  - id: "nuki_card_callback"
    alias: Nuki Card Callback
    description: Automation for the Nuki doorlock
    trigger:
      - platform: webhook
        webhook_id: !secret nuki_bridge_webhook
      - platform: event
        event_type: event_template_reloaded
      - platform: state
        entity_id: sensor.nuki_door_sensor_state
        to:
          - "open"
          - "closed"
    condition: []
    action:
      - choose:
          - conditions: >
              {{ trigger.platform == 'webhook' }}
            sequence:
              # state
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_state
                data:
                  value: >
                    {% set my_state = {0: 'uncalibrated', 1: 'locked', 2:'unlocking', 3: 'unlocked', 4: 'locking', 5: 'unlatched', 6: "unlocked (lock 'n' go)", 7: 'unlatching', 254: 'motor blocked', 255: 'undefined'} %}
                    {{ my_state[trigger.json.state] }}
              # stateName
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_state_name
                data:
                  value: >
                    {{ trigger.json.stateName }}
              # batteryCritical        
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_battery_critical
                data:
                  value: >
                    {{ trigger.json.batteryCritical }}
              # batteryChargeState        
              - service: input_number.set_value
                target:
                  entity_id: input_number.nuki_battery_charge_state
                data:
                  value: >
                    {{ trigger.json.batteryChargeState }}
              # keypadBatteryCritical
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_keypad_battery_critical
                data:
                  value: >
                    {{ trigger.json.batteryCritical }}
              # lastActivity
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_last_activity
                data:
                  value: >
                    {{ as_timestamp(now()) | timestamp_custom("%H:%M:%S (%b %d)") }}
              # triggerPlatform
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_trigger_platform
                data:
                  value: >
                    {{ trigger.platform }}
          - conditions: >
              {{ trigger.platform == 'state' or trigger.platform == 'event' }}
            sequence:
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_state
                data:
                  value: >
                    {{ states('sensor.nuki_lock_sensor_state') }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_last_activity
                data:
                  value: >
                    {{ states('sensor.nuki_last_activity') }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_triggerPlatform
                data:
                  value: >
                    {{ trigger.platform }}
        default: []
    mode: single

#######################################################################################################################
###                                                                                                                 ###
### Binary sensors                                                                                                  ###
###                                                                                                                 ###
#######################################################################################################################

binary_sensor:
  - platform: template
    sensors:
      nuki_door_state:
        unique_id: nuki_door_state
        friendly_name: "Nuki Door State"
        device_class: door
        value_template: >
          {{ is_state('input_text.nuki_state_name', 'unlocked') }}
        icon_template: >
          {% set mystate = states('input_text.nuki_state_name') %}
          {% if (mystate == 'unlocked') %}
            mdi:door-open
          {% elif mystate == 'locked' %}
            mdi:door-closed-lock
          {% else %}
            mdi:alert-box-outline
          {% endif %}
        attribute_templates:
          lock_state: >
            {{ states('input_text.nuki_state_name') }}
          trigger_platform: >
            {{ states('input_text.nuki_trigger_platform') }}
          lock_battery_charge_state: >
            {{ states('input_text.nuki_battery_charge_state') }}
          lock_battery_critical: >
            {{ states('input_text.nuki_battery_critical') }}
          keypad_battery_critical: >
            {{ states('input_text.nuki_keypad_battery_critical') }}
          last_update: >
            {{ states('input_text.nuki_last_activity') }}


#######################################################################################################################
###                                                                                                                 ###
### Input Texts                                                                                                           ###
###                                                                                                                 ###
#######################################################################################################################
input_text:
  nuki_bridge_host:
    name: nukiBridgeHost
    initial: !secret nuki_bridge_host
  nuki_bridge_port:
    name: nukiBridgePort
    initial: !secret nuki_bridge_port
  nuki_bridge_token:
    name: nukiBridgeToken
    initial: !secret nuki_bridge_token
  nuki_state: 
    name: nukiState
  nuki_state_name: 
    name: nukiStateName
  nuki_battery_critical: 
    name: nukiBatteryCritical
  nuki_keypad_battery_critical: 
    name: nukiKeypadBatteryCritical
  nuki_last_activity: 
    name: nukiLastActivity
  nuki_trigger_platform: 
    name: nukiTriggerPlatform
input_number:
  nuki_battery_charge_state: 
    name: nuki_BatteryChargeState
    min: 0
    max: 100

#######################################################################################################################
###                                                                                                                 ###
### Locks                                                                                                           ###
###                                                                                                                 ###
#######################################################################################################################

lock:
  - platform: template
    name: "Nuki Lock Action"
    unique_id: nuki_lock_action
    value_template: >
      {{ is_state_attr('binary_sensor.nuki_door_state', 'lock_state', 'locked') }}
    availability_template: >
      {{ is_state_attr('binary_sensor.nuki_door_state', 'lock_state', 'locked') or
        is_state_attr('binary_sensor.nuki_door_state', 'lock_state', 'unlocked')}}
    lock:
      service: rest_command.nuki_lock_action
      data:
        action: 2
    unlock:
      service: rest_command.nuki_lock_action
      data:
        action: 1
      

#######################################################################################################################
###                                                                                                                 ###
### rest commands                                                                                                   ###
###                                                                                                                 ###
#######################################################################################################################

rest_command:
  nuki_lock_action:
    url: "http://{{ states('input_text.nuki_bridge_host') }}:{{ states('input_text.nuki_bridge_port') }}/lockAction?nukiId= 625580253&token={{ states('input_text.nuki_bridge_token') }}&action={{ action }}"

#######################################################################################################################
###                                                                                                                 ###
### sensors                                                                                                         ###
###                                                                                                                 ###
#######################################################################################################################

I deliberately did not define the sensors in the source code above (they can be included like @alexdelprete did, but are not essential).

The smart lock is defined as cover in configuration.yaml (or in the package-file):

  - platform: template
    covers:
      frontdoof:
        device_class: door
        friendly_name: "Front Door"
        value_template: >
          {{ states("input_text.nuki_state") | string }}
        open_cover:
          service: script.open_frontdoor_rednode
        close_cover:
          service: script.close_frontdoor_rednode

The automation itself was made in Node-Red (a great tool!):

Description:

The flow is triggered by an input_boolean-Variable (set in home assistant by a tap action on a custom-button-card - details later).

The Boolean variable is first reset to false.

Then the bridge rest api is called (GET, lockState - see NUKI api documentation).
The http-request return “locked” or “unlocked” which is the current state of the smart lock. The next node extracts the stateName from the json payload returned by the bridge. The following if (switch) calls the rest-api of the Nuki Bridge again (GET, lockAction with parameters 1 or 2). So is the door is locked the unlock-procedure is triggered an vice versa.
As you can see in the cover definition of the front door, the value of the variable input_text.nuki_state is used as state.
So node red not only sends the lockAction-request but also sets the variable input_text.nuki_state to “locking” and “unlocking” resp. With that the custom button card will show user definable icons for the unlocking and locking sequence.
The wled node in the screenshot is a led stripe mounted outside the house next to the door. If the door is unlocked it is turned on with color green to indicate that a Siri command (for example) to unlock the door is not necessary.

On the Lovelace dashboard the smart lock is displayed as custom button card

      - type: custom:button-card
        entity: input_text.nuki_state
        icon: mdi:door
        show_state: false
        show_label: true
        name: Haustüre
        label: |
          [[[ 
            var s = entity.state;
            var text = {"locked": "Geschlossen", "unlocked": "Offen", "unlocking": "Öffnet", "locking": "Schließt"} 
            return text[s];
          ]]]
        tap_action:
          action: call-service
          service: input_boolean.turn_on
          service_data:
            entity_id: input_boolean.nuki_action
        styles:
          label:
            - font-size: smaller
            - color: rgb(211,211,211)
        state:
          - value: locked
            color: rgb(0,255,0)
            icon: mdi:door-closed-lock
          - value: unlocked
            color: rgb(255, 214, 10)
            icon: mdi:door-open
          - value: locking
            color: rgb(0,255,0)
            icon: mdi:door-open
          - value: unlocking
            color: rgb(255,214,10)
            icon: mdi:door-closed

The variable text is used for translation to German.
By tapping onto the icon (of the custom button card) the Boolean variable input_boolean.nuki_action is set to “on” which then triggers the Node-Red flow shown in the screenshot.

By the way: the actions defined in the cover template are never used (because the custom button card tap action is used instead).

The rest sensors (@alexdelprete) where not (yet) implemented as I do not see a big need. The info home assistant gets back from the POST-callback (webhook) request from the bridge are sufficient for me. Time based (i.e. every 5 min) requests for the state of the lock could be easily implemented in Node-Red.

The following screenshot shows a part of the dashboard (motor driven garden door (Gartentor), garage door and the Nuki smart lock:

I am still testing, but so far it works well. I am very interested in feedback to my approach. As I am not yet very experienced in home assistant I appreciate any help and suggestions for improvements.

Best regards from Austria
Walter

1 Like