Use Kelvin as the preferred color temperature unit (#79591)

* Use Kelvin as the preferred white temperature unit

* Update homekit

* Adjust tests
This commit is contained in:
Erik Montnemery 2022-10-06 12:22:39 +02:00 committed by GitHub
parent 1e39f42df5
commit 47d0598e75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 325 additions and 135 deletions

View File

@ -193,7 +193,10 @@ class Light(HomeAccessory):
params[ATTR_COLOR_TEMP] = temp params[ATTR_COLOR_TEMP] = temp
elif self.rgbww_supported: elif self.rgbww_supported:
params[ATTR_RGBWW_COLOR] = color_temperature_to_rgbww( params[ATTR_RGBWW_COLOR] = color_temperature_to_rgbww(
temp, bright_val, self.min_mireds, self.max_mireds color_temperature_mired_to_kelvin(temp),
bright_val,
color_temperature_mired_to_kelvin(self.max_mireds),
color_temperature_mired_to_kelvin(self.min_mireds),
) )
elif self.rgbw_supported: elif self.rgbw_supported:
params[ATTR_RGBW_COLOR] = (*(0,) * 3, bright_val) params[ATTR_RGBW_COLOR] = (*(0,) * 3, bright_val)

View File

@ -196,10 +196,13 @@ ATTR_RGBW_COLOR = "rgbw_color"
ATTR_RGBWW_COLOR = "rgbww_color" ATTR_RGBWW_COLOR = "rgbww_color"
ATTR_XY_COLOR = "xy_color" ATTR_XY_COLOR = "xy_color"
ATTR_HS_COLOR = "hs_color" ATTR_HS_COLOR = "hs_color"
ATTR_COLOR_TEMP = "color_temp" ATTR_COLOR_TEMP = "color_temp" # Deprecated in HA Core 2022.11
ATTR_KELVIN = "kelvin" ATTR_KELVIN = "kelvin" # Deprecated in HA Core 2022.11
ATTR_MIN_MIREDS = "min_mireds" ATTR_MIN_MIREDS = "min_mireds" # Deprecated in HA Core 2022.11
ATTR_MAX_MIREDS = "max_mireds" ATTR_MAX_MIREDS = "max_mireds" # Deprecated in HA Core 2022.11
ATTR_COLOR_TEMP_KELVIN = "color_temp_kelvin"
ATTR_MIN_COLOR_TEMP_KELVIN = "min_color_temp_kelvin"
ATTR_MAX_COLOR_TEMP_KELVIN = "max_color_temp_kelvin"
ATTR_COLOR_NAME = "color_name" ATTR_COLOR_NAME = "color_name"
ATTR_WHITE = "white" ATTR_WHITE = "white"
@ -249,6 +252,7 @@ LIGHT_TURN_ON_SCHEMA = {
vol.Exclusive(ATTR_COLOR_TEMP, COLOR_GROUP): vol.All( vol.Exclusive(ATTR_COLOR_TEMP, COLOR_GROUP): vol.All(
vol.Coerce(int), vol.Range(min=1) vol.Coerce(int), vol.Range(min=1)
), ),
vol.Exclusive(ATTR_COLOR_TEMP_KELVIN, COLOR_GROUP): cv.positive_int,
vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): cv.positive_int, vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): cv.positive_int,
vol.Exclusive(ATTR_HS_COLOR, COLOR_GROUP): vol.All( vol.Exclusive(ATTR_HS_COLOR, COLOR_GROUP): vol.All(
vol.Coerce(tuple), vol.Coerce(tuple),
@ -309,9 +313,20 @@ def preprocess_turn_on_alternatives(
_LOGGER.warning("Got unknown color %s, falling back to white", color_name) _LOGGER.warning("Got unknown color %s, falling back to white", color_name)
params[ATTR_RGB_COLOR] = (255, 255, 255) params[ATTR_RGB_COLOR] = (255, 255, 255)
if (mired := params.pop(ATTR_COLOR_TEMP, None)) is not None:
kelvin = color_util.color_temperature_mired_to_kelvin(mired)
params[ATTR_COLOR_TEMP] = int(mired)
params[ATTR_COLOR_TEMP_KELVIN] = int(kelvin)
if (kelvin := params.pop(ATTR_KELVIN, None)) is not None: if (kelvin := params.pop(ATTR_KELVIN, None)) is not None:
mired = color_util.color_temperature_kelvin_to_mired(kelvin) mired = color_util.color_temperature_kelvin_to_mired(kelvin)
params[ATTR_COLOR_TEMP] = int(mired) params[ATTR_COLOR_TEMP] = int(mired)
params[ATTR_COLOR_TEMP_KELVIN] = int(kelvin)
if (kelvin := params.pop(ATTR_COLOR_TEMP_KELVIN, None)) is not None:
mired = color_util.color_temperature_kelvin_to_mired(kelvin)
params[ATTR_COLOR_TEMP] = int(mired)
params[ATTR_COLOR_TEMP_KELVIN] = int(kelvin)
brightness_pct = params.pop(ATTR_BRIGHTNESS_PCT, None) brightness_pct = params.pop(ATTR_BRIGHTNESS_PCT, None)
if brightness_pct is not None: if brightness_pct is not None:
@ -350,6 +365,7 @@ def filter_turn_on_params(light: LightEntity, params: dict[str, Any]) -> dict[st
params.pop(ATTR_BRIGHTNESS, None) params.pop(ATTR_BRIGHTNESS, None)
if ColorMode.COLOR_TEMP not in supported_color_modes: if ColorMode.COLOR_TEMP not in supported_color_modes:
params.pop(ATTR_COLOR_TEMP, None) params.pop(ATTR_COLOR_TEMP, None)
params.pop(ATTR_COLOR_TEMP_KELVIN, None)
if ColorMode.HS not in supported_color_modes: if ColorMode.HS not in supported_color_modes:
params.pop(ATTR_HS_COLOR, None) params.pop(ATTR_HS_COLOR, None)
if ColorMode.RGB not in supported_color_modes: if ColorMode.RGB not in supported_color_modes:
@ -424,22 +440,28 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
supported_color_modes = light.supported_color_modes supported_color_modes = light.supported_color_modes
# If a color temperature is specified, emulate it if not supported by the light # If a color temperature is specified, emulate it if not supported by the light
if ATTR_COLOR_TEMP in params: if ATTR_COLOR_TEMP_KELVIN in params:
if ( if (
supported_color_modes supported_color_modes
and ColorMode.COLOR_TEMP not in supported_color_modes and ColorMode.COLOR_TEMP not in supported_color_modes
and ColorMode.RGBWW in supported_color_modes and ColorMode.RGBWW in supported_color_modes
): ):
color_temp = params.pop(ATTR_COLOR_TEMP) params.pop(ATTR_COLOR_TEMP)
color_temp = params.pop(ATTR_COLOR_TEMP_KELVIN)
brightness = params.get(ATTR_BRIGHTNESS, light.brightness) brightness = params.get(ATTR_BRIGHTNESS, light.brightness)
params[ATTR_RGBWW_COLOR] = color_util.color_temperature_to_rgbww( params[ATTR_RGBWW_COLOR] = color_util.color_temperature_to_rgbww(
color_temp, brightness, light.min_mireds, light.max_mireds color_temp,
brightness,
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 legacy_supported_color_modes:
color_temp = params.pop(ATTR_COLOR_TEMP) params.pop(ATTR_COLOR_TEMP)
color_temp = params.pop(ATTR_COLOR_TEMP_KELVIN)
if color_supported(legacy_supported_color_modes): if color_supported(legacy_supported_color_modes):
temp_k = color_util.color_temperature_mired_to_kelvin(color_temp) params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs(
params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs(temp_k) color_temp
)
# If a color is specified, convert to the color space supported by the light # 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 # Backwards compatibility: Fall back to hs color if light.supported_color_modes
@ -457,7 +479,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
elif (rgbww_color := params.pop(ATTR_RGBWW_COLOR, None)) is not None: elif (rgbww_color := params.pop(ATTR_RGBWW_COLOR, None)) is not None:
# https://github.com/python/mypy/issues/13673 # https://github.com/python/mypy/issues/13673
rgb_color = color_util.color_rgbww_to_rgb( # type: ignore[call-arg] rgb_color = color_util.color_rgbww_to_rgb( # type: ignore[call-arg]
*rgbww_color, light.min_mireds, light.max_mireds *rgbww_color,
light.min_color_temp_kelvin,
light.max_color_temp_kelvin,
) )
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) 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: elif ATTR_HS_COLOR in params and ColorMode.HS not in supported_color_modes:
@ -470,7 +494,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
elif ColorMode.RGBWW in supported_color_modes: elif ColorMode.RGBWW in supported_color_modes:
rgb_color = color_util.color_hs_to_RGB(*hs_color) rgb_color = color_util.color_hs_to_RGB(*hs_color)
params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww(
*rgb_color, light.min_mireds, light.max_mireds *rgb_color, light.min_color_temp_kelvin, light.max_color_temp_kelvin
) )
elif ColorMode.XY in supported_color_modes: elif ColorMode.XY in supported_color_modes:
params[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color) params[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
@ -481,7 +505,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
elif ColorMode.RGBWW in supported_color_modes: elif ColorMode.RGBWW in supported_color_modes:
# https://github.com/python/mypy/issues/13673 # https://github.com/python/mypy/issues/13673
params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( # type: ignore[call-arg] params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( # type: ignore[call-arg]
*rgb_color, light.min_mireds, light.max_mireds *rgb_color, light.min_color_temp_kelvin, light.max_color_temp_kelvin
) )
elif ColorMode.HS in supported_color_modes: elif ColorMode.HS in supported_color_modes:
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
@ -499,7 +523,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
elif ColorMode.RGBWW in supported_color_modes: elif ColorMode.RGBWW in supported_color_modes:
rgb_color = color_util.color_xy_to_RGB(*xy_color) rgb_color = color_util.color_xy_to_RGB(*xy_color)
params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww(
*rgb_color, light.min_mireds, light.max_mireds *rgb_color, light.min_color_temp_kelvin, light.max_color_temp_kelvin
) )
elif ATTR_RGBW_COLOR in params and ColorMode.RGBW not in supported_color_modes: elif ATTR_RGBW_COLOR in params and ColorMode.RGBW not in supported_color_modes:
rgbw_color = params.pop(ATTR_RGBW_COLOR) rgbw_color = params.pop(ATTR_RGBW_COLOR)
@ -508,7 +532,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
params[ATTR_RGB_COLOR] = rgb_color params[ATTR_RGB_COLOR] = rgb_color
elif ColorMode.RGBWW in supported_color_modes: elif ColorMode.RGBWW in supported_color_modes:
params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww(
*rgb_color, light.min_mireds, light.max_mireds *rgb_color, light.min_color_temp_kelvin, light.max_color_temp_kelvin
) )
elif ColorMode.HS in supported_color_modes: elif ColorMode.HS in supported_color_modes:
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
@ -520,7 +544,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
assert (rgbww_color := params.pop(ATTR_RGBWW_COLOR)) is not None assert (rgbww_color := params.pop(ATTR_RGBWW_COLOR)) is not None
# https://github.com/python/mypy/issues/13673 # https://github.com/python/mypy/issues/13673
rgb_color = color_util.color_rgbww_to_rgb( # type: ignore[call-arg] rgb_color = color_util.color_rgbww_to_rgb( # type: ignore[call-arg]
*rgbww_color, light.min_mireds, light.max_mireds *rgbww_color, light.min_color_temp_kelvin, light.max_color_temp_kelvin
) )
if ColorMode.RGB in supported_color_modes: if ColorMode.RGB in supported_color_modes:
params[ATTR_RGB_COLOR] = rgb_color params[ATTR_RGB_COLOR] = rgb_color
@ -755,11 +779,16 @@ class LightEntity(ToggleEntity):
_attr_brightness: int | None = None _attr_brightness: int | None = None
_attr_color_mode: ColorMode | str | None = None _attr_color_mode: ColorMode | str | None = None
_attr_color_temp: int | None = None _attr_color_temp: int | None = None
_attr_color_temp_kelvin: int | None = None
_attr_effect_list: list[str] | None = None _attr_effect_list: list[str] | None = None
_attr_effect: str | None = None _attr_effect: str | None = None
_attr_hs_color: tuple[float, float] | None = None _attr_hs_color: tuple[float, float] | None = None
_attr_max_mireds: int = 500 # Default to the Philips Hue value that HA has always assumed
_attr_min_mireds: int = 153 # https://developers.meethue.com/documentation/core-concepts
_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 # 6535 K
_attr_rgb_color: tuple[int, int, int] | None = None _attr_rgb_color: tuple[int, int, int] | None = None
_attr_rgbw_color: tuple[int, 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 _attr_rgbww_color: tuple[int, int, int, int, int] | None = None
@ -787,7 +816,7 @@ class LightEntity(ToggleEntity):
if ColorMode.HS in supported and self.hs_color is not None: if ColorMode.HS in supported and self.hs_color is not None:
return ColorMode.HS return ColorMode.HS
if ColorMode.COLOR_TEMP in supported and self.color_temp is not None: if ColorMode.COLOR_TEMP in supported and self.color_temp_kelvin is not None:
return ColorMode.COLOR_TEMP return ColorMode.COLOR_TEMP
if ColorMode.BRIGHTNESS in supported and self.brightness is not None: if ColorMode.BRIGHTNESS in supported and self.brightness is not None:
return ColorMode.BRIGHTNESS return ColorMode.BRIGHTNESS
@ -833,20 +862,37 @@ class LightEntity(ToggleEntity):
"""Return the CT color value in mireds.""" """Return the CT color value in mireds."""
return self._attr_color_temp 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 self.color_temp:
return color_util.color_temperature_mired_to_kelvin(self.color_temp)
return self._attr_color_temp_kelvin
@property @property
def min_mireds(self) -> int: def min_mireds(self) -> int:
"""Return the coldest color_temp that this light supports.""" """Return the coldest color_temp that this light supports."""
# Default to the Philips Hue value that HA has always assumed
# https://developers.meethue.com/documentation/core-concepts
return self._attr_min_mireds return self._attr_min_mireds
@property @property
def max_mireds(self) -> int: def max_mireds(self) -> int:
"""Return the warmest color_temp that this light supports.""" """Return the warmest color_temp that this light supports."""
# Default to the Philips Hue value that HA has always assumed
# https://developers.meethue.com/documentation/core-concepts
return self._attr_max_mireds 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:
return color_util.color_temperature_mired_to_kelvin(self.max_mireds)
return self._attr_min_color_temp_kelvin
@property
def max_color_temp_kelvin(self) -> int:
"""Return the coldest color_temp_kelvin that this light supports."""
if self._attr_min_color_temp_kelvin is None:
return color_util.color_temperature_mired_to_kelvin(self.min_mireds)
return self._attr_min_color_temp_kelvin
@property @property
def effect_list(self) -> list[str] | None: def effect_list(self) -> list[str] | None:
"""Return the list of supported effects.""" """Return the list of supported effects."""
@ -867,6 +913,8 @@ class LightEntity(ToggleEntity):
if ColorMode.COLOR_TEMP in supported_color_modes: if ColorMode.COLOR_TEMP in supported_color_modes:
data[ATTR_MIN_MIREDS] = self.min_mireds data[ATTR_MIN_MIREDS] = self.min_mireds
data[ATTR_MAX_MIREDS] = self.max_mireds data[ATTR_MAX_MIREDS] = self.max_mireds
data[ATTR_MIN_COLOR_TEMP_KELVIN] = self.min_color_temp_kelvin
data[ATTR_MAX_COLOR_TEMP_KELVIN] = self.max_color_temp_kelvin
if supported_features & LightEntityFeature.EFFECT: if supported_features & LightEntityFeature.EFFECT:
data[ATTR_EFFECT_LIST] = self.effect_list data[ATTR_EFFECT_LIST] = self.effect_list
@ -904,16 +952,14 @@ class LightEntity(ToggleEntity):
elif color_mode == ColorMode.RGBWW and self.rgbww_color: elif color_mode == ColorMode.RGBWW and self.rgbww_color:
rgbww_color = self.rgbww_color rgbww_color = self.rgbww_color
rgb_color = color_util.color_rgbww_to_rgb( rgb_color = color_util.color_rgbww_to_rgb(
*rgbww_color, self.min_mireds, self.max_mireds *rgbww_color, self.min_color_temp_kelvin, self.max_color_temp_kelvin
) )
data[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) data[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3]) data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3])
data[ATTR_RGBWW_COLOR] = tuple(int(x) for x in rgbww_color[0:5]) data[ATTR_RGBWW_COLOR] = tuple(int(x) for x in rgbww_color[0:5])
data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color)
elif color_mode == ColorMode.COLOR_TEMP and self.color_temp: elif color_mode == ColorMode.COLOR_TEMP and self.color_temp_kelvin:
hs_color = color_util.color_temperature_to_hs( hs_color = color_util.color_temperature_to_hs(self.color_temp_kelvin)
color_util.color_temperature_mired_to_kelvin(self.color_temp)
)
data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3)) data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3))
data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color) data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color)
data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color) data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
@ -949,7 +995,13 @@ class LightEntity(ToggleEntity):
data[ATTR_BRIGHTNESS] = self.brightness data[ATTR_BRIGHTNESS] = self.brightness
if color_mode == ColorMode.COLOR_TEMP: if color_mode == ColorMode.COLOR_TEMP:
data[ATTR_COLOR_TEMP] = self.color_temp data[ATTR_COLOR_TEMP_KELVIN] = self.color_temp_kelvin
if not self.color_temp_kelvin:
data[ATTR_COLOR_TEMP] = None
else:
data[ATTR_COLOR_TEMP] = color_util.color_temperature_kelvin_to_mired(
self.color_temp_kelvin
)
if color_mode in COLOR_MODES_COLOR or color_mode == ColorMode.COLOR_TEMP: if color_mode in COLOR_MODES_COLOR or color_mode == ColorMode.COLOR_TEMP:
data.update(self._light_internal_convert_color(color_mode)) data.update(self._light_internal_convert_color(color_mode))
@ -957,7 +1009,13 @@ class LightEntity(ToggleEntity):
if supported_features & SUPPORT_COLOR_TEMP and not self.supported_color_modes: if supported_features & SUPPORT_COLOR_TEMP and not self.supported_color_modes:
# Backwards compatibility # Backwards compatibility
# Add warning in 2021.6, remove in 2021.10 # Add warning in 2021.6, remove in 2021.10
data[ATTR_COLOR_TEMP] = self.color_temp data[ATTR_COLOR_TEMP_KELVIN] = self.color_temp_kelvin
if not self.color_temp_kelvin:
data[ATTR_COLOR_TEMP] = None
else:
data[ATTR_COLOR_TEMP] = color_util.color_temperature_kelvin_to_mired(
self.color_temp_kelvin
)
if supported_features & LightEntityFeature.EFFECT: if supported_features & LightEntityFeature.EFFECT:
data[ATTR_EFFECT] = self.effect data[ATTR_EFFECT] = self.effect

