From 9b74cb950796ad7ca7d19cd04fb8953a92cf812a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 4 May 2023 07:55:47 -0500 Subject: [PATCH] Fix lifx light strips when color zones are not initially populated (#92487) fixes #92456 --- homeassistant/components/lifx/coordinator.py | 13 ++++-- homeassistant/components/lifx/light.py | 2 +- tests/components/lifx/test_light.py | 44 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/lifx/coordinator.py b/homeassistant/components/lifx/coordinator.py index 66cea18f119..f8964e78f63 100644 --- a/homeassistant/components/lifx/coordinator.py +++ b/homeassistant/components/lifx/coordinator.py @@ -205,13 +205,20 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator[None]): methods, DEFAULT_ATTEMPTS, OVERALL_TIMEOUT ) + def get_number_of_zones(self) -> int: + """Return the number of zones. + + If the number of zones is not yet populated, return 0 + """ + return len(self.device.color_zones) if self.device.color_zones else 0 + @callback def _async_build_color_zones_update_requests(self) -> list[Callable]: """Build a color zones update request.""" device = self.device return [ partial(device.get_color_zones, start_index=zone) - for zone in range(0, len(device.color_zones), 8) + for zone in range(0, self.get_number_of_zones(), 8) ] async def _async_update_data(self) -> None: @@ -224,7 +231,7 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator[None]): ): await self._async_populate_device_info() - num_zones = len(device.color_zones) if device.color_zones is not None else 0 + num_zones = self.get_number_of_zones() features = lifx_features(self.device) is_extended_multizone = features["extended_multizone"] is_legacy_multizone = not is_extended_multizone and features["multizone"] @@ -256,7 +263,7 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator[None]): if is_extended_multizone or is_legacy_multizone: self.active_effect = FirmwareEffect[self.device.effect.get("effect", "OFF")] - if is_legacy_multizone and num_zones != len(device.color_zones): + if is_legacy_multizone and num_zones != self.get_number_of_zones(): # The number of zones has changed so we need # to update the zones again. This happens rarely. await self.async_get_color_zones() diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 4f4223817bc..cb901dcbe47 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -391,7 +391,7 @@ class LIFXMultiZone(LIFXColor): """Send a color change to the bulb.""" bulb = self.bulb color_zones = bulb.color_zones - num_zones = len(color_zones) + num_zones = self.coordinator.get_number_of_zones() # Zone brightness is not reported when powered off if not self.is_on and hsbk[HSBK_BRIGHTNESS] is None: diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index 42c540a74ef..fe68bd6547a 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -36,6 +36,7 @@ from homeassistant.components.light import ( ATTR_TRANSITION, ATTR_XY_COLOR, DOMAIN as LIGHT_DOMAIN, + SERVICE_TURN_ON, ColorMode, ) from homeassistant.const import ( @@ -1741,3 +1742,46 @@ async def test_set_hev_cycle_state_fails_for_color_bulb(hass: HomeAssistant) -> {ATTR_ENTITY_ID: entity_id, ATTR_POWER: True}, blocking=True, ) + + +async def test_light_strip_zones_not_populated_yet(hass: HomeAssistant) -> None: + """Test a light strip were zones are not populated initially.""" + already_migrated_config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL + ) + already_migrated_config_entry.add_to_hass(hass) + bulb = _mocked_light_strip() + bulb.power_level = 65535 + bulb.color_zones = None + bulb.color = [65535, 65535, 65535, 65535] + 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() + + entity_id = "light.my_bulb" + + state = hass.states.get(entity_id) + assert state.state == "on" + attributes = state.attributes + assert attributes[ATTR_BRIGHTNESS] == 255 + assert attributes[ATTR_COLOR_MODE] == ColorMode.HS + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ + ColorMode.COLOR_TEMP, + ColorMode.HS, + ] + assert attributes[ATTR_HS_COLOR] == (360.0, 100.0) + assert attributes[ATTR_RGB_COLOR] == (255, 0, 0) + assert attributes[ATTR_XY_COLOR] == (0.701, 0.299) + + await hass.services.async_call( + LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + assert bulb.set_power.calls[0][0][0] is True + bulb.set_power.reset_mock() + + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + state = hass.states.get(entity_id) + assert state.state == STATE_ON