From 84b2ec224418ef74fa9e98c5d6a23bdf8b70cced Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Feb 2022 13:36:30 -0600 Subject: [PATCH] Fix warm/cold reversal in rgbww_to_color_temperature (#65677) --- homeassistant/util/color.py | 26 ++++++- tests/components/light/test_init.py | 61 +++++++++++++++- tests/util/test_color.py | 105 ++++++++++++++++++++++++++-- 3 files changed, 180 insertions(+), 12 deletions(-) diff --git a/homeassistant/util/color.py b/homeassistant/util/color.py index f308595adbd..f055a5f32eb 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -531,13 +531,33 @@ def color_temperature_to_rgb( 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.""" + """Convert color temperature in mireds to rgbcw.""" mired_range = max_mireds - min_mireds - warm = ((max_mireds - temperature) / mired_range) * brightness - cold = brightness - warm + cold = ((max_mireds - temperature) / mired_range) * brightness + warm = brightness - cold return (0, 0, 0, round(cold), round(warm)) +def rgbww_to_color_temperature( + rgbww: tuple[int, int, int, int, int], min_mireds: int, max_mireds: int +) -> tuple[int, int]: + """Convert rgbcw to color temperature in mireds.""" + _, _, _, cold, warm = rgbww + return while_levels_to_color_temperature(cold, warm, min_mireds, max_mireds) + + +def while_levels_to_color_temperature( + cold: int, warm: int, min_mireds: int, max_mireds: int +) -> tuple[int, int]: + """Convert whites to color temperature in mireds.""" + brightness = warm / 255 + cold / 255 + if brightness == 0: + return (max_mireds, 0) + return round( + ((cold / 255 / brightness) * (min_mireds - max_mireds)) + max_mireds + ), min(255, round(brightness * 255)) + + 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 a8a6ebc901e..640a4b4533b 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -1899,7 +1899,8 @@ async def test_light_service_call_color_temp_conversion( _, 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)} + # Home Assistant uses RGBCW so a mireds of 153 should be maximum cold at 100% brightness so 255 + assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 255, 0)} await hass.services.async_call( "light", @@ -1917,7 +1918,63 @@ async def test_light_service_call_color_temp_conversion( _, 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)} + # Home Assistant uses RGBCW so a mireds of 500 should be maximum warm at 50% brightness so 128 + assert data == {"brightness": 128, "rgbww_color": (0, 0, 0, 0, 128)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + ], + "brightness_pct": 100, + "color_temp": 327, + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 255, "color_temp": 327} + _, data = entity1.last_call("turn_on") + # Home Assistant uses RGBCW so a mireds of 328 should be the midway point at 100% brightness so 127 (rounding), 128 + assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 127, 128)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + ], + "brightness_pct": 100, + "color_temp": 240, + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 255, "color_temp": 240} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 191, 64)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + ], + "brightness_pct": 100, + "color_temp": 410, + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 255, "color_temp": 410} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 66, 189)} async def test_light_service_call_white_mode(hass, enable_custom_integrations): diff --git a/tests/util/test_color.py b/tests/util/test_color.py index 31778781676..0b1b8f7d17f 100644 --- a/tests/util/test_color.py +++ b/tests/util/test_color.py @@ -406,46 +406,137 @@ def test_color_rgb_to_rgbww(): def test_color_temperature_to_rgbww(): - """Test color temp to warm, cold conversion.""" + """Test color temp to warm, cold conversion. + + Temperature values must be in mireds + Home Assistant uses rgbcw for rgbww + """ assert color_util.color_temperature_to_rgbww(153, 255, 153, 500) == ( 0, 0, 0, - 0, 255, + 0, ) assert color_util.color_temperature_to_rgbww(153, 128, 153, 500) == ( 0, 0, 0, - 0, 128, + 0, ) assert color_util.color_temperature_to_rgbww(500, 255, 153, 500) == ( 0, 0, 0, - 255, 0, + 255, ) assert color_util.color_temperature_to_rgbww(500, 128, 153, 500) == ( 0, 0, 0, - 128, 0, + 128, ) assert color_util.color_temperature_to_rgbww(347, 255, 153, 500) == ( 0, 0, 0, - 143, 112, + 143, ) assert color_util.color_temperature_to_rgbww(347, 128, 153, 500) == ( 0, 0, 0, - 72, 56, + 72, + ) + + +def test_rgbww_to_color_temperature(): + """Test rgbww conversion to color temp. + + Temperature values must be in mireds + Home Assistant uses rgbcw for rgbww + """ + assert ( + color_util.rgbww_to_color_temperature( + ( + 0, + 0, + 0, + 255, + 0, + ), + 153, + 500, + ) + == (153, 255) + ) + assert color_util.rgbww_to_color_temperature((0, 0, 0, 128, 0), 153, 500) == ( + 153, + 128, + ) + assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 255), 153, 500) == ( + 500, + 255, + ) + assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 128), 153, 500) == ( + 500, + 128, + ) + assert color_util.rgbww_to_color_temperature((0, 0, 0, 112, 143), 153, 500) == ( + 348, + 255, + ) + assert color_util.rgbww_to_color_temperature((0, 0, 0, 56, 72), 153, 500) == ( + 348, + 128, + ) + assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 0), 153, 500) == ( + 500, + 0, + ) + + +def test_white_levels_to_color_temperature(): + """Test warm, cold conversion to color temp. + + Temperature values must be in mireds + Home Assistant uses rgbcw for rgbww + """ + assert ( + color_util.while_levels_to_color_temperature( + 255, + 0, + 153, + 500, + ) + == (153, 255) + ) + assert color_util.while_levels_to_color_temperature(128, 0, 153, 500) == ( + 153, + 128, + ) + assert color_util.while_levels_to_color_temperature(0, 255, 153, 500) == ( + 500, + 255, + ) + assert color_util.while_levels_to_color_temperature(0, 128, 153, 500) == ( + 500, + 128, + ) + assert color_util.while_levels_to_color_temperature(112, 143, 153, 500) == ( + 348, + 255, + ) + assert color_util.while_levels_to_color_temperature(56, 72, 153, 500) == ( + 348, + 128, + ) + assert color_util.while_levels_to_color_temperature(0, 0, 153, 500) == ( + 500, + 0, )