Convert color temperature to visible color in lights (#55219)

This commit is contained in:
J. Nick Koston 2021-08-25 14:56:36 -05:00 committed by GitHub
parent 59d401e7b7
commit e6e8d7eded
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 49 additions and 24 deletions

View file

@ -412,10 +412,11 @@ class ColorSettingTrait(_Trait):
def query_attributes(self): def query_attributes(self):
"""Return color temperature query attributes.""" """Return color temperature query attributes."""
color_modes = self.state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) color_mode = self.state.attributes.get(light.ATTR_COLOR_MODE)
color = {} color = {}
if light.color_supported(color_modes): if light.color_supported([color_mode]):
color_hs = self.state.attributes.get(light.ATTR_HS_COLOR) color_hs = self.state.attributes.get(light.ATTR_HS_COLOR)
brightness = self.state.attributes.get(light.ATTR_BRIGHTNESS, 1) brightness = self.state.attributes.get(light.ATTR_BRIGHTNESS, 1)
if color_hs is not None: if color_hs is not None:
@ -425,7 +426,7 @@ class ColorSettingTrait(_Trait):
"value": brightness / 255, "value": brightness / 255,
} }
if light.color_temp_supported(color_modes): if light.color_temp_supported([color_mode]):
temp = self.state.attributes.get(light.ATTR_COLOR_TEMP) temp = self.state.attributes.get(light.ATTR_COLOR_TEMP)
# Some faulty integrations might put 0 in here, raising exception. # Some faulty integrations might put 0 in here, raising exception.
if temp == 0: if temp == 0:

View file

@ -829,6 +829,13 @@ class LightEntity(ToggleEntity):
data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3]) data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3])
data[ATTR_RGBWW_COLOR] = tuple(int(x) for x in rgbww_color[0:5]) data[ATTR_RGBWW_COLOR] = tuple(int(x) for x in rgbww_color[0:5])
data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color)
elif color_mode == COLOR_MODE_COLOR_TEMP and self.color_temp:
hs_color = color_util.color_temperature_to_hs(
color_util.color_temperature_mired_to_kelvin(self.color_temp)
)
data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3))
data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color)
data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
return data return data
@final @final
@ -863,7 +870,7 @@ class LightEntity(ToggleEntity):
if color_mode == COLOR_MODE_COLOR_TEMP: if color_mode == COLOR_MODE_COLOR_TEMP:
data[ATTR_COLOR_TEMP] = self.color_temp data[ATTR_COLOR_TEMP] = self.color_temp
if color_mode in COLOR_MODES_COLOR: if color_mode in COLOR_MODES_COLOR or color_mode == COLOR_MODE_COLOR_TEMP:
data.update(self._light_internal_convert_color(color_mode)) data.update(self._light_internal_convert_color(color_mode))
if supported_features & SUPPORT_COLOR_TEMP and not self.supported_color_modes: if supported_features & SUPPORT_COLOR_TEMP and not self.supported_color_modes:

View file

