How can I read the "Last Seen" value of Zigbee device?

I’m trying to detect when a zigbee sensor stops responding or is unavailable. When that happens the sensor value is sometimes “unavailable”, “N/A” or it remains with the last known value.

Since it’s hard to figure this out via sensor value, I’d like to read the “Last Seen” attribute for the device and send an alert when it’s been over 30 minutes.

I can figure out the alert part but I don’t know how to read the device attributes. See this image for the attribute I’m referring to

If I can’t read this value from the sensor directly, maybe there’s a way to read it from a database table or some other log?

Thanks!

+1

And reading LQI/RSSI would be very helpful, too. E.g to create a sensor to track LQI over time to help detect intermittent interference.

You should add the sensor using mqtt:

You should add the sensor using mqtt:

I’m trying to make the sensors more reliable and avoid adding more failure points between the sensor and the automation. However, shadowing the sensor with MQTT and creating my own log with Node Red is an interesting alternative. I could track each sensor update and alert if it’s been longer than X minutes since the last update.

I was hoping to be able to just read the meta data but this is a decent alternative.

Turns out you can do this by using states.sensor.DEVICE.last_updated or .last_changed in a template.

For example, I’d get last_changed like this

states.sensor.lumi_lumi_sens_57884f03_temperature.last_changed

You can also get the other attributes by using .attributes.WHATEVER, like this

states.sensor.lumi_lumi_sens_57884f03_temperature.attributes.friendly_name
1 Like

I think you meant states.sensor...... instead of state.sensor... above. Just FYI…

Are you doing anything special to access those attributes? Which integration are you using for your Zigbee controller? I tried two different Zigbee devices, and neither has those attributes. I’m using a ConBeeII stick with the ZHA integration.

Example (I think it’s even the same Aqara sensor you mentioned):

{{ states.sensor.humidity_sensor_temperature.attributes }}

Yields:

