Migrate hue lights to use Kelvin (#132835)

This commit is contained in:
epenet 2024-12-10 13:55:28 +01:00 committed by GitHub
parent 6f3a230524
commit 416a4c02b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 51 deletions

View File

@ -12,7 +12,7 @@ import aiohue
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP, ATTR_COLOR_TEMP_KELVIN,
ATTR_EFFECT, ATTR_EFFECT,
ATTR_FLASH, ATTR_FLASH,
ATTR_HS_COLOR, ATTR_HS_COLOR,
@ -35,7 +35,7 @@ from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator, DataUpdateCoordinator,
UpdateFailed, UpdateFailed,
) )
from homeassistant.util import color from homeassistant.util import color as color_util
from ..bridge import HueBridge from ..bridge import HueBridge
from ..const import ( from ..const import (
@ -362,7 +362,7 @@ class HueLight(CoordinatorEntity, LightEntity):
"bulb in the Philips Hue App." "bulb in the Philips Hue App."
) )
LOGGER.warning(err, self.name) LOGGER.warning(err, self.name)
if self.gamut and not color.check_valid_gamut(self.gamut): if self.gamut and not color_util.check_valid_gamut(self.gamut):
err = "Color gamut of %s: %s, not valid, setting gamut to None." err = "Color gamut of %s: %s, not valid, setting gamut to None."
LOGGER.debug(err, self.name, str(self.gamut)) LOGGER.debug(err, self.name, str(self.gamut))
self.gamut_typ = GAMUT_TYPE_UNAVAILABLE self.gamut_typ = GAMUT_TYPE_UNAVAILABLE
@ -427,49 +427,50 @@ class HueLight(CoordinatorEntity, LightEntity):
source = self.light.action if self.is_group else self.light.state source = self.light.action if self.is_group else self.light.state
if mode in ("xy", "hs") and "xy" in source: if mode in ("xy", "hs") and "xy" in source:
return color.color_xy_to_hs(*source["xy"], self.gamut) return color_util.color_xy_to_hs(*source["xy"], self.gamut)
return None return None
@property @property
def color_temp(self): def color_temp_kelvin(self) -> int | None:
"""Return the CT color value.""" """Return the color temperature value in Kelvin."""
# Don't return color temperature unless in color temperature mode # Don't return color temperature unless in color temperature mode
if self._color_mode != "ct": if self._color_mode != "ct":
return None return None
if self.is_group: ct = (
return self.light.action.get("ct") self.light.action.get("ct") if self.is_group else self.light.state.get("ct")
return self.light.state.get("ct") )
return color_util.color_temperature_mired_to_kelvin(ct) if ct else None
@property @property
def min_mireds(self): def max_color_temp_kelvin(self) -> int:
"""Return the coldest color_temp that this light supports.""" """Return the coldest color_temp_kelvin that this light supports."""
if self.is_group: if self.is_group:
return super().min_mireds return super().max_color_temp_kelvin
min_mireds = self.light.controlcapabilities.get("ct", {}).get("min") min_mireds = self.light.controlcapabilities.get("ct", {}).get("min")
# We filter out '0' too, which can be incorrectly reported by 3rd party buls # We filter out '0' too, which can be incorrectly reported by 3rd party buls
if not min_mireds: if not min_mireds:
return super().min_mireds return super().max_color_temp_kelvin
return min_mireds return color_util.color_temperature_mired_to_kelvin(min_mireds)
@property @property
def max_mireds(self): def min_color_temp_kelvin(self) -> int:
"""Return the warmest color_temp that this light supports.""" """Return the warmest color_temp_kelvin that this light supports."""
if self.is_group: if self.is_group:
return super().max_mireds return super().min_color_temp_kelvin
if self.is_livarno: if self.is_livarno:
return 500 return 500
max_mireds = self.light.controlcapabilities.get("ct", {}).get("max") max_mireds = self.light.controlcapabilities.get("ct", {}).get("max")
if not max_mireds: if not max_mireds:
return super().max_mireds return super().min_color_temp_kelvin
return max_mireds return color_util.color_temperature_mired_to_kelvin(max_mireds)
@property @property
def is_on(self): def is_on(self):
@ -541,11 +542,14 @@ class HueLight(CoordinatorEntity, LightEntity):
# Philips hue bulb models respond differently to hue/sat # Philips hue bulb models respond differently to hue/sat
# requests, so we convert to XY first to ensure a consistent # requests, so we convert to XY first to ensure a consistent
# color. # color.
xy_color = color.color_hs_to_xy(*kwargs[ATTR_HS_COLOR], self.gamut) xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR], self.gamut)
command["xy"] = xy_color command["xy"] = xy_color
elif ATTR_COLOR_TEMP in kwargs: elif ATTR_COLOR_TEMP_KELVIN in kwargs:
temp = kwargs[ATTR_COLOR_TEMP] temp_k = max(
command["ct"] = max(self.min_mireds, min(temp, self.max_mireds)) self.min_color_temp_kelvin,
min(self.max_color_temp_kelvin, kwargs[ATTR_COLOR_TEMP_KELVIN]),
)
command["ct"] = color_util.color_temperature_kelvin_to_mired(temp_k)
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
command["bri"] = hass_to_hue_brightness(kwargs[ATTR_BRIGHTNESS]) command["bri"] = hass_to_hue_brightness(kwargs[ATTR_BRIGHTNESS])

