List all ZHA devices that are offline

I was looking for a simple way to show all the devices that were disconnected from the Zigbee network, but I couldn’t find any solution that I liked.

Most of the solutions I found listed the disconnected entities, but not the disconnected devices. Since a device can have multiple entities, the generated listings contained many duplicates.

After many hours of testing, I got a working solution with the following code:

type: custom:auto-entities
card:
  type: entities
unique: false
filter:
  template: |
    {% set data = namespace(numbers=[]) %}
    {%- for da in expand(integration_entities('zha') ) |selectattr( 'state',
    'eq', 'unavailable') | list -%}
      {% set data.numbers = data.numbers + [device_entities(device_attr(da.entity_id, 'id')) | first] %}
    {%- endfor -%}
    [
    {%- for filt in data.numbers | unique -%}
      {{ {"name": device_attr(filt, 'name_by_user'), "secondary": device_attr(filt, 'name'), "state": "", "icon": "mdi:devices", "type": "custom:template-entity-row" } }} 
      ,
    {%- endfor -%}
    ]
else:
  type: markdown
  content: Ninguno

You will need to first install the cards “Auto Entities” and “Template Entity Row”.

This is how it looks:
Screenshot 2023-12-22 at 01-36-28 Resumen – Home Assistant

And the same thing in a dashboard:

I thought it would be a good idea to share the code in case anyone had the same problem. I hope it helps!

5 Likes

That’s brilliant! Thank you very much :slight_smile:
How do I get the entire table with the details of the Zigbee devices?

Hey
I’ve installed this custom card through HACS: GitHub - dmulcahey/zha-network-card: Custom Lovelace card that displays ZHA network and device information

And this is the code that I’m using:

clickable: true
columns:
  - attr: available
    id: available
    modify: >-
      if (x===true){"<span style='color:green;'>✓</span>"}else{"<span
      style='color:red;'>✕</span>"}
    name: ' '
    align: center
  - name: Nombre
    prop: name
  - attr: manufacturer
    name: Fabricante
  - attr: model
    name: Modelo
  - attr: ieee
    name: IEEE
  - name: NWK
    prop: nwk
    align: center
  - attr: lqi
    name: LQI (%)
    align: center
    modify: Math.round((x*100)/255) || ""
  - attr: last_seen
    name: Última vez
    modify: >-
      if (new Date(Date.parse(x)).getDate() == new Date().getDate()) {String(new
      Date(Date.parse(x)).getHours()).padStart(2, "0")+":"+String(new
      Date(Date.parse(x)).getMinutes()).padStart(2, "0") }else{String(new
      Date(Date.parse(x)).getDate()).padStart(2, "0")+"/"+String(new
      Date(Date.parse(x)).getMonth()+1).padStart(2, "0")+" "+String(new
      Date(Date.parse(x)).getHours()).padStart(2, "0")+":"+String(new
      Date(Date.parse(x)).getMinutes()).padStart(2, "0")}
    align: center
  - attr: power_source
    name: A
    modify: if (x=='Mains'){"🔌"}else{"🔋"}
    align: center
  - attr: quirk_class
    name: Quirk
    modify: x.replace(/^(zhaquirks.)/,"");
sort_by: available-
type: custom:zha-network-card
view_layout:
  position: main
2 Likes

Hey thank you very much! I’ll test the card right away ;)’

I use the auto-entities vard for this. Copied the setup from some other post here on the formum and edited it for my need. So I do not take any credit for the code.

type: custom:auto-entities
card:
  type: custom:bar-card
  positions:
    name: inside
    value: inside
    indicator: 'off'
    icon: 'off'
  unit_of_measurement: '%'
  max: 100
  severity:
    - color: '#ff4d00'
      from: 0
      to: 10
    - color: '#ff8100'
      from: 11
      to: 20
    - color: LimeGreen
      from: 21
      to: 100
  height: 30px
  style: "\n#states { padding: 0 } \nbar-card-name,\nbar-card-value {\n  \n  color: #ffffff;\n  }\nbar-card-value\t{\n  font-weight: bold;\n}\nbar-card-indicator {\n transform: scaleY(-1);\n}\n"
  stack: vertical
  direction: right
  decimals: 2
entities: []
sort:
  method: state
  numeric: true
  reverse: false
filter:
  include:
    - state: unavailable
  exclude:
    - state: '> 40'
    - entity_id: sensor.*_*state*
    - entity_id: sensor.*phone*

I can’t seem to find the auto-entities cards. How are you installing them?

Old thread, but for those who want to retrieve ZHA devices programmatically. Here a pyscript solution:

The ZHA card retrieves all ZHA info via a web socket call, using this command:
{"type": "zha/devices"}

This code can be used for that:

# All credits how to use websockets to retrieve the lovelace configuration goes to https://github.com/qui3xote:
# https://github.com/custom-components/pyscript/discussions/272#discussioncomment-1709728

import json
import websockets

async def websocket_command(command: dict, url=None, token=None):
    """ returns json result as python data """
    if token==None and 'token' in pyscript.config['global']:
        token = pyscript.config['global']['token']
    if url==None:
        url = 'ws://127.0.0.1:8123/api/websocket'
    # ids are returned in the receive phase so you tie back to originating command. 
    # The API has feelings about these: they must increment, at least per session
    websocket_command.counter += 1
    command['id'] = websocket_command.counter  
    async with websockets.connect(url) as websocket:
        log.info(websocket.recv()) #first receive will give you HA version, if it matters
        await websocket.send(json.dumps({"type": "auth", "access_token": token})) #send auth
        log.info(websocket.recv()) #once ok is received we go into command phase
        await websocket.send(json.dumps(command))
        result = websocket.recv()
        log.info(result)
    return json.loads(result)
websocket_command.counter = 0

@service(supports_response="only")
def zha_devices(available=None, device_type=None, area_id=None, short=True):
    """yaml
description: Lookup ZHA devices, Optionally filter
fields:
    available:
        description: optional filter on available true or false 
        example: true
        selector:
            boolean:
    device_type:
        description: optional filter on a particular device_type
        example: Router
        selector:
          select:
            options:
             - Router
             - EndDevice
             - Coordinator
        multiple: false
    area_id:
        description: optional filter on a particular area
        example: Living
        selector:
            text:
    short:
        description: return summary or full device info
        example: true
        selector:
            boolean:
"""
    command={"type": "zha/devices"}
    response = websocket_command(command)
    if 'result' in response and response['success']:
      devices=[]
      for device in response['result']:
        if ((available is None or available == device['available']) and 
            (device_type is None or device_type==device['device_type']) and
            (area_id is None or area_id==device['area_id']) ):
          name = device['user_given_name'] if device['user_given_name'] else device['name']
          if short and device_type is None and area_id is None:
            devices.append({"name":name, "area_id": device['area_id'],"device_type": device['device_type'],"last_seen":device['last_seen']})
          elif short and device_type is None:
            devices.append({"name":name, "device_type": device['device_type'],"last_seen":device['last_seen']})
          elif short and area_id is None:
            devices.append({"name":name, "area_id": device['area_id'],"last_seen":device['last_seen']})
          elif short:
            devices.append({"name":name,"last_seen":device['last_seen']})
          else:
            devices.append(device)
      return  {"result": True,"total_devices":len(response['result']),"returned": len(devices),"devices":devices }
    return {"result": False }

The script can be called without arguments (all parameters are optional). It allows to filter on a combination of available, device_type and area_id. Example:

service: pyscript.zha_devices
data:
  short: true
  device_type: Router

Response (I have 18 routers out of 52 devices):