ZHA show last_seen, LQI and RSSI as entity attributes

We all learning and nubs :wink: Just take small steps each evolution. A couple routes you could go.

  1. Do an alert outside HA. Take a look at the simple example I wrote, ws02.py. And then use something like the code I link below to give you a iOS or Android alert.

ws02.py just prints out the current ZHA view of all of its zigbee devices every 5 seconds in JSON format. This should give you a feel for how you read a HA websocket and what the ZHA JSON data looks like. The parsing examples I show using JQ app to extract out subsets of the ZHA JSON data should give you a feel for how to get to the data you want in the ZHA websocket output. Python’s JSON libraries can do the same extract and filtering that the JQ app does.

You can look at the ‘last seen’ and ‘online’ values for each of your zigbee devices and used these to help determine the devices states. You could mash this up with the code I show below to create an app outside of HA that alerts you when the age of a device exceeds a selected value.

I wrote a simple python script for monitoring whether MQTT bluetooth sensors drop off, it is a bit different a flow, but it might give you an idea of how to do an alert outside of HA when a sensor goes rogue. I use a iOS and Android alerting service called ‘Pushover’ in this app to alert me when a MQTT value has not updated for a period:

  1. If you want to create a HA sensor, using AppDaemon is the route I know. There maybe other ways. However, you need to be able to call a HA websocket in the environment you are running, I think you can do this with javascript within HA (as the ZHA Network Card example shows) however I do not know how or if you can create a HA sensor using this route. I am not familiar with how to call websockets using python within HA, I am not sure all the necessary libraries are available in HA’s python. So this is why I recommended the AppDaemon route.

I would familiarize yourself with how AppDaemon works/runs apps by creating a simple dummy sensor from their examples. After you are comfortable with creating a HA sensor within App Daemon that updates on a regular period, I would then add in the ZHA websocket calling logic similar to the code in ws02.py combined with your logic to check the age of zigbee devices and then set some type of state in the HA sensor that you create to show the dead zigbee device info to HA.

Good hunting!

Thanks for your detailed reply. Trust me, I have spent hours trying to figure out the py code to do this with no luck. I just do not have the python skills. You have provided some additional info that might help me so I will try again.
I wish that ZHA would include this data as sensor attributes, just like zigbee2mqtt does. Oh well.
thanks again for the help.

See if you can get the ws02.py working and then see if you can make a few changes to it to check the last_seen and rssi attributes, below is how you would do it piping the output of ws02.py to the jq utility:

./ws02.py | jq -c '.result[] | {date: (now|strftime("%s")), ieee: .ieee, ls: .last_seen, rssi: .rssi}'

{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:45:52","rssi":-48}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-06-25T11:08:36","rssi":null}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:33","rssi":-40}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:46","rssi":null}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:46","rssi":null}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:46","rssi":null}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:46","rssi":null}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:46","rssi":null}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:41","rssi":-36}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:41","rssi":-41}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:42","rssi":-40}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:56:07","rssi":-38}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:56:45","rssi":-49}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:51:18","rssi":-66}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:25","rssi":-44}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:57:14","rssi":-40}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:50:30","rssi":-37}
{"date":"1626152267","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:18:30","rssi":-62}

–or–

./ws02.py | jq -c '.result[] | {date: (now|strftime("%s")), ieee: .ieee, ls: .last_seen, rssi: .rssi, name: .user_given_name}'

