Add warning when light entities do not provide kelvin attributes or properties (#132723)

pull/132680/head^2
epenet 2024-12-13 15:34:02 +01:00 committed by GitHub
parent 067daad70e
commit 8080ad14bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 143 additions and 13 deletions

View File

@ -32,6 +32,7 @@ from homeassistant.helpers.deprecation import (
)
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.frame import ReportBehavior, report_usage
from homeassistant.helpers.typing import ConfigType, VolDictType
from homeassistant.loader import bind_hass
import homeassistant.util.color as color_util
@ -41,6 +42,8 @@ from .const import ( # noqa: F401
COLOR_MODES_COLOR,
DATA_COMPONENT,
DATA_PROFILES,
DEFAULT_MAX_KELVIN,
DEFAULT_MIN_KELVIN,
DOMAIN,
SCAN_INTERVAL,
VALID_COLOR_MODES,
@ -863,17 +866,15 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
entity_description: LightEntityDescription
_attr_brightness: int | None = None
_attr_color_mode: ColorMode | str | None = None
_attr_color_temp: int | None = None
_attr_color_temp_kelvin: int | None = None
_attr_effect_list: list[str] | None = None
_attr_effect: str | None = None
_attr_hs_color: tuple[float, float] | None = None
# Default to the Philips Hue value that HA has always assumed
# https://developers.meethue.com/documentation/core-concepts
# We cannot set defaults without causing breaking changes until mireds
# are fully removed. Until then, developers can explicitly
# use DEFAULT_MIN_KELVIN and DEFAULT_MAX_KELVIN
_attr_max_color_temp_kelvin: int | None = None
_attr_min_color_temp_kelvin: int | None = None
_attr_max_mireds: int = 500 # 2000 K
_attr_min_mireds: int = 153 # 6500 K
_attr_rgb_color: tuple[int, int, int] | None = None
_attr_rgbw_color: tuple[int, int, int, int] | None = None
_attr_rgbww_color: tuple[int, int, int, int, int] | None = None
@ -881,6 +882,11 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_supported_features: LightEntityFeature = LightEntityFeature(0)
_attr_xy_color: tuple[float, float] | None = None
# Deprecated, see https://github.com/home-assistant/core/pull/79591
_attr_color_temp: Final[int | None] = None
_attr_max_mireds: Final[int] = 500 # = 2000 K
_attr_min_mireds: Final[int] = 153 # = 6535.94 K (~ 6500 K)
__color_mode_reported = False
@cached_property
@ -956,32 +962,70 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the rgbww color value [int, int, int, int, int]."""
return self._attr_rgbww_color
@final
@cached_property
def color_temp(self) -> int | None:
"""Return the CT color value in mireds."""
"""Return the CT color value in mireds.
Deprecated, see https://github.com/home-assistant/core/pull/79591
"""
return self._attr_color_temp
@property
def color_temp_kelvin(self) -> int | None:
"""Return the CT color value in Kelvin."""
if self._attr_color_temp_kelvin is None and (color_temp := self.color_temp):
report_usage(
"is using mireds for current light color temperature, when "
"it should be adjusted to use the kelvin attribute "
"`_attr_color_temp_kelvin` or override the kelvin property "
"`color_temp_kelvin` (see "
"https://github.com/home-assistant/core/pull/79591)",
breaks_in_ha_version="2026.1",
core_behavior=ReportBehavior.LOG,
integration_domain=self.platform.platform_name
if self.platform
else None,
exclude_integrations={DOMAIN},
)
return color_util.color_temperature_mired_to_kelvin(color_temp)
return self._attr_color_temp_kelvin
@final
@cached_property
def min_mireds(self) -> int:
"""Return the coldest color_temp that this light supports."""
"""Return the coldest color_temp that this light supports.
Deprecated, see https://github.com/home-assistant/core/pull/79591
"""
return self._attr_min_mireds
@final
@cached_property
def max_mireds(self) -> int:
"""Return the warmest color_temp that this light supports."""
"""Return the warmest color_temp that this light supports.
Deprecated, see https://github.com/home-assistant/core/pull/79591
"""
return self._attr_max_mireds
@property
def min_color_temp_kelvin(self) -> int:
"""Return the warmest color_temp_kelvin that this light supports."""
if self._attr_min_color_temp_kelvin is None:
report_usage(
"is using mireds for warmest light color temperature, when "
"it should be adjusted to use the kelvin attribute "
"`_attr_min_color_temp_kelvin` or override the kelvin property "
"`min_color_temp_kelvin`, possibly with default DEFAULT_MIN_KELVIN "
"(see https://github.com/home-assistant/core/pull/79591)",
breaks_in_ha_version="2026.1",
core_behavior=ReportBehavior.LOG,
integration_domain=self.platform.platform_name
if self.platform
else None,
exclude_integrations={DOMAIN},
)
return color_util.color_temperature_mired_to_kelvin(self.max_mireds)
return self._attr_min_color_temp_kelvin
@ -989,6 +1033,19 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def max_color_temp_kelvin(self) -> int:
"""Return the coldest color_temp_kelvin that this light supports."""
if self._attr_max_color_temp_kelvin is None:
report_usage(
"is using mireds for coldest light color temperature, when "
"it should be adjusted to use the kelvin attribute "
"`_attr_max_color_temp_kelvin` or override the kelvin property "
"`max_color_temp_kelvin`, possibly with default DEFAULT_MAX_KELVIN "
"(see https://github.com/home-assistant/core/pull/79591)",
breaks_in_ha_version="2026.1",
core_behavior=ReportBehavior.LOG,
integration_domain=self.platform.platform_name
if self.platform
else None,
exclude_integrations={DOMAIN},
)
return color_util.color_temperature_mired_to_kelvin(self.min_mireds)
return self._attr_max_color_temp_kelvin

View File

@ -66,3 +66,8 @@ COLOR_MODES_COLOR = {
ColorMode.RGBWW,
ColorMode.XY,
}
# Default to the Philips Hue value that HA has always assumed
# https://developers.meethue.com/documentation/core-concepts
DEFAULT_MIN_KELVIN = 2000 # 500 mireds
DEFAULT_MAX_KELVIN = 6535 # 153 mireds

View File

@ -21,6 +21,8 @@ from homeassistant.components.light import (
ATTR_TRANSITION,
ATTR_WHITE,
ATTR_XY_COLOR,
DEFAULT_MAX_KELVIN,
DEFAULT_MIN_KELVIN,
DOMAIN,
ColorMode,
LightEntity,
@ -153,8 +155,8 @@ TURN_ON_ARG_TO_COLOR_MODE = {
class MockLight(MockToggleEntity, LightEntity):
"""Mock light class."""
_attr_max_color_temp_kelvin = 6500
_attr_min_color_temp_kelvin = 2000
_attr_max_color_temp_kelvin = DEFAULT_MAX_KELVIN
_attr_min_color_temp_kelvin = DEFAULT_MIN_KELVIN
supported_features = LightEntityFeature(0)
brightness = None

View File

@ -20,6 +20,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, Unauthorized
from homeassistant.helpers import frame
from homeassistant.setup import async_setup_component
import homeassistant.util.color as color_util
@ -1209,7 +1210,7 @@ async def test_light_state_off(hass: HomeAssistant) -> None:
"hs_color": None,
"rgb_color": None,
"xy_color": None,
"max_color_temp_kelvin": 6500,
"max_color_temp_kelvin": 6535,
"max_mireds": 500,
"min_color_temp_kelvin": 2000,
"min_mireds": 153,
@ -1842,7 +1843,7 @@ async def test_light_service_call_color_temp_conversion(hass: HomeAssistant) ->
assert entity1.min_mireds == 153
assert entity1.max_mireds == 500
assert entity1.min_color_temp_kelvin == 2000
assert entity1.max_color_temp_kelvin == 6500
assert entity1.max_color_temp_kelvin == 6535
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
await hass.async_block_till_done()
@ -1855,7 +1856,7 @@ async def test_light_service_call_color_temp_conversion(hass: HomeAssistant) ->
assert state.attributes["min_mireds"] == 153
assert state.attributes["max_mireds"] == 500
assert state.attributes["min_color_temp_kelvin"] == 2000
assert state.attributes["max_color_temp_kelvin"] == 6500
assert state.attributes["max_color_temp_kelvin"] == 6535
state = hass.states.get(entity1.entity_id)
assert state.attributes["supported_color_modes"] == [light.ColorMode.RGBWW]
@ -2547,6 +2548,71 @@ def test_report_invalid_color_modes(
assert (expected_warning in caplog.text) is warning_expected
@pytest.mark.parametrize(
("attributes", "expected_warnings", "expected_values"),
[
(
{
"_attr_color_temp_kelvin": 4000,
"_attr_min_color_temp_kelvin": 3000,
"_attr_max_color_temp_kelvin": 5000,
},
{"current": False, "warmest": False, "coldest": False},
# Just highlighting that the attributes match the
# converted kelvin values, not the mired properties
(3000, 4000, 5000, 200, 250, 333, 153, None, 500),
),
(
{"_attr_color_temp": 350, "_attr_min_mireds": 300, "_attr_max_mireds": 400},
{"current": True, "warmest": True, "coldest": True},
(2500, 2857, 3333, 300, 350, 400, 300, 350, 400),
),
(
{},
{"current": False, "warmest": True, "coldest": True},
(2000, None, 6535, 153, None, 500, 153, None, 500),
),
],
ids=["with_kelvin", "with_mired_values", "with_mired_defaults"],
)
@patch.object(frame, "_REPORTED_INTEGRATIONS", set())
def test_missing_kelvin_property_warnings(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
attributes: dict[str, int | None],
expected_warnings: dict[str, bool],
expected_values: tuple[int, int | None, int],
) -> None:
"""Test missing kelvin properties."""
class MockLightEntityEntity(light.LightEntity):
_attr_color_mode = light.ColorMode.COLOR_TEMP
_attr_is_on = True
_attr_supported_features = light.LightEntityFeature.EFFECT
_attr_supported_color_modes = {light.ColorMode.COLOR_TEMP}
platform = MockEntityPlatform(hass, platform_name="test")
entity = MockLightEntityEntity()
for k, v in attributes.items():
setattr(entity, k, v)
state = entity._async_calculate_state()
for warning, expected in expected_warnings.items():
assert (
f"is using mireds for {warning} light color temperature" in caplog.text
) is expected, f"Expected {expected} for '{warning}'"
assert state.attributes[light.ATTR_MIN_COLOR_TEMP_KELVIN] == expected_values[0]
assert state.attributes[light.ATTR_COLOR_TEMP_KELVIN] == expected_values[1]
assert state.attributes[light.ATTR_MAX_COLOR_TEMP_KELVIN] == expected_values[2]
assert state.attributes[light.ATTR_MIN_MIREDS] == expected_values[3]
assert state.attributes[light.ATTR_COLOR_TEMP] == expected_values[4]
assert state.attributes[light.ATTR_MAX_MIREDS] == expected_values[5]
assert entity.min_mireds == expected_values[6]
assert entity.color_temp == expected_values[7]
assert entity.max_mireds == expected_values[8]
@pytest.mark.parametrize(
"module",
[light],