Adjust color_mode checks when lights render effects (#108737)
* Adjust color_mode checks when lights render effects * Improve comment * Avoid calling effect property if light does not support effects * Fix test
This commit is contained in:
parent
431e4b38ac
commit
c3de193e2e
2 changed files with 101 additions and 11 deletions
|
@ -234,6 +234,7 @@ ATTR_EFFECT_LIST = "effect_list"
|
|||
# Apply an effect to the light, can be EFFECT_COLORLOOP.
|
||||
ATTR_EFFECT = "effect"
|
||||
EFFECT_COLORLOOP = "colorloop"
|
||||
EFFECT_OFF = "off"
|
||||
EFFECT_RANDOM = "random"
|
||||
EFFECT_WHITE = "white"
|
||||
|
||||
|
@ -1060,6 +1061,51 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||
data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
|
||||
return data
|
||||
|
||||
def __validate_color_mode(
|
||||
self,
|
||||
color_mode: ColorMode | str | None,
|
||||
supported_color_modes: set[ColorMode] | set[str],
|
||||
effect: str | None,
|
||||
) -> None:
|
||||
"""Validate the color mode."""
|
||||
if color_mode is None:
|
||||
# The light is turned off
|
||||
return
|
||||
|
||||
if not effect or effect == EFFECT_OFF:
|
||||
# No effect is active, the light must set color mode to one of the supported
|
||||
# color modes
|
||||
if color_mode in supported_color_modes:
|
||||
return
|
||||
# Increase severity to warning in 2024.3, reject in 2025.3
|
||||
_LOGGER.debug(
|
||||
"%s: set to unsupported color_mode: %s, supported_color_modes: %s",
|
||||
self.entity_id,
|
||||
color_mode,
|
||||
supported_color_modes,
|
||||
)
|
||||
return
|
||||
|
||||
# When an effect is active, the color mode should indicate what adjustments are
|
||||
# supported by the effect. To make this possible, we allow the light to set its
|
||||
# color mode to on_off, and to brightness if the light allows adjusting
|
||||
# brightness, in addition to the otherwise supported color modes.
|
||||
effect_color_modes = supported_color_modes | {ColorMode.ONOFF}
|
||||
if brightness_supported(effect_color_modes):
|
||||
effect_color_modes.add(ColorMode.BRIGHTNESS)
|
||||
|
||||
if color_mode in effect_color_modes:
|
||||
return
|
||||
|
||||
# Increase severity to warning in 2024.3, reject in 2025.3
|
||||
_LOGGER.debug(
|
||||
"%s: set to unsupported color_mode: %s, supported for effect: %s",
|
||||
self.entity_id,
|
||||
color_mode,
|
||||
effect_color_modes,
|
||||
)
|
||||
return
|
||||
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, Any] | None:
|
||||
|
@ -1074,14 +1120,13 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||
_is_on = self.is_on
|
||||
color_mode = self._light_internal_color_mode if _is_on else None
|
||||
|
||||
if color_mode and color_mode not in legacy_supported_color_modes:
|
||||
# Increase severity to warning in 2024.3, reject in 2025.3
|
||||
_LOGGER.debug(
|
||||
"%s: set to unsupported color_mode: %s, supported_color_modes: %s",
|
||||
self.entity_id,
|
||||
color_mode,
|
||||
legacy_supported_color_modes,
|
||||
)
|
||||
effect: str | None
|
||||
if LightEntityFeature.EFFECT in supported_features:
|
||||
data[ATTR_EFFECT] = effect = self.effect if _is_on else None
|
||||
else:
|
||||
effect = None
|
||||
|
||||
self.__validate_color_mode(color_mode, legacy_supported_color_modes, effect)
|
||||
|
||||
data[ATTR_COLOR_MODE] = color_mode
|
||||
|
||||
|
@ -1140,9 +1185,6 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||
if color_mode:
|
||||
data.update(self._light_internal_convert_color(color_mode))
|
||||
|
||||
if LightEntityFeature.EFFECT in supported_features:
|
||||
data[ATTR_EFFECT] = self.effect if _is_on else None
|
||||
|
||||
return data
|
||||
|
||||
@property
|
||||
|
|
|
@ -2610,3 +2610,51 @@ def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) ->
|
|||
caplog.clear()
|
||||
assert entity.supported_features_compat is light.LightEntityFeature(1)
|
||||
assert "is using deprecated supported features values" not in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("color_mode", "supported_color_modes", "effect", "warning_expected"),
|
||||
[
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.ONOFF}, None, False),
|
||||
# A light which supports brightness should not set its color mode to on_off
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.BRIGHTNESS}, None, True),
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.BRIGHTNESS}, light.EFFECT_OFF, True),
|
||||
# Unless it renders an effect
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.BRIGHTNESS}, "effect", False),
|
||||
(light.ColorMode.BRIGHTNESS, {light.ColorMode.BRIGHTNESS}, "effect", False),
|
||||
(light.ColorMode.BRIGHTNESS, {light.ColorMode.BRIGHTNESS}, None, False),
|
||||
# A light which supports color should not set its color mode to brightnes
|
||||
(light.ColorMode.BRIGHTNESS, {light.ColorMode.HS}, None, True),
|
||||
(light.ColorMode.BRIGHTNESS, {light.ColorMode.HS}, light.EFFECT_OFF, True),
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.HS}, None, True),
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.HS}, light.EFFECT_OFF, True),
|
||||
# Unless it renders an effect
|
||||
(light.ColorMode.BRIGHTNESS, {light.ColorMode.HS}, "effect", False),
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.HS}, "effect", False),
|
||||
(light.ColorMode.HS, {light.ColorMode.HS}, "effect", False),
|
||||
# A light which supports brightness should not set its color mode to hs even
|
||||
# if rendering an effect
|
||||
(light.ColorMode.HS, {light.ColorMode.BRIGHTNESS}, "effect", True),
|
||||
],
|
||||
)
|
||||
def test_report_invalid_color_mode(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
color_mode: str,
|
||||
supported_color_modes: set[str],
|
||||
effect: str | None,
|
||||
warning_expected: bool,
|
||||
) -> None:
|
||||
"""Test a light setting an invalid color mode."""
|
||||
|
||||
class MockLightEntityEntity(light.LightEntity):
|
||||
_attr_color_mode = color_mode
|
||||
_attr_effect = effect
|
||||
_attr_is_on = True
|
||||
_attr_supported_features = light.LightEntityFeature.EFFECT
|
||||
_attr_supported_color_modes = supported_color_modes
|
||||
|
||||
entity = MockLightEntityEntity()
|
||||
entity._async_calculate_state()
|
||||
expected_warning = f"set to unsupported color_mode: {color_mode}"
|
||||
assert (expected_warning in caplog.text) is warning_expected
|
||||
|
|
Loading…
Add table
Reference in a new issue