View File

@ -436,10 +436,12 @@ def color_rgbw_to_rgb(r: int, g: int, b: int, w: int) -> tuple[int, int, int]:
def color_rgb_to_rgbww( def color_rgb_to_rgbww(
r: int, g: int, b: int, min_mireds: int, max_mireds: int r: int, g: int, b: int, min_kelvin: int, max_kelvin: int
) -> tuple[int, int, int, int, int]: ) -> tuple[int, int, int, int, int]:
"""Convert an rgb color to an rgbww representation.""" """Convert an rgb color to an rgbww representation."""
# Find the color temperature when both white channels have equal brightness # Find the color temperature when both white channels have equal brightness
max_mireds = color_temperature_kelvin_to_mired(min_kelvin)
min_mireds = color_temperature_kelvin_to_mired(max_kelvin)
mired_range = max_mireds - min_mireds mired_range = max_mireds - min_mireds
mired_midpoint = min_mireds + mired_range / 2 mired_midpoint = min_mireds + mired_range / 2
color_temp_kelvin = color_temperature_mired_to_kelvin(mired_midpoint) color_temp_kelvin = color_temperature_mired_to_kelvin(mired_midpoint)
@ -460,10 +462,12 @@ def color_rgb_to_rgbww(
def color_rgbww_to_rgb( def color_rgbww_to_rgb(
r: int, g: int, b: int, cw: int, ww: int, min_mireds: int, max_mireds: int r: int, g: int, b: int, cw: int, ww: int, min_kelvin: int, max_kelvin: int
) -> tuple[int, int, int]: ) -> tuple[int, int, int]:
"""Convert an rgbww color to an rgb representation.""" """Convert an rgbww color to an rgb representation."""
# Calculate color temperature of the white channels # Calculate color temperature of the white channels
max_mireds = color_temperature_kelvin_to_mired(min_kelvin)
min_mireds = color_temperature_kelvin_to_mired(max_kelvin)
mired_range = max_mireds - min_mireds mired_range = max_mireds - min_mireds
try: try:
ct_ratio = ww / (cw + ww) ct_ratio = ww / (cw + ww)
@ -530,9 +534,15 @@ def color_temperature_to_rgb(
def color_temperature_to_rgbww( def color_temperature_to_rgbww(
temperature: int, brightness: int, min_mireds: int, max_mireds: int temperature: int, brightness: int, min_kelvin: int, max_kelvin: int
) -> tuple[int, int, int, int, int]: ) -> tuple[int, int, int, int, int]:
"""Convert color temperature in mireds to rgbcw.""" """Convert color temperature in kelvin to rgbcw.
Returns a (r, g, b, cw, ww) tuple.
"""
max_mireds = color_temperature_kelvin_to_mired(min_kelvin)
min_mireds = color_temperature_kelvin_to_mired(max_kelvin)
temperature = color_temperature_kelvin_to_mired(temperature)
mired_range = max_mireds - min_mireds mired_range = max_mireds - min_mireds
cold = ((max_mireds - temperature) / mired_range) * brightness cold = ((max_mireds - temperature) / mired_range) * brightness
warm = brightness - cold warm = brightness - cold
@ -540,22 +550,33 @@ def color_temperature_to_rgbww(
def rgbww_to_color_temperature( def rgbww_to_color_temperature(
rgbww: tuple[int, int, int, int, int], min_mireds: int, max_mireds: int rgbww: tuple[int, int, int, int, int], min_kelvin: int, max_kelvin: int
) -> tuple[int, int]: ) -> tuple[int, int]:
"""Convert rgbcw to color temperature in mireds.""" """Convert rgbcw to color temperature in kelvin.
Returns a tuple (color_temperature, brightness).
"""
_, _, _, cold, warm = rgbww _, _, _, cold, warm = rgbww
return while_levels_to_color_temperature(cold, warm, min_mireds, max_mireds) return _white_levels_to_color_temperature(cold, warm, min_kelvin, max_kelvin)
def while_levels_to_color_temperature( def _white_levels_to_color_temperature(
cold: int, warm: int, min_mireds: int, max_mireds: int cold: int, warm: int, min_kelvin: int, max_kelvin: int
) -> tuple[int, int]: ) -> tuple[int, int]:
"""Convert whites to color temperature in mireds.""" """Convert whites to color temperature in kelvin.
Returns a tuple (color_temperature, brightness).
"""
max_mireds = color_temperature_kelvin_to_mired(min_kelvin)
min_mireds = color_temperature_kelvin_to_mired(max_kelvin)
brightness = warm / 255 + cold / 255 brightness = warm / 255 + cold / 255
if brightness == 0: if brightness == 0:
return (max_mireds, 0) # Return the warmest color if brightness is 0
return (min_kelvin, 0)
return round( return round(
((cold / 255 / brightness) * (min_mireds - max_mireds)) + max_mireds color_temperature_mired_to_kelvin(
((cold / 255 / brightness) * (min_mireds - max_mireds)) + max_mireds
)
), min(255, round(brightness * 255)) ), min(255, round(brightness * 255))

View File

@ -37,6 +37,8 @@ async def test_nanoleaf_nl55_setup(hass):
unique_id="homekit-AAAA011111111111-19", unique_id="homekit-AAAA011111111111-19",
supported_features=0, supported_features=0,
capabilities={ capabilities={
"max_color_temp_kelvin": 6535,
"min_color_temp_kelvin": 2127,
"max_mireds": 470, "max_mireds": 470,
"min_mireds": 153, "min_mireds": 153,
"supported_color_modes": ["color_temp", "hs"], "supported_color_modes": ["color_temp", "hs"],

View File

@ -629,11 +629,33 @@ async def test_default_profiles_group(
}, },
{ {
light.ATTR_COLOR_TEMP: 600, light.ATTR_COLOR_TEMP: 600,
light.ATTR_COLOR_TEMP_KELVIN: 1666,
light.ATTR_BRIGHTNESS: 11, light.ATTR_BRIGHTNESS: 11,
light.ATTR_TRANSITION: 1, light.ATTR_TRANSITION: 1,
}, },
{ {
light.ATTR_COLOR_TEMP: 600, light.ATTR_COLOR_TEMP: 600,
light.ATTR_COLOR_TEMP_KELVIN: 1666,
light.ATTR_BRIGHTNESS: 11,
light.ATTR_TRANSITION: 1,
},
),
(
# Color temp in turn on params, color from profile ignored
{
light.ATTR_COLOR_TEMP_KELVIN: 6500,
light.ATTR_BRIGHTNESS: 11,
light.ATTR_TRANSITION: 1,
},
{
light.ATTR_COLOR_TEMP: 153,
light.ATTR_COLOR_TEMP_KELVIN: 6500,
light.ATTR_BRIGHTNESS: 11,
light.ATTR_TRANSITION: 1,
},
{
light.ATTR_COLOR_TEMP: 153,
light.ATTR_COLOR_TEMP_KELVIN: 6500,
light.ATTR_BRIGHTNESS: 11, light.ATTR_BRIGHTNESS: 11,
light.ATTR_TRANSITION: 1, light.ATTR_TRANSITION: 1,
}, },
@ -1440,7 +1462,7 @@ async def test_light_service_call_color_conversion(hass, enable_custom_integrati
_, data = entity5.last_call("turn_on") _, data = entity5.last_call("turn_on")
assert data == {"brightness": 255, "rgbw_color": (0, 0, 0, 255)} assert data == {"brightness": 255, "rgbw_color": (0, 0, 0, 255)}
_, data = entity6.last_call("turn_on") _, data = entity6.last_call("turn_on")
# The midpoint the the white channels is warm, compensated by adding green + blue # The midpoint of the the white channels is warm, compensated by adding green + blue
assert data == {"brightness": 255, "rgbww_color": (0, 76, 141, 255, 255)} assert data == {"brightness": 255, "rgbww_color": (0, 76, 141, 255, 255)}
await hass.services.async_call( await hass.services.async_call(
@ -1843,7 +1865,7 @@ async def test_light_service_call_color_temp_emulation(
blocking=True, blocking=True,
) )
_, data = entity0.last_call("turn_on") _, data = entity0.last_call("turn_on")
assert data == {"brightness": 255, "color_temp": 200} assert data == {"brightness": 255, "color_temp": 200, "color_temp_kelvin": 5000}
_, data = entity1.last_call("turn_on") _, data = entity1.last_call("turn_on")
assert data == {"brightness": 255, "hs_color": (27.001, 19.243)} assert data == {"brightness": 255, "hs_color": (27.001, 19.243)}
_, data = entity2.last_call("turn_on") _, data = entity2.last_call("turn_on")
@ -1868,6 +1890,10 @@ async def test_light_service_call_color_temp_conversion(
entity1 = platform.ENTITIES[1] entity1 = platform.ENTITIES[1]
entity1.supported_color_modes = {light.ColorMode.RGBWW} entity1.supported_color_modes = {light.ColorMode.RGBWW}
assert entity1.min_mireds == 153
assert entity1.max_mireds == 500
assert entity1.min_color_temp_kelvin == 2000
assert entity1.max_color_temp_kelvin == 6535
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1895,7 +1921,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True, blocking=True,
) )
_, data = entity0.last_call("turn_on") _, data = entity0.last_call("turn_on")
assert data == {"brightness": 255, "color_temp": 153} assert data == {"brightness": 255, "color_temp": 153, "color_temp_kelvin": 6535}
_, data = entity1.last_call("turn_on") _, data = entity1.last_call("turn_on")
# Home Assistant uses RGBCW so a mireds of 153 should be maximum cold at 100% brightness so 255 # Home Assistant uses RGBCW so a mireds of 153 should be maximum cold at 100% brightness so 255
assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 255, 0)} assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 255, 0)}
@ -1914,7 +1940,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True, blocking=True,
) )
_, data = entity0.last_call("turn_on") _, data = entity0.last_call("turn_on")
assert data == {"brightness": 128, "color_temp": 500} assert data == {"brightness": 128, "color_temp": 500, "color_temp_kelvin": 2000}
_, data = entity1.last_call("turn_on") _, data = entity1.last_call("turn_on")
# Home Assistant uses RGBCW so a mireds of 500 should be maximum warm at 50% brightness so 128 # Home Assistant uses RGBCW so a mireds of 500 should be maximum warm at 50% brightness so 128
assert data == {"brightness": 128, "rgbww_color": (0, 0, 0, 0, 128)} assert data == {"brightness": 128, "rgbww_color": (0, 0, 0, 0, 128)}
@ -1933,7 +1959,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True, blocking=True,
) )
_, data = entity0.last_call("turn_on") _, data = entity0.last_call("turn_on")
assert data == {"brightness": 255, "color_temp": 327} assert data == {"brightness": 255, "color_temp": 327, "color_temp_kelvin": 3058}
_, data = entity1.last_call("turn_on") _, data = entity1.last_call("turn_on")
# Home Assistant uses RGBCW so a mireds of 328 should be the midway point at 100% brightness so 127 (rounding), 128 # Home Assistant uses RGBCW so a mireds of 328 should be the midway point at 100% brightness so 127 (rounding), 128
assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 127, 128)} assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 127, 128)}
@ -1952,7 +1978,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True, blocking=True,
) )
_, data = entity0.last_call("turn_on") _, data = entity0.last_call("turn_on")
assert data == {"brightness": 255, "color_temp": 240} assert data == {"brightness": 255, "color_temp": 240, "color_temp_kelvin": 4166}
_, data = entity1.last_call("turn_on") _, data = entity1.last_call("turn_on")
assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 191, 64)} assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 191, 64)}
@ -1970,7 +1996,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True, blocking=True,
) )
_, data = entity0.last_call("turn_on") _, data = entity0.last_call("turn_on")
assert data == {"brightness": 255, "color_temp": 410} assert data == {"brightness": 255, "color_temp": 410, "color_temp_kelvin": 2439}
_, data = entity1.last_call("turn_on") _, data = entity1.last_call("turn_on")
assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 66, 189)} assert data == {"brightness": 255, "rgbww_color": (0, 0, 0, 66, 189)}

