To assist in my maintenance of a remote installation, I would like a dashboard panel that lists all the devices that are unavailable, preferably sorted according to integration (in my case Tuya, Local Tuya, Sonoff/eWeLink and ZHA).
Has anyone done this?
I did not quite exactly what you are searching for but something very similar.
I have a script written in pyscript that checks for unavailable devices every 30 minutes and uses persistent notifications to notify me when it finds any. It’s quick and dirty and can be improved, but it does its job well enough.
It basically works by getting a list of all devices from the device registry (the huge advantage of pyscript compared to other solutions like AppDaemon or Node Red is, that pyscript allows you to access internal APIs of home assistant like the device registry) and then checking all entities associated with a device. When all entities are in state “unavailable” I then assume that the whole device is unavailable.
ignore_devices = [
# Add ids of devices to be ignored here
]
ignore_entities = [
# Add entities whose devices should be ignored here
]
CHECKER_ID = "pyscript.device_availability_checker"
from homeassistant.helpers import entity_registry as erm
from homeassistant.helpers import device_registry as drm
from homeassistant.const import STATE_UNAVAILABLE
@time_trigger('cron(*/30 * * * *)')
def device_availability_checker_cron():
er = erm.async_get(hass)
dr = drm.async_get(hass)
unavailable_devices = {}
unavailable_since = {}
# Iterate over all devices and check whether they are unavailable
# A device is supposed to be unavailable if all related entities are in
# state "unavailable"
for d in dr.devices:
if d in ignore_devices:
continue
unavailable = False
since = None
for e in erm.async_entries_for_device(er, d):
if e.entity_id in ignore_entities:
unavailable = False
break
elif hass.states.is_state(e.entity_id, STATE_UNAVAILABLE):
unavailable = True
since = hass.states.get(e.entity_id).last_changed
else:
unavailable = False
break
if unavailable:
unavailable_devices[d] = dr.async_get(d)
unavailable_since[d] = since
notifs = []
devices = {}
# Iterate over all unavailable devices and construct notification text
# for a persistent_notification
for k, d in unavailable_devices.items():
manufacturer = d.manufacturer
model = d.model
text = f'- {d.name}'
desc = ""
if model is not None:
desc += model
if manufacturer is not None:
if desc != "":
desc += f" [{manufacturer}]"
else:
desc += manufacturer
if desc != "":
text += "\n - " + desc
text += f"\n - Since: {unavailable_since[k]}"
text += f"\n - ID: {d.id}"
devices[k] = {
"name": d.name,
"manufacturer": manufacturer,
"model": model,
"since": unavailable_since[k]
}
notifs.append(text)
# Show a persistent notification or dismiss an old one if there is nothing
# to show
ntext = "\n".join(notifs)
if ntext != "":
hass.services.async_call("persistent_notification", "create", {
"notification_id": "device_availability_warning",
"title": "List of Unavailable Devices",
"message": ntext
}, False)
else:
hass.services.async_call("persistent_notification", "dismiss", {
"notification_id": "device_availability_warning"
}, False)
state.set(CHECKER_ID, len(devices), devices = devices)
@time_trigger('startup')
def device_availability_checker_startup_trigger():
log.info("device_availability_checker.startup_trigger()")
task.sleep(60)
device_availability_checker_cron()
1 Like