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