From 94ae6ac2b27ebe4c809e0889d14dad1847d64bbd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 16 Dec 2021 05:39:33 -0600 Subject: [PATCH] Handle color temp to RGBWW conversion (#61473) Co-authored-by: Erik Montnemery --- homeassistant/components/light/__init__.py | 24 +++++--- homeassistant/util/color.py | 10 ++++ tests/components/light/test_init.py | 68 ++++++++++++++++++++++ tests/util/test_color.py | 46 +++++++++++++++ 4 files changed, 140 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 390c675886d..3156306554c 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -382,14 +382,22 @@ async def async_setup(hass, config): # noqa: C901 params[ATTR_WHITE_VALUE] = rgbw_color[3] # If a color temperature is specified, emulate it if not supported by the light - if ( - ATTR_COLOR_TEMP in params - and COLOR_MODE_COLOR_TEMP not in legacy_supported_color_modes - ): - color_temp = params.pop(ATTR_COLOR_TEMP) - if color_supported(legacy_supported_color_modes): - temp_k = color_util.color_temperature_mired_to_kelvin(color_temp) - params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs(temp_k) + if ATTR_COLOR_TEMP in params: + if ( + supported_color_modes + and COLOR_MODE_COLOR_TEMP not in supported_color_modes + and COLOR_MODE_RGBWW in supported_color_modes + ): + color_temp = params.pop(ATTR_COLOR_TEMP) + brightness = params.get(ATTR_BRIGHTNESS, light.brightness) + params[ATTR_RGBWW_COLOR] = color_util.color_temperature_to_rgbww( + color_temp, brightness, light.min_mireds, light.max_mireds + ) + elif COLOR_MODE_COLOR_TEMP not in legacy_supported_color_modes: + color_temp = params.pop(ATTR_COLOR_TEMP) + if color_supported(legacy_supported_color_modes): + temp_k = color_util.color_temperature_mired_to_kelvin(color_temp) + params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs(temp_k) # If a color is specified, convert to the color space supported by the light # Backwards compatibility: Fall back to hs color if light.supported_color_modes diff --git a/homeassistant/util/color.py b/homeassistant/util/color.py index 3d4f7122ad0..e8f802e3aa5 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -528,6 +528,16 @@ def color_temperature_to_rgb( return red, green, blue +def color_temperature_to_rgbww( + temperature: int, brightness: int, min_mireds: int, max_mireds: int +) -> tuple[int, int, int, int, int]: + """Convert color temperature to rgbcw.""" + mired_range = max_mireds - min_mireds + warm = ((max_mireds - temperature) / mired_range) * brightness + cold = brightness - warm + return (0, 0, 0, round(cold), round(warm)) + + def _clamp(color_component: float, minimum: float = 0, maximum: float = 255) -> float: """ Clamp the given color component value between the given min and max values. diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 1f7fe431f0e..a8a6ebc901e 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -1852,6 +1852,74 @@ async def test_light_service_call_color_temp_emulation( assert data == {"brightness": 255, "hs_color": (27.001, 19.243)} +async def test_light_service_call_color_temp_conversion( + hass, enable_custom_integrations +): + """Test color temp conversion in service calls.""" + platform = getattr(hass.components, "test.light") + platform.init(empty=True) + + platform.ENTITIES.append(platform.MockLight("Test_rgbww_ct", STATE_ON)) + platform.ENTITIES.append(platform.MockLight("Test_rgbww", STATE_ON)) + + entity0 = platform.ENTITIES[0] + entity0.supported_color_modes = { + light.COLOR_MODE_COLOR_TEMP, + light.COLOR_MODE_RGBWW, + } + + entity1 = platform.ENTITIES[1] + entity1.supported_color_modes = {light.COLOR_MODE_RGBWW} + + assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) + await hass.async_block_till_done() + + state = hass.states.get(entity0.entity_id) + assert state.attributes["supported_color_modes"] == [ + light.COLOR_MODE_COLOR_TEMP, + light.COLOR_MODE_RGBWW, + ] + + state = hass.states.get(entity1.entity_id) + assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGBWW] + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + ], + "brightness_pct": 100, + "color_temp": 153, + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 255, "color_temp": 153} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 0, 255)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + ], + "brightness_pct": 50, + "color_temp": 500, + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 128, "color_temp": 500} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 128, "rgbww_color": (0, 0, 0, 128, 0)} + + async def test_light_service_call_white_mode(hass, enable_custom_integrations): """Test color_mode white in service calls.""" platform = getattr(hass.components, "test.light") diff --git a/tests/util/test_color.py b/tests/util/test_color.py index d806a941965..9c4b781dc65 100644 --- a/tests/util/test_color.py +++ b/tests/util/test_color.py @@ -401,3 +401,49 @@ def test_color_rgb_to_rgbww(): assert color_util.color_rgb_to_rgbww(64, 64, 64, 154, 370) == (0, 14, 25, 64, 64) assert color_util.color_rgb_to_rgbww(32, 64, 16, 154, 370) == (9, 64, 0, 38, 38) assert color_util.color_rgb_to_rgbww(0, 0, 0, 154, 370) == (0, 0, 0, 0, 0) + + +def test_color_temperature_to_rgbww(): + """Test color temp to warm, cold conversion.""" + assert color_util.color_temperature_to_rgbww(153, 255, 153, 500) == ( + 0, + 0, + 0, + 0, + 255, + ) + assert color_util.color_temperature_to_rgbww(153, 128, 153, 500) == ( + 0, + 0, + 0, + 0, + 128, + ) + assert color_util.color_temperature_to_rgbww(500, 255, 153, 500) == ( + 0, + 0, + 0, + 255, + 0, + ) + assert color_util.color_temperature_to_rgbww(500, 128, 153, 500) == ( + 0, + 0, + 0, + 128, + 0, + ) + assert color_util.color_temperature_to_rgbww(347, 255, 153, 500) == ( + 0, + 0, + 0, + 143, + 112, + ) + assert color_util.color_temperature_to_rgbww(347, 128, 153, 500) == ( + 0, + 0, + 0, + 72, + 56, + )