{'on': True, 'unit_of_measurement': '°F', 'friendly_name': Humidity Sensor Temperature', 'icon': 'mdi:thermometer', 'device_class': 'temperature'}

Interestingly, if I check one of my door sensors that’s connected to a different ConBeeII stick via the deCONZ integration, I see this:

<template TemplateState(<state binary_sensor.door=off; on=True, friendly_name=Door, device_class=door @ 2021-02-17T11:42:19.780824-00:00>)>

In that case, both the last_changed and last_updated attributes return the date in the template. By inspecting a few sensors, it seems the timestamp is actually for ‘last_changed’ vs. ‘last_updated’. It also looks like the Kasa and Lutron integrations have last_changed attributes, too. Perhaps it’s just the ZHA integration that doesn’t populate these attributes?

Yes, you’re right. It should be states and I updated my previous comment. Thanks!

I’m using ZHA with the GoControl Zigbee/ZWave stick. My sensors are the round style of the Xiaomi Mijia temperature and humidity sensors. They connect without too much trouble but my main complaint is the temp won’t update until there’s a 0.9F change. There’s no way to change the update rate or poll the devices from HASS.

I know a more frequent update would drain the battery more but I’d be willing to change them twice a year, instead of annually, for smaller temperature changes.

The best way to experiment with getting the sensor values is to use Developer Tools → Template. You type in the various templates and see the results in real-time. Have a look at my screenshot.

Here’s a copy of the template I used in the screenshot. Don’t forget to replace my sensor name with yours

**********************************************
states.sensor.lumi_lumi_sens_ff884f03_temperature
**********************************************

{{ states.sensor.lumi_lumi_sens_ff884f03_temperature }}

**********************************************
states.sensor.lumi_lumi_sens_ff884f03_temperature.state
**********************************************

{{ states.sensor.lumi_lumi_sens_ff884f03_temperature.state }}

**********************************************
states.sensor.lumi_lumi_sens_ff884f03_temperature.attributes
**********************************************

{{ states.sensor.lumi_lumi_sens_ff884f03_temperature.attributes }}

**********************************************
states.sensor.lumi_lumi_sens_ff884f03_temperature.attributes.friendly_name
**********************************************

{{ states.sensor.lumi_lumi_sens_ff884f03_temperature.attributes.friendly_name }}


**********************************************
states.sensor.lumi_lumi_sens_temperature.last_updated
**********************************************

{{ states.sensor.lumi_lumi_sens_temperature.last_updated }}

Good luck!

This doesn’t seem enough, I’ve opened an issue about: ZHA: Expose last seen date as device (or child sensors) attribute · Issue #58955 · home-assistant/core · GitHub

2 Likes

last_updated or last_changed don’t have the same value as last_seen
I would also like to get the last_seen info, in my case to check if smoke detector is still connecting via ZHA.

2 Likes

I’ve created a workaround as I also need the last_seen for determining when zha devices is unavailable and HA needs restart to reactivate device.

I’ve created a template sensor from one attribute which I know is updating (most) often:

template:
  - sensor:
      - name: "Ally TRV demand"
        unit_of_measurement: "%"
        state: "{{states.climate.ally_trv.attributes.pi_heating_demand}}"
        unique_id: "100000021"

From templates it can then be determined when the sensor was last updated:

{{states.sensor.ally_trv_demand.last_updated}}

It’s not as good as last_seen but better than nothing I believe.
Alternatively you can create a sqlite sensor updating whenever a row is updated for a ieee:

sensor:
  - platform: sql
    db_url: sqlite:////config/zigbee.db
    scan_interval: 60
    queries:
      - name: "Ally TRV last received"
        query: "SELECT group_concat(value) as value FROM attributes_cache_v7 where ieee = 'ieee value'"
        column: "value"```
2 Likes

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 events 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 ToolsTemplate 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>

2 Likes

Look here:

Remember “xxx.xxx_last_seen” Entitiy must be enabled/hidden in HA UI, not disabled.

This is only for Zigbee2MQTT, not for ZHA. ZHA doesn’t expose last seen.

ZHA does not directly expose it, but ZHA-Toolkit can help with that.

Demonstration (results can be found in the home-assistant.log file) using this script:

alias: Loop over zha_devices, extract some device data
sequence:
  - parallel:
      - sequence:
          - wait_for_trigger:
              - platform: event
                event_type: zha_devices_ready
          - service: system_log.write
            data:
              logger: zha_devices
              level: error
              message: "{{ \"Got event %s\" % ( wait.trigger.event.data.devices ) }}"
          - service: system_log.write
            alias: Do single action for all devices, Loop over data in template
            data:
              logger: zha_devices
              level: error
              message: >
                {% set ns = namespace(names=[]) %} {% for item in
                wait.trigger.event.data.devices if not item.available %}
                  {% set ns.names = ns.names + [ "'%s'" % (item.name) ] %}
                {% endfor %} Items: {{ ns.names | join(', ') }}
          - repeat:
              for_each: "{{ wait.trigger.event.data.devices }}"
              sequence:
                - service: system_log.write
                  data:
                    logger: zha_devices
                    level: error
                    message: >-
                      {{ "Item '%s' Power: %s dBm Available: %s Last seen: %s" %
                      ( repeat.item.name, repeat.item.rssi,
                      repeat.item.available, repeat.item.last_seen ) }}
        alias: Wait for event and then use event data.
      - service: zha_toolkit.zha_devices
        data:
          event_done: zha_devices_ready
        alias: Start zha_devices service which generates event captured above.
  - repeat:
      count: 2
      sequence: []
mode: single

Edit: You can also write to an arbitrary entity (sensor) with zha_toolkit.ha_set_state. So it would be possible to setup an automation calling upon zha_toolkit.zha_devices every 15 minutes for instance that sets a last_seen state for each device or for a group of devices.

Your post here was very informative, provided great information.

Based on what you wrote, I made a blueprint to scan daily and notify when Zigbee/Zwave battery powered devices have gone offline, (I borrowed heavily from a blueprint from Sybx).

Have posted it here

I found a solution with an SQL query, but it needs one entity for each device :

Using SQL integration in the UI:

Database URL : sqlite:////config/zigbee.db
Column : last_seen
Select Query : SELECT last_seen FROM devices_v12 where ieee = ‘xx:xx:xx:xx:xx:xx:xx:xx’

sensor entity displays : 1690215703.74021

if you add the line

Value Template : {{ value | as_datetime }}

sensor entity displays : 2023-07-24 16:47:52.080090+00:00

Using configuration.yaml :

sql:
name: “SQL sensor name”
db_url: sqlite:////config/zigbee.db
query: “SELECT last_seen FROM devices_v12 where ieee = ‘xx:xx:xx:xx:xx:xx:xx:xx’”
column: “last_seen”

2 Likes

Next move is to find a way of scanning all zigbee devices without creating entities, to display in ascending order the last_seen time next to the devices names (and not IEEE)

Querying the SQL database is one method, but the name changes regularly when zigpy receives updates.

The latest ZHA-Toolkit version implements response data which makes it easier to use the command in a script, the field filter already existed.

service: zha_toolkit.zha_devices
data:
  csvlabel: name
  command_data: [name,last_seen,user_given_name]
  response_variable: last_seen_data

When you use that in script or automation, then you can access the list using “last_seen_data.devices”. You’re free to choose the name for the variable. “response_variable” is a home assistant feature, available when the service provides a response.

Interactively, in Developer Tools, you’ll see the response below the service request.

1 Like

Nice, I used your zha toolkit example to create a template binary sensor to reflect if ZHA is online (True if at least least one zigbee device, ‘on mains power’, is on_line)

- trigger:
  - platform: time_pattern
    minutes: /2
  action:
    - service: zha_toolkit.zha_devices
      data:
        command_data:
          - name
          - available
          - device_type
      response_variable: Result
    - variables:
        available: >
          {{ (Result.devices or [])|rejectattr('available','eq',False)|list|count }}
        av_mains: >
          {{ (Result.devices or [])|rejectattr('available','eq',False)|rejectattr('device_type','eq','EndDevice')|list|count }}
        unavailable: >
          {{ (Result.devices or [])|rejectattr('available','eq',True)|list|count }}
  binary_sensor:
  - unique_id: zha_online
    name: 'ZHA Online'
    state: "{{ av_mains > 0 }}"
    attributes:
        available_all: "{{ available }}"
        available_mains: "{{ av_mains }}"
        unavailable_all: "{{ unavailable }}"
    device_class: connectivity

Example of the sensor in Dev Tools:

Pity there is not a diagnostics sensor offered by ZHA to provide this kind of ‘integration health’ info

May I ask why you feel you need one?