From 3732ae738e78b258d9fba755d44b4cf2cbae51ff Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 25 Oct 2021 21:53:13 -0500 Subject: [PATCH] Fix flux_led with RGB/W bulbs (model 0x44) (#58438) --- homeassistant/components/flux_led/light.py | 23 +++- .../components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/flux_led/test_light.py | 112 ++++++++++++++++++ 5 files changed, 134 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index 655b868a4f5..6f1db96a7aa 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -30,12 +30,14 @@ from homeassistant.components.light import ( ATTR_RGB_COLOR, ATTR_RGBW_COLOR, ATTR_RGBWW_COLOR, + ATTR_WHITE, COLOR_MODE_BRIGHTNESS, COLOR_MODE_COLOR_TEMP, COLOR_MODE_ONOFF, COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW, + COLOR_MODE_WHITE, EFFECT_COLORLOOP, EFFECT_RANDOM, PLATFORM_SCHEMA, @@ -100,7 +102,6 @@ FLUX_COLOR_MODE_TO_HASS: Final = { FLUX_COLOR_MODE_RGBW: COLOR_MODE_RGBW, FLUX_COLOR_MODE_RGBWW: COLOR_MODE_RGBWW, FLUX_COLOR_MODE_CCT: COLOR_MODE_COLOR_TEMP, - FLUX_COLOR_MODE_DIM: COLOR_MODE_BRIGHTNESS, } EFFECT_SUPPORT_MODES = {COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW} @@ -195,6 +196,15 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) +def _flux_color_mode_to_hass(flux_color_mode: str, flux_color_modes: set[str]) -> str: + """Map the flux color mode to Home Assistant color mode.""" + if flux_color_mode == FLUX_COLOR_MODE_DIM: + if len(flux_color_modes) > 1: + return COLOR_MODE_WHITE + return COLOR_MODE_BRIGHTNESS + return FLUX_COLOR_MODE_TO_HASS.get(flux_color_mode, COLOR_MODE_ONOFF) + + async def async_setup_platform( hass: HomeAssistant, config: ConfigType, @@ -300,7 +310,7 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): ) # for rounding self._attr_max_mireds = color_temperature_kelvin_to_mired(self._device.min_temp) self._attr_supported_color_modes = { - FLUX_COLOR_MODE_TO_HASS.get(mode, COLOR_MODE_ONOFF) + _flux_color_mode_to_hass(mode, self._device.color_modes) for mode in self._device.color_modes } if self._attr_supported_color_modes.intersection(EFFECT_SUPPORT_MODES): @@ -352,7 +362,9 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): @property def color_mode(self) -> str: """Return the color mode of the light.""" - return FLUX_COLOR_MODE_TO_HASS.get(self._device.color_mode, COLOR_MODE_ONOFF) + return _flux_color_mode_to_hass( + self._device.color_mode, self._device.color_modes + ) @property def effect(self) -> str | None: @@ -410,6 +422,9 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): rgbcw = kwargs[ATTR_RGBWW_COLOR] await self._device.async_set_levels(*rgbcw_to_rgbwc(rgbcw)) return + if ATTR_WHITE in kwargs: + await self._device.async_set_levels(w=kwargs[ATTR_WHITE]) + return if ATTR_EFFECT in kwargs: effect = kwargs[ATTR_EFFECT] # Random color effect @@ -456,7 +471,7 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): await self._device.async_set_levels(*rgbww_brightness(rgbwc, brightness)) return # Handle Brightness Only Color Mode - if self.color_mode == COLOR_MODE_BRIGHTNESS: + if self.color_mode in {COLOR_MODE_WHITE, COLOR_MODE_BRIGHTNESS}: await self._device.async_set_levels(w=brightness) return raise ValueError(f"Unsupported color mode {self.color_mode}") diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index dfe166e65cf..4b5a63542c9 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.24.12"], + "requirements": ["flux_led==0.24.13"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index f04ff45af8e..6ee4e154dde 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -655,7 +655,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.24.12 +flux_led==0.24.13 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index eefe731f51a..d153bef0caa 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -390,7 +390,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.24.12 +flux_led==0.24.13 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index a3f381b0c1d..6f0ad5aa253 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -9,6 +9,7 @@ from flux_led.const import ( COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB, COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW, COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW, + COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W, ) import pytest @@ -38,6 +39,7 @@ from homeassistant.components.light import ( ATTR_RGBW_COLOR, ATTR_RGBWW_COLOR, ATTR_SUPPORTED_COLOR_MODES, + ATTR_WHITE, DOMAIN as LIGHT_DOMAIN, ) from homeassistant.const import ( @@ -485,6 +487,116 @@ async def test_rgbw_light(hass: HomeAssistant) -> None: bulb.async_set_preset_pattern.reset_mock() +async def test_rgb_or_w_light(hass: HomeAssistant) -> None: + """Test an rgb or w light.""" + 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.color_modes = FLUX_COLOR_MODES_RGB_W + bulb.color_mode = FLUX_COLOR_MODE_RGB + 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() + + entity_id = "light.az120444_aabbccddeeff" + + state = hass.states.get(entity_id) + assert state.state == STATE_ON + attributes = state.attributes + assert attributes[ATTR_BRIGHTNESS] == 128 + assert attributes[ATTR_COLOR_MODE] == "rgb" + assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb", "white"] + assert attributes[ATTR_RGB_COLOR] == (255, 0, 0) + + await hass.services.async_call( + LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + bulb.async_turn_off.assert_called_once() + await async_mock_device_turn_off(hass, bulb) + + assert hass.states.get(entity_id).state == STATE_OFF + + await hass.services.async_call( + LIGHT_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + bulb.async_turn_on.assert_called_once() + bulb.async_turn_on.reset_mock() + bulb.is_on = True + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100}, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(255, 0, 0, brightness=100) + bulb.async_set_levels.reset_mock() + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + { + ATTR_ENTITY_ID: entity_id, + ATTR_RGB_COLOR: (255, 255, 255), + ATTR_BRIGHTNESS: 128, + }, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(255, 255, 255, brightness=128) + bulb.async_set_levels.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"}, + blocking=True, + ) + bulb.async_set_levels.assert_called_once() + bulb.async_set_levels.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "purple_fade"}, + blocking=True, + ) + bulb.async_set_preset_pattern.assert_called_with(43, 50) + bulb.async_set_preset_pattern.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + { + ATTR_ENTITY_ID: entity_id, + ATTR_WHITE: 128, + }, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(w=128) + bulb.async_set_levels.reset_mock() + + bulb.color_mode = FLUX_COLOR_MODE_DIM + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + { + ATTR_ENTITY_ID: entity_id, + ATTR_BRIGHTNESS: 100, + }, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(w=100) + bulb.async_set_levels.reset_mock() + + async def test_rgbcw_light(hass: HomeAssistant) -> None: """Test an rgbcw light.""" config_entry = MockConfigEntry(