diff --git a/homeassistant/components/zwave_js/diagnostics.py b/homeassistant/components/zwave_js/diagnostics.py index 52f1b836833..a580e908173 100644 --- a/homeassistant/components/zwave_js/diagnostics.py +++ b/homeassistant/components/zwave_js/diagnostics.py @@ -1,12 +1,18 @@ """Provides diagnostics for Z-Wave JS.""" from __future__ import annotations +from zwave_js_server.client import Client from zwave_js_server.dump import dump_msgs +from zwave_js_server.model.node import NodeDataType from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_URL from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.device_registry import DeviceEntry + +from .const import DATA_CLIENT, DOMAIN +from .helpers import get_home_and_node_id_from_device_entry async def async_get_config_entry_diagnostics( @@ -17,3 +23,15 @@ async def async_get_config_entry_diagnostics( config_entry.data[CONF_URL], async_get_clientsession(hass) ) return msgs + + +async def async_get_device_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry, device: DeviceEntry +) -> NodeDataType: + """Return diagnostics for a device.""" + client: Client = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT] + identifiers = get_home_and_node_id_from_device_entry(device) + node_id = identifiers[1] if identifiers else None + 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") + return client.driver.controller.nodes[node_id].data diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index 363762ac72b..3f57f4bbe6f 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -80,13 +80,26 @@ def get_device_id_ext(client: ZwaveClient, node: ZwaveNode) -> tuple[str, str] | @callback -def get_home_and_node_id_from_device_id(device_id: tuple[str, ...]) -> list[str]: +def get_home_and_node_id_from_device_entry( + device_entry: dr.DeviceEntry, +) -> tuple[str, int] | None: """ Get home ID and node ID for Z-Wave device registry entry. - Returns [home_id, node_id] + Returns (home_id, node_id) or None if not found. """ - return device_id[1].split("-") + device_id = next( + ( + identifier[1] + for identifier in device_entry.identifiers + if identifier[0] == DOMAIN + ), + None, + ) + if device_id is None: + return None + id_ = device_id.split("-") + return (id_[0], int(id_[1])) @callback @@ -128,16 +141,9 @@ def async_get_node_from_device_id( # Get node ID from device identifier, perform some validation, and then get the # node - identifier = next( - ( - get_home_and_node_id_from_device_id(identifier) - for identifier in device_entry.identifiers - if identifier[0] == DOMAIN - ), - None, - ) + identifiers = get_home_and_node_id_from_device_entry(device_entry) - node_id = int(identifier[1]) if identifier is not None else None + node_id = identifiers[1] if identifiers else None 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") diff --git a/tests/components/diagnostics/__init__.py b/tests/components/diagnostics/__init__.py index 63961c8013a..69df193d284 100644 --- a/tests/components/diagnostics/__init__.py +++ b/tests/components/diagnostics/__init__.py @@ -18,6 +18,8 @@ async def get_diagnostics_for_config_entry(hass, hass_client, config_entry): async def get_diagnostics_for_device(hass, hass_client, config_entry, device): """Return the diagnostics for the specified device.""" + assert await async_setup_component(hass, "diagnostics", {}) + client = await hass_client() response = await client.get( f"/api/diagnostics/config_entry/{config_entry.entry_id}/device/{device.id}" diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 5abce53fcbb..f668a46bc9d 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -1,16 +1,52 @@ """Test the Z-Wave JS diagnostics.""" from unittest.mock import patch -from homeassistant.components.zwave_js.diagnostics import ( - async_get_config_entry_diagnostics, +import pytest + +from homeassistant.components.zwave_js.diagnostics import async_get_device_diagnostics +from homeassistant.components.zwave_js.helpers import get_device_id +from homeassistant.helpers.device_registry import async_get + +from tests.components.diagnostics import ( + get_diagnostics_for_config_entry, + get_diagnostics_for_device, ) -async def test_config_entry_diagnostics(hass, integration): +async def test_config_entry_diagnostics(hass, hass_client, integration): """Test the config entry level diagnostics data dump.""" with patch( "homeassistant.components.zwave_js.diagnostics.dump_msgs", return_value=[{"hello": "world"}, {"second": "msg"}], ): - result = await async_get_config_entry_diagnostics(hass, integration) - assert result == [{"hello": "world"}, {"second": "msg"}] + assert await get_diagnostics_for_config_entry( + hass, hass_client, integration + ) == [{"hello": "world"}, {"second": "msg"}] + + +async def test_device_diagnostics( + hass, + client, + aeon_smart_switch_6, + aeon_smart_switch_6_state, + integration, + hass_client, +): + """Test the device level diagnostics data dump.""" + dev_reg = async_get(hass) + device = dev_reg.async_get_device({get_device_id(client, aeon_smart_switch_6)}) + assert device + assert ( + await get_diagnostics_for_device(hass, hass_client, integration, device) + == aeon_smart_switch_6_state + ) + + +async def test_device_diagnostics_error(hass, integration): + """Test the device diagnostics raises exception when an invalid device is used.""" + dev_reg = async_get(hass) + device = dev_reg.async_get_or_create( + config_entry_id=integration.entry_id, identifiers={("test", "test")} + ) + with pytest.raises(ValueError): + await async_get_device_diagnostics(hass, integration, device)