Refactor light reproduce state to use kelvin attribute (#132854)

This commit is contained in:
epenet 2024-12-12 21:17:05 +01:00 committed by GitHub
parent d79dc8d22f
commit b9a7307df8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 50 additions and 19 deletions

View File

@ -15,11 +15,13 @@ from homeassistant.const import (
STATE_ON,
)
from homeassistant.core import Context, HomeAssistant, State
from homeassistant.util import color as color_util
from . import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_COLOR_TEMP,
ATTR_COLOR_TEMP_KELVIN,
ATTR_EFFECT,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
@ -40,6 +42,7 @@ ATTR_GROUP = [ATTR_BRIGHTNESS, ATTR_EFFECT]
COLOR_GROUP = [
ATTR_HS_COLOR,
ATTR_COLOR_TEMP,
ATTR_COLOR_TEMP_KELVIN,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_RGBWW_COLOR,
@ -55,7 +58,7 @@ class ColorModeAttr(NamedTuple):
COLOR_MODE_TO_ATTRIBUTE = {
ColorMode.COLOR_TEMP: ColorModeAttr(ATTR_COLOR_TEMP, ATTR_COLOR_TEMP),
ColorMode.COLOR_TEMP: ColorModeAttr(ATTR_COLOR_TEMP_KELVIN, ATTR_COLOR_TEMP_KELVIN),
ColorMode.HS: ColorModeAttr(ATTR_HS_COLOR, ATTR_HS_COLOR),
ColorMode.RGB: ColorModeAttr(ATTR_RGB_COLOR, ATTR_RGB_COLOR),
ColorMode.RGBW: ColorModeAttr(ATTR_RGBW_COLOR, ATTR_RGBW_COLOR),
@ -124,13 +127,25 @@ async def _async_reproduce_state(
color_mode = state.attributes[ATTR_COLOR_MODE]
if cm_attr := COLOR_MODE_TO_ATTRIBUTE.get(color_mode):
if (cm_attr_state := state.attributes.get(cm_attr.state_attr)) is None:
if (
color_mode != ColorMode.COLOR_TEMP
or (mireds := state.attributes.get(ATTR_COLOR_TEMP)) is None
):
_LOGGER.warning(
"Color mode %s specified but attribute %s missing for: %s",
color_mode,
cm_attr.state_attr,
state.entity_id,
)
return
_LOGGER.warning(
"Color mode %s specified but attribute %s missing for: %s",
"Color mode %s specified but attribute %s missing for: %s, "
"using color_temp (mireds) as fallback",
color_mode,
cm_attr.state_attr,
state.entity_id,
)
return
cm_attr_state = color_util.color_temperature_mired_to_kelvin(mireds)
service_data[cm_attr.parameter] = cm_attr_state
else:
# Fall back to Choosing the first color that is specified

View File

@ -10,7 +10,7 @@ from tests.common import async_mock_service
VALID_BRIGHTNESS = {"brightness": 180}
VALID_EFFECT = {"effect": "random"}
VALID_COLOR_TEMP = {"color_temp": 240}
VALID_COLOR_TEMP_KELVIN = {"color_temp_kelvin": 4200}
VALID_HS_COLOR = {"hs_color": (345, 75)}
VALID_RGB_COLOR = {"rgb_color": (255, 63, 111)}
VALID_RGBW_COLOR = {"rgbw_color": (255, 63, 111, 10)}
@ -19,7 +19,7 @@ VALID_XY_COLOR = {"xy_color": (0.59, 0.274)}
NONE_BRIGHTNESS = {"brightness": None}
NONE_EFFECT = {"effect": None}
NONE_COLOR_TEMP = {"color_temp": None}
NONE_COLOR_TEMP_KELVIN = {"color_temp_kelvin": None}
NONE_HS_COLOR = {"hs_color": None}
NONE_RGB_COLOR = {"rgb_color": None}
NONE_RGBW_COLOR = {"rgbw_color": None}
@ -34,7 +34,7 @@ async def test_reproducing_states(
hass.states.async_set("light.entity_off", "off", {})
hass.states.async_set("light.entity_bright", "on", VALID_BRIGHTNESS)
hass.states.async_set("light.entity_effect", "on", VALID_EFFECT)
hass.states.async_set("light.entity_temp", "on", VALID_COLOR_TEMP)
hass.states.async_set("light.entity_temp", "on", VALID_COLOR_TEMP_KELVIN)
hass.states.async_set("light.entity_hs", "on", VALID_HS_COLOR)
hass.states.async_set("light.entity_rgb", "on", VALID_RGB_COLOR)
hass.states.async_set("light.entity_xy", "on", VALID_XY_COLOR)
@ -49,7 +49,7 @@ async def test_reproducing_states(
State("light.entity_off", "off"),
State("light.entity_bright", "on", VALID_BRIGHTNESS),
State("light.entity_effect", "on", VALID_EFFECT),
State("light.entity_temp", "on", VALID_COLOR_TEMP),
State("light.entity_temp", "on", VALID_COLOR_TEMP_KELVIN),
State("light.entity_hs", "on", VALID_HS_COLOR),
State("light.entity_rgb", "on", VALID_RGB_COLOR),
State("light.entity_xy", "on", VALID_XY_COLOR),
@ -73,7 +73,7 @@ async def test_reproducing_states(
State("light.entity_xy", "off"),
State("light.entity_off", "on", VALID_BRIGHTNESS),
State("light.entity_bright", "on", VALID_EFFECT),
State("light.entity_effect", "on", VALID_COLOR_TEMP),
State("light.entity_effect", "on", VALID_COLOR_TEMP_KELVIN),
State("light.entity_temp", "on", VALID_HS_COLOR),
State("light.entity_hs", "on", VALID_RGB_COLOR),
State("light.entity_rgb", "on", VALID_XY_COLOR),
@ -92,7 +92,7 @@ async def test_reproducing_states(
expected_bright["entity_id"] = "light.entity_bright"
expected_calls.append(expected_bright)
expected_effect = dict(VALID_COLOR_TEMP)
expected_effect = dict(VALID_COLOR_TEMP_KELVIN)
expected_effect["entity_id"] = "light.entity_effect"
expected_calls.append(expected_effect)
@ -146,7 +146,7 @@ async def test_filter_color_modes(
"""Test filtering of parameters according to color mode."""
hass.states.async_set("light.entity", "off", {})
all_colors = {
**VALID_COLOR_TEMP,
**VALID_COLOR_TEMP_KELVIN,
**VALID_HS_COLOR,
**VALID_RGB_COLOR,
**VALID_RGBW_COLOR,
@ -162,7 +162,7 @@ async def test_filter_color_modes(
)
expected_map = {
light.ColorMode.COLOR_TEMP: {**VALID_BRIGHTNESS, **VALID_COLOR_TEMP},
light.ColorMode.COLOR_TEMP: {**VALID_BRIGHTNESS, **VALID_COLOR_TEMP_KELVIN},
light.ColorMode.BRIGHTNESS: VALID_BRIGHTNESS,
light.ColorMode.HS: {**VALID_BRIGHTNESS, **VALID_HS_COLOR},
light.ColorMode.ONOFF: {**VALID_BRIGHTNESS},
@ -201,13 +201,14 @@ async def test_filter_color_modes_missing_attributes(
hass.states.async_set("light.entity", "off", {})
expected_log = (
"Color mode color_temp specified "
"but attribute color_temp missing for: light.entity"
"but attribute color_temp_kelvin missing for: light.entity"
)
expected_fallback_log = "using color_temp (mireds) as fallback"
turn_on_calls = async_mock_service(hass, "light", "turn_on")
all_colors = {
**VALID_COLOR_TEMP,
**VALID_COLOR_TEMP_KELVIN,
**VALID_HS_COLOR,
**VALID_RGB_COLOR,
**VALID_RGBW_COLOR,
@ -216,9 +217,9 @@ async def test_filter_color_modes_missing_attributes(
**VALID_BRIGHTNESS,
}
# Test missing `color_temp` attribute
# Test missing `color_temp_kelvin` attribute
stored_attributes = {**all_colors}
stored_attributes.pop("color_temp")
stored_attributes.pop("color_temp_kelvin")
caplog.clear()
await async_reproduce_state(
hass,
@ -226,11 +227,25 @@ async def test_filter_color_modes_missing_attributes(
)
assert len(turn_on_calls) == 0
assert expected_log in caplog.text
assert expected_fallback_log not in caplog.text
# Test with correct `color_temp` attribute
stored_attributes["color_temp"] = 240
expected = {"brightness": 180, "color_temp": 240}
# Test with deprecated `color_temp` attribute
stored_attributes["color_temp"] = 250
expected = {"brightness": 180, "color_temp_kelvin": 4000}
caplog.clear()
await async_reproduce_state(
hass,
[State("light.entity", "on", {**stored_attributes, "color_mode": color_mode})],
)
assert len(turn_on_calls) == 1
assert expected_log in caplog.text
assert expected_fallback_log in caplog.text
# Test with correct `color_temp_kelvin` attribute
expected = {"brightness": 180, "color_temp_kelvin": 4200}
caplog.clear()
turn_on_calls.clear()
await async_reproduce_state(
hass,
[State("light.entity", "on", {**all_colors, "color_mode": color_mode})],
@ -239,6 +254,7 @@ async def test_filter_color_modes_missing_attributes(
assert turn_on_calls[0].domain == "light"
assert dict(turn_on_calls[0].data) == {"entity_id": "light.entity", **expected}
assert expected_log not in caplog.text
assert expected_fallback_log not in caplog.text
@pytest.mark.parametrize(
@ -246,7 +262,7 @@ async def test_filter_color_modes_missing_attributes(
[
NONE_BRIGHTNESS,
NONE_EFFECT,
NONE_COLOR_TEMP,
NONE_COLOR_TEMP_KELVIN,
NONE_HS_COLOR,
NONE_RGB_COLOR,
NONE_RGBW_COLOR,