Hi!
I used this automation a while ago, which sent me a notification when a Zigbee device was no longer reachable.
The peculiarity was that every so many hours it sent me the notification with the updated time.
For example:
Zigbee Devices Offline: Bedroom Light - from 5 hours
After 2 hours…
Zigbee Devices Offline: Bedroom Light - from 7 hours
Unfortunately it doesn’t work anymore. If I trigger the automation manually, only the message arrives but without the entity.
At the scheduled times, however, the message does not even arrive, the automation does not trigger (probably a problem with the condition?)
- alias: ALERT Zigbee Offline Devices
id: zigbee_device_missing_alert
trigger:
- platform: time
at: "07:00"
- platform: time
at: "16:00"
- platform: time
at: "20:00"
condition:
- condition: template
value_template: >
{% set ns = namespace(break = false) %}
{% for state in states -%}
{%- if state.attributes.last_seen %}
{%- if (as_timestamp(now()) - as_timestamp(state.attributes.last_seen) > (5 * 60 * 60) ) and ns.break == false %}
{%- set ns.break = true %}
true
{%- endif -%}
{%- endif -%}
{%- endfor %}
action:
- service: notify.notification_service
data:
title: Z2M DEVICE ALERT
message: >
Zigbee Devices Offline:
{% macro GetDroppedZigbee() -%}
{% for state in states.sensor -%}
{%- if ("linkquality" in state.name and state_attr(state.entity_id, "last_seen") != None and (as_timestamp(now()) - as_timestamp(state_attr(state.entity_id, "last_seen")) > (5 * 60 * 60))) -%}
{{ state.name | regex_replace(find=' linkquality', replace='', ignorecase=False) }} -- Da {{ ((as_timestamp(now()) - as_timestamp(state.attributes.last_seen)) / (3600)) | round(0) }} ore {{- '\n' -}}
{%- endif -%}
{%- endfor %}
{%- endmacro -%}
{{ GetDroppedZigbee() }}
What could be the problem? I just can’t solve it
Thanks
I filtered the linkquality entities in developer tool states, but I only have 2 columns, one with the name of the entity and next to it I have the numeric value of the linkquality.
But for each zigbee device, I have a sensor sensor.name_last_seen
If there is a dedicated “Last seen” entity, why have you created a Template Condition that checks for a last_seenattribute?
{% for state in states -%}
{%- if state.attributes.last_seen %}
^^^^^^^^^^^^^^^^^^^^^^^^^^
The template in your macro does the same thing:
{% for state in states.sensor -%}
{%- if ("linkquality" in state.name and
state_attr(state.entity_id, "last_seen") != None and
^^^^ ^^^^^^^^^
(as_timestamp(now()) - as_timestamp(state_attr(state.entity_id, "last_seen")) > (5 * 60 * 60))) -%}
^^^^ ^^^^^^^^^
{{ state.name | regex_replace(find=' linkquality', replace='', ignorecase=False) }} --
Da {{ ((as_timestamp(now()) - as_timestamp(state.attributes.last_seen)) / (3600)) | round(0) }} ore {{- '\n' -}}
^^^^^^^^^^^^^^^^^^^^^^^^^^
NOTE
I have never enabled Zigbee2MQTT’s Last Seen option so I have never seen what happens in Home Assistant when it’s enabled. Your templates led me to believe that the Linkquality entity has a last_seen attribute. However, now I suspect that might not be true, especially now that I have see you have a Last Seen entity.
EDIT
I searched the forum for “last_seen” and I found several posts, one only a year old and others several years old, and none of them attempt to reference a last_seenattribute like your templates do. All reference the state of a Last Seen sensor entity.
It’s an old automation, before surely the last_seen was an attribute, now an entity.
How would you modify the condition? I need the result to be as I described in the first post…
There are several similar automations, but they only notify once when the last_seen sensor is unavailable. Mine also notifies how long it has been offline.
If it does what it should then let me know what this reports:
{% set entities = states.sensor | selectattr('object_id', 'search', 'last_seen') | map(attribute='entity_id') | list -%}
{% set values = entities | map(attribute='state') | map('as_datetime') | list -%}
{% set z = zip(entities, values) | list -%}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{{ state_attr(e, 'friendly_name') | replace(' linkquality', '') }} -- {{ ((now() - v).total_seconds() / 3600) | round(0) }}
{% endfor -%}
If it works properly, I can show you how to use it an automation.
This one doesn’t work, the error is UndefinedError: ‘str object’ has no attribute ‘state’
{% set entities = states.sensor | selectattr('object_id', 'search', 'last_seen') | map(attribute='entity_id') | list -%}
{% set values = entities | map(attribute='state') | map('as_datetime') | list -%}
{% set z = zip(entities, values) | list -%}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{{ state_attr(e, 'friendly_name') | replace(' linkquality', '') }} -- {{ ((now() - v).total_seconds() / 3600) | round(0) }}
{% endfor -%}
The error mentioned above is why the condition no longer works. There is a different way to test for the presence of the last seen attribute though. This might work as expected:
{% set ns = namespace(break = false) %}
{% for state in states -%}
{%- if state.attributes.get('last_seen') != none %}
{%- if (as_timestamp(now()) - as_timestamp(state.attributes.last_seen) > (5 * 60 * 60) ) and ns.break == false %}
{%- set ns.break = true %}
true
{%- endif -%}
{%- endif -%}
{%- endfor %}
But the other answers here will likely be better ways to reach the same goal.
Let me know if this version also produces an error.
{% set entities = states.sensor | selectattr('object_id', 'search', 'last_seen') | map(attribute='entity_id') | list -%}
{% set values = entities | map('states') | map('as_datetime') | list -%}
{% set z = zip(entities, values) | list -%}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{{ state_attr(e, 'friendly_name') }} -- {{ ((now() - v).total_seconds() / 3600) | round(0) }}
{% endfor -%}
The first version contained a mistake. In the second version I replaced map(attribute='state') with map('states').
According to the OP, there’s no last_seen attribute. It’s a sensor entity whose entity_id contains the string “last_seen”. I provided a test template to list those sensors and the OP confirmed it did.
OK, that means some of your Last Seen sensors don’t have a state value (or it’s not a datetime string). The template will need to be modified to exclude those sensors. Let’s try the has_value filter.
{% set entities = states.sensor
| selectattr('object_id', 'search', 'last_seen')
| map(attribute='entity_id')
| select('has_value') | list -%}
{% set values = entities
| map('states')
| map('as_datetime') | list -%}
{% set z = zip(entities, values) | list -%}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{{ state_attr(e, 'friendly_name') }} -- {{ ((now() - v).total_seconds() / 3600) | round(0) }}
{% endfor -%}
Sorry this is taking several attempts. I don’t have Last Seen sensors in my system so I can’t easily test the template. We’ll get it to work eventually.
NOTE
If this version also fails with an error then I will need to see a few examples of your Last Seen sensors. It will help me understand what kind of data the template must process. Specifically, the sensor’s state value as it appears in Developer Tools → States.
{% set entities = states.sensor
| selectattr('object_id', 'search', 'last_seen')
| map(attribute='entity_id')
| select('has_value') | list -%}
{% set values = entities
| map('states')
| map('as_datetime') | list -%}
{% set z = zip(entities, values) | list -%}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{{ state_attr(e, 'friendly_name') | replace(' Last seen', '') }} -- Da {{ ((now() - v).total_seconds() / 3600) | round(0) }} ore
{% endfor -%}
I’m fixing it and testing it well, the action theme seems to be working.
But now the problem is in the condition, because the automation works if it is triggered manually, but it doesn’t work automatically at the preset times. The automation always stops at the condition that is not satisfied
Test this version of the template in the Template Editor and confirm the resulting list contains the sensor’s name and time.
{% set entities = states.sensor | selectattr('object_id', 'search', 'last_seen')
| map(attribute='entity_id') | select('has_value') | list -%}
{% set values = entities | map('states') | map('as_datetime') | list -%}
{% set z = zip(entities, values) | list %}
{% set ns = namespace(x=[]) %}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{% set ns.x = ns.x + [(state_attr(e, 'friendly_name') | replace(' Last seen', ''), ((now() - v).total_seconds() / 3600) | round(0))] %}
{% endfor -%}
{{ ns.x }}
It should look something like this:
[('Sirena Allarme', 13)]
If it does then it can be used in an automation like this:
alias: ALERT Zigbee Offline Devices
id: zigbee_device_missing_alert
triggers:
- trigger: time
at:
- "07:00:00"
- "16:00:00"
- "20:00:00"
conditions: []
actions:
- variables:
ls: >
{% set entities = states.sensor | selectattr('object_id', 'search', 'last_seen')
| map(attribute='entity_id') | select('has_value') | list -%}
{% set values = entities | map('states') | map('as_datetime') | list -%}
{% set z = zip(entities, values) | list %}
{% set ns = namespace(x=[]) %}
{% for e, v in z if now() - v > timedelta(hours=5) -%}
{% set ns.x = ns.x + [(state_attr(e, 'friendly_name') | replace(' Last seen', ''), ((now() - v).total_seconds() / 3600) | round(0))] %}
{% endfor -%}
{{ ns.x }}
- if: "{{ ls | count > 0 }}"
then:
- action: notify.notification_service
data:
title: Z2M DEVICE ALERT
message: >
{% for n, t in ls -%}
{{ n }} -- Da {{ t }} ore
{% endfor -%}