View File

@ -12,7 +12,7 @@ from aiohue.v2.models.feature import DynamicStatus
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP, ATTR_COLOR_TEMP_KELVIN,
ATTR_FLASH, ATTR_FLASH,
ATTR_TRANSITION, ATTR_TRANSITION,
ATTR_XY_COLOR, ATTR_XY_COLOR,
@ -27,6 +27,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.helpers.entity_registry as er import homeassistant.helpers.entity_registry as er
from homeassistant.util import color as color_util
from ..bridge import HueBridge from ..bridge import HueBridge
from ..const import DOMAIN from ..const import DOMAIN
@ -157,7 +158,7 @@ class GroupedHueLight(HueBaseEntity, LightEntity):
"""Turn the grouped_light on.""" """Turn the grouped_light on."""
transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
xy_color = kwargs.get(ATTR_XY_COLOR) xy_color = kwargs.get(ATTR_XY_COLOR)
color_temp = normalize_hue_colortemp(kwargs.get(ATTR_COLOR_TEMP)) color_temp = normalize_hue_colortemp(kwargs.get(ATTR_COLOR_TEMP_KELVIN))
brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS)) brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS))
flash = kwargs.get(ATTR_FLASH) flash = kwargs.get(ATTR_FLASH)
@ -235,9 +236,21 @@ class GroupedHueLight(HueBaseEntity, LightEntity):
if color_temp := light.color_temperature: if color_temp := light.color_temperature:
lights_with_color_temp_support += 1 lights_with_color_temp_support += 1
# we assume mired values from the first capable light # we assume mired values from the first capable light
self._attr_color_temp = color_temp.mirek self._attr_color_temp_kelvin = (
self._attr_max_mireds = color_temp.mirek_schema.mirek_maximum color_util.color_temperature_mired_to_kelvin(color_temp.mirek)
self._attr_min_mireds = color_temp.mirek_schema.mirek_minimum if color_temp.mirek
else None
)
self._attr_min_color_temp_kelvin = (
color_util.color_temperature_mired_to_kelvin(
color_temp.mirek_schema.mirek_maximum
)
)
self._attr_max_color_temp_kelvin = (
color_util.color_temperature_mired_to_kelvin(
color_temp.mirek_schema.mirek_minimum
)
)
if color_temp.mirek is not None and color_temp.mirek_valid: if color_temp.mirek is not None and color_temp.mirek_valid:
lights_in_colortemp_mode += 1 lights_in_colortemp_mode += 1
if color := light.color: if color := light.color:

View File

@ -2,6 +2,8 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.util import color as color_util
def normalize_hue_brightness(brightness: float | None) -> float | None: def normalize_hue_brightness(brightness: float | None) -> float | None:
"""Return calculated brightness values.""" """Return calculated brightness values."""
@ -21,10 +23,11 @@ def normalize_hue_transition(transition: float | None) -> float | None:
return transition return transition
def normalize_hue_colortemp(colortemp: int | None) -> int | None: def normalize_hue_colortemp(colortemp_k: int | None) -> int | None:
"""Return color temperature within Hue's ranges.""" """Return color temperature within Hue's ranges."""
if colortemp is not None: if colortemp_k is None:
# Hue only accepts a range between 153..500 return None
colortemp = min(colortemp, 500) colortemp = color_util.color_temperature_kelvin_to_mired(colortemp_k)
colortemp = max(colortemp, 153) # Hue only accepts a range between 153..500
return colortemp colortemp = min(colortemp, 500)
return max(colortemp, 153)

