From 7e5dfadc279e675da458519892944cae28a156ae Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 5 Oct 2021 14:53:39 -1000 Subject: [PATCH] Add sw_version and model to flux_led device info (#56958) --- homeassistant/components/flux_led/__init__.py | 3 ++ homeassistant/components/flux_led/light.py | 41 +++++++------------ tests/components/flux_led/__init__.py | 1 + tests/components/flux_led/test_init.py | 23 ++++++++++- tests/components/flux_led/test_light.py | 36 ++++++++++++++-- 5 files changed, 72 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index df7334a8ebc..fa3ad23a41c 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -140,3 +140,6 @@ class FluxLedUpdateCoordinator(DataUpdateCoordinator): await self.hass.async_add_executor_job(self.device.update_state) except FLUX_LED_EXCEPTIONS as ex: raise UpdateFailed(ex) from ex + + if not self.device.raw_state: + raise UpdateFailed("The device failed to update") diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index a480a956a58..8c14a1c22b6 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -28,11 +28,11 @@ from homeassistant.components.light import ( LightEntity, ) from homeassistant.const import ( - ATTR_IDENTIFIERS, ATTR_MANUFACTURER, ATTR_MODE, ATTR_MODEL, ATTR_NAME, + ATTR_SW_VERSION, CONF_DEVICES, CONF_HOST, CONF_MAC, @@ -41,9 +41,8 @@ from homeassistant.const import ( CONF_PROTOCOL, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_platform +from homeassistant.helpers import device_registry as dr, entity_platform import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -271,23 +270,22 @@ class FluxLight(CoordinatorEntity, LightEntity): """Initialize the light.""" super().__init__(coordinator) self._bulb: WifiLedBulb = coordinator.device - self._name = name - self._unique_id = unique_id + self._attr_name = name + self._attr_unique_id = unique_id self._ip_address = coordinator.host self._mode = mode self._custom_effect_colors = custom_effect_colors self._custom_effect_speed_pct = custom_effect_speed_pct self._custom_effect_transition = custom_effect_transition - - @property - def unique_id(self) -> str | None: - """Return the unique ID of the light.""" - return self._unique_id - - @property - def name(self) -> str: - """Return the name of the device.""" - return self._name + old_protocol = self._bulb.protocol == "LEDENET_ORIGINAL" + if self.unique_id: + self._attr_device_info = { + "connections": {(dr.CONNECTION_NETWORK_MAC, self.unique_id)}, + ATTR_MODEL: f"0x{self._bulb.raw_state[1]:02X}", + ATTR_SW_VERSION: "1" if old_protocol else str(self._bulb.raw_state[10]), + ATTR_NAME: self.name, + ATTR_MANUFACTURER: "FluxLED/Magic Home", + } @property def is_on(self) -> bool: @@ -341,17 +339,6 @@ class FluxLight(CoordinatorEntity, LightEntity): "ip_address": self._ip_address, } - @property - def device_info(self) -> DeviceInfo: - """Return the device information.""" - assert self._unique_id is not None - return { - ATTR_IDENTIFIERS: {(DOMAIN, self._unique_id)}, - ATTR_NAME: self._name, - ATTR_MANUFACTURER: "FluxLED/Magic Home", - ATTR_MODEL: "LED Lights", - } - async def async_turn_on(self, **kwargs: Any) -> None: """Turn the specified or all lights on.""" await self.hass.async_add_executor_job(partial(self._turn_on, **kwargs)) @@ -464,7 +451,7 @@ class FluxLight(CoordinatorEntity, LightEntity): self._mode = MODE_RGB _LOGGER.debug( "Detected mode for %s (%s) with raw_state=%s rgbwcapable=%s is %s", - self._name, + self.name, self.unique_id, self._bulb.raw_state, self._bulb.rgbwcapable, diff --git a/tests/components/flux_led/__init__.py b/tests/components/flux_led/__init__.py index 49b9db491f3..5ca6244655a 100644 --- a/tests/components/flux_led/__init__.py +++ b/tests/components/flux_led/__init__.py @@ -36,6 +36,7 @@ def _mocked_bulb() -> WifiLedBulb: bulb.getRgbw = MagicMock(return_value=[255, 0, 0, 50]) bulb.brightness = 128 bulb.rgbwcapable = True + bulb.raw_state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] return bulb diff --git a/tests/components/flux_led/test_init.py b/tests/components/flux_led/test_init.py index 7b01088d1f6..734fb64520f 100644 --- a/tests/components/flux_led/test_init.py +++ b/tests/components/flux_led/test_init.py @@ -11,7 +11,14 @@ from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow -from . import FLUX_DISCOVERY, IP_ADDRESS, MAC_ADDRESS, _patch_discovery, _patch_wifibulb +from . import ( + FLUX_DISCOVERY, + IP_ADDRESS, + MAC_ADDRESS, + _mocked_bulb, + _patch_discovery, + _patch_wifibulb, +) from tests.common import MockConfigEntry, async_fire_time_changed @@ -56,3 +63,17 @@ async def test_config_entry_retry(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() assert config_entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_config_entry_retry_when_state_missing(hass: HomeAssistant) -> None: + """Test that a config entry is retried when state is missing.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: IP_ADDRESS}, unique_id=MAC_ADDRESS + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb() + bulb.raw_state = None + with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.SETUP_RETRY diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index c2e98158b0b..4ddf9a7d04d 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -39,7 +39,7 @@ from homeassistant.const import ( STATE_ON, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow @@ -93,6 +93,35 @@ async def test_light_no_unique_id(hass: HomeAssistant) -> None: assert state.state == STATE_ON +@pytest.mark.parametrize( + "protocol,sw_version,model", [("LEDENET_ORIGINAL", 1, 0x35), ("LEDENET", 8, 0x33)] +) +async def test_light_device_registry( + hass: HomeAssistant, protocol: str, sw_version: int, model: int +) -> None: + """Test a light device registry entry.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE}, + unique_id=MAC_ADDRESS, + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb() + bulb.protocol = protocol + bulb.raw_state[1] = model + bulb.raw_state[10] = sw_version + with _patch_discovery(no_device=True), _patch_wifibulb(device=bulb): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + + device_registry = dr.async_get(hass) + device = device_registry.async_get_device( + identifiers={}, connections={(dr.CONNECTION_NETWORK_MAC, MAC_ADDRESS)} + ) + assert device.sw_version == str(sw_version) + assert device.model == f"0x{model:02X}" + + async def test_rgb_light(hass: HomeAssistant) -> None: """Test an rgb light.""" config_entry = MockConfigEntry( @@ -276,7 +305,6 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None: ) config_entry.add_to_hass(hass) bulb = _mocked_bulb() - bulb.raw_state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] bulb.raw_state[9] = 1 bulb.raw_state[11] = 2 @@ -466,7 +494,7 @@ async def test_rgb_light_custom_effects( ) bulb.setCustomPattern.assert_called_with([[0, 0, 255], [255, 0, 0]], 88, "jump") bulb.setCustomPattern.reset_mock() - bulb.raw_state = [0, 0, 0, EFFECT_CUSTOM_CODE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + bulb.raw_state[3] = EFFECT_CUSTOM_CODE bulb.is_on = True async_fire_time_changed(hass, utcnow() + timedelta(seconds=20)) await hass.async_block_till_done() @@ -484,7 +512,7 @@ async def test_rgb_light_custom_effects( ) bulb.setCustomPattern.assert_called_with([[0, 0, 255], [255, 0, 0]], 88, "jump") bulb.setCustomPattern.reset_mock() - bulb.raw_state = [0, 0, 0, EFFECT_CUSTOM_CODE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + bulb.raw_state[3] = EFFECT_CUSTOM_CODE bulb.is_on = True async_fire_time_changed(hass, utcnow() + timedelta(seconds=20)) await hass.async_block_till_done()