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

Just installed V4.7: it looks very good and I am happy about it too :slight_smile:
Thanks again for your effort.

1 Like

I just added a card in the default dashboard in order to access easily to a big button with HA android app (and another one to be able to check at the same time door status and battery level).

It has been a real pleasure. Happy to have helped in some way. :slight_smile:

1 Like

that was the whole purpose of the card: now every user has all the data needed and all actions available to customize it as they prefer. :slight_smile:

glad it’s working fine also for you.

@Friedrieck it’s working fine now, the only issue remaining is that at every HA restart, I see these in the logs, I guess it’s due to the REST calls not being completed yet while HA tries to render the templates, but I’m not sure. Let me know your thoughts…

Not easy to find out about the first warning. Details when you click on it would have been helpful. Assuming it could be the state of the Nuki Lock Sensor State, then maybe try this:

    state: >
      {% 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[state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['state']] | default('loading...') }}

Same approach for the other:

  - name: "Nuki Device Name"
    state: "{{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] | default('loading...') }}"
    icon: mdi:alphabetical-variant

I’m sorry, here are the details for all three.

First:

Logger: homeassistant.helpers.event
Source: helpers/template.py:391
First occurred: 31 May 2021, 22:13:48 (4 occurrences)
Last logged: 31 May 2021, 22:13:48

Error while processing template: Template("{{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}")
Error while processing template: Template("{{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['rssi'] }}")
Error while processing template: Template("{% 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 %}")
Error while processing template: Template("{% 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 %}")
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 389, in async_render
    render_result = _render_with_context(self.template, compiled, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1358, in _render_with_context
    return template.render(**kwargs)
  File "/usr/local/lib/python3.8/site-packages/jinja2/environment.py", line 1304, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.8/site-packages/jinja2/environment.py", line 925, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "<template>", line 1, in top-level template code
  File "/usr/local/lib/python3.8/site-packages/jinja2/sandbox.py", line 303, in getitem
    return obj[argument]
jinja2.exceptions.UndefinedError: None has no element 0

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 505, in async_render_to_info
    render_info._result = self.async_render(variables, strict=strict, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 391, in async_render
    raise TemplateError(err) from err
homeassistant.exceptions.TemplateError: UndefinedError: None has no element 0

Second:

Logger: homeassistant.components.template.template_entity
Source: components/template/template_entity.py:72
Integration: Template (documentation, issues)
First occurred: 31 May 2021, 22:13:48 (4 occurrences)
Last logged: 31 May 2021, 22:13:48

TemplateError('UndefinedError: None has no element 0') while processing template 'Template("{{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['name'] }}")' for attribute '_state' in entity 'sensor.nuki_device_name'
TemplateError('UndefinedError: None has no element 0') while processing template 'Template("{{ state_attr('sensor.nuki_endpoint_info','scanResults')[0]['rssi'] }}")' for attribute '_state' in entity 'sensor.nuki_bridge_lock_bt_rssi'
TemplateError('UndefinedError: None has no element 0') while processing template 'Template("{% 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 %}")' for attribute '_state' in entity 'sensor.nuki_bridge_lock_bt_state'
TemplateError('UndefinedError: None has no element 0') while processing template 'Template("{% 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 %}")' for attribute '_icon' in entity 'sensor.nuki_bridge_lock_bt_state'

Third:

Logger: homeassistant.helpers.template
Source: helpers/template.py:1366
First occurred: 31 May 2021, 22:13:48 (19 occurrences)
Last logged: 31 May 2021, 22:14:00

Template variable warning: 'None' has no attribute 'batteryCritical' when rendering '{{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCritical'] }}'
Template variable warning: 'None' has no attribute 'keypadBatteryCritical' when rendering '{{ state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['keypadBatteryCritical'] }}'
Template variable warning: 'None' has no attribute 'batteryCharging' when rendering '{% set battery_level = state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryChargeState'] | default(0) | int %} {% if state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['batteryCharging'] %} {{ battery_level }} {% else %} {{ battery_level }} {% endif %}'
Template variable warning: 'None' has no attribute 'timestamp' when rendering '{{ (as_timestamp(state_attr('sensor.nuki_endpoint_list', 'lastKnownState')['timestamp'])) | timestamp_custom("%H:%M (%b %d)") }}'
Template variable warning: No first item, sequence was empty. when rendering '{%- set up_time = as_timestamp(now())-as_timestamp(states('sensor.uptime')) %} {%- macro phrase(name, divisor, mod=None) %} {%- set value = ((up_time // divisor) % (mod if mod else divisor)) | int %} {%- set end = 's' if value > 1 else '' %} {{- '{} {}{}'.format(value, name, end) if value | int > 0 else '' }} {%- endmacro %} {%- set values = [ phrase('week', 60*60*24*7), phrase('day', 60*60*24, 7), phrase('hour', 60*60, 24), phrase('min', 60) ] | select('!=','') | list %} {{ values | first }} ago'

I have another REST polling sensor that was also triggering errors of that sort. Most were solved by the automation below (yes, back to homeassistant start event), and the rest by default() in the templates (as in my last post):

alias: MàJ FullUp
description: ''
trigger:
  - event: start
    platform: homeassistant
condition: []
action:
  - delay: '00:00:05'
  - entity_id: sensor.fullup
    service: homeassistant.update_entity
mode: single

or you live with these errors in your log (but nobody likes it)

Thx for this awesome work.
Just one question: undernuki_bridge_host do I just put in the ip address? Or is it something more?

The IP. As an example:

nuki_bridge_host: "192.168.0.5"
nuki_bridge_port: "8080"

I almost never restart HA (I create/modify automations sometime but it is not needed to restart to take them in account) so I really don’t care. Of course, one would want everything be perfect but in this case, it seems these errors doesn’t have any negative consequences. Thanks again to alexdelprete and the others who helped to build this nice “integration”.

You can use the IP or the HOSTNAME. If you use the hostname, make sure that DNS resolves it.

nuki_bridge_host: "nuki-bridge.axel.dom"
nuki_bridge_port: "8080"

Did you read the instructions and the example regarding the secrets.yaml content? I am showing my configuration there. I use a hostname, but you can use an IP.

it also supports the hostname. :slight_smile:

I think it’s too much to configure another automation for that problem.

The other thing I noticed is that it throws errors only on sensors with the [0]. I think it’s something syntax related. Maybe I could reinforce the code putting the value in a variable and then validate its content. How do I check if any kind of value (string, int, boolean) exists, I mean, it’s not NULL like in the startup phase? I don’t think checking against “” would work, is there a clean way?

Thanks.

Not 100% sure, but look at this:
https://jinja.palletsprojects.com/en/3.0.x/templates/#if
And here as well:
https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-tests
especially defined() or none()

It’s a catch-22 situation. If you do an IF to test if the sensor exists (is defined, etc.) and then in case it is, you extract the value, it throws the same error. It’s during the initial rendering, not during the evaluation of the statement.

I don’t want to spend all this time on it…

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.