View File

@ -861,14 +861,16 @@ async def test_device_types(hass: HomeAssistant, caplog):
await hass.async_block_till_done() await hass.async_block_till_done()
bright = round(255 * int(PROPERTIES["bright"]) / 100) bright = round(255 * int(PROPERTIES["bright"]) / 100)
ct = color_temperature_kelvin_to_mired(int(PROPERTIES["ct"])) ct = int(PROPERTIES["ct"])
ct_mired = color_temperature_kelvin_to_mired(int(PROPERTIES["ct"]))
hue = int(PROPERTIES["hue"]) hue = int(PROPERTIES["hue"])
sat = int(PROPERTIES["sat"]) sat = int(PROPERTIES["sat"])
rgb = int(PROPERTIES["rgb"]) rgb = int(PROPERTIES["rgb"])
rgb_color = ((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF) rgb_color = ((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF)
hs_color = (hue, sat) hs_color = (hue, sat)
bg_bright = round(255 * int(PROPERTIES["bg_bright"]) / 100) bg_bright = round(255 * int(PROPERTIES["bg_bright"]) / 100)
bg_ct = color_temperature_kelvin_to_mired(int(PROPERTIES["bg_ct"])) bg_ct = int(PROPERTIES["bg_ct"])
bg_ct_kelvin = color_temperature_kelvin_to_mired(int(PROPERTIES["bg_ct"]))
bg_hue = int(PROPERTIES["bg_hue"]) bg_hue = int(PROPERTIES["bg_hue"])
bg_sat = int(PROPERTIES["bg_sat"]) bg_sat = int(PROPERTIES["bg_sat"])
bg_rgb = int(PROPERTIES["bg_rgb"]) bg_rgb = int(PROPERTIES["bg_rgb"])
@ -911,6 +913,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -918,7 +924,8 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
"brightness": bright, "brightness": bright,
"color_temp": ct, "color_temp_kelvin": ct,
"color_temp": ct_mired,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"], "supported_color_modes": ["color_temp", "hs", "rgb"],
"hs_color": (26.812, 34.87), "hs_color": (26.812, 34.87),
@ -936,6 +943,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
"hs_color": (28.401, 100.0), "hs_color": (28.401, 100.0),
"rgb_color": (255, 120, 0), "rgb_color": (255, 120, 0),
"xy_color": (0.621, 0.367), "xy_color": (0.621, 0.367),
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -945,6 +956,7 @@ async def test_device_types(hass: HomeAssistant, caplog):
"brightness": nl_br, "brightness": nl_br,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"], "supported_color_modes": ["color_temp", "hs", "rgb"],
"color_temp_kelvin": model_specs["color_temp"]["min"],
"color_temp": color_temperature_kelvin_to_mired( "color_temp": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
@ -960,6 +972,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -989,6 +1005,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1019,6 +1039,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1046,6 +1070,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1072,6 +1100,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": model_specs["color_temp"]["min"],
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1097,6 +1129,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST, "effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["min"])
),
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1104,7 +1142,8 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
"brightness": bright, "brightness": bright,
"color_temp": ct, "color_temp_kelvin": ct,
"color_temp": ct_mired,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp"], "supported_color_modes": ["color_temp"],
"hs_color": (26.812, 34.87), "hs_color": (26.812, 34.87),
@ -1120,6 +1159,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
nightlight_mode_properties={ nightlight_mode_properties={
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST, "effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["min"])
),
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1127,6 +1172,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
"brightness": nl_br, "brightness": nl_br,
"color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["min"])
),
"color_temp": color_temperature_kelvin_to_mired( "color_temp": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
@ -1151,6 +1199,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
"flowing": False, "flowing": False,
"night_light": True, "night_light": True,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["min"])
),
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1158,7 +1212,8 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
"brightness": bright, "brightness": bright,
"color_temp": ct, "color_temp_kelvin": ct,
"color_temp": ct_mired,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp"], "supported_color_modes": ["color_temp"],
"hs_color": (26.812, 34.87), "hs_color": (26.812, 34.87),
@ -1177,6 +1232,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
"flowing": False, "flowing": False,
"night_light": True, "night_light": True,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["min"])
),
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["max"])
),
"min_mireds": color_temperature_kelvin_to_mired( "min_mireds": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["max"] model_specs["color_temp"]["max"]
), ),
@ -1184,6 +1245,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
"brightness": nl_br, "brightness": nl_br,
"color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(model_specs["color_temp"]["min"])
),
"color_temp": color_temperature_kelvin_to_mired( "color_temp": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["min"] model_specs["color_temp"]["min"]
), ),
@ -1202,10 +1266,15 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": 1700,
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(6500)
),
"min_mireds": color_temperature_kelvin_to_mired(6500), "min_mireds": color_temperature_kelvin_to_mired(6500),
"max_mireds": color_temperature_kelvin_to_mired(1700), "max_mireds": color_temperature_kelvin_to_mired(1700),
"brightness": bg_bright, "brightness": bg_bright,
"color_temp": bg_ct, "color_temp_kelvin": bg_ct,
"color_temp": bg_ct_kelvin,
"color_mode": "color_temp", "color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"], "supported_color_modes": ["color_temp", "hs", "rgb"],
"hs_color": (27.001, 19.243), "hs_color": (27.001, 19.243),
@ -1224,6 +1293,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": 1700,
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(6500)
),
"min_mireds": color_temperature_kelvin_to_mired(6500), "min_mireds": color_temperature_kelvin_to_mired(6500),
"max_mireds": color_temperature_kelvin_to_mired(1700), "max_mireds": color_temperature_kelvin_to_mired(1700),
"brightness": bg_bright, "brightness": bg_bright,
@ -1245,6 +1318,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{ {
"effect_list": YEELIGHT_COLOR_EFFECT_LIST, "effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT, "supported_features": SUPPORT_YEELIGHT,
"min_color_temp_kelvin": 1700,
"max_color_temp_kelvin": color_temperature_mired_to_kelvin(
color_temperature_kelvin_to_mired(6500)
),
"min_mireds": color_temperature_kelvin_to_mired(6500), "min_mireds": color_temperature_kelvin_to_mired(6500),
"max_mireds": color_temperature_kelvin_to_mired(1700), "max_mireds": color_temperature_kelvin_to_mired(1700),
"brightness": bg_bright, "brightness": bg_bright,

View File

@ -370,82 +370,84 @@ def test_get_color_in_voluptuous():
def test_color_rgb_to_rgbww(): def test_color_rgb_to_rgbww():
"""Test color_rgb_to_rgbww conversions.""" """Test color_rgb_to_rgbww conversions."""
assert color_util.color_rgb_to_rgbww(255, 255, 255, 154, 370) == ( # Light with mid point at ~4600K (warm white) -> output compensated by adding blue
assert color_util.color_rgb_to_rgbww(255, 255, 255, 2702, 6493) == (
0, 0,
54, 54,
98, 98,
255, 255,
255, 255,
) )
assert color_util.color_rgb_to_rgbww(255, 255, 255, 100, 1000) == ( # Light with mid point at ~5500K (less warm white) -> output compensated by adding less blue
assert color_util.color_rgb_to_rgbww(255, 255, 255, 1000, 10000) == (
255, 255,
255, 255,
255, 255,
0, 0,
0, 0,
) )
assert color_util.color_rgb_to_rgbww(255, 255, 255, 1, 1000) == ( # Light with mid point at ~1MK (unrealistically cold white) -> output compensated by adding red
assert color_util.color_rgb_to_rgbww(255, 255, 255, 1000, 1000000) == (
0, 0,
118, 118,
241, 241,
255, 255,
255, 255,
) )
assert color_util.color_rgb_to_rgbww(128, 128, 128, 154, 370) == ( assert color_util.color_rgb_to_rgbww(128, 128, 128, 2702, 6493) == (
0, 0,
27, 27,
49, 49,
128, 128,
128, 128,
) )
assert color_util.color_rgb_to_rgbww(64, 64, 64, 154, 370) == (0, 14, 25, 64, 64) assert color_util.color_rgb_to_rgbww(64, 64, 64, 2702, 6493) == (0, 14, 25, 64, 64)
assert color_util.color_rgb_to_rgbww(32, 64, 16, 154, 370) == (9, 64, 0, 38, 38) assert color_util.color_rgb_to_rgbww(32, 64, 16, 2702, 6493) == (9, 64, 0, 38, 38)
assert color_util.color_rgb_to_rgbww(0, 0, 0, 154, 370) == (0, 0, 0, 0, 0) assert color_util.color_rgb_to_rgbww(0, 0, 0, 2702, 6493) == (0, 0, 0, 0, 0)
assert color_util.color_rgb_to_rgbww(0, 0, 0, 0, 100) == (0, 0, 0, 0, 0) assert color_util.color_rgb_to_rgbww(0, 0, 0, 10000, 1000000) == (0, 0, 0, 0, 0)
assert color_util.color_rgb_to_rgbww(255, 255, 255, 1, 5) == (103, 69, 0, 255, 255) assert color_util.color_rgb_to_rgbww(255, 255, 255, 200000, 1000000) == (
103,
69,
0,
255,
255,
)
def test_color_rgbww_to_rgb(): def test_color_rgbww_to_rgb():
"""Test color_rgbww_to_rgb conversions.""" """Test color_rgbww_to_rgb conversions."""
assert color_util.color_rgbww_to_rgb(0, 54, 98, 255, 255, 154, 370) == ( assert color_util.color_rgbww_to_rgb(0, 54, 98, 255, 255, 2702, 6493) == (
255, 255,
255, 255,
255, 255,
) )
assert color_util.color_rgbww_to_rgb(255, 255, 255, 0, 0, 154, 370) == ( # rgb fully on, + both white channels turned off -> rgb fully on
assert color_util.color_rgbww_to_rgb(255, 255, 255, 0, 0, 2702, 6493) == (
255, 255,
255, 255,
255, 255,
) )
assert color_util.color_rgbww_to_rgb(0, 118, 241, 255, 255, 154, 370) == ( # r < g < b + both white channels fully enabled -> r < g < b capped at 255
assert color_util.color_rgbww_to_rgb(0, 118, 241, 255, 255, 2702, 6493) == (
163, 163,
204, 204,
255, 255,
) )
assert color_util.color_rgbww_to_rgb(0, 27, 49, 128, 128, 154, 370) == ( # r < g < b + both white channels 50% enabled -> r < g < b capped at 128
assert color_util.color_rgbww_to_rgb(0, 27, 49, 128, 128, 2702, 6493) == (
128, 128,
128, 128,
128, 128,
) )
assert color_util.color_rgbww_to_rgb(0, 14, 25, 64, 64, 154, 370) == (64, 64, 64) # r < g < b + both white channels 25% enabled -> r < g < b capped at 64
assert color_util.color_rgbww_to_rgb(9, 64, 0, 38, 38, 154, 370) == (32, 64, 16) assert color_util.color_rgbww_to_rgb(0, 14, 25, 64, 64, 2702, 6493) == (64, 64, 64)
assert color_util.color_rgbww_to_rgb(0, 0, 0, 0, 0, 154, 370) == (0, 0, 0) assert color_util.color_rgbww_to_rgb(9, 64, 0, 38, 38, 2702, 6493) == (32, 64, 16)
assert color_util.color_rgbww_to_rgb(103, 69, 0, 255, 255, 153, 370) == ( assert color_util.color_rgbww_to_rgb(0, 0, 0, 0, 0, 2702, 6493) == (0, 0, 0)
assert color_util.color_rgbww_to_rgb(103, 69, 0, 255, 255, 2702, 6535) == (
255, 255,
193, 193,
112, 112,
) )
assert color_util.color_rgbww_to_rgb(255, 255, 255, 0, 0, 0, 0) == (255, 255, 255)
assert color_util.color_rgbww_to_rgb(255, 255, 255, 255, 255, 0, 0) == (
255,
161,
128,
)
assert color_util.color_rgbww_to_rgb(255, 255, 255, 255, 255, 0, 370) == (
255,
245,
237,
)
def test_color_temperature_to_rgbww(): def test_color_temperature_to_rgbww():
@ -454,42 +456,45 @@ def test_color_temperature_to_rgbww():
Temperature values must be in mireds Temperature values must be in mireds
Home Assistant uses rgbcw for rgbww Home Assistant uses rgbcw for rgbww
""" """
assert color_util.color_temperature_to_rgbww(153, 255, 153, 500) == ( # Coldest color temperature -> only cold channel enabled
assert color_util.color_temperature_to_rgbww(6535, 255, 2000, 6535) == (
0, 0,
0, 0,
0, 0,
255, 255,
0, 0,
) )
assert color_util.color_temperature_to_rgbww(153, 128, 153, 500) == ( assert color_util.color_temperature_to_rgbww(6535, 128, 2000, 6535) == (
0, 0,
0, 0,
0, 0,
128, 128,
0, 0,
) )
assert color_util.color_temperature_to_rgbww(500, 255, 153, 500) == ( # Warmest color temperature -> only cold channel enabled
assert color_util.color_temperature_to_rgbww(2000, 255, 2000, 6535) == (
0, 0,
0, 0,
0, 0,
0, 0,
255, 255,
) )
assert color_util.color_temperature_to_rgbww(500, 128, 153, 500) == ( assert color_util.color_temperature_to_rgbww(2000, 128, 2000, 6535) == (
0, 0,
0, 0,
0, 0,
0, 0,
128, 128,
) )
assert color_util.color_temperature_to_rgbww(347, 255, 153, 500) == ( # Warmer than mid point color temperature -> More warm than cold channel enabled
assert color_util.color_temperature_to_rgbww(2881, 255, 2000, 6535) == (
0, 0,
0, 0,
0, 0,
112, 112,
143, 143,
) )
assert color_util.color_temperature_to_rgbww(347, 128, 153, 500) == ( assert color_util.color_temperature_to_rgbww(2881, 128, 2000, 6535) == (
0, 0,
0, 0,
0, 0,
@ -504,39 +509,36 @@ def test_rgbww_to_color_temperature():
Temperature values must be in mireds Temperature values must be in mireds
Home Assistant uses rgbcw for rgbww Home Assistant uses rgbcw for rgbww
""" """
assert color_util.rgbww_to_color_temperature( # Only cold channel enabled -> coldest color temperature
( assert color_util.rgbww_to_color_temperature((0, 0, 0, 255, 0), 2000, 6535) == (
0, 6535,
0,
0,
255,
0,
),
153,
500,
) == (153, 255)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 128, 0), 153, 500) == (
153,
128,
)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 255), 153, 500) == (
500,
255, 255,
) )
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 128), 153, 500) == ( assert color_util.rgbww_to_color_temperature((0, 0, 0, 128, 0), 2000, 6535) == (
500, 6535,
128, 128,
) )
assert color_util.rgbww_to_color_temperature((0, 0, 0, 112, 143), 153, 500) == ( # Only warm channel enabled -> warmest color temperature
348, assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 255), 2000, 6535) == (
2000,
255, 255,
) )
assert color_util.rgbww_to_color_temperature((0, 0, 0, 56, 72), 153, 500) == ( assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 128), 2000, 6535) == (
348, 2000,
128, 128,
) )
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 0), 153, 500) == ( # More warm than cold channel enabled -> warmer than mid point
500, assert color_util.rgbww_to_color_temperature((0, 0, 0, 112, 143), 2000, 6535) == (
2876,
255,
)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 56, 72), 2000, 6535) == (
2872,
128,
)
# Both channels turned off -> warmest color temperature
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 0), 2000, 6535) == (
2000,
0, 0,
) )
@ -547,33 +549,34 @@ def test_white_levels_to_color_temperature():
Temperature values must be in mireds Temperature values must be in mireds
Home Assistant uses rgbcw for rgbww Home Assistant uses rgbcw for rgbww
""" """
assert color_util.while_levels_to_color_temperature( # Only cold channel enabled -> coldest color temperature
255, assert color_util._white_levels_to_color_temperature(255, 0, 2000, 6535) == (
0, 6535,
153,
500,
) == (153, 255)
assert color_util.while_levels_to_color_temperature(128, 0, 153, 500) == (
153,
128,
)
assert color_util.while_levels_to_color_temperature(0, 255, 153, 500) == (
500,
255, 255,
) )
assert color_util.while_levels_to_color_temperature(0, 128, 153, 500) == ( assert color_util._white_levels_to_color_temperature(128, 0, 2000, 6535) == (
500, 6535,
128, 128,
) )
assert color_util.while_levels_to_color_temperature(112, 143, 153, 500) == ( # Only warm channel enabled -> warmest color temperature
348, assert color_util._white_levels_to_color_temperature(0, 255, 2000, 6535) == (
2000,
255, 255,
) )
assert color_util.while_levels_to_color_temperature(56, 72, 153, 500) == ( assert color_util._white_levels_to_color_temperature(0, 128, 2000, 6535) == (
348, 2000,
128, 128,
) )
assert color_util.while_levels_to_color_temperature(0, 0, 153, 500) == ( assert color_util._white_levels_to_color_temperature(112, 143, 2000, 6535) == (
500, 2876,
255,
)
assert color_util._white_levels_to_color_temperature(56, 72, 2000, 6535) == (
2872,
128,
)
# Both channels turned off -> warmest color temperature
assert color_util._white_levels_to_color_temperature(0, 0, 2000, 6535) == (
2000,
0, 0,
) )