View File

@ -13,7 +13,7 @@ from aiohue.v2.models.light import Light
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP, ATTR_COLOR_TEMP_KELVIN,
ATTR_EFFECT, ATTR_EFFECT,
ATTR_FLASH, ATTR_FLASH,
ATTR_TRANSITION, ATTR_TRANSITION,
@ -28,6 +28,7 @@ from homeassistant.components.light import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import color as color_util
from ..bridge import HueBridge from ..bridge import HueBridge
from ..const import DOMAIN from ..const import DOMAIN
@ -39,9 +40,9 @@ from .helpers import (
) )
EFFECT_NONE = "None" EFFECT_NONE = "None"
FALLBACK_MIN_MIREDS = 153 # 6500 K FALLBACK_MIN_KELVIN = 6500
FALLBACK_MAX_MIREDS = 500 # 2000 K FALLBACK_MAX_KELVIN = 2000
FALLBACK_MIREDS = 173 # halfway FALLBACK_KELVIN = 5800 # halfway
async def async_setup_entry( async def async_setup_entry(
@ -164,28 +165,32 @@ class HueLight(HueBaseEntity, LightEntity):
return None return None
@property @property
def color_temp(self) -> int: def color_temp_kelvin(self) -> int | None:
"""Return the color temperature.""" """Return the color temperature value in Kelvin."""
if color_temp := self.resource.color_temperature: if color_temp := self.resource.color_temperature:
return color_temp.mirek return color_util.color_temperature_mired_to_kelvin(color_temp.mirek)
# return a fallback value to prevent issues with mired->kelvin conversions # return a fallback value to prevent issues with mired->kelvin conversions
return FALLBACK_MIREDS return FALLBACK_KELVIN
@property @property
def min_mireds(self) -> int: def max_color_temp_kelvin(self) -> int:
"""Return the coldest color_temp that this light supports.""" """Return the coldest color_temp_kelvin that this light supports."""
if color_temp := self.resource.color_temperature: if color_temp := self.resource.color_temperature:
return color_temp.mirek_schema.mirek_minimum return color_util.color_temperature_mired_to_kelvin(
color_temp.mirek_schema.mirek_minimum
)
# return a fallback value to prevent issues with mired->kelvin conversions # return a fallback value to prevent issues with mired->kelvin conversions
return FALLBACK_MIN_MIREDS return FALLBACK_MAX_KELVIN
@property @property
def max_mireds(self) -> int: def min_color_temp_kelvin(self) -> int:
"""Return the warmest color_temp that this light supports.""" """Return the warmest color_temp_kelvin that this light supports."""
if color_temp := self.resource.color_temperature: if color_temp := self.resource.color_temperature:
return color_temp.mirek_schema.mirek_maximum return color_util.color_temperature_mired_to_kelvin(
color_temp.mirek_schema.mirek_maximum
)
# return a fallback value to prevent issues with mired->kelvin conversions # return a fallback value to prevent issues with mired->kelvin conversions
return FALLBACK_MAX_MIREDS return FALLBACK_MIN_KELVIN
@property @property
def extra_state_attributes(self) -> dict[str, str] | None: def extra_state_attributes(self) -> dict[str, str] | None:
@ -210,7 +215,7 @@ class HueLight(HueBaseEntity, LightEntity):
"""Turn the device on.""" """Turn the device on."""
transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
xy_color = kwargs.get(ATTR_XY_COLOR) xy_color = kwargs.get(ATTR_XY_COLOR)
color_temp = normalize_hue_colortemp(kwargs.get(ATTR_COLOR_TEMP)) color_temp = normalize_hue_colortemp(kwargs.get(ATTR_COLOR_TEMP_KELVIN))
brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS)) brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS))
if self._last_brightness and brightness is None: if self._last_brightness and brightness is None:
# The Hue bridge sets the brightness to 1% when turning on a bulb # The Hue bridge sets the brightness to 1% when turning on a bulb