From 443463e19d74dc1362b58c26d03bdfced08454ed Mon Sep 17 00:00:00 2001 From: Erik Montnemery <erik@montnemery.com> Date: Wed, 9 Jun 2021 11:23:01 +0200 Subject: [PATCH] Emulate color_temp for lights which support color or white (#51654) * Emulate color_temp for lights which support color or white * Support legacy lights * Tidy up group.light code * Improve color_temp to white conversion * Remove color_temp to white conversion * Add test * Tweak deconz test --- homeassistant/components/group/light.py | 61 +++------------------- homeassistant/components/light/__init__.py | 16 ++++-- tests/components/deconz/test_light.py | 1 - tests/components/light/test_init.py | 60 +++++++++++++++++++++ 4 files changed, 79 insertions(+), 59 deletions(-) diff --git a/homeassistant/components/group/light.py b/homeassistant/components/group/light.py index 9567735c9eb..0abe842af2c 100644 --- a/homeassistant/components/group/light.py +++ b/homeassistant/components/group/light.py @@ -1,7 +1,6 @@ """This platform allows several lights to be grouped into one light.""" from __future__ import annotations -import asyncio from collections import Counter from collections.abc import Iterator import itertools @@ -34,8 +33,6 @@ from homeassistant.components.light import ( SUPPORT_FLASH, SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, - color_supported, - color_temp_supported, ) from homeassistant.const import ( ATTR_ENTITY_ID, @@ -49,7 +46,6 @@ from homeassistant.core import CoreState, HomeAssistant, State import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.typing import ConfigType -from homeassistant.util import color as color_util from . import GroupEntity @@ -233,7 +229,6 @@ class LightGroup(GroupEntity, light.LightEntity): async def async_turn_on(self, **kwargs): """Forward the turn_on command to all lights in the light group.""" data = {ATTR_ENTITY_ID: self._entity_ids} - emulate_color_temp_entity_ids = [] if ATTR_BRIGHTNESS in kwargs: data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] @@ -256,21 +251,6 @@ class LightGroup(GroupEntity, light.LightEntity): if ATTR_COLOR_TEMP in kwargs: data[ATTR_COLOR_TEMP] = kwargs[ATTR_COLOR_TEMP] - # Create a new entity list to mutate - updated_entities = list(self._entity_ids) - - # Walk through initial entity ids, split entity lists by support - for entity_id in self._entity_ids: - state = self.hass.states.get(entity_id) - if not state: - continue - support = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES) - # Only pass color temperature to supported entity_ids - if color_supported(support) and not color_temp_supported(support): - emulate_color_temp_entity_ids.append(entity_id) - updated_entities.remove(entity_id) - data[ATTR_ENTITY_ID] = updated_entities - if ATTR_WHITE_VALUE in kwargs: data[ATTR_WHITE_VALUE] = kwargs[ATTR_WHITE_VALUE] @@ -283,41 +263,12 @@ class LightGroup(GroupEntity, light.LightEntity): if ATTR_FLASH in kwargs: data[ATTR_FLASH] = kwargs[ATTR_FLASH] - if not emulate_color_temp_entity_ids: - await self.hass.services.async_call( - light.DOMAIN, - light.SERVICE_TURN_ON, - data, - blocking=True, - context=self._context, - ) - return - - emulate_color_temp_data = data.copy() - temp_k = color_util.color_temperature_mired_to_kelvin( - emulate_color_temp_data[ATTR_COLOR_TEMP] - ) - hs_color = color_util.color_temperature_to_hs(temp_k) - emulate_color_temp_data[ATTR_HS_COLOR] = hs_color - del emulate_color_temp_data[ATTR_COLOR_TEMP] - - emulate_color_temp_data[ATTR_ENTITY_ID] = emulate_color_temp_entity_ids - - await asyncio.gather( - self.hass.services.async_call( - light.DOMAIN, - light.SERVICE_TURN_ON, - data, - blocking=True, - context=self._context, - ), - self.hass.services.async_call( - light.DOMAIN, - light.SERVICE_TURN_ON, - emulate_color_temp_data, - blocking=True, - context=self._context, - ), + await self.hass.services.async_call( + light.DOMAIN, + light.SERVICE_TURN_ON, + data, + blocking=True, + context=self._context, ) async def async_turn_off(self, **kwargs): diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 32494592697..a4693857178 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -370,13 +370,13 @@ async def async_setup(hass, config): # noqa: C901 ): profiles.apply_default(light.entity_id, light.is_on, params) + legacy_supported_color_modes = ( + light._light_internal_supported_color_modes # pylint: disable=protected-access + ) supported_color_modes = light.supported_color_modes # Backwards compatibility: if an RGBWW color is specified, convert to RGB + W # for legacy lights if ATTR_RGBW_COLOR in params: - legacy_supported_color_modes = ( - light._light_internal_supported_color_modes # pylint: disable=protected-access - ) if ( COLOR_MODE_RGBW in legacy_supported_color_modes and not supported_color_modes @@ -385,6 +385,16 @@ async def async_setup(hass, config): # noqa: C901 params[ATTR_RGB_COLOR] = rgbw_color[0:3] 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 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 # is not implemented diff --git a/tests/components/deconz/test_light.py b/tests/components/deconz/test_light.py index a5e27709ebf..6ec73085322 100644 --- a/tests/components/deconz/test_light.py +++ b/tests/components/deconz/test_light.py @@ -170,7 +170,6 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket): SERVICE_TURN_ON, { ATTR_ENTITY_ID: "light.rgb_light", - ATTR_COLOR_TEMP: 2500, ATTR_BRIGHTNESS: 200, ATTR_TRANSITION: 5, ATTR_FLASH: FLASH_SHORT, diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 368a5b0dab4..b3682cbdd2c 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -1586,6 +1586,66 @@ async def test_light_service_call_color_conversion(hass, enable_custom_integrati assert data == {"brightness": 128, "rgbww_color": (0, 75, 140, 255, 255)} +async def test_light_service_call_color_temp_emulation( + hass, enable_custom_integrations +): + """Test color conversion in service calls.""" + platform = getattr(hass.components, "test.light") + platform.init(empty=True) + + platform.ENTITIES.append(platform.MockLight("Test_hs_ct", STATE_ON)) + platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON)) + platform.ENTITIES.append(platform.MockLight("Test_hs_white", STATE_ON)) + + entity0 = platform.ENTITIES[0] + entity0.supported_color_modes = {light.COLOR_MODE_COLOR_TEMP, light.COLOR_MODE_HS} + + entity1 = platform.ENTITIES[1] + entity1.supported_color_modes = {light.COLOR_MODE_HS} + + entity2 = platform.ENTITIES[2] + entity2.supported_color_modes = {light.COLOR_MODE_HS, light.COLOR_MODE_WHITE} + + 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_HS, + ] + + state = hass.states.get(entity1.entity_id) + assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] + + state = hass.states.get(entity2.entity_id) + assert state.attributes["supported_color_modes"] == [ + light.COLOR_MODE_HS, + light.COLOR_MODE_WHITE, + ] + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": [ + entity0.entity_id, + entity1.entity_id, + entity2.entity_id, + ], + "brightness_pct": 100, + "color_temp": 200, + }, + blocking=True, + ) + _, data = entity0.last_call("turn_on") + assert data == {"brightness": 255, "color_temp": 200} + _, data = entity1.last_call("turn_on") + assert data == {"brightness": 255, "hs_color": (27.001, 19.243)} + _, data = entity2.last_call("turn_on") + assert data == {"brightness": 255, "hs_color": (27.001, 19.243)} + + 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")