Add diagnostics support for ZHA (#69756)
parent
c93c7e8eff
commit
5f37f58673
|
@ -0,0 +1,79 @@
|
||||||
|
"""Provides diagnostics for ZHA."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import bellows
|
||||||
|
import pkg_resources
|
||||||
|
import zigpy
|
||||||
|
from zigpy.config import CONF_NWK_EXTENDED_PAN_ID
|
||||||
|
import zigpy_deconz
|
||||||
|
import zigpy_xbee
|
||||||
|
import zigpy_zigate
|
||||||
|
import zigpy_znp
|
||||||
|
|
||||||
|
from homeassistant.components.diagnostics.util import async_redact_data
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_UNIQUE_ID
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
|
||||||
|
from .core.const import ATTR_IEEE, DATA_ZHA, DATA_ZHA_CONFIG, DATA_ZHA_GATEWAY
|
||||||
|
from .core.device import ZHADevice
|
||||||
|
from .core.gateway import ZHAGateway
|
||||||
|
from .core.helpers import async_get_zha_device
|
||||||
|
|
||||||
|
KEYS_TO_REDACT = {
|
||||||
|
ATTR_IEEE,
|
||||||
|
CONF_UNIQUE_ID,
|
||||||
|
"network_key",
|
||||||
|
CONF_NWK_EXTENDED_PAN_ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def shallow_asdict(obj: Any) -> dict:
|
||||||
|
"""Return a shallow copy of a dataclass as a dict."""
|
||||||
|
if hasattr(obj, "__dataclass_fields__"):
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for field in dataclasses.fields(obj):
|
||||||
|
result[field.name] = shallow_asdict(getattr(obj, field.name))
|
||||||
|
|
||||||
|
return result
|
||||||
|
if hasattr(obj, "as_dict"):
|
||||||
|
return obj.as_dict()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_config_entry_diagnostics(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> dict:
|
||||||
|
"""Return diagnostics for a config entry."""
|
||||||
|
config: dict = hass.data[DATA_ZHA][DATA_ZHA_CONFIG]
|
||||||
|
gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
|
||||||
|
return async_redact_data(
|
||||||
|
{
|
||||||
|
"config": config,
|
||||||
|
"config_entry": config_entry.as_dict(),
|
||||||
|
"application_state": shallow_asdict(gateway.application_controller.state),
|
||||||
|
"versions": {
|
||||||
|
"bellows": bellows.__version__,
|
||||||
|
"zigpy": zigpy.__version__,
|
||||||
|
"zigpy_deconz": zigpy_deconz.__version__,
|
||||||
|
"zigpy_xbee": zigpy_xbee.__version__,
|
||||||
|
"zigpy_znp": zigpy_znp.__version__,
|
||||||
|
"zigpy_zigate": zigpy_zigate.__version__,
|
||||||
|
"zhaquirks": pkg_resources.get_distribution("zha-quirks").version,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KEYS_TO_REDACT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_device_diagnostics(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry, device: dr.DeviceEntry
|
||||||
|
) -> dict:
|
||||||
|
"""Return diagnostics for a device."""
|
||||||
|
zha_device: ZHADevice = await async_get_zha_device(hass, device.id)
|
||||||
|
return async_redact_data(zha_device.zha_device_info, KEYS_TO_REDACT)
|
|
@ -11,6 +11,7 @@ from zigpy.const import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
|
||||||
import zigpy.device
|
import zigpy.device
|
||||||
import zigpy.group
|
import zigpy.group
|
||||||
import zigpy.profiles
|
import zigpy.profiles
|
||||||
|
from zigpy.state import State
|
||||||
import zigpy.types
|
import zigpy.types
|
||||||
import zigpy.zdo.types as zdo_t
|
import zigpy.zdo.types as zdo_t
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ def zigpy_app_controller():
|
||||||
app.ieee.return_value = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32")
|
app.ieee.return_value = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32")
|
||||||
type(app).nwk = PropertyMock(return_value=zigpy.types.NWK(0x0000))
|
type(app).nwk = PropertyMock(return_value=zigpy.types.NWK(0x0000))
|
||||||
type(app).devices = PropertyMock(return_value={})
|
type(app).devices = PropertyMock(return_value={})
|
||||||
|
type(app).state = PropertyMock(return_value=State())
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
"""Tests for the diagnostics data provided by the ESPHome integration."""
|
||||||
|
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import zigpy.profiles.zha as zha
|
||||||
|
import zigpy.zcl.clusters.security as security
|
||||||
|
|
||||||
|
from homeassistant.components.diagnostics.const import REDACTED
|
||||||
|
from homeassistant.components.zha.core.device import ZHADevice
|
||||||
|
from homeassistant.components.zha.diagnostics import KEYS_TO_REDACT
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.device_registry import async_get
|
||||||
|
|
||||||
|
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
|
||||||
|
|
||||||
|
from tests.components.diagnostics import (
|
||||||
|
get_diagnostics_for_config_entry,
|
||||||
|
get_diagnostics_for_device,
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_ENTRY_DIAGNOSTICS_KEYS = [
|
||||||
|
"config",
|
||||||
|
"config_entry",
|
||||||
|
"application_state",
|
||||||
|
"versions",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def zigpy_device(zigpy_device_mock):
|
||||||
|
"""Device tracker zigpy device."""
|
||||||
|
endpoints = {
|
||||||
|
1: {
|
||||||
|
SIG_EP_INPUT: [security.IasAce.cluster_id],
|
||||||
|
SIG_EP_OUTPUT: [],
|
||||||
|
SIG_EP_TYPE: zha.DeviceType.IAS_ANCILLARY_CONTROL,
|
||||||
|
SIG_EP_PROFILE: zha.PROFILE_ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zigpy_device_mock(
|
||||||
|
endpoints, node_descriptor=b"\x02@\x8c\x02\x10RR\x00\x00\x00R\x00\x00"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_diagnostics_for_config_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client,
|
||||||
|
config_entry,
|
||||||
|
zha_device_joined,
|
||||||
|
zigpy_device,
|
||||||
|
):
|
||||||
|
"""Test diagnostics for config entry."""
|
||||||
|
await zha_device_joined(zigpy_device)
|
||||||
|
diagnostics_data = await get_diagnostics_for_config_entry(
|
||||||
|
hass, hass_client, config_entry
|
||||||
|
)
|
||||||
|
assert diagnostics_data
|
||||||
|
for key in CONFIG_ENTRY_DIAGNOSTICS_KEYS:
|
||||||
|
assert key in diagnostics_data
|
||||||
|
assert diagnostics_data[key] is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_diagnostics_for_device(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client,
|
||||||
|
config_entry,
|
||||||
|
zha_device_joined,
|
||||||
|
zigpy_device,
|
||||||
|
):
|
||||||
|
"""Test diagnostics for device."""
|
||||||
|
|
||||||
|
zha_device: ZHADevice = await zha_device_joined(zigpy_device)
|
||||||
|
dev_reg = async_get(hass)
|
||||||
|
device = dev_reg.async_get_device({("zha", str(zha_device.ieee))})
|
||||||
|
assert device
|
||||||
|
diagnostics_data = await get_diagnostics_for_device(
|
||||||
|
hass, hass_client, config_entry, device
|
||||||
|
)
|
||||||
|
assert diagnostics_data
|
||||||
|
device_info: dict = zha_device.zha_device_info
|
||||||
|
for key, value in device_info.items():
|
||||||
|
assert key in diagnostics_data
|
||||||
|
if key not in KEYS_TO_REDACT:
|
||||||
|
assert key in diagnostics_data
|
||||||
|
else:
|
||||||
|
assert diagnostics_data[key] == REDACTED
|
Loading…
Reference in New Issue