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.group
|
||||
import zigpy.profiles
|
||||
from zigpy.state import State
|
||||
import zigpy.types
|
||||
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")
|
||||
type(app).nwk = PropertyMock(return_value=zigpy.types.NWK(0x0000))
|
||||
type(app).devices = PropertyMock(return_value={})
|
||||
type(app).state = PropertyMock(return_value=State())
|
||||
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