{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T14:03:36","rssi":-38,"name":"SmartThings PGC313 01 Entry Way"}
{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T14:05:15","rssi":-49,"name":"IKEA of Sweden TRADFRI motion sensor 01 Dining Room"}
{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:51:18","rssi":-66,"name":"CREE  Connected A-19 60W Equivalent  01 HVAC Closet"}
{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T14:05:25","rssi":-44,"name":"OSRAM LIGHTIFY A19 Tunable White 01 Living Room"}
{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T14:03:56","rssi":-53,"name":"eWeLink TH01 Test"}
{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T13:50:30","rssi":-37,"name":"IKEA of Sweden TRADFRI on/off switch stove"}
{"date":"1626152745","ieee":"xx:yy:xx:yy:xx:yy:xx:yy","ls":"2021-07-12T14:00:39","rssi":-62,"name":"LUMI lumi.weather Test"}

shout if I can help.

Thanks so much for your sample code. It was very helpful and I was able to create an AppDaemon app that does exactly what I need. :slight_smile: I am sure that the code could be better or simplified as I am not a python coder but it works!
I do not set a timer via AppDaemon to run this but through an automation as it suited my needs better.
I have included my code below in case it could help someone else.
cheers

import appdaemon.plugins.hass.hassapi as hass
import sys
import json
import time
import datetime

from websocket import create_connection

ACCESS_TOKEN = "***"

class zha_check(hass.Hass):

  def initialize(self):
    self.listen_event(self.ZHAmonitor, "ZHAcheck")
    #call this every 60 minutes from an HA automation by action: - event: ZHAcheck

    self.ZHAdump()

  def ZHAmonitor (self, event_name, data, kwargs): 
    self.ZHAdump()

  def ZHAdump (self): 
    count = 0
    last_seen = {}
    max_time = 5400 #90 minutes
    now = datetime.datetime.now()

    ws = create_connection("ws://192.168.1.*:8123/api/websocket")
    result =  ws.recv()

    ws.send(json.dumps( {'type': 'auth', 'access_token': ACCESS_TOKEN} ))
    result =  ws.recv()
    
    ident = 1
    ws.send(json.dumps( {'id': ident, 'type': 'zha/devices'} ))
    result =  ws.recv()    

    # convert the string that came back to JSON
    json_result = json.loads(result)

    # retrieve each device that was returned
    for device in json_result["result"] :
      #self.log("Device: " + str(device["user_given_name"]) + " " + str(device["last_seen"]))

      last_date = str(device["last_seen"])
      last_dat2 = last_date[:19]
      last_dat3 = datetime.datetime.strptime(last_dat2, '%Y-%m-%dT%H:%M:%S')
      diff = (now - last_dat3).seconds
      if diff > max_time:
        last_seen[str(device["user_given_name"])] = last_dat3
        count = count + 1

    last_seen['friendly_name'] = 'Last Seen ZHA'
    last_seen['icon'] = 'mdi:message-alert-outline'
    self.set_state('sensor.last_seen_zha',state=count,attributes=last_seen)
    #this will create a sensor called sensor.last_seen_zha with a list of attributes containing the name and last_seen time of each sensor not reporting for more then 90 minutes.
    #I then have an automation that emails me an alert when this sensor is greater than 0


2 Likes

Great job! As I was looking at my code and yours, I am not sure if the web socket JSON return give back timezone info (there might be a way in python to return a proper UTC time), so there might be a burp in code on DST change days.

I upvoted this feature request.

Check also the idea to get last_seen from the ZHA devices file:

FYI, basic RSSI and LQI sensors for diagnostics were added to ZHA in Home Assistant 2022.2 release:

https://community.home-assistant.io/t/showing-basic-rssi-prometheus-exporter/390632/

https://github.com/home-assistant/core/pull/62716

https://www.home-assistant.io/blog/2022/02/02/release-20222/

2 Likes

Hey Guys,

Meanwhile indeed RSS and LQI can be seen under the diagnostics tab.

Iam also interested in the last_seen (laatst gezien in dutch) property, but monitoring the last_seen property via the ZHA_Card I noticed that the last_seen property from OFFLINE devices is updated every second at least from the lightbulbs.

Notice the last seen property while the lamp is offline, for 2 months, in fact the lamp is in a drawer!!!

last_seen but not online2
3 bulbs all offline in a drawer, but updated every second as last seen…
last_seen but not online3

The 4th row location ‘woonkamer’ is a remote control without batteries and the last seen property stays at january 30th…

anybody an explanation why the lightbulbs in the drawer (lol) keeps being updated??

Regards Frank

@fantangelo Borrowed your appdaemon code and made some changes and made it hacs compatible

TY this has worked perfectly for me

3 Likes

Thanks for improving the script. It’s a lot easier to use now.
cheers

1 Like

How do we install this via hacs?

Voted for this as well.

Having the same problem that with ZHA I can not display the “last seen” attribute in my LoveLace ui.

Found a solution.

You can configure ZHA now and set battery and main powered devices as unavailable after a certain period in seconds. For example, 7200 second for two hours. After that, the device gets the value “unavailable”.

You can trigger an automation on this change. For example, when your PIR LQI sensor did not update for 2 hours and gets the state “unavailable” you can send yourself a message that the device is offline.
Only thing is, that the automation get’s pretty long when you have a lot of devices you need to add as a trigger. But it should work.

1 Like

Ideally last seen would be exposed like it is for some other integrations, but using the ZHA timeouts on something like LSI is a a good solution until/unless it is made available.

Yeah, last_seen would be much better. Hopefully it is added soon.

2 Likes

Easy access to last_seen, like with RSSI and LQI, would be amazing and would enable better tracking and device health monitoring
Please, please, please consider adding this

2 Likes

@le_top as workaround do you think can get RSSI/LQI values via “zha-toolkit” for use in automation?

Thanks Hedda,
I have moved on to z2m.

It can, here is an example - zha-toolkit/examples/script_use_zha_devices.yaml at f8a8f5243188afcd9cdcdfbf71029fb1c3ac356a · mdeweerd/zha-toolkit · GitHub .

EDIT: I updated zha_devices to optionally select just one device - will be in zha-toolkit >= 0.8.40 .

I’d recommend monitoring the primary or battery sensors for going unavailable since the lqi ones are a bit unreliable, especially after you restart some services, they can take a while to populate.

Also The Watchman off Hacs is great for monitoring all sensors to see and report when they go unavailable.