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:
Raman Gupta 2022-01-20 05:49:24 -05:00 committed by GitHub
parent 20a277c0ab
commit 11d0dcf7ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 17 deletions

View File

@ -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

View File

@ -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")

View File

@ -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}"

View File

@ -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)