@ -45,7 +45,7 @@ async def test_light_state_temperature(
assert state assert state
assert state.attributes.get(ATTR_BRIGHTNESS) == 54 assert state.attributes.get(ATTR_BRIGHTNESS) == 54
assert state.attributes.get(ATTR_COLOR_TEMP) == 297 assert state.attributes.get(ATTR_COLOR_TEMP) == 297
assert state.attributes.get(ATTR_HS_COLOR) is None assert state.attributes.get(ATTR_HS_COLOR) == (27.316, 47.743)
assert state.attributes.get(ATTR_COLOR_MODE) == COLOR_MODE_COLOR_TEMP assert state.attributes.get(ATTR_COLOR_MODE) == COLOR_MODE_COLOR_TEMP
assert state.attributes.get(ATTR_MIN_MIREDS) == 143 assert state.attributes.get(ATTR_MIN_MIREDS) == 143
assert state.attributes.get(ATTR_MAX_MIREDS) == 344 assert state.attributes.get(ATTR_MAX_MIREDS) == 344

View file

@ -576,6 +576,7 @@ async def test_color_setting_color_light(hass, supported_color_modes):
{ {
light.ATTR_HS_COLOR: (20, 94), light.ATTR_HS_COLOR: (20, 94),
light.ATTR_BRIGHTNESS: 200, light.ATTR_BRIGHTNESS: 200,
light.ATTR_COLOR_MODE: "hs",
"supported_color_modes": supported_color_modes, "supported_color_modes": supported_color_modes,
}, },
), ),
@ -634,6 +635,7 @@ async def test_color_setting_temperature_light(hass):
STATE_ON, STATE_ON,
{ {
light.ATTR_MIN_MIREDS: 200, light.ATTR_MIN_MIREDS: 200,
light.ATTR_COLOR_MODE: "color_temp",
light.ATTR_COLOR_TEMP: 300, light.ATTR_COLOR_TEMP: 300,
light.ATTR_MAX_MIREDS: 500, light.ATTR_MAX_MIREDS: 500,
"supported_color_modes": ["color_temp"], "supported_color_modes": ["color_temp"],

View file

@ -618,12 +618,12 @@ async def test_emulated_color_temp_group(hass, enable_custom_integrations):
state = hass.states.get("light.test1") state = hass.states.get("light.test1")
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes[ATTR_COLOR_TEMP] == 200 assert state.attributes[ATTR_COLOR_TEMP] == 200
assert ATTR_HS_COLOR not in state.attributes.keys() assert ATTR_HS_COLOR in state.attributes.keys()
state = hass.states.get("light.test2") state = hass.states.get("light.test2")
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes[ATTR_COLOR_TEMP] == 200 assert state.attributes[ATTR_COLOR_TEMP] == 200
assert ATTR_HS_COLOR not in state.attributes.keys() assert ATTR_HS_COLOR in state.attributes.keys()
state = hass.states.get("light.test3") state = hass.states.get("light.test3")
assert state.state == STATE_ON assert state.state == STATE_ON

View file

@ -260,7 +260,7 @@ async def test_lights_color_mode(hass, mock_bridge):
assert lamp_1.state == "on" assert lamp_1.state == "on"
assert lamp_1.attributes["brightness"] == 145 assert lamp_1.attributes["brightness"] == 145
assert lamp_1.attributes["color_temp"] == 467 assert lamp_1.attributes["color_temp"] == 467
assert "hs_color" not in lamp_1.attributes assert "hs_color" in lamp_1.attributes
async def test_groups(hass, mock_bridge): async def test_groups(hass, mock_bridge):

View file

@ -1161,6 +1161,9 @@ async def test_light_backwards_compatibility_color_mode(
state = hass.states.get(entity2.entity_id) state = hass.states.get(entity2.entity_id)
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_COLOR_TEMP] assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_COLOR_TEMP]
assert state.attributes["color_mode"] == light.COLOR_MODE_COLOR_TEMP assert state.attributes["color_mode"] == light.COLOR_MODE_COLOR_TEMP
assert state.attributes["rgb_color"] == (201, 218, 255)
assert state.attributes["hs_color"] == (221.575, 20.9)
assert state.attributes["xy_color"] == (0.277, 0.287)
state = hass.states.get(entity3.entity_id) state = hass.states.get(entity3.entity_id)
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS]

View file

