Add zwave_js device diagnostics (#64504)
* Add zwave_js device diagnostics * Add diagnostics as a dependency in manifest * Add failure scenario test * fix device diagnostics helper and remove dependency * tweak
This commit is contained in:
parent
20a277c0ab
commit
11d0dcf7ac
4 changed files with 79 additions and 17 deletions
|
@ -1,12 +1,18 @@
|
||||||
"""Provides diagnostics for Z-Wave JS."""
|
"""Provides diagnostics for Z-Wave JS."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from zwave_js_server.client import Client
|
||||||
from zwave_js_server.dump import dump_msgs
|
from zwave_js_server.dump import dump_msgs
|
||||||
|
from zwave_js_server.model.node import NodeDataType
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_URL
|
from homeassistant.const import CONF_URL
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
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(
|
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)
|
config_entry.data[CONF_URL], async_get_clientsession(hass)
|
||||||
)
|
)
|
||||||
return msgs
|
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
|
||||||
|
|
|
@ -80,13 +80,26 @@ def get_device_id_ext(client: ZwaveClient, node: ZwaveNode) -> tuple[str, str] |
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@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.
|
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
|
@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
|
# Get node ID from device identifier, perform some validation, and then get the
|
||||||
# node
|
# node
|
||||||
identifier = next(
|
identifiers = get_home_and_node_id_from_device_entry(device_entry)
|
||||||
(
|
|
||||||
get_home_and_node_id_from_device_id(identifier)
|
|
||||||
for identifier in device_entry.identifiers
|
|
||||||
if identifier[0] == DOMAIN
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
|
|
||||||
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:
|
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")
|
raise ValueError(f"Node for device {device_id} can't be found")
|
||||||
|
|
|
@ -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):
|
async def get_diagnostics_for_device(hass, hass_client, config_entry, device):
|
||||||
"""Return the diagnostics for the specified device."""
|
"""Return the diagnostics for the specified device."""
|
||||||
|
assert await async_setup_component(hass, "diagnostics", {})
|
||||||
|
|
||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
f"/api/diagnostics/config_entry/{config_entry.entry_id}/device/{device.id}"
|
f"/api/diagnostics/config_entry/{config_entry.entry_id}/device/{device.id}"
|
||||||
|
|
|
@ -1,16 +1,52 @@
|
||||||
"""Test the Z-Wave JS diagnostics."""
|
"""Test the Z-Wave JS diagnostics."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.components.zwave_js.diagnostics import (
|
import pytest
|
||||||
async_get_config_entry_diagnostics,
|
|
||||||
|
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."""
|
"""Test the config entry level diagnostics data dump."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.zwave_js.diagnostics.dump_msgs",
|
"homeassistant.components.zwave_js.diagnostics.dump_msgs",
|
||||||
return_value=[{"hello": "world"}, {"second": "msg"}],
|
return_value=[{"hello": "world"}, {"second": "msg"}],
|
||||||
):
|
):
|
||||||
result = await async_get_config_entry_diagnostics(hass, integration)
|
assert await get_diagnostics_for_config_entry(
|
||||||
assert result == [{"hello": "world"}, {"second": "msg"}]
|
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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue