I’ve also been looking for a way to detect unresponsive devices for some time so thanks for opening that issue. Unfortunately, the comment that closed that issue doesn’t provide any details about how to access last_seen
from any of the “diagnostics entities” it mentions.
I have noticed that there are “new” RSSI and LQI entities for my Zigbee devices and looking at those and the rest of the device’s entities it seems like the last_seen
time stamp from the Zigbee info
strongly correlates to the most recent last_updated
time stamp from all of the device’s entities. According to the state docs, however, last_updated
won’t change even if an update has been received from the integration but with the same exact state and attributes. So my understanding is that the most recent entity last_updated
may usually be the same as the Zigbee last_seen
there can be cases where last_seen
is actually more recent. For example, if a Zigbee device has state and attributes that stay exactly the same for days at a time, then the most recent entity last_updated
could be days older than Zigbee last_seen
. If I’m correct then HASS definitely has access to more accurate data than we as users can currently get access to outside of the UI. I can imagine that this isn’t available in things like template sensors to keep the state engine effecient/responsive. But maybe not, for example does the UI update the Zigbee last_seen
automatically? If so, then why can’t I use it in a template sensor?
While looking into this, I stumbled across Checkin event
s in the logbook for one model of Zigbee device I have but not for other models of Zigbee devices. I don’t actually know, but I can imagine that in low-power wireless mesh networks like Zigbee and Z-Wave, emphasis on low-power, accurate details on when devices were last responsive might come at the expense of battery life and as such may not be available at the lowest level.
Given those two considerations, device battery life and state engine responsiveness, then maybe the thing we’re looking for is an anti-feature. IOW, if you want your low-power battery devices to never die, then physically test (and maybe change batteries) on a conservative regular schedule. If that doesn’t work for you, then resign yourself to dealing with unresponsive devices as you discover them. Please, if someone actually knows the technical underpinnings here, chime in!
As it stands, the best available way to detect the time a device was last responsive may be the most recent last_updated
time stamp of all the entities of the given device. Realizing this, I removed the battery from a Zigbee device and found that at least one of the entities of the device had a state of unknown
or unavailable
within the same rough time span from the most recent last_updated
where I’d want to check on the device IRL. So for my purposes waiting for at least one entity to become unknown
or unavailable
is what I’m going to go with. But others could also use the most recent entity last_updated
if they want to be more aggressive in unresponsive device detection.
I used Developer Tools
→ Template
to create a Jinja filter based on an unknown
or unavailable
state and added regular expression exclusions to weed out spurious false positives until I got a result that works for me. When I removed the battery from a Zigbee device, I got a notification within a few hours. I’d like to know sooner, but not enough to develop something based on last_updated
let alone trade off battery efficiency or make the state engine unresponsive. In case it helps someone, here are the template sensors I’m using:
template:
# Unavailable entities: dead batteries, Zigbee/ZWave mesh network issues, etc
# https://www.home-assistant.io/integrations/template/#trigger-based-template-binary-sensors-buttons-numbers-selects-and-sensors
- trigger:
# Avoid frenetic updates on every state change for all entities
# https://www.home-assistant.io/docs/automation/trigger/#time-pattern-triggre
- platform: "time_pattern"
minutes: "*"
sensor:
- unique_id: "unavailable_entities"
name: "Unavailable Entities"
state: >-
{{
(states|
selectattr('state', 'in', ['unavailable', 'unknown'])|
rejectattr('entity_id', 'match', '^('
'(binary_|)sensor.unavailable_entities|'
'.*_('
'identify|'
'restart|'
'ping|'
'wake|'
'effect_speed|'
'next_alarm|'
'electrical_measurement_power_factor|'
'phone_detected_activity'
')|'
'sensor.router_kib_s_(sent|received)|'
'.+_accuweather_[^.]+|'
'sensor.openweathermap_dew_point|'
'sensor.openweathermap_forecast_temperature_low|'
'sensor.openweathermap_uv_index'
')$')|
map(attribute='name')|
join(', '))[:255]
}}
- binary_sensor:
- unique_id: "unavailable_entities"
name: "Unavailable Entities"
state: >-
{% if states('sensor.unavailable_entities') and
not is_state('sensor.unavailable_entities', 'unknown')
%}true{% endif %}
</rabbit-hole>