@ -393,7 +393,7 @@ async def test_legacy_controlling_state_via_topic(hass, mqtt_mock):
async_fire_mqtt_message(hass, "test_light_rgb/rgb/status", "125,125,125") async_fire_mqtt_message(hass, "test_light_rgb/rgb/status", "125,125,125")
light_state = hass.states.get("light.test") light_state = hass.states.get("light.test")
assert light_state.attributes.get("rgb_color") is None assert light_state.attributes.get("rgb_color") == (255, 187, 131)
assert light_state.attributes.get(light.ATTR_COLOR_MODE) == "color_temp" assert light_state.attributes.get(light.ATTR_COLOR_MODE) == "color_temp"
assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
@ -636,13 +636,13 @@ async def test_legacy_invalid_state_via_topic(hass, mqtt_mock, caplog):
state = hass.states.get("light.test") state = hass.states.get("light.test")
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get("rgb_color") is None assert state.attributes.get("rgb_color") == (255, 254, 250)
assert state.attributes.get("brightness") == 255 assert state.attributes.get("brightness") == 255
assert state.attributes.get("color_temp") == 153 assert state.attributes.get("color_temp") == 153
assert state.attributes.get("effect") == "none" assert state.attributes.get("effect") == "none"
assert state.attributes.get("hs_color") is None assert state.attributes.get("hs_color") == (54.768, 1.6)
assert state.attributes.get("white_value") == 255 assert state.attributes.get("white_value") == 255
assert state.attributes.get("xy_color") is None assert state.attributes.get("xy_color") == (0.326, 0.333)
async_fire_mqtt_message(hass, "test_light_rgb/color_temp/status", "") async_fire_mqtt_message(hass, "test_light_rgb/color_temp/status", "")
assert "Ignoring empty color temp message" in caplog.text assert "Ignoring empty color temp message" in caplog.text
@ -776,12 +776,12 @@ async def test_invalid_state_via_topic(hass, mqtt_mock, caplog):
state = hass.states.get("light.test") state = hass.states.get("light.test")
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get("rgb_color") is None assert state.attributes.get("rgb_color") == (255, 254, 250)
assert state.attributes.get("brightness") == 255 assert state.attributes.get("brightness") == 255
assert state.attributes.get("color_temp") == 153 assert state.attributes.get("color_temp") == 153
assert state.attributes.get("effect") == "none" assert state.attributes.get("effect") == "none"
assert state.attributes.get("hs_color") is None assert state.attributes.get("hs_color") == (54.768, 1.6)
assert state.attributes.get("xy_color") is None assert state.attributes.get("xy_color") == (0.326, 0.333)
async_fire_mqtt_message(hass, "test_light_rgb/color_temp/status", "") async_fire_mqtt_message(hass, "test_light_rgb/color_temp/status", "")
assert "Ignoring empty color temp message" in caplog.text assert "Ignoring empty color temp message" in caplog.text
@ -988,7 +988,7 @@ async def test_legacy_controlling_state_via_topic_with_templates(hass, mqtt_mock
state = hass.states.get("light.test") state = hass.states.get("light.test")
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get("brightness") == 50 assert state.attributes.get("brightness") == 50
assert state.attributes.get("rgb_color") is None assert state.attributes.get("rgb_color") == (255, 187, 131)
assert state.attributes.get("color_temp") == 300 assert state.attributes.get("color_temp") == 300
assert state.attributes.get("effect") == "rainbow" assert state.attributes.get("effect") == "rainbow"
assert state.attributes.get("white_value") == 75 assert state.attributes.get("white_value") == 75
@ -1260,11 +1260,11 @@ async def test_legacy_sending_mqtt_commands_and_optimistic(hass, mqtt_mock):
state = hass.states.get("light.test") state = hass.states.get("light.test")
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get("rgb_color") is None assert state.attributes.get("rgb_color") == (221, 229, 255)
assert state.attributes["brightness"] == 50 assert state.attributes["brightness"] == 50
assert state.attributes.get("hs_color") is None assert state.attributes.get("hs_color") == (224.772, 13.249)
assert state.attributes["white_value"] == 80 assert state.attributes["white_value"] == 80
assert state.attributes.get("xy_color") is None assert state.attributes.get("xy_color") == (0.296, 0.301)
assert state.attributes["color_temp"] == 125 assert state.attributes["color_temp"] == 125

View file

@ -392,7 +392,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock):
async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color":null}') async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color":null}')
light_state = hass.states.get("light.test") light_state = hass.states.get("light.test")
assert "hs_color" not in light_state.attributes assert "hs_color" in light_state.attributes
async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color_temp":155}') async_fire_mqtt_message(hass, "test_light_rgb", '{"state":"ON", "color_temp":155}')

View file

