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

Ok, solved it. It wasn’t a catch-22. v5.0 released.

@Friedrieck the problem (and the solution) were the two REST sensors, that contain all the JSON data from the nuki, at startup they are not initialized so fast. If you check their code, I put value_state = “OK” to both of them, so I only needed to check if states() == “OK” to be sure they’re initialized.

- platform: rest
  scan_interval: 300
  resource_template: "http://{{ states('sensor.nuki_bridge_host') }}:{{ states('sensor.nuki_bridge_port') }}/list?&token={{ states('sensor.nuki_bridge_token') }}"
  name: "Nuki Endpoint List"
  value_template: "OK"
  json_attributes:
    - lastKnownState
    - firmwareVersion
    - nukiId
    - name
      {% if states('sensor.nuki_endpoint_info') == "OK" %}
        {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
      {% endif %}

If states() is used with an uninitialized sensor, you can’t use none() to test it, because it returnes “unknown”.

So, this doesn’t work:

      {% if states('sensor.nuki_endpoint_info') is not none %}
        {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
      {% endif %}

But this works:

      {% if states('sensor.nuki_endpoint_info') != "unknown" %}
        {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
      {% endif %}

The alternative method, is to test if state_attr(), without the [0][‘name’], is none():

{% if state_attr('sensor.nuki_endpoint_info','scanResults') is not none %}
  {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
{% endif %}

This for example doesn’t work:

{% if state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] is not none %}
  {{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}
{% endif %}

And while looking for some info, I found this. :sweat_smile:

Endless hours of frustration…literally!! But we made it, in the end. Perseverance is the key. :slight_smile:

Thanks for all the advices, going to get some sleep now.

2 Likes

:+1: :clap: :clap: :clap: :sweat_smile:

1 Like

Great work. I’ve followed your instructions and almost got it to work. The polling is ok and the sensor are populated. But the callback doesn’t arrive. I have not taken the time yet to read all the discussion above to debug, but I’ll report back here if I can get it running.

Two questions:

  • I noticed that there is a sensor for the token. I don’t feel well with that. Doesn’t that mean that the token is stored in the recorder? Is that necessary? For the interface, I don’t see a need for the token to be in a sensor. It is stored in the secrets.yaml anyhow and I could always look it up there.
  • The opener is not included. Is that planned as an extension?

Thanks for the effort!

Haha, I tricked myself. The bridge is on a different VLAN and the hostname of HA doesn’t resolve there. Putting in the correct IP of HA from that VLAN makes the callback work like a charm.

Unfortunately it was the only workaround I found to have a global parameter, in order to make it very easy for the user to configure this integration. I’ll show you what I mean, check these two REST sensors:

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

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

These two sensors are the ones responsible for polling data. The interesting thing here is that I didn’t want to use static URLs etc, because I just need those 3 parameters you put in secrets to dynamically construct the URL. While implementing it, I found out you can’t recall secret items in there, so I had to use a sensor’s state. If there’s a better way to pull in there secret items, I’m happy to adapt the code. :slight_smile:

Unfortunately not, because I don’t have it and so I can’t test it. And furthermore, the official integration in the future should have support for callbacks etc, so probably this integration, if that one does everything this does, won’t be needed anymore. We’ll see what happens…:slight_smile:

For me it’s been an excercise to force me to learn HA more. I’m happy that it’s working for you.

I see the problem. Maybe, it is slightly better to stick the values into an input_text and not a sensor as strictly speaking, they are not changing and thus the sensor is not sensing anything. That’s the logic discussed here: How to use !secret in sensor template - #4 by AhmadK

I have one so I might for the sake of following your learning curve experiment with it. If I get anything setup, I’ll share it here. But you’re right, ideally the native integration would pick this stuff up.

From a configuration perspective, it’s easier to just ask the user to configure all user-parameters in secrets (they are secrets) and then I pull them up as sensors when users pastes all the code in the sensor file. If I used input_texts there would be additional user steps to follow, that’s all. So, either I ask to put everything as input_text or as secrets, I didn’t like mixing things, the instructions are already too complex for my taste.

If you don’t like the sensors being recorded, you can filter it out.

Feel free to adapt it to your needs if you prefer input_text. The code is there to be changed/adapted by anybody. :slight_smile:

Sury, but that can also be done with input_text and not just with sensors :wink:

The beauty of HA is that you can solve the same problem in 100 ways, I chose one, and I explained why. I could argue that putting my sensitive information in input_text is not something that I like, but it’s subjective. :wink:

Like I said, input_text would require extra configuration steps, and I preferred to minimize those.

I’ll be more than glad, once it’s working, to add the opener bit to the integration. Let’s make sure that someone else with the opener can test it, obviously. :slight_smile:

Thanks a lot.

I went the easy way and simply used your callback idea to force an update of the Nuki integration itself. That includes the opener then, but it lacks all the wonderful attributes that you pull from the bridge.

Setup is fairly easy and I created a blueprint for that:

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Yeah, the easy way still requires the Nuki official integration. My goal was different. But it’s good to have choices. :slight_smile:

1 Like

It’s a nice feeling being able to click “ignore” while still having a fully working integration, that provides all info about the Nuki, and faster status feedback. :slight_smile:

image

1 Like

Great work! This method is so much faster then just using the rest api.
Is it possible to get the lock_state on callback? The current binary sensor only updates when a door status change is triggered.
I’m trying to create a binary sensor, which is on when the lock is unlatching or unlocking (7 or 2) but can’t get it working.

Thank you, glad you liked it.

It’s already like that. Lock state and Door state are separate and update independently, each time a callback is received.

If it doesn’t work for you, make sure you configured everything correctly.

If you look at the code of the trigger sensor you should be able to understand how to do it. Take into account that the way I implemented it is a special case: it is a device_class door binary sensor and it is a sensor that is updated BOTH via callbacks from the bridge and polling via REST sensors.

You can create a simpler template trigger binary sensor, that is updated only by callback, and when called, you check the lock state (2 or 7) to turn it on/off. Or you could use this integration: The Lock state is represented by this attribute of the binary door sensor: state_attr('binary_sensor.nuki_door_state', 'lock_state'). If you check the code of the lock sensor, you will see that its state is retrieved by that attribute.

Thanks for the help! Yes I’ve seen the attribute, but for me it only updates from locked to unlocked (and vice versa).
It never reaches unlocking/unlatching (2/7). I’ve looked at the callback content and it seems like Nuki doesn’t send one on lock state change to 2/7, but rather it goes directly to 1 or 3. Seems like my use-case doesn’t work with the current callback logic.

I think the callback is done only on 1/3, check this log from the bridge: at 32:27 I do the unlock, at 32:31 the lock is completed, at 32:37 the callback is done. That means the callback is done only at the end of the action, so you will never receive the status changes you were looking for.

When you say paste the code above in the file templates.yaml, do i need to create such a file in the config directory? How do i reference it then in configuration.yaml?

You are right, I took for granted that every user knows how to better organize the config.yaml file when it becomes too complex, I’ll show you the end of my config.yaml, so you can understand what I mean in the instructions. Let me know if it’s clear.

automation: !include automations.yaml
climate: !include climate.yaml
group: !include groups.yaml
http: !include http.yaml
influxdb: !include influxdb.yaml
input_boolean: !include input_booleans.yaml
light: !include lights.yaml
lock: !include locks.yaml
logger: !include logger.yaml
panel_custom: !include panel_custom.yaml
rest_command: !include rest_commands.yaml
scene: !include scenes.yaml
sensor: !include sensors.yaml
script: !include scripts.yaml
switch: !include switches.yaml
template: !include templates.yaml
utility_meter: !include utility_meter.yaml
weather: !include weather.yaml
zha: !include zha.yaml