Add additional data to HomeKit diagnostics (#80980)

This commit is contained in:
J. Nick Koston 2022-10-26 03:05:33 -05:00 committed by GitHub
parent 7dd1f58d04
commit a90ef3a575
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 182 additions and 2 deletions

View File

@ -6,12 +6,16 @@ from typing import Any
from pyhap.accessory_driver import AccessoryDriver from pyhap.accessory_driver import AccessoryDriver
from pyhap.state import State from pyhap.state import State
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import HomeKit from . import HomeKit
from .accessories import HomeAccessory, HomeBridge
from .const import DOMAIN, HOMEKIT from .const import DOMAIN, HOMEKIT
TO_REDACT = {"access_token", "entity_picture"}
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: ConfigEntry
@ -30,6 +34,11 @@ async def async_get_config_entry_diagnostics(
if not homekit.driver: # not started yet or startup failed if not homekit.driver: # not started yet or startup failed
return data return data
driver: AccessoryDriver = homekit.driver driver: AccessoryDriver = homekit.driver
if driver.accessory:
if isinstance(driver.accessory, HomeBridge):
data["bridge"] = _get_bridge_diagnostics(hass, driver.accessory)
else:
data["accessory"] = _get_accessory_diagnostics(hass, driver.accessory)
data.update(driver.get_accessories()) data.update(driver.get_accessories())
state: State = driver.state state: State = driver.state
data.update( data.update(
@ -42,3 +51,27 @@ async def async_get_config_entry_diagnostics(
} }
) )
return data return data
def _get_bridge_diagnostics(hass: HomeAssistant, bridge: HomeBridge) -> dict[int, Any]:
"""Return diagnostics for a bridge."""
return {
aid: _get_accessory_diagnostics(hass, accessory)
for aid, accessory in bridge.accessories.items()
}
def _get_accessory_diagnostics(
hass: HomeAssistant, accessory: HomeAccessory
) -> dict[str, Any]:
"""Return diagnostics for an accessory."""
return {
"aid": accessory.aid,
"config": accessory.config,
"category": accessory.category,
"name": accessory.display_name,
"entity_id": accessory.entity_id,
"entity_state": async_redact_data(
hass.states.get(accessory.entity_id), TO_REDACT
),
}

View File

@ -1,7 +1,11 @@
"""Test homekit diagnostics.""" """Test homekit diagnostics."""
from unittest.mock import ANY, patch from unittest.mock import ANY, patch
from homeassistant.components.homekit.const import DOMAIN from homeassistant.components.homekit.const import (
CONF_HOMEKIT_MODE,
DOMAIN,
HOMEKIT_MODE_ACCESSORY,
)
from homeassistant.const import CONF_NAME, CONF_PORT, EVENT_HOMEASSISTANT_STARTED from homeassistant.const import CONF_NAME, CONF_PORT, EVENT_HOMEASSISTANT_STARTED
from .util import async_init_integration from .util import async_init_integration
@ -28,7 +32,7 @@ async def test_config_entry_not_running(
async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zeroconf): async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zeroconf):
"""Test generating diagnostics for a config entry.""" """Test generating diagnostics for a bridge config entry."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, data={CONF_NAME: "mock_name", CONF_PORT: 12345} domain=DOMAIN, data={CONF_NAME: "mock_name", CONF_PORT: 12345}
) )
@ -38,6 +42,7 @@ async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zer
await hass.async_block_till_done() await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, entry) diag = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert diag == { assert diag == {
"bridge": {},
"accessories": [ "accessories": [
{ {
"aid": 1, "aid": 1,
@ -117,3 +122,145 @@ async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zer
), patch("homeassistant.components.homekit.async_port_is_available"): ), patch("homeassistant.components.homekit.async_port_is_available"):
assert await hass.config_entries.async_unload(entry.entry_id) assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
async def test_config_entry_accessory(
hass, hass_client, hk_driver, mock_async_zeroconf
):
"""Test generating diagnostics for an accessory config entry."""
hass.states.async_set("light.demo", "on")
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_NAME: "mock_name",
CONF_PORT: 12345,
CONF_HOMEKIT_MODE: HOMEKIT_MODE_ACCESSORY,
"filter": {
"exclude_domains": [],
"exclude_entities": [],
"include_domains": [],
"include_entities": ["light.demo"],
},
},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert diag == {
"accessories": [
{
"aid": 1,
"services": [
{
"characteristics": [
{"format": "bool", "iid": 2, "perms": ["pw"], "type": "14"},
{
"format": "string",
"iid": 3,
"perms": ["pr"],
"type": "20",
"value": "Home Assistant " "Light",
},
{
"format": "string",
"iid": 4,
"perms": ["pr"],
"type": "21",
"value": "Light",
},
{
"format": "string",
"iid": 5,
"perms": ["pr"],
"type": "23",
"value": "demo",
},
{
"format": "string",
"iid": 6,
"perms": ["pr"],
"type": "30",
"value": "light.demo",
},
{
"format": "string",
"iid": 7,
"perms": ["pr"],
"type": "52",
"value": "2022.11.0",
},
],
"iid": 1,
"type": "3E",
},
{
"characteristics": [
{
"format": "string",
"iid": 9,
"perms": ["pr", "ev"],
"type": "37",
"value": "01.01.00",
}
],
"iid": 8,
"type": "A2",
},
{
"characteristics": [
{
"format": "bool",
"iid": 11,
"perms": ["pr", "pw", "ev"],
"type": "25",
"value": True,
}
],
"iid": 10,
"type": "43",
},
],
}
],
"accessory": {
"aid": 1,
"category": 5,
"config": {},
"entity_id": "light.demo",
"entity_state": {
"attributes": {},
"context": {"id": ANY, "parent_id": None, "user_id": None},
"entity_id": "light.demo",
"last_changed": ANY,
"last_updated": ANY,
"state": "on",
},
"name": "demo",
},
"client_properties": {},
"config-entry": {
"data": {"name": "mock_name", "port": 12345},
"options": {
"filter": {
"exclude_domains": [],
"exclude_entities": [],
"include_domains": [],
"include_entities": ["light.demo"],
},
"mode": "accessory",
},
"title": "Mock Title",
"version": 1,
},
"config_version": 2,
"pairing_id": ANY,
"status": 1,
}
with patch("pyhap.accessory_driver.AccessoryDriver.async_start"), patch(
"homeassistant.components.homekit.HomeKit.async_stop"
), patch("homeassistant.components.homekit.async_port_is_available"):
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()