mirror of
https://github.com/Electric-Special/ha-core.git
synced 2026-03-21 05:06:13 +01:00
Raise on missing supported color modes (#162717)
This commit is contained in:
@@ -363,14 +363,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
||||
):
|
||||
profiles.apply_default(light.entity_id, light.is_on, params)
|
||||
|
||||
legacy_supported_color_modes = light._light_internal_supported_color_modes # noqa: SLF001
|
||||
supported_color_modes = light.supported_color_modes
|
||||
supported_color_modes = light._light_internal_supported_color_modes # noqa: SLF001
|
||||
|
||||
# If a color temperature is specified, emulate it if not supported by the light
|
||||
if ATTR_COLOR_TEMP_KELVIN in params:
|
||||
if (
|
||||
supported_color_modes
|
||||
and ColorMode.COLOR_TEMP not in supported_color_modes
|
||||
ColorMode.COLOR_TEMP not in supported_color_modes
|
||||
and ColorMode.RGBWW in supported_color_modes
|
||||
):
|
||||
color_temp = params.pop(ATTR_COLOR_TEMP_KELVIN)
|
||||
@@ -381,35 +379,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
||||
light.min_color_temp_kelvin,
|
||||
light.max_color_temp_kelvin,
|
||||
)
|
||||
elif ColorMode.COLOR_TEMP not in legacy_supported_color_modes:
|
||||
elif ColorMode.COLOR_TEMP not in supported_color_modes:
|
||||
color_temp = params.pop(ATTR_COLOR_TEMP_KELVIN)
|
||||
if color_supported(legacy_supported_color_modes):
|
||||
if color_supported(supported_color_modes):
|
||||
params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs(
|
||||
color_temp
|
||||
)
|
||||
|
||||
# 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
|
||||
rgb_color: tuple[int, int, int] | None
|
||||
rgbww_color: tuple[int, int, int, int, int] | None
|
||||
if not supported_color_modes:
|
||||
if (rgb_color := params.pop(ATTR_RGB_COLOR, None)) is not None:
|
||||
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||
elif (xy_color := params.pop(ATTR_XY_COLOR, None)) is not None:
|
||||
params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color)
|
||||
elif (rgbw_color := params.pop(ATTR_RGBW_COLOR, None)) is not None:
|
||||
rgb_color = color_util.color_rgbw_to_rgb(*rgbw_color)
|
||||
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||
elif (rgbww_color := params.pop(ATTR_RGBWW_COLOR, None)) is not None:
|
||||
# https://github.com/python/mypy/issues/13673
|
||||
rgb_color = color_util.color_rgbww_to_rgb( # type: ignore[call-arg]
|
||||
*rgbww_color,
|
||||
light.min_color_temp_kelvin,
|
||||
light.max_color_temp_kelvin,
|
||||
)
|
||||
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||
elif ATTR_HS_COLOR in params and ColorMode.HS not in supported_color_modes:
|
||||
if ATTR_HS_COLOR in params and ColorMode.HS not in supported_color_modes:
|
||||
hs_color = params.pop(ATTR_HS_COLOR)
|
||||
if ColorMode.RGB in supported_color_modes:
|
||||
params[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color)
|
||||
@@ -517,11 +497,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
||||
params[ATTR_WHITE] = light.brightness
|
||||
|
||||
# If both white and brightness are specified, override white
|
||||
if (
|
||||
supported_color_modes
|
||||
and ATTR_WHITE in params
|
||||
and ColorMode.WHITE in supported_color_modes
|
||||
):
|
||||
if ATTR_WHITE in params and ColorMode.WHITE in supported_color_modes:
|
||||
params[ATTR_WHITE] = params.pop(ATTR_BRIGHTNESS, params[ATTR_WHITE])
|
||||
|
||||
# Remove deprecated white value if the light supports color mode
|
||||
@@ -793,8 +769,6 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
_attr_supported_features: LightEntityFeature = LightEntityFeature(0)
|
||||
_attr_xy_color: tuple[float, float] | None = None
|
||||
|
||||
__color_mode_reported = False
|
||||
|
||||
@cached_property
|
||||
def brightness(self) -> int | None:
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
@@ -1007,10 +981,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""Return state attributes."""
|
||||
data: dict[str, Any] = {}
|
||||
supported_features = self.supported_features
|
||||
supported_color_modes = self.supported_color_modes
|
||||
legacy_supported_color_modes = (
|
||||
supported_color_modes or self._light_internal_supported_color_modes
|
||||
)
|
||||
supported_color_modes = self._light_internal_supported_color_modes
|
||||
|
||||
_is_on = self.is_on
|
||||
color_mode = self.color_mode if _is_on else None
|
||||
@@ -1025,7 +996,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
effect = self.effect
|
||||
data[ATTR_EFFECT] = effect
|
||||
|
||||
self.__validate_color_mode(color_mode, legacy_supported_color_modes, effect)
|
||||
self.__validate_color_mode(color_mode, supported_color_modes, effect)
|
||||
|
||||
data[ATTR_COLOR_MODE] = color_mode
|
||||
|
||||
@@ -1041,15 +1012,15 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
else:
|
||||
data[ATTR_COLOR_TEMP_KELVIN] = None
|
||||
|
||||
if color_supported(legacy_supported_color_modes) or color_temp_supported(
|
||||
legacy_supported_color_modes
|
||||
if color_supported(supported_color_modes) or color_temp_supported(
|
||||
supported_color_modes
|
||||
):
|
||||
data[ATTR_HS_COLOR] = None
|
||||
data[ATTR_RGB_COLOR] = None
|
||||
data[ATTR_XY_COLOR] = None
|
||||
if ColorMode.RGBW in legacy_supported_color_modes:
|
||||
if ColorMode.RGBW in supported_color_modes:
|
||||
data[ATTR_RGBW_COLOR] = None
|
||||
if ColorMode.RGBWW in legacy_supported_color_modes:
|
||||
if ColorMode.RGBWW in supported_color_modes:
|
||||
data[ATTR_RGBWW_COLOR] = None
|
||||
if color_mode:
|
||||
data.update(self._light_internal_convert_color(color_mode))
|
||||
@@ -1058,27 +1029,13 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
|
||||
@property
|
||||
def _light_internal_supported_color_modes(self) -> set[ColorMode]:
|
||||
"""Calculate supported color modes with backwards compatibility."""
|
||||
if (_supported_color_modes := self.supported_color_modes) is not None:
|
||||
self.__validate_supported_color_modes(_supported_color_modes)
|
||||
return _supported_color_modes
|
||||
|
||||
# Backwards compatibility for supported_color_modes added in 2021.4
|
||||
# Warning added in 2024.3, remove in 2025.3
|
||||
if not self.__color_mode_reported and self.__should_report_light_issue():
|
||||
self.__color_mode_reported = True
|
||||
report_issue = self._suggest_report_issue()
|
||||
_LOGGER.warning(
|
||||
(
|
||||
"%s (%s) does not set supported color modes, this will stop working"
|
||||
" in Home Assistant Core 2025.3, please %s"
|
||||
),
|
||||
self.entity_id,
|
||||
type(self),
|
||||
report_issue,
|
||||
"""Get validated supported color modes."""
|
||||
if (_supported_color_modes := self.supported_color_modes) is None:
|
||||
raise HomeAssistantError(
|
||||
f"{self.entity_id} ({type(self)}) does not set supported color modes"
|
||||
)
|
||||
|
||||
return {ColorMode.ONOFF}
|
||||
self.__validate_supported_color_modes(_supported_color_modes)
|
||||
return _supported_color_modes
|
||||
|
||||
@cached_property
|
||||
def supported_color_modes(self) -> set[ColorMode] | None:
|
||||
@@ -1089,10 +1046,3 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
def supported_features(self) -> LightEntityFeature:
|
||||
"""Flag supported features."""
|
||||
return self._attr_supported_features
|
||||
|
||||
def __should_report_light_issue(self) -> bool:
|
||||
"""Return if light color mode issues should be reported."""
|
||||
if not self.platform:
|
||||
return True
|
||||
# philips_js has known issues, we don't need users to open issues
|
||||
return self.platform.platform_name != "philips_js"
|
||||
|
||||
@@ -2388,7 +2388,7 @@ async def test_report_no_color_mode(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("color_mode", "supported_color_modes", "warning_expected"),
|
||||
("color_mode", "supported_color_modes", "error_expected"),
|
||||
[
|
||||
(light.ColorMode.ONOFF, None, True),
|
||||
(light.ColorMode.ONOFF, {light.ColorMode.ONOFF}, False),
|
||||
@@ -2399,7 +2399,7 @@ async def test_report_no_color_modes(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
color_mode: str,
|
||||
supported_color_modes: set[str],
|
||||
warning_expected: bool,
|
||||
error_expected: bool,
|
||||
) -> None:
|
||||
"""Test a light setting no color mode."""
|
||||
|
||||
@@ -2412,9 +2412,13 @@ async def test_report_no_color_modes(
|
||||
entity = MockLightEntityEntity()
|
||||
platform = MockEntityPlatform(hass, domain="test", platform_name="test")
|
||||
await platform.async_add_entities([entity])
|
||||
entity._async_calculate_state()
|
||||
expected_warning = "does not set supported color modes"
|
||||
assert (expected_warning in caplog.text) is warning_expected
|
||||
raised_error = ""
|
||||
try:
|
||||
entity._async_calculate_state()
|
||||
except HomeAssistantError as err:
|
||||
raised_error = str(err)
|
||||
expected_error = "does not set supported color modes"
|
||||
assert (expected_error in raised_error) is error_expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user