Nuki Card with Callback support (supports both Lock & Opener, it replaces the official integration)

Well, you can split it as you want now, the core has been ported so the package works, you can manipulate it as you prefer. :slight_smile:

Let me know if it works though, so we can release it. Keep in mind that I don’t like this approach very much, in order to make it work I had to use “global variables” (input_text). The original solution is much more “elegant”. :slight_smile:

I have no issues locally. What did you find wrong? What did you fix?

It’s not only a matter of style, but functionality and efficiency. The fact that with the new Template Trigger sensor the sensors you create are part of the platform, and not separate is more efficient because from the binary_sensor and the sensors created, you can access to the trigger data, without using tricks like globals/input_texts.

BTW: did you test the !secret for webhook? Does it now work for you?

This is what I was talking about in my previous comment. I understood where the devs are going with the template platform/integration. I like where they’re going, but obviously there are consequences…

This is exactly what I tried to explain you in my previous comments.

If you check Nuki Bridge API, you’ll notice that the json payload info it sends with the callback is minimal, only the status of the door sensor and the lock (and battery). Why? Because the callback has to be focused on IMPORTANT sensors, not all the info sensors. And that’s how I designed the integration.

The long list of sensors is managed via polling by the two rest sensors. The important information, like door and lock status get immediate status updates through callbacks. This is the best approach, because it follows what Nuki is doing and we also keep the polling calls at minimum, because the information of the extra sensors is not mission-critical.

I think now you can understand fully how I approached it. :slight_smile:

I think they opted for a soft-landing scenario: they write in docs that they recommend modern-syntax, while keeping the old working, basically they don’t want to break all the existing old style configurations because it would upset many users. So for now they are keeping both, but it’s pretty clear they are heading towards a new template system, so new stuff should be implemented in that way, IMHO. We’re just speculating, obviously. Maybe they’ll find a way to go in the new path and also make the old things work…but I think if it could be done they would’ve already done it. :slight_smile:

@alexdelprete I have a new beta version of the package.

This is what I did:

  • Saved all available parameters from the webhook into an input_text
  • Referenced, where possible, all sensors to the according sensor.nuki_endpoint_list or the sensor.nuki or input_text. This way the sensors should react on both an update from the webhook or the rest api calls (Hope I did everything right…)

This should give a more accurate overview.

The code:

########################
# Nuki Card v6.0-beta2 #
########################