@ -525,7 +525,7 @@ async def test_light(hass: HomeAssistant, light_mock_data: LightMockData) -> Non
assert state.state == "on" assert state.state == "on"
assert state.attributes["brightness"] == 51 assert state.attributes["brightness"] == 51
assert state.attributes["color_temp"] == 222 assert state.attributes["color_temp"] == 222
assert "hs_color" not in state.attributes assert "hs_color" in state.attributes
assert light_state["on_off"] == 1 assert light_state["on_off"] == 1
await hass.services.async_call( await hass.services.async_call(
@ -582,7 +582,7 @@ async def test_light(hass: HomeAssistant, light_mock_data: LightMockData) -> Non
assert state.state == "on" assert state.state == "on"
assert state.attributes["brightness"] == 168 assert state.attributes["brightness"] == 168
assert state.attributes["color_temp"] == 156 assert state.attributes["color_temp"] == 156
assert "hs_color" not in state.attributes assert "hs_color" in state.attributes
assert light_state["brightness"] == 66 assert light_state["brightness"] == 66
assert light_state["hue"] == 77 assert light_state["hue"] == 77
assert light_state["saturation"] == 78 assert light_state["saturation"] == 78

View file

@ -673,6 +673,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
"color_temp": ct, "color_temp": ct,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"], "supported_color_modes": ["color_temp", "hs", "rgb"],
"hs_color": (26.812, 34.87),
"rgb_color": (255, 205, 166),
"xy_color": (0.421, 0.364),
}, },
{ {
"supported_features": 0, "supported_features": 0,
@ -837,6 +840,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
"color_temp": ct, "color_temp": ct,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp"], "supported_color_modes": ["color_temp"],
"hs_color": (26.812, 34.87),
"rgb_color": (255, 205, 166),
"xy_color": (0.421, 0.364),
}, },
{ {
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST, "effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
@ -870,6 +876,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
"color_temp": ct, "color_temp": ct,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp"], "supported_color_modes": ["color_temp"],
"hs_color": (26.812, 34.87),
"rgb_color": (255, 205, 166),
"xy_color": (0.421, 0.364),
}, },
{ {
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST, "effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
@ -893,6 +902,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
"color_temp": bg_ct, "color_temp": bg_ct,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"], "supported_color_modes": ["color_temp", "hs", "rgb"],
"hs_color": (27.001, 19.243),
"rgb_color": (255, 228, 205),
"xy_color": (0.372, 0.35),
}, },
name=f"{UNIQUE_FRIENDLY_NAME} Ambilight", name=f"{UNIQUE_FRIENDLY_NAME} Ambilight",
entity_id=f"{ENTITY_LIGHT}_ambilight", entity_id=f"{ENTITY_LIGHT}_ambilight",

View file

@ -129,7 +129,7 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
assert state.attributes[ATTR_COLOR_MODE] == "color_temp" assert state.attributes[ATTR_COLOR_MODE] == "color_temp"
assert state.attributes[ATTR_BRIGHTNESS] == 255 assert state.attributes[ATTR_BRIGHTNESS] == 255
assert state.attributes[ATTR_COLOR_TEMP] == 370 assert state.attributes[ATTR_COLOR_TEMP] == 370
assert ATTR_RGB_COLOR not in state.attributes assert ATTR_RGB_COLOR in state.attributes
# Test turning on with same brightness # Test turning on with same brightness
await hass.services.async_call( await hass.services.async_call(
@ -387,7 +387,7 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
assert state.attributes[ATTR_COLOR_MODE] == "color_temp" assert state.attributes[ATTR_COLOR_MODE] == "color_temp"
assert state.attributes[ATTR_BRIGHTNESS] == 255 assert state.attributes[ATTR_BRIGHTNESS] == 255
assert state.attributes[ATTR_COLOR_TEMP] == 170 assert state.attributes[ATTR_COLOR_TEMP] == 170
assert ATTR_RGB_COLOR not in state.attributes assert ATTR_RGB_COLOR in state.attributes
# Test turning on with same color temp # Test turning on with same color temp
await hass.services.async_call( await hass.services.async_call(