Add device-specific diagnostics to the LIFX integration (#79964)

This commit is contained in:
Avi Miller 2022-10-24 14:49:18 +11:00 committed by GitHub
parent 3c170f7627
commit 6e453ae471
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 455 additions and 0 deletions

View File

@ -12,6 +12,7 @@ MESSAGE_RETRIES = 5
OVERALL_TIMEOUT = 9
UNAVAILABLE_GRACE = 90
CONF_LABEL = "label"
CONF_SERIAL = "serial"
IDENTIFY_WAVEFORM = {

View File

@ -117,6 +117,44 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
"""Return the current infrared brightness as a string."""
return infrared_brightness_value_to_option(self.device.infrared_brightness)
async def diagnostics(self) -> dict[str, Any]:
"""Return diagnostic information about the device."""
features = lifx_features(self.device)
device_data = {
"firmware": self.device.host_firmware_version,
"vendor": self.device.vendor,
"product_id": self.device.product,
"features": features,
"hue": self.device.color[0],
"saturation": self.device.color[1],
"brightness": self.device.color[2],
"kelvin": self.device.color[3],
"power": self.device.power_level,
}
if features["multizone"] is True:
zones = {"count": self.device.zones_count, "state": {}}
for index, zone_color in enumerate(self.device.color_zones):
zones["state"][index] = {
"hue": zone_color[0],
"saturation": zone_color[1],
"brightness": zone_color[2],
"kelvin": zone_color[3],
}
device_data["zones"] = zones
if features["hev"] is True:
device_data["hev"] = {
"hev_cycle": self.device.hev_cycle,
"hev_config": self.device.hev_cycle_configuration,
"last_result": self.device.last_hev_cycle_result,
}
if features["infrared"] is True:
device_data["infrared"] = {"brightness": self.device.infrared_brightness}
return device_data
def async_get_entity_id(self, platform: Platform, key: str) -> str | None:
"""Return the entity_id from the platform and key provided."""
ent_reg = er.async_get(self.hass)

View File

@ -0,0 +1,28 @@
"""Diagnostics support for LIFX."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_IP_ADDRESS, CONF_MAC
from homeassistant.core import HomeAssistant
from .const import CONF_LABEL, DOMAIN
from .coordinator import LIFXUpdateCoordinator
TO_REDACT = [CONF_LABEL, CONF_HOST, CONF_IP_ADDRESS, CONF_MAC]
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a LIFX config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
return {
"entry": {
"title": entry.title,
"data": async_redact_data(dict(entry.data), TO_REDACT),
},
"data": async_redact_data(await coordinator.diagnostics(), TO_REDACT),
}

View File

@ -123,12 +123,15 @@ def _mocked_clean_bulb() -> Light:
bulb = _mocked_bulb()
bulb.get_hev_cycle = MockLifxCommand(bulb)
bulb.set_hev_cycle = MockLifxCommand(bulb)
bulb.get_hev_configuration = MockLifxCommand(bulb)
bulb.get_last_hev_cycle_result = MockLifxCommand(bulb)
bulb.hev_cycle_configuration = {"duration": 7200, "indication": False}
bulb.hev_cycle = {
"duration": 7200,
"remaining": 30,
"last_power": False,
}
bulb.last_hev_cycle_result = 0
bulb.product = 90
return bulb

View File

@ -0,0 +1,385 @@
"""Test LIFX diagnostics."""
from homeassistant.components import lifx
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
_mocked_bulb,
_mocked_clean_bulb,
_mocked_infrared_bulb,
_mocked_light_strip,
_patch_config_flow_try_connect,
_patch_device,
_patch_discovery,
)
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
async def test_bulb_diagnostics(hass: HomeAssistant, hass_client) -> None:
"""Test diagnostics for a standard bulb."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb()
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert diag == {
"data": {
"brightness": 3,
"features": {
"buttons": False,
"chain": False,
"color": True,
"extended_multizone": False,
"hev": False,
"infrared": False,
"matrix": False,
"max_kelvin": 9000,
"min_kelvin": 2500,
"multizone": False,
"relays": False,
},
"firmware": "3.00",
"hue": 1,
"kelvin": 4,
"power": 0,
"product_id": 1,
"saturation": 2,
"vendor": None,
},
"entry": {"data": {"host": "**REDACTED**"}, "title": "My Bulb"},
}
async def test_clean_bulb_diagnostics(hass: HomeAssistant, hass_client) -> None:
"""Test diagnostics for a standard bulb."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_clean_bulb()
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert diag == {
"data": {
"brightness": 3,
"features": {
"buttons": False,
"chain": False,
"color": True,
"extended_multizone": False,
"hev": True,
"infrared": False,
"matrix": False,
"max_kelvin": 9000,
"min_kelvin": 1500,
"multizone": False,
"relays": False,
},
"firmware": "3.00",
"hev": {
"hev_config": {"duration": 7200, "indication": False},
"hev_cycle": {"duration": 7200, "last_power": False, "remaining": 30},
"last_result": 0,
},
"hue": 1,
"kelvin": 4,
"power": 0,
"product_id": 90,
"saturation": 2,
"vendor": None,
},
"entry": {"data": {"host": "**REDACTED**"}, "title": "My Bulb"},
}
async def test_infrared_bulb_diagnostics(hass: HomeAssistant, hass_client) -> None:
"""Test diagnostics for a standard bulb."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert diag == {
"data": {
"brightness": 3,
"features": {
"buttons": False,
"chain": False,
"color": True,
"extended_multizone": False,
"hev": False,
"infrared": True,
"matrix": False,
"max_kelvin": 9000,
"min_kelvin": 1500,
"multizone": False,
"relays": False,
},
"firmware": "3.00",
"hue": 1,
"infrared": {"brightness": 65535},
"kelvin": 4,
"power": 0,
"product_id": 29,
"saturation": 2,
"vendor": None,
},
"entry": {"data": {"host": "**REDACTED**"}, "title": "My Bulb"},
}
async def test_legacy_multizone_bulb_diagnostics(
hass: HomeAssistant, hass_client
) -> None:
"""Test diagnostics for a standard bulb."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_light_strip()
bulb.zones_count = 8
bulb.color_zones = [
(54612, 65535, 65535, 3500),
(54612, 65535, 65535, 3500),
(54612, 65535, 65535, 3500),
(54612, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
]
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert diag == {
"data": {
"brightness": 3,
"features": {
"buttons": False,
"chain": False,
"color": True,
"extended_multizone": False,
"hev": False,
"infrared": False,
"matrix": False,
"max_kelvin": 9000,
"min_kelvin": 2500,
"multizone": True,
"relays": False,
},
"firmware": "3.00",
"hue": 1,
"kelvin": 4,
"power": 0,
"product_id": 31,
"saturation": 2,
"vendor": None,
"zones": {
"count": 8,
"state": {
"0": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"1": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"2": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"3": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"4": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
"5": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
"6": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
"7": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
},
},
},
"entry": {"data": {"host": "**REDACTED**"}, "title": "My Bulb"},
}
async def test_multizone_bulb_diagnostics(hass: HomeAssistant, hass_client) -> None:
"""Test diagnostics for a standard bulb."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_light_strip()
bulb.product = 38
bulb.zones_count = 8
bulb.color_zones = [
(54612, 65535, 65535, 3500),
(54612, 65535, 65535, 3500),
(54612, 65535, 65535, 3500),
(54612, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
(46420, 65535, 65535, 3500),
]
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert diag == {
"data": {
"brightness": 3,
"features": {
"buttons": False,
"chain": False,
"color": True,
"extended_multizone": True,
"hev": False,
"infrared": False,
"matrix": False,
"max_kelvin": 9000,
"min_ext_mz_firmware": 1532997580,
"min_ext_mz_firmware_components": [2, 77],
"min_kelvin": 1500,
"multizone": True,
"relays": False,
},
"firmware": "3.00",
"hue": 1,
"kelvin": 4,
"power": 0,
"product_id": 38,
"saturation": 2,
"vendor": None,
"zones": {
"count": 8,
"state": {
"0": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"1": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"2": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"3": {
"brightness": 65535,
"hue": 54612,
"kelvin": 3500,
"saturation": 65535,
},
"4": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
"5": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
"6": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
"7": {
"brightness": 65535,
"hue": 46420,
"kelvin": 3500,
"saturation": 65535,
},
},
},
},
"entry": {"data": {"host": "**REDACTED**"}, "title": "My Bulb"},
}