#######################################################################################################################
###                                                                                                                 ###
### 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:
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_door_sensor_state
                data:
                  value: >
                    {% set my_state = {1: 'deactivated', 2: 'closed', 3: 'open', 4: 'unknown', 5: 'calibrating'} %}
                    {{ my_state[trigger.json.doorsensorState] }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_lock_sensor_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] }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_lock_sensor_state_name
                data:
                  value: >
                    {{ trigger.json.stateName }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_door_sensor_mode
                data:
                  value: >
                    {{ trigger.json.mode }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_nuki_id
                data:
                  value: >
                    {{ trigger.json.nukiId }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_device_type
                data:
                  value: >
                    {{ trigger.json.deviceType }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_battery_critical
                data:
                  value: >
                    {{ trigger.json.batteryCritical }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_battery_charging
                data:
                  value: >
                    {{ trigger.json.batteryCharging }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_battery_charge_state
                data:
                  value: >
                    {{ trigger.json.batteryChargeState }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_lock_sensor_state_name
                data:
                  value: >
                    {{ trigger.json.doorsensorStateName }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_last_activity
                data:
                  value: >
                    {{ as_timestamp(now()) | timestamp_custom("%H:%M:%S (%b %d)") }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_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_bridge_door_sensor_state
                data:
                  value: >
                    {{ states('sensor.nuki_door_sensor_state') }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_bridge_lock_sensor_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_bridge_trigger_platform
                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_bridge_door_sensor_state', 'open') }}
        availability_template: >
          {{ (is_state('sensor.nuki_door_sensor_state', 'open') or is_state('sensor.nuki_door_sensor_state', 'closed')) }}
        icon_template: >
          {% set trigdoor = states('input_text.nuki_bridge_door_sensor_state') %}
          {% set triglock = states('input_text.nuki_bridge_lock_sensor_state') %}
          {% if (trigdoor == 'open') %}
            mdi:door-open
          {% elif trigdoor == 'closed' and triglock == 'locked' %}
            mdi:door-closed-lock
          {% elif trigdoor == 'closed' and triglock == 'unlocked' %}
            mdi:door-closed
          {% else %}
            mdi:alert-box-outline
          {% endif %}
        attribute_templates:
          trigger_platform: >
            {{ states('input_text.nuki_bridge_trigger_platform') }}
          nuki_id: >
            {{ states('sensor.nuki_id') }}
          door_state: >
            {{ states('input_text.nuki_bridge_door_sensor_state') }}
          lock_state: >
            {{ states('input_text.nuki_bridge_lock_sensor_state') }}
          lock_battery: >
            {{ states('sensor.nuki_lock_battery_level') }}
          lock_battery_critical: >
            {{ states('sensor.nuki_lock_battery_critical_state') }}
          keypad_battery_critical: >
            {% if states('sensor.nuki_keypad_battery_critical_state') != 'unknown' %}
              {{ states('sensor.nuki_keypad_battery_critical_state') }}
            {% else %}
              not installed 
            {% endif %}
          last_update: >
            {{ states('input_text.nuki_bridge_last_activity') }}
          last_update_polled: >
            {{ states('sensor.nuki_last_activity') }}
          door_sensor_polled: >
            {{ states('sensor.nuki_door_sensor_state') }}
          lock_sensor_polled: >
            {{ states('sensor.nuki_lock_sensor_state') }}

#######################################################################################################################
###                                                                                                                 ###
### Input Texts                                                                                                           ###
###                                                                                                                 ###
#######################################################################################################################
input_text:
  nuki_bridge_host:
    initial: !secret nuki_bridge_host
  nuki_bridge_port:
    initial: !secret nuki_bridge_port
  nuki_bridge_token:
    initial: !secret nuki_bridge_token
  nuki_bridge_battery_critical:
  nuki_bridge_battery_charging:
  nuki_bridge_battery_charge_state:
  nuki_bridge_device_type:
  nuki_bridge_door_sensor_state:
  nuki_bridge_door_sensor_state_name:
  nuki_bridge_door_sensor_mode:
  nuki_bridge_lock_sensor_state:
  nuki_bridge_lock_sensor_state_name:
  nuki_bridge_last_activity:
  nuki_bridge_nuki_id:
  nuki_bridge_trigger_platform:

#######################################################################################################################
###                                                                                                                 ###
### 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

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

sensor:
  - platform: rest
    scan_interval: 150
    resource_template: "http://{{ states('input_text.nuki_bridge_host') }}:{{ states('input_text.nuki_bridge_port') }}/list?&token={{ states('input_text.nuki_bridge_token') }}"
    name: "Nuki Endpoint List"
    value_template: "OK"
    json_attributes:
      - lastKnownState
      - firmwareVersion
      - nukiId
      - name

  - platform: rest
    scan_interval: 150
    resource_template: "http://{{ states('input_text.nuki_bridge_host') }}:{{ states('input_text.nuki_bridge_port') }}/info?&token={{ states('input_text.nuki_bridge_token') }}"
    name: "Nuki Endpoint Info"
    value_template: "OK"
    json_attributes:
      - versions
      - scanResults
      - wlanConnected
      - serverConnected

  - platform: template
    sensors:
      nuki_device_name:
        unique_id: nuki_device_name
        friendly_name: "Nuki Device Name"
        icon_template: mdi:alphabetical-variant
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
          {% endif %}

  - platform: template
    sensors:
      nuki_bridge_fw_version:
        unique_id: nuki_bridge_fw_version
        friendly_name: "Nuki Bridge FW Version"
        icon_template: mdi:numeric
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_info','versions')['firmwareVersion'] }}
          {% endif %}

      nuki_bridge_lock_bt_rssi:
        unique_id: nuki_bridge_lock_bt_rssi
        friendly_name: "Nuki Bridge<->Lock BT RSSI"
        icon_template: mdi:signal-distance-variant
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['rssi'] }}
          {% endif %}

      nuki_bridge_wifi_connected:
        unique_id: nuki_bridge_wifi_connected
        friendly_name: "Nuki Bridge WiFi Connected"
        icon_template: mdi:wifi-cog
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_info','wlanConnected') }}
          {% endif %}

      nuki_bridge_wifi_fw_version:
        unique_id: nuki_bridge_wifi_fw_version
        friendly_name: "Nuki Bridge WiFi FW Version"
        icon_template: mdi:numeric
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_info','versions')['wifiFirmwareVersion'] }}
          {% endif %}

      nuki_bridge_cloud_connected:
        unique_id: nuki_bridge_cloud_connected
        friendly_name: "Nuki Bridge Cloud Connected"
        icon_template: mdi:server-network
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_info','serverConnected') }}
          {% endif %}

      nuki_bridge_lock_bt_state:
        unique_id: nuki_bridge_lock_bt_state
        friendly_name: "Nuki Bridge<->Lock BT State"
        icon_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {% if state_attr('sensor.nuki_endpoint_info','scanResults')[0]['paired'] %}
              mdi:bluetooth-connect
            {% elif not state_attr('sensor.nuki_endpoint_info','scanResults')[0]['paired'] %}
              mdi:bluetooth-off
            {% else %}
              mdi:bluetooth-audio
            {% endif %}
          {% endif %}
        value_template: >
          {% if states('sensor.nuki_endpoint_info') == "OK" %}
            {% if state_attr('sensor.nuki_endpoint_info','scanResults')[0]['paired'] %}
              connected
            {% elif not state_attr('sensor.nuki_endpoint_info','scanResults')[0]['paired'] %}
              disconnected
            {% else %}
              Unknown
            {% endif %}
          {% endif %}

      nuki_id:
        unique_id: nuki_id
        friendly_name: "Nuki ID"
        icon_template: mdi:numeric
        value_template: >
          
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_list','nukiId') }}
          {% elif states('input_text.nuki_bridge_nuki_id') != null %}
            {{ states('input_text.nuki_bridge_nuki_id') }}
          {% endif %}

      nuki_lock_fw_version:
        unique_id: nuki_lock_fw_version
        friendly_name: "Nuki Lock FW Version"
        icon_template: mdi:numeric
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_list','firmwareVersion') }}
          {% endif %}

      nuki_lock_battery_critical_state:
        unique_id: nuki_lock_battery_critical_state
        friendly_name: "Nuki Lock Battery Critical State"
        icon_template: mdi:battery-alert-variant-outline
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCritical'] }}
          {% elif states('input_text.nuki_bridge_battery_critical') != null%}
            {{ states('input_text.nuki_bridge_battery_critical') }}
          {% endif %}

      nuki_lock_battery_charging:
        unique_id: nuki_lock_battery_charging
        friendly_name: "Nuki Lock Battery Charging"
        icon_template: mdi:battery-charging
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCharging'] }}
          {% elif states('input_text.nuki_bridge_battery_charging') != null %}
            {{ states('input_text.nuki_bridge_battery_charging') }}
          {% endif %}

      nuki_lock_battery_level:
        unique_id: nuki_lock_battery_level
        friendly_name: "Nuki Lock Battery Level"
        device_class: "battery"
        unit_of_measurement: "%"
        icon_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {% set battery_level = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryChargeState'] | default(0) | int %}
            {% set battery_charging = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCharging'] %}
          {% elif states('input_text.nuki_bridge_battery_charge_state') != null %}
            {% set battery_level = states('input_text.nuki_bridge_battery_charge_state') | default(0) | int %}
            {% set battery_charging = states('input_text.nuki_bridge_battery_charging') %}
          {% endif %}
          {% set battery_round = (battery_level / 10) | int * 10 %}
          {% if battery_round >= 100 and not battery_charging %}
            mdi:battery
          {% elif battery_round >= 100 and battery_charging %}
            mdi:battery-charging
          {% elif battery_round > 0 and not battery_charging %}
            mdi:battery-{{ battery_round }}
          {% elif battery_round > 0 and battery_charging %}
            mdi:battery-charging-{{ battery_round }}
          {% else %}
            mdi:battery-alert-variant-outline
          {% endif %}
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {% set battery_level = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryChargeState'] | default(0) | int %}
            {% set battery_charging = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCharging'] %}
          {% elif states('input_text.nuki_bridge_battery_charge_state') != null %}
            {% set battery_level = states('input_text.nuki_bridge_battery_charge_state') | default(0) | int %}
            {% set battery_charging = states('input_text.nuki_bridge_battery_charging') %}
          {% endif %}
          {% if battery_charging %}
            {{ battery_level }}
          {% else %}
            {{ battery_level }}
          {% endif %}

      nuki_friendly_name:
        unique_id: nuki_friendly_name
        friendly_name: "Nuki Friendly Name"
        icon_template: mdi:numeric
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ state_attr('sensor.nuki_endpoint_list','name') }}
          {% endif %}

      nuki_last_activity:
        unique_id: nuki_last_activity
        friendly_name: "Nuki Last Activity"
        icon_template: mdi:clock-check-outline
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ (as_timestamp(state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['timestamp'])) | timestamp_custom("%H:%M:%S (%b %d)") }}
          {% endif %}

      nuki_door_sensor_state:
        unique_id: nuki_door_sensor_state
        friendly_name: "Nuki Door Sensor State"
        icon_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {% set door_icon = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['doorsensorState'] %}
            {% set lock_icon = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['state'] %}
          {% elif states('input_text.nuki_bridge_door_sensor_state') != null %}
            {% set door_icon = states('input_text.nuki_bridge_door_sensor_state') %}
            {% set lock_icon = states('input_text.nuki_bridge_lock_sensor_state') %}
          {% endif %}
          {% if door_icon == 2 and lock_icon == 3 %}
            mdi:door-closed
          {% elif door_icon == 2 and lock_icon == 1 %}
            mdi:door-closed-lock
          {% elif door_icon == 3 %}
            mdi:door-open
          {% endif %}
        value_template: >
          {% set my_state = {1: 'deactivated', 2: 'closed', 3: 'open', 4: 'unknown', 5: 'calibrating'} %}
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ my_state[state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['doorsensorState']] }}
          {% elif states('input_text.nuki_bridge_door_sensor_state') != null %}
            {{ my_state[states('input_text.nuki_bridge_door_sensor_state')] }}
          {% endif %}

      nuki_lock_sensor_state:
        unique_id: nuki_lock_sensor_state
        friendly_name: "Nuki Lock Sensor State"
        icon_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {% set lock_icon = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['state'] %}
          {% elif states('input_text.nuki_bridge_lock_sensor_state') != null %}
            {% set lock_icon = states('input_text.nuki_bridge_lock_sensor_state') %}
          {% endif %}
          {% if lock_icon == 1 %}
            mdi:lock-outline
          {% elif lock_icon == 3 %}
            mdi:lock-open-outline
          {% endif %}
        value_template: >
          {% 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'} %}
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {{ my_state[state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['state']] }}
          {% elif states('input_text.nuki_bridge_lock_sensor_state') != null %}
            {{ mystate[states('input_text.nuki_bridge_lock_sensor_state')] }}
          {% endif %}

      nuki_keypad_battery_critical_state:
        unique_id: nuki_keypad_battery_critical_state
        friendly_name: "Nuki Keypad Battery Critical State"
        icon_template: mdi:battery-alert-variant-outline
        value_template: >
          {% if states('sensor.nuki_endpoint_list') == "OK" %}
            {% if state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['keypadBatteryCritical'] != null %}
              {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['keypadBatteryCritical'] }}
            {% else %}
              not installed 
            {% endif %}
          {% endif %}

@alexdelprete I just took a quick look at the blueprint: https://www.home-assistant.io/docs/blueprint/tutorial/. It seems that this is compatible with the modern syntax, at least I see a trigger defined in the example. So probably with a blueprint, we can use the modern syntax…

The code I shared already does take info from trigger data AND rest sensors. I took a look at your version and it has several problems, you also forgot the rest_command from the code. The info sensors don’t need to take data from both, they are updated only by the rest commands, because in the trigger data you only have a minimum set of info that is already captured, apart from battery info that is not critical, so I decided not to take that info, I left it on purpose only to the rest sensors.

The code I shared last night is only to be tested, it does the same things as the modern-style split version, but it’s structured as an automation now, and I don’t want to add or change the structure because I know it works pretty good. :slight_smile:

I only did it to help new users, that are only beginners with HA, install it without too many issues. But I don’t really like it too much. But it works, and I see it as a bridge solution until the modern-style version can be packaged.

I’ll wait for some feedbacks on the tests of beta 1, then I’ll release it as 6.0 official.

Obviously without you I wouldn’t have the right “push” to do it, but I would have saved a night of sleep. :slight_smile:

I’m not sure that’s the new template trigger sensor, it’s just a way to configure in a user-friendly way an automation, so probably it will work with the new 6.0 version, with legacy style. But I’ll check it…

With the 6.0 automation, the only thing users have to do is define the 4 secret parameters, and configure the callback on the bridge, that’s it. So probably the blueprint is not necessary. I will think about it…

I just did put in all the arguments for completeness.

Forgetting the rest command was stupid.

The reason why I did put in the data of the inputs, is that this way the sensors can be updated more quickly, ie immediate when the webhook is received and don’t have to wait for the next time the rest command is fired (which could be about 15 seconds extra in the worst case according to the scan intervals). So it should be more responsive…

Sorry to hear it took you one night of sleep, it took two nights of sleep for me… (Probably the reason why I forgot to put in the rest command.)

In the end I’m glad you took the time to create this Nuki integration. Saved me a lot of nights with little sleep. The only thing I tried to accomplish was to put everything into one file so other people that would like to integrate the Nuki into HA would have it a little easier. For me it was also the first time to make use of the package solution in HA. In the end if we were able to migrate it to python would be the ultimate solution, the card could then also be incorporated in the code and the component could be published via HACS. Unfortunately I don’t have the development environment of HA installed and I don’t know python. If I did know python and had some experience in creating HA components, I would have tried to migrate your code into python, at least you have paved the way. You created all the logic needed.

Thanks for everything.

PS: I just saw my code isn’t working the way I expected the way I thought it would do. For example the lock sensor state wasn’t update after receiving the webhook, it was updated after executing the rest command…

I just tried the packaged version and it works fine for me!

Thanks for your great work @alexdelprete :slight_smile:

1 Like

The thing that you still don’t understand is that when you receive the callback, and you use the callback’s json data, you have updated info ONLY for the lock, the door, and the battery. So you can’t update all the sensors, unless you force a REST call, and that is not the proper way like discussed earlier.

image

No problem, I do this because I like it, not because I have to. Since I have a family, I can do certain things only after my kids go to bed. :slight_smile:

Yes I wanted that too, but like I wrote many days ago, I couldn’t do it because of the known template trigger issue. But I understand why you wanted it at all costs, it was a nice intent, you did it to help others and that’s why I decided in the end to do it even if I don’t like the way to do it. You basically convinced me to do it even if I don’t like the solution. Sometimes you need an external push to do some things. Thanks for that. :slight_smile:

Yes, we also discussed in private with someone about creating a component, but I think to do a proper job we needed an experienced python dev, I’m not a programmer, just an old tinkerer who loves tech and having fun with HA and automation. If one of my kids was a programmer believe me, we would already have an HACS component now. :slight_smile:

Actually, I have created my first HACS component, but I started by modifying a similar component. In the end I basically rewrote it, but I did it to understand how these components really worked. Learned a lot. In the end, I think in order to really learn something, you have to DO it, not only study it. :slight_smile:

I think packaging everything in one file is a huge step forward in terms of usability for newcomers. Probably a HACS component now is not a priority anymore, but I know it would be nice to create one.

Thanks to you for exchanging your ideas and your code, I wouldn’t have done this without meeting in this small community people like you who have inspired me and with which I can talk about these things and have fun together. :slight_smile:

Yes I noticed several things that are not correct in that code, but from the code I can see your enthusiasm and that’s the most important thing. Keep doing it, even doing mistakes, you learn a lot from mistakes. :slight_smile:

Thanks again Jeroen.

My pleasure Floo. It’s great to know it’s working fine. Did you test everything? Opening/closing the door give immediate feedback on the GUI?

Thanks for your kind words. In real life I’m a programmer, unfortunately not in one of the main stream languages or yaml, but in the end programming is just logic thinking and that was the also my drive that there should be a solution to get everything into one file. For now I’m still not satisfied, I think there is a way of getting the sensors updated the way I want. I’m convinced, I can find a way to achieve that the sensor will be updated either from the automation or the rest interface. If I find a solution I might show it here, just for showing how it can be achieved.

Sure there’s, when the trigger from the callback arrives to HA you activate the REST sensors and update ALL of them. But I told you why it’s not efficient, from my point of view obviously. Why do you have to update the firmware version sensor every time the callback arrives? It doesn’t make sense. If all info was important, Nuki would have programmed the bridge to send ALL the info contained in the /list or /info API commands, instead they only send the important info. And I tend to agree with them.

It’s not a technical issue you have to solve, it’s not a programmer’s challenge, it’s just understanding how the system really works. It looks very logical to me and I’m more than satisfied having made an integration that improves a lot over the official one by integrating both callback and polled data.

So far after whole day 0.6b1 works fine for me, it’s not super fast in terms lock / unlock and updating state but it’s reliable. How keypad is handled? As I’m thinking on buying along with proper lock :wink:

Yes I tested everything. The updates in GUI are not immediate but faster than in official integration.
I also didnt use the previous versions of your integration before. Just started with v6.0 beta1 today.

The installation process is super easy now. :+1:

The only little problem I had was registering the webhook via curl on Windows 10 command promt. I got an error message there.
But I could also register the webhook with opening the respective url in my webbrowser. This might be an even easier way for unadvanced users.

@alexdelprete I’ve managed to create a package which will update the sensors by both the webhook and rest interface. The sensors are updated from input_text fields that are set by either one of the 3 automations incorporated in the package. To force the update from a REST call, I set the according sensors to “force_update: true”. So every time a REST call is made from HA, the sensors are updated, which gave me the possibility to create automations that trigger when the according sensor is updated.

As you already stated the information from the REST call is less important so I kept the interval at 2,5 minutes. In the end this results in an update of all the sensors every 2,5 minutes unless an action is triggered at the lock, then the sensors are updated when the webhook is received.

So here is my code just for being complete:

#################################
# Nuki Card version from spokin #
#################################

#######################################################################################################################
###                                                                                                                 ###
### 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
    condition: []
    action:
      - choose:
          - conditions: >
              {{ trigger.platform == 'webhook' }}
            sequence:
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_door_sensor_state
                data:
                  value: >
                    {{ trigger.json.doorsensorState }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_sensor_state
                data:
                  value: >
                    {{ trigger.json.state }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_sensor_state_name
                data:
                  value: >
                    {{ trigger.json.stateName }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_mode
                data:
                  value: >
                    {{ trigger.json.mode }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_nuki_id
                data:
                  value: >
                    {{ trigger.json.nukiId }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_device_type
                data:
                  value: >
                    {{ trigger.json.deviceType }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_battery_critical
                data:
                  value: >
                    {{ trigger.json.batteryCritical }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_battery_charging
                data:
                  value: >
                    {{ trigger.json.batteryCharging }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_battery_charge_state
                data:
                  value: >
                    {{ trigger.json.batteryChargeState }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_door_sensor_state_name
                data:
                  value: >
                    
                    {% set my_state = {1: 'deactivated', 2: 'closed', 3: 'open', 4: 'unknown', 5: 'calibrating'} %}
                    {{ my_state[trigger.json.doorsensorState] }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_last_activity_timestamp
                data:
                  value: >
                    {{ as_timestamp(now()) | timestamp_custom("%H:%M:%S (%b %d)") }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_last_update_card_callback
                data:
                  value: >
                    {{ as_timestamp(now()) | timestamp_custom("%H:%M:%S (%b %d)") }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.trigger_platform
                data:
                  value: >
                    {{ trigger.platform }}
          - conditions: >
              {{ trigger.platform == 'event' }}
            sequence:
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_door_sensor_state
                data:
                  value: >
                    {{ states('sensor.nuki_door_sensor_state') }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_sensor_state
                data:
                  value: >
                    {{ states('sensor.nuki_lock_sensor_state') }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_lock_last_activity_timestamp
                data:
                  value: >
                    {{ states('sensor.nuki_last_activity') }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.nuki_trigger_platform
                data:
                  value: >
                    {{ trigger.platform }}
        default: []
    mode: single

automation 2:
  - id: 'Nuki_endpoint_list'
    alias: Nuki Endpoint List
    description: 'Nuki Automation to update sensors based on the Nuki Endpoint List restfull call'
    trigger:
      - platform: state
        entity_id: sensor.nuki_endpoint_list
    condition: []
    action:
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_mode
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['mode'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_sensor_state
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['state'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_sensor_state_name
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['stateName'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_battery_critical
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCritical'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_battery_charging
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCharging'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_battery_charge_state
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryChargeState'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_door_sensor_state
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['doorsensorState'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_door_sensor_state_name
        data:
          value: >-
            {% set my_state = {1: 'deactivated', 2: 'closed', 3: 'open', 4: 'unknown', 5: 'calibrating'} %}
            {{ my_state[state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['doorsensorState']] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_last_activity_timestamp
        data:
          value: >-
            {{ (as_timestamp(state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['timestamp'])) | timestamp_custom("%H:%M:%S (%b %d)") }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_last_update_endpoint_list
        data:
          value: >-
            {{ as_timestamp(now()) | timestamp_custom("%H:%M:%S (%b %d)") }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_firmware_version
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'firmwareVersion') }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_nuki_id
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'nukiId') }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_bridge_name
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'name') }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_keypad_battery_critical
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['keypadBatteryCritical'] }}
    mode: single

automation 3:
  - id: 'Nuki_endpoint_info'
    alias: Nuki Endpoint Info
    description: 'Nuki Automation to update sensors based on the Nuki Endpoint Info restfull call'
    trigger:
      - platform: state
        entity_id: sensor.nuki_endpoint_info
    condition: []
    action:
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_bridge_firmware_version
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','versions')['firmwareVersion'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_bridge_wifi_firmware_version
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','versions')['wifiFirmwareVersion'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_device_name
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_rssi
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['rssi'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_lock_paired
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['paired'] }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_bridge_wlan_connected
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','wlanConnected') }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_bridge_server_connected
        data:
          value: >-
            {{ state_attr('sensor.nuki_endpoint_info','serverConnected') }}
      - service: input_text.set_value
        target:
          entity_id: input_text.nuki_last_update_endpoint_info
        data:
          value: >-
            {{ as_timestamp(now()) | timestamp_custom("%H:%M:%S (%b %d)") }}
    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('sensor.nuki_door_sensor_state', 'open') }}
        availability_template: >
          {{ (is_state('sensor.nuki_door_sensor_state', 'open') or is_state('sensor.nuki_door_sensor_state', 'closed')) }}
        icon_template: >
          {% set trigdoor = states('sensor.nuki_door_sensor_state') %}
          {% set triglock = states('sensor.nuki_lock_sensor_state') %}
          {% if (trigdoor == 'open') %}
            mdi:door-open
          {% elif trigdoor == 'closed' and triglock == 'locked' %}
            mdi:door-closed-lock
          {% elif trigdoor == 'closed' and triglock == 'unlocked' %}
            mdi:door-closed
          {% else %}
            mdi:alert-box-outline
          {% endif %}
        attribute_templates:
          trigger_platform: >
            {{ states('input_text.nuki_bridge_trigger_platform') }}
          nuki_id: >
            {{ states('sensor.nuki_id') }}
          door_state: >
            {{ states('sensor.nuki_door_sensor_state') }}
          lock_state: >
            {{ states('sensor.nuki_lock_sensor_state') }}
          lock_battery: >
            {{ states('sensor.nuki_lock_battery_level') }}
          lock_battery_critical: >
            {{ states('sensor.nuki_lock_battery_critical_state') }}
          keypad_battery_critical: >
            {% if states('sensor.nuki_keypad_battery_critical_state') != '' %}
              {{ states('sensor.nuki_keypad_battery_critical_state') }}
            {% else %}
              not installed 
            {% endif %}
          last_update_card_callback: >
            {{ states('input_text.nuki_last_update_card_callback') }}
          last_update_endpoint_info: >
            {{ states('input_text.nuki_last_update_endpoint_info') }}
          last_update_endpoint_list: >
            {{ states('input_text.nuki_last_update_endpoint_list') }}
          last_update_polled: >
            {{ states('sensor.nuki_last_activity') }}
          door_sensor_polled: >
            {{ states('sensor.nuki_door_sensor_state') }}
          lock_sensor_polled: >
            {{ states('sensor.nuki_lock_sensor_state') }}

#######################################################################################################################
###                                                                                                                 ###
### Input Texts                                                                                                     ###
###                                                                                                                 ###
#######################################################################################################################
input_text:
  nuki_bridge_host:
    initial: !secret nuki_bridge_host
  nuki_bridge_port:
    initial: !secret nuki_bridge_port
  nuki_bridge_token:
    initial: !secret nuki_bridge_token
  
  nuki_bridge_device_type:
  nuki_bridge_firmware_version:
  nuki_bridge_name:
  nuki_bridge_server_connected:
  nuki_bridge_wifi_firmware_version:
  nuki_bridge_wlan_connected:
  
  nuki_keypad_battery_critical:
  
  nuki_lock_battery_critical:
  nuki_lock_battery_charging:
  nuki_lock_battery_charge_state:
  nuki_lock_device_name:
  nuki_lock_door_sensor_state:
  nuki_lock_door_sensor_state_name:
  nuki_lock_firmware_version:
  nuki_lock_mode:
  nuki_lock_nuki_id:
  nuki_lock_paired:
  nuki_lock_rssi:
  nuki_lock_sensor_state:
  nuki_lock_sensor_state_name:
  nuki_lock_last_activity_timestamp:
  
  nuki_last_update_card_callback:
  nuki_last_update_endpoint_info:
  nuki_last_update_endpoint_list:

  nuki_trigger_platform:

#######################################################################################################################
###                                                                                                                 ###
### 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={{ states('sensor.nuki_id') }}&token={{ states('input_text.nuki_bridge_token') }}&action={{ action }}"

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

sensor:
  - platform: rest
    scan_interval: 150
    force_update: true
    resource_template: "http://{{ states('input_text.nuki_bridge_host') }}:{{ states('input_text.nuki_bridge_port') }}/list?&token={{ states('input_text.nuki_bridge_token') }}"
    name: "Nuki Endpoint List"
    value_template: "OK"
    json_attributes:
      - lastKnownState
      - firmwareVersion
      - nukiId
      - name

  - platform: rest
    scan_interval: 150
    force_update: true
    resource_template: "http://{{ states('input_text.nuki_bridge_host') }}:{{ states('input_text.nuki_bridge_port') }}/info?&token={{ states('input_text.nuki_bridge_token') }}"
    name: "Nuki Endpoint Info"
    value_template: "OK"
    json_attributes:
      - versions
      - scanResults
      - wlanConnected
      - serverConnected

  - platform: template
    sensors:
      nuki_device_name:
        unique_id: nuki_device_name
        friendly_name: "Nuki Device Name"
        icon_template: mdi:alphabetical-variant
        value_template: >
          {{ states('input_text.nuki_lock_device_name') }}

  - platform: template
    sensors:
      nuki_bridge_fw_version:
        unique_id: nuki_bridge_fw_version
        friendly_name: "Nuki Bridge FW Version"
        icon_template: mdi:numeric
        value_template: >
          {{ states('input_text.nuki_bridge_firmware_version') }}

      nuki_bridge_lock_bt_rssi:
        unique_id: nuki_bridge_lock_bt_rssi
        friendly_name: "Nuki Bridge<->Lock BT RSSI"
        icon_template: mdi:signal-distance-variant
        value_template: >
          {{ states('input_text.nuki_lock_rssi') }}

      nuki_bridge_wifi_connected:
        unique_id: nuki_bridge_wifi_connected
        friendly_name: "Nuki Bridge WiFi Connected"
        icon_template: mdi:wifi-cog
        value_template: >
          {{ states('input_text.nuki_bridge_wlan_connected') }}

      nuki_bridge_wifi_fw_version:
        unique_id: nuki_bridge_wifi_fw_version
        friendly_name: "Nuki Bridge WiFi FW Version"
        icon_template: mdi:numeric
        value_template: >
          {{ states('input_text.nuki_bridge_wifi_firmware_version') }}

      nuki_bridge_cloud_connected:
        unique_id: nuki_bridge_cloud_connected
        friendly_name: "Nuki Bridge Cloud Connected"
        icon_template: mdi:server-network
        value_template: >
          {{ states('input_text.nuki_bridge_server_connected') }}

      nuki_bridge_lock_bt_state:
        unique_id: nuki_bridge_lock_bt_state
        friendly_name: "Nuki Bridge<->Lock BT State"
        icon_template: >
          {% if states('input_text.nuki_lock_paired') %}
            mdi:bluetooth-connect
          {% elif states('input_text.nuki_lock_paired') %}
            mdi:bluetooth-off
          {% else %}
            mdi:bluetooth-audio
          {% endif %}
        value_template: >
          {% if states('input_text.nuki_lock_paired') %}
            connected
          {% elif not states('input_text.nuki_lock_paired') %}
            disconnected
          {% else %}
            Unknown
          {% endif %}

      nuki_id:
        unique_id: nuki_id
        friendly_name: "Nuki ID"
        icon_template: mdi:numeric
        value_template: >
          {{ states('input_text.nuki_lock_nuki_id') }}

      nuki_lock_fw_version:
        unique_id: nuki_lock_fw_version
        friendly_name: "Nuki Lock FW Version"
        icon_template: mdi:numeric
        value_template: >
          {{ states('input_text.nuki_lock_firmware_version') }}

      nuki_lock_battery_critical_state:
        unique_id: nuki_lock_battery_critical_state
        friendly_name: "Nuki Lock Battery Critical State"
        icon_template: mdi:battery-alert-variant-outline
        value_template: >
          {{ states('input_text.nuki_lock_battery_critical') }}

      nuki_lock_battery_charging:
        unique_id: nuki_lock_battery_charging
        friendly_name: "Nuki Lock Battery Charging"
        icon_template: mdi:battery-charging
        value_template: >
          {{ states('input_text.nuki_lock_battery_charging') }}

      nuki_lock_battery_level:
        unique_id: nuki_lock_battery_level
        friendly_name: "Nuki Lock Battery Level"
        device_class: "battery"
        unit_of_measurement: "%"
        icon_template: >
          {% set battery_level = states('input_text.nuki_lock_battery_charge_state') | default(0) | int %}
          {% set battery_charging = states('input_text.nuki_lock_battery_charging') %}
          {% set battery_round = (battery_level / 10) | int * 10 %}
          {% if battery_round >= 100 and not battery_charging %}
            mdi:battery
          {% elif battery_round >= 100 and battery_charging %}
            mdi:battery-charging
          {% elif battery_round > 0 and not battery_charging %}
            mdi:battery-{{ battery_round }}
          {% elif battery_round > 0 and battery_charging %}
            mdi:battery-charging-{{ battery_round }}
          {% else %}
            mdi:battery-alert-variant-outline
          {% endif %}
        value_template: >
          {% set battery_level = states('input_text.nuki_lock_battery_charge_state') | default(0) | int %}
          {% set battery_charging = states('input_text.nuki_lock_battery_charging') %}
          {% if battery_charging %}
            {{ battery_level }}
          {% else %}
            {{ battery_level }}
          {% endif %}

      nuki_friendly_name:
        unique_id: nuki_friendly_name
        friendly_name: "Nuki Friendly Name"
        icon_template: mdi:numeric
        value_template: >
          {{ states('input_text.nuki_bridge_name') }}

      nuki_last_activity:
        unique_id: nuki_last_activity
        friendly_name: "Nuki Last Activity"
        icon_template: mdi:clock-check-outline
        value_template: >
          {{ states('input_text.nuki_lock_last_activity_timestamp') }}

      nuki_door_sensor_state:
        unique_id: nuki_door_sensor_state
        friendly_name: "Nuki Door Sensor State"
        icon_template: >
          {% set door_icon = states('input_text.nuki_lock_door_sensor_state') %}
          {% set lock_icon = states('input_text.nuki_lock_sensor_state') %}
          {% if door_icon == '2' and lock_icon == '3' %}
            mdi:door-closed
          {% elif door_icon == '2' and lock_icon == '1' %}
            mdi:door-closed-lock
          {% elif door_icon == '3' %}
            mdi:door-open
          {% endif %}
        value_template: >
          {% set my_state = {'1': 'deactivated', '2': 'closed', '3': 'open', '4': 'unknown', '5': 'calibrating'} %}
          {{ my_state[states('input_text.nuki_lock_door_sensor_state')] }}

      nuki_lock_sensor_state:
        unique_id: nuki_lock_sensor_state
        friendly_name: "Nuki Lock Sensor State"
        icon_template: >
          {% set lock_icon = states('input_text.nuki_lock_sensor_state') %}
          {% if lock_icon == '1' %}
            mdi:lock-outline
          {% elif lock_icon == '3' %}
            mdi:lock-open-outline
          {% endif %}
        value_template: >
          {% 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[states('input_text.nuki_lock_sensor_state')] }}

      nuki_keypad_battery_critical_state:
        unique_id: nuki_keypad_battery_critical_state
        friendly_name: "Nuki Keypad Battery Critical State"
        icon_template: mdi:battery-alert-variant-outline
        value_template: >
          {% if states('input_text.nuki_keypad_battery_critical') != '' %}
            {{ states('input_text.nuki_keypad_battery_critical') }}
          {% else %}
            not installed 
          {% endif %}

If you say it’s not fast you should see with the official integration. :slight_smile:
Unfortunately, you have to take into account the time the bridge takes to do the callback, 2-3 secs from the event. HA reaction is immediate. But for the door sensor is good enough.For the lock, you have to take into account the time it takes to turn the key-lock mechanism + the bridge reaction, it-s around 8-10 secs overall. I can’t do anything on the bridge timings unfortunately.

For the keypad we only have a sensor to monitor the battery, that’s all. It’s a cool device, I have it attached on the door, above the handle, in case someone has to enter, I give the code and that’s it.