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
elif self.rgbww_supported:
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:
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_XY_COLOR = "xy_color"
ATTR_HS_COLOR = "hs_color"
ATTR_COLOR_TEMP = "color_temp"
ATTR_KELVIN = "kelvin"
ATTR_MIN_MIREDS = "min_mireds"
ATTR_MAX_MIREDS = "max_mireds"
ATTR_COLOR_TEMP = "color_temp" # Deprecated in HA Core 2022.11
ATTR_KELVIN = "kelvin" # Deprecated in HA Core 2022.11
ATTR_MIN_MIREDS = "min_mireds" # Deprecated in HA Core 2022.11
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_WHITE = "white"
@ -249,6 +252,7 @@ LIGHT_TURN_ON_SCHEMA = {
vol.Exclusive(ATTR_COLOR_TEMP, COLOR_GROUP): vol.All(
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_HS_COLOR, COLOR_GROUP): vol.All(
vol.Coerce(tuple),
@ -309,9 +313,20 @@ def preprocess_turn_on_alternatives(
_LOGGER.warning("Got unknown color %s, falling back to white", color_name)
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:
mired = color_util.color_temperature_kelvin_to_mired(kelvin)
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)
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)
if ColorMode.COLOR_TEMP not in supported_color_modes:
params.pop(ATTR_COLOR_TEMP, None)
params.pop(ATTR_COLOR_TEMP_KELVIN, None)
if ColorMode.HS not in supported_color_modes:
params.pop(ATTR_HS_COLOR, None)
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
# 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 (
supported_color_modes
and ColorMode.COLOR_TEMP not 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)
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:
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):
temp_k = color_util.color_temperature_mired_to_kelvin(color_temp)
params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs(temp_k)
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
@ -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:
# https://github.com/python/mypy/issues/13673
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)
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:
rgb_color = color_util.color_hs_to_RGB(*hs_color)
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:
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:
# https://github.com/python/mypy/issues/13673
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:
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:
rgb_color = color_util.color_xy_to_RGB(*xy_color)
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:
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
elif ColorMode.RGBWW in supported_color_modes:
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:
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
# https://github.com/python/mypy/issues/13673
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:
params[ATTR_RGB_COLOR] = rgb_color
@ -755,11 +779,16 @@ class LightEntity(ToggleEntity):
_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
_attr_max_mireds: int = 500
_attr_min_mireds: int = 153
# Default to the Philips Hue value that HA has always assumed
# 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_rgbw_color: tuple[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:
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
if ColorMode.BRIGHTNESS in supported and self.brightness is not None:
return ColorMode.BRIGHTNESS
@ -833,20 +862,37 @@ class LightEntity(ToggleEntity):
"""Return the CT color value in mireds."""
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
def min_mireds(self) -> int:
"""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
@property
def max_mireds(self) -> int:
"""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
@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
def effect_list(self) -> list[str] | None:
"""Return the list of supported effects."""
@ -867,6 +913,8 @@ class LightEntity(ToggleEntity):
if ColorMode.COLOR_TEMP in supported_color_modes:
data[ATTR_MIN_MIREDS] = self.min_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:
data[ATTR_EFFECT_LIST] = self.effect_list
@ -904,16 +952,14 @@ class LightEntity(ToggleEntity):
elif color_mode == ColorMode.RGBWW and self.rgbww_color:
rgbww_color = self.rgbww_color
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_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_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color)
elif color_mode == ColorMode.COLOR_TEMP and self.color_temp:
hs_color = color_util.color_temperature_to_hs(
color_util.color_temperature_mired_to_kelvin(self.color_temp)
)
elif color_mode == ColorMode.COLOR_TEMP and self.color_temp_kelvin:
hs_color = color_util.color_temperature_to_hs(self.color_temp_kelvin)
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_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
@ -949,7 +995,13 @@ class LightEntity(ToggleEntity):
data[ATTR_BRIGHTNESS] = self.brightness
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:
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:
# Backwards compatibility
# 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:
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(
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]:
"""Convert an rgb color to an rgbww representation."""
# 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_midpoint = min_mireds + mired_range / 2
color_temp_kelvin = color_temperature_mired_to_kelvin(mired_midpoint)
@ -460,10 +462,12 @@ def color_rgb_to_rgbww(
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]:
"""Convert an rgbww color to an rgb representation."""
# 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
try:
ct_ratio = ww / (cw + ww)
@ -530,9 +534,15 @@ def color_temperature_to_rgb(
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]:
"""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
cold = ((max_mireds - temperature) / mired_range) * brightness
warm = brightness - cold
@ -540,22 +550,33 @@ def color_temperature_to_rgbww(
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]:
"""Convert rgbcw to color temperature in mireds."""
"""Convert rgbcw to color temperature in kelvin.
Returns a tuple (color_temperature, brightness).
"""
_, _, _, 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(
cold: int, warm: int, min_mireds: int, max_mireds: int
def _white_levels_to_color_temperature(
cold: int, warm: int, min_kelvin: int, max_kelvin: 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
if brightness == 0:
return (max_mireds, 0)
# Return the warmest color if brightness is 0
return (min_kelvin, 0)
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))

View file

@ -37,6 +37,8 @@ async def test_nanoleaf_nl55_setup(hass):
unique_id="homekit-AAAA011111111111-19",
supported_features=0,
capabilities={
"max_color_temp_kelvin": 6535,
"min_color_temp_kelvin": 2127,
"max_mireds": 470,
"min_mireds": 153,
"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_KELVIN: 1666,
light.ATTR_BRIGHTNESS: 11,
light.ATTR_TRANSITION: 1,
},
{
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_TRANSITION: 1,
},
@ -1440,7 +1462,7 @@ async def test_light_service_call_color_conversion(hass, enable_custom_integrati
_, data = entity5.last_call("turn_on")
assert data == {"brightness": 255, "rgbw_color": (0, 0, 0, 255)}
_, 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)}
await hass.services.async_call(
@ -1843,7 +1865,7 @@ async def test_light_service_call_color_temp_emulation(
blocking=True,
)
_, 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")
assert data == {"brightness": 255, "hs_color": (27.001, 19.243)}
_, data = entity2.last_call("turn_on")
@ -1868,6 +1890,10 @@ async def test_light_service_call_color_temp_conversion(
entity1 = platform.ENTITIES[1]
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"}})
await hass.async_block_till_done()
@ -1895,7 +1921,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True,
)
_, 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")
# 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)}
@ -1914,7 +1940,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True,
)
_, 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")
# 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)}
@ -1933,7 +1959,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True,
)
_, 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")
# 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)}
@ -1952,7 +1978,7 @@ async def test_light_service_call_color_temp_conversion(
blocking=True,
)
_, 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")
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,
)
_, 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")
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()
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"])
sat = int(PROPERTIES["sat"])
rgb = int(PROPERTIES["rgb"])
rgb_color = ((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF)
hs_color = (hue, sat)
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_sat = int(PROPERTIES["bg_sat"])
bg_rgb = int(PROPERTIES["bg_rgb"])
@ -911,6 +913,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -918,7 +924,8 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"]
),
"brightness": bright,
"color_temp": ct,
"color_temp_kelvin": ct,
"color_temp": ct_mired,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"],
"hs_color": (26.812, 34.87),
@ -936,6 +943,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
"hs_color": (28.401, 100.0),
"rgb_color": (255, 120, 0),
"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(
model_specs["color_temp"]["max"]
),
@ -945,6 +956,7 @@ async def test_device_types(hass: HomeAssistant, caplog):
"brightness": nl_br,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"],
"color_temp_kelvin": model_specs["color_temp"]["min"],
"color_temp": color_temperature_kelvin_to_mired(
model_specs["color_temp"]["min"]
),
@ -960,6 +972,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -989,6 +1005,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -1019,6 +1039,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -1046,6 +1070,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -1072,6 +1100,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -1097,6 +1129,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -1104,7 +1142,8 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"]
),
"brightness": bright,
"color_temp": ct,
"color_temp_kelvin": ct,
"color_temp": ct_mired,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp"],
"hs_color": (26.812, 34.87),
@ -1120,6 +1159,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
nightlight_mode_properties={
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
"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(
model_specs["color_temp"]["max"]
),
@ -1127,6 +1172,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"]
),
"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(
model_specs["color_temp"]["min"]
),
@ -1151,6 +1199,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
"flowing": False,
"night_light": True,
"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(
model_specs["color_temp"]["max"]
),
@ -1158,7 +1212,8 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"]
),
"brightness": bright,
"color_temp": ct,
"color_temp_kelvin": ct,
"color_temp": ct_mired,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp"],
"hs_color": (26.812, 34.87),
@ -1177,6 +1232,12 @@ async def test_device_types(hass: HomeAssistant, caplog):
"flowing": False,
"night_light": True,
"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(
model_specs["color_temp"]["max"]
),
@ -1184,6 +1245,9 @@ async def test_device_types(hass: HomeAssistant, caplog):
model_specs["color_temp"]["min"]
),
"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(
model_specs["color_temp"]["min"]
),
@ -1202,10 +1266,15 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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),
"max_mireds": color_temperature_kelvin_to_mired(1700),
"brightness": bg_bright,
"color_temp": bg_ct,
"color_temp_kelvin": bg_ct,
"color_temp": bg_ct_kelvin,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp", "hs", "rgb"],
"hs_color": (27.001, 19.243),
@ -1224,6 +1293,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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),
"max_mireds": color_temperature_kelvin_to_mired(1700),
"brightness": bg_bright,
@ -1245,6 +1318,10 @@ async def test_device_types(hass: HomeAssistant, caplog):
{
"effect_list": YEELIGHT_COLOR_EFFECT_LIST,
"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),
"max_mireds": color_temperature_kelvin_to_mired(1700),
"brightness": bg_bright,

View file

@ -370,82 +370,84 @@ def test_get_color_in_voluptuous():
def test_color_rgb_to_rgbww():
"""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,
54,
98,
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,
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,
118,
241,
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,
27,
49,
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(32, 64, 16, 154, 370) == (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, 0, 100) == (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(64, 64, 64, 2702, 6493) == (0, 14, 25, 64, 64)
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, 2702, 6493) == (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, 200000, 1000000) == (
103,
69,
0,
255,
255,
)
def test_color_rgbww_to_rgb():
"""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,
)
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,
)
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,
204,
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,
)
assert color_util.color_rgbww_to_rgb(0, 14, 25, 64, 64, 154, 370) == (64, 64, 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, 0, 0, 0, 0, 154, 370) == (0, 0, 0)
assert color_util.color_rgbww_to_rgb(103, 69, 0, 255, 255, 153, 370) == (
# r < g < b + both white channels 25% enabled -> r < g < b capped at 64
assert color_util.color_rgbww_to_rgb(0, 14, 25, 64, 64, 2702, 6493) == (64, 64, 64)
assert color_util.color_rgbww_to_rgb(9, 64, 0, 38, 38, 2702, 6493) == (32, 64, 16)
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,
193,
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():
@ -454,42 +456,45 @@ def test_color_temperature_to_rgbww():
Temperature values must be in mireds
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,
255,
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,
128,
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,
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,
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,
112,
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,
@ -504,39 +509,36 @@ def test_rgbww_to_color_temperature():
Temperature values must be in mireds
Home Assistant uses rgbcw for rgbww
"""
assert color_util.rgbww_to_color_temperature(
(
0,
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,
# Only cold channel enabled -> coldest color temperature
assert color_util.rgbww_to_color_temperature((0, 0, 0, 255, 0), 2000, 6535) == (
6535,
255,
)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 128), 153, 500) == (
500,
assert color_util.rgbww_to_color_temperature((0, 0, 0, 128, 0), 2000, 6535) == (
6535,
128,
)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 112, 143), 153, 500) == (
348,
# Only warm channel enabled -> warmest color temperature
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 255), 2000, 6535) == (
2000,
255,
)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 56, 72), 153, 500) == (
348,
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 128), 2000, 6535) == (
2000,
128,
)
assert color_util.rgbww_to_color_temperature((0, 0, 0, 0, 0), 153, 500) == (
500,
# More warm than cold channel enabled -> warmer than mid point
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,
)
@ -547,33 +549,34 @@ def test_white_levels_to_color_temperature():
Temperature values must be in mireds
Home Assistant uses rgbcw for rgbww
"""
assert color_util.while_levels_to_color_temperature(
255,
0,
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,
# Only cold channel enabled -> coldest color temperature
assert color_util._white_levels_to_color_temperature(255, 0, 2000, 6535) == (
6535,
255,
)
assert color_util.while_levels_to_color_temperature(0, 128, 153, 500) == (
500,
assert color_util._white_levels_to_color_temperature(128, 0, 2000, 6535) == (
6535,
128,
)
assert color_util.while_levels_to_color_temperature(112, 143, 153, 500) == (
348,
# Only warm channel enabled -> warmest color temperature
assert color_util._white_levels_to_color_temperature(0, 255, 2000, 6535) == (
2000,
255,
)
assert color_util.while_levels_to_color_temperature(56, 72, 153, 500) == (
348,
assert color_util._white_levels_to_color_temperature(0, 128, 2000, 6535) == (
2000,
128,
)
assert color_util.while_levels_to_color_temperature(0, 0, 153, 500) == (
500,
assert color_util._white_levels_to_color_temperature(112, 143, 2000, 6535) == (
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,
)