From 45a80f182df3d67acc8b95b26985b38322511f65 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 22 Mar 2022 23:50:02 -0400 Subject: [PATCH] Dump entities in zwave_js device diagnostics (#68536) --- .../components/zwave_js/diagnostics.py | 61 ++++++++++++++++++- tests/components/zwave_js/test_diagnostics.py | 8 ++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave_js/diagnostics.py b/homeassistant/components/zwave_js/diagnostics.py index c9e45f09685..dd88f2b6d07 100644 --- a/homeassistant/components/zwave_js/diagnostics.py +++ b/homeassistant/components/zwave_js/diagnostics.py @@ -1,9 +1,11 @@ """Provides diagnostics for Z-Wave JS.""" from __future__ import annotations +from typing import Any + from zwave_js_server.client import Client from zwave_js_server.dump import dump_msgs -from zwave_js_server.model.node import NodeDataType +from zwave_js_server.model.node import Node, NodeDataType from homeassistant.components.diagnostics.util import async_redact_data from homeassistant.config_entries import ConfigEntry @@ -11,6 +13,8 @@ from homeassistant.const import CONF_URL from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.device_registry import DeviceEntry +from homeassistant.helpers.entity_registry import async_entries_for_device, async_get from .const import DATA_CLIENT, DOMAIN from .helpers import get_home_and_node_id_from_device_entry @@ -18,6 +22,59 @@ from .helpers import get_home_and_node_id_from_device_entry TO_REDACT = {"homeId", "location"} +def get_device_entities( + hass: HomeAssistant, node: Node, device: DeviceEntry +) -> list[dict[str, Any]]: + """Get entities for a device.""" + entity_entries = async_entries_for_device( + async_get(hass), device.id, include_disabled_entities=True + ) + entities = [] + for entry in entity_entries: + state_key = None + split_unique_id = entry.unique_id.split(".") + # If the unique ID has three parts, it's either one of the generic per node + # entities (node status sensor, ping button) or a binary sensor for a particular + # state. If we can get the state key, we will add it to the dictionary. + if len(split_unique_id) == 3: + try: + state_key = int(split_unique_id[-1]) + # If the third part of the unique ID isn't a state key, the entity must be a + # generic entity. We won't add those since they won't help with + # troubleshooting. + except ValueError: + continue + value_id = split_unique_id[1] + zwave_value = node.values[value_id] + primary_value_data = { + "command_class": zwave_value.command_class, + "command_class_name": zwave_value.command_class_name, + "endpoint": zwave_value.endpoint, + "property": zwave_value.property_, + "property_name": zwave_value.property_name, + "property_key": zwave_value.property_key, + "property_key_name": zwave_value.property_key_name, + } + if state_key is not None: + primary_value_data["state_key"] = state_key + entity = { + "domain": entry.domain, + "entity_id": entry.entity_id, + "original_name": entry.original_name, + "original_device_class": entry.original_device_class, + "disabled": entry.disabled, + "disabled_by": entry.disabled_by, + "hidden_by": entry.hidden_by, + "original_icon": entry.original_icon, + "entity_category": entry.entity_category, + "supported_features": entry.supported_features, + "unit_of_measurement": entry.unit_of_measurement, + "primary_value": primary_value_data, + } + entities.append(entity) + return entities + + async def async_get_config_entry_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry ) -> list[dict]: @@ -38,6 +95,7 @@ async def async_get_device_diagnostics( if node_id is None or node_id not in client.driver.controller.nodes: raise ValueError(f"Node for device {device.id} can't be found") node = client.driver.controller.nodes[node_id] + entities = get_device_entities(hass, node, device) return { "versionInfo": { "driverVersion": client.version.driver_version, @@ -45,5 +103,6 @@ async def async_get_device_diagnostics( "minSchemaVersion": client.version.min_schema_version, "maxSchemaVersion": client.version.max_schema_version, }, + "entities": entities, "state": async_redact_data(node.data, TO_REDACT), } diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 332c8c84635..84fb401e31b 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -5,6 +5,7 @@ import pytest from zwave_js_server.event import Event from homeassistant.components.zwave_js.diagnostics import async_get_device_diagnostics +from homeassistant.components.zwave_js.discovery import async_discover_node_values from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.helpers.device_registry import async_get @@ -69,7 +70,12 @@ async def test_device_diagnostics( "minSchemaVersion": 0, "maxSchemaVersion": 0, } - + # Assert that we only have the entities that were discovered for this device + # Entities that are created outside of discovery (e.g. node status sensor and + # ping button) should not be in dump. + assert len(diagnostics_data["entities"]) == len( + list(async_discover_node_values(multisensor_6, device, {device.id: set()})) + ) assert diagnostics_data["state"] == multisensor_6.data -- GitLab