Emulate color temperature for non-ct lights in light groups (#23495)

* Emulate color temperature for non-ct lights in light groups

* fix tests

* Address review comments

* Fix black formatting

* Fix for pylint

* Address comments

* Fix black formatting

* Address comments
This commit is contained in:
Bryan York 2019-09-15 11:53:05 -07:00 committed by Martin Hjelmare
parent fd359c6222
commit f45f8f2f3d
3 changed files with 97 additions and 3 deletions

View File

@ -308,7 +308,7 @@ class ColorSettingTrait(_Trait):
if features & light.SUPPORT_COLOR_TEMP: if features & light.SUPPORT_COLOR_TEMP:
# Max Kelvin is Min Mireds K = 1000000 / mireds # Max Kelvin is Min Mireds K = 1000000 / mireds
# Min Kevin is Max Mireds K = 1000000 / mireds # Min Kelvin is Max Mireds K = 1000000 / mireds
response["colorTemperatureRange"] = { response["colorTemperatureRange"] = {
"temperatureMaxK": color_util.color_temperature_mired_to_kelvin( "temperatureMaxK": color_util.color_temperature_mired_to_kelvin(
attrs.get(light.ATTR_MIN_MIREDS) attrs.get(light.ATTR_MIN_MIREDS)

View File

@ -1,4 +1,5 @@
"""This platform allows several lights to be grouped into one light.""" """This platform allows several lights to be grouped into one light."""
import asyncio
from collections import Counter from collections import Counter
import itertools import itertools
import logging import logging
@ -19,6 +20,7 @@ from homeassistant.core import State, callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.typing import ConfigType, HomeAssistantType from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.util import color as color_util
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
@ -179,6 +181,7 @@ class LightGroup(light.Light):
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Forward the turn_on command to all lights in the light group.""" """Forward the turn_on command to all lights in the light group."""
data = {ATTR_ENTITY_ID: self._entity_ids} data = {ATTR_ENTITY_ID: self._entity_ids}
emulate_color_temp_entity_ids = []
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS]
@ -189,6 +192,23 @@ class LightGroup(light.Light):
if ATTR_COLOR_TEMP in kwargs: if ATTR_COLOR_TEMP in kwargs:
data[ATTR_COLOR_TEMP] = kwargs[ATTR_COLOR_TEMP] 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_FEATURES)
# Only pass color temperature to supported entity_ids
if bool(support & SUPPORT_COLOR) and not bool(
support & SUPPORT_COLOR_TEMP
):
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: if ATTR_WHITE_VALUE in kwargs:
data[ATTR_WHITE_VALUE] = kwargs[ATTR_WHITE_VALUE] data[ATTR_WHITE_VALUE] = kwargs[ATTR_WHITE_VALUE]
@ -201,8 +221,32 @@ class LightGroup(light.Light):
if ATTR_FLASH in kwargs: if ATTR_FLASH in kwargs:
data[ATTR_FLASH] = kwargs[ATTR_FLASH] data[ATTR_FLASH] = kwargs[ATTR_FLASH]
await self.hass.services.async_call( if not emulate_color_temp_entity_ids:
light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True await self.hass.services.async_call(
light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True
)
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
),
self.hass.services.async_call(
light.DOMAIN,
light.SERVICE_TURN_ON,
emulate_color_temp_data,
blocking=True,
),
) )
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):

View File

@ -186,6 +186,56 @@ async def test_color_temp(hass):
assert state.attributes["color_temp"] == 1000 assert state.attributes["color_temp"] == 1000
async def test_emulated_color_temp_group(hass):
"""Test emulated color temperature in a group."""
await async_setup_component(
hass,
"light",
{
"light": [
{"platform": "demo"},
{
"platform": "group",
"entities": [
"light.bed_light",
"light.ceiling_lights",
"light.kitchen_lights",
],
},
]
},
)
await hass.async_block_till_done()
hass.states.async_set("light.bed_light", "on", {"supported_features": 2})
await hass.async_block_till_done()
hass.states.async_set("light.ceiling_lights", "on", {"supported_features": 63})
await hass.async_block_till_done()
hass.states.async_set("light.kitchen_lights", "on", {"supported_features": 61})
await hass.async_block_till_done()
await hass.services.async_call(
"light",
"turn_on",
{"entity_id": "light.light_group", "color_temp": 200},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("light.bed_light")
assert state.state == "on"
assert state.attributes["color_temp"] == 200
assert "hs_color" not in state.attributes.keys()
state = hass.states.get("light.ceiling_lights")
assert state.state == "on"
assert state.attributes["color_temp"] == 200
assert "hs_color" in state.attributes.keys()
state = hass.states.get("light.kitchen_lights")
assert state.state == "on"
assert state.attributes["hs_color"] == (27.001, 19.243)
async def test_min_max_mireds(hass): async def test_min_max_mireds(hass):
"""Test min/max mireds reporting.""" """Test min/max mireds reporting."""
await async_setup_component( await async_setup_component(