From 39867c9b83d1804f97b39702520bb00a314fbfb5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery <erik@montnemery.com> Date: Fri, 29 Oct 2021 15:48:11 +0200 Subject: [PATCH] Convert RGBW and RGBWW colors in light turn_on calls (#58680) --- homeassistant/components/light/__init__.py | 36 ++++++ tests/components/light/test_init.py | 135 +++++++++++++++++++++ tests/components/tasmota/test_light.py | 16 +-- 3 files changed, 179 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index a09a2fcd58e..8b1d8fe1ec6 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -401,6 +401,14 @@ async def async_setup(hass, config): # noqa: C901 params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) elif (xy_color := params.pop(ATTR_XY_COLOR, None)) is not None: params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color) + elif (rgbw_color := params.pop(ATTR_RGBW_COLOR, None)) is not None: + rgb_color = color_util.color_rgbw_to_rgb(*rgbw_color) + params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) + elif (rgbww_color := params.pop(ATTR_RGBWW_COLOR, None)) is not None: + rgb_color = color_util.color_rgbww_to_rgb( + *rgbww_color, light.min_mireds, light.max_mireds + ) + params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) elif ATTR_HS_COLOR in params and COLOR_MODE_HS not in supported_color_modes: hs_color = params.pop(ATTR_HS_COLOR) if COLOR_MODE_RGB in supported_color_modes: @@ -441,6 +449,34 @@ async def async_setup(hass, config): # noqa: C901 params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( *rgb_color, light.min_mireds, light.max_mireds ) + elif ATTR_RGBW_COLOR in params and COLOR_MODE_RGBW not in supported_color_modes: + rgbw_color = params.pop(ATTR_RGBW_COLOR) + rgb_color = color_util.color_rgbw_to_rgb(*rgbw_color) + if COLOR_MODE_RGB in supported_color_modes: + params[ATTR_RGB_COLOR] = rgb_color + elif COLOR_MODE_RGBWW in supported_color_modes: + params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( + *rgb_color, light.min_mireds, light.max_mireds + ) + elif COLOR_MODE_HS in supported_color_modes: + params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) + elif COLOR_MODE_XY in supported_color_modes: + params[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) + elif ( + ATTR_RGBWW_COLOR in params and COLOR_MODE_RGBWW not in supported_color_modes + ): + rgbww_color = params.pop(ATTR_RGBWW_COLOR) + rgb_color = color_util.color_rgbww_to_rgb( + *rgbww_color, light.min_mireds, light.max_mireds + ) + if COLOR_MODE_RGB in supported_color_modes: + params[ATTR_RGB_COLOR] = rgb_color + elif COLOR_MODE_RGBW in supported_color_modes: + params[ATTR_RGBW_COLOR] = color_util.color_rgb_to_rgbw(*rgb_color) + elif COLOR_MODE_HS in supported_color_modes: + params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) + elif COLOR_MODE_XY in supported_color_modes: + params[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) # If both white and brightness are specified, override white if ( diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index f3bd4583676..2a61a3bbf4d 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -1588,6 +1588,141 @@ async def test_light_service_call_color_conversion(hass, enable_custom_integrati # The midpoint the the white channels is warm, compensated by adding green + blue assert data == {"brightness": 128, "rgbww_color": (0, 75, 140, 255, 255)} + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + entity2.entity_id, + entity3.entity_id, + entity4.entity_id, + entity5.entity_id, + entity6.entity_id, + ], + "brightness_pct": 50, + "rgbw_color": (128, 0, 0, 64), + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (0.0, 66.406)} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (128, 43, 43)} + _, data = entity2.last_call("turn_on") + assert data == {"brightness": 128, "xy_color": (0.592, 0.308)} + _, data = entity3.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (128, 43, 43)} + _, data = entity4.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (0.0, 66.406)} + _, data = entity5.last_call("turn_on") + assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 64)} + _, data = entity6.last_call("turn_on") + # The midpoint the the white channels is warm, compensated by adding green + blue + assert data == {"brightness": 128, "rgbww_color": (128, 0, 30, 117, 117)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + entity2.entity_id, + entity3.entity_id, + entity4.entity_id, + entity5.entity_id, + entity6.entity_id, + ], + "brightness_pct": 50, + "rgbw_color": (255, 255, 255, 255), + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (0.0, 0.0)} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (255, 255, 255)} + _, data = entity2.last_call("turn_on") + assert data == {"brightness": 128, "xy_color": (0.323, 0.329)} + _, data = entity3.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (255, 255, 255)} + _, data = entity4.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (0.0, 0.0)} + _, data = entity5.last_call("turn_on") + assert data == {"brightness": 128, "rgbw_color": (255, 255, 255, 255)} + _, data = entity6.last_call("turn_on") + # The midpoint the the white channels is warm, compensated by adding green + blue + assert data == {"brightness": 128, "rgbww_color": (0, 76, 141, 255, 255)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + entity2.entity_id, + entity3.entity_id, + entity4.entity_id, + entity5.entity_id, + entity6.entity_id, + ], + "brightness_pct": 50, + "rgbww_color": (128, 0, 0, 64, 32), + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (4.118, 79.688)} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (128, 33, 26)} + _, data = entity2.last_call("turn_on") + assert data == {"brightness": 128, "xy_color": (0.639, 0.312)} + _, data = entity3.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (128, 33, 26)} + _, data = entity4.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (4.118, 79.688)} + _, data = entity5.last_call("turn_on") + assert data == {"brightness": 128, "rgbw_color": (128, 9, 0, 33)} + _, data = entity6.last_call("turn_on") + assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 64, 32)} + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + entity2.entity_id, + entity3.entity_id, + entity4.entity_id, + entity5.entity_id, + entity6.entity_id, + ], + "brightness_pct": 50, + "rgbww_color": (255, 255, 255, 255, 255), + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (27.429, 27.451)} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (255, 217, 185)} + _, data = entity2.last_call("turn_on") + assert data == {"brightness": 128, "xy_color": (0.396, 0.359)} + _, data = entity3.last_call("turn_on") + assert data == {"brightness": 128, "rgb_color": (255, 217, 185)} + _, data = entity4.last_call("turn_on") + assert data == {"brightness": 128, "hs_color": (27.429, 27.451)} + _, data = entity5.last_call("turn_on") + # The midpoint the the white channels is warm, compensated by decreasing green + blue + assert data == {"brightness": 128, "rgbw_color": (96, 44, 0, 255)} + _, data = entity6.last_call("turn_on") + assert data == {"brightness": 128, "rgbww_color": (255, 255, 255, 255, 255)} + async def test_light_service_call_color_temp_emulation( hass, enable_custom_integrations diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index ec3d67ef8f2..f85cf0d3c5b 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -885,21 +885,21 @@ async def test_sending_mqtt_commands_rgbw_legacy(hass, mqtt_mock, setup_tasmota) ) mqtt_mock.async_publish.reset_mock() - # rgbw_color should be ignored + # rgbw_color should be converted await common.async_turn_on(hass, "light.test", rgbw_color=[128, 64, 32, 0]) mqtt_mock.async_publish.assert_called_once_with( "tasmota_49A3BC/cmnd/Backlog", - "NoDelay;Power1 ON", + "NoDelay;Power1 ON;NoDelay;HsbColor1 20;NoDelay;HsbColor2 75", 0, False, ) mqtt_mock.async_publish.reset_mock() - # rgbw_color should be ignored + # rgbw_color should be converted await common.async_turn_on(hass, "light.test", rgbw_color=[16, 64, 32, 128]) mqtt_mock.async_publish.assert_called_once_with( "tasmota_49A3BC/cmnd/Backlog", - "NoDelay;Power1 ON", + "NoDelay;Power1 ON;NoDelay;HsbColor1 141;NoDelay;HsbColor2 25", 0, False, ) @@ -992,21 +992,21 @@ async def test_sending_mqtt_commands_rgbw(hass, mqtt_mock, setup_tasmota): ) mqtt_mock.async_publish.reset_mock() - # rgbw_color should be ignored + # rgbw_color should be converted await common.async_turn_on(hass, "light.test", rgbw_color=[128, 64, 32, 0]) mqtt_mock.async_publish.assert_called_once_with( "tasmota_49A3BC/cmnd/Backlog", - "NoDelay;Power1 ON", + "NoDelay;Power1 ON;NoDelay;HsbColor1 20;NoDelay;HsbColor2 75", 0, False, ) mqtt_mock.async_publish.reset_mock() - # rgbw_color should be ignored + # rgbw_color should be converted await common.async_turn_on(hass, "light.test", rgbw_color=[16, 64, 32, 128]) mqtt_mock.async_publish.assert_called_once_with( "tasmota_49A3BC/cmnd/Backlog", - "NoDelay;Power1 ON", + "NoDelay;Power1 ON;NoDelay;HsbColor1 141;NoDelay;HsbColor2 25", 0, False, ) -- GitLab