mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Fix ESPHome color temperature precision for light entities (#91424)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
91611bbd3b
commit
983ff10541
@ -13,7 +13,7 @@ from aioesphomeapi import (
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_TEMP,
|
||||
ATTR_COLOR_TEMP_KELVIN,
|
||||
ATTR_EFFECT,
|
||||
ATTR_FLASH,
|
||||
ATTR_RGB_COLOR,
|
||||
@ -98,6 +98,20 @@ _COLOR_MODE_MAPPING = {
|
||||
}
|
||||
|
||||
|
||||
def _mired_to_kelvin(mired_temperature: float) -> int:
|
||||
"""Convert absolute mired shift to degrees kelvin.
|
||||
|
||||
This function rounds the converted value instead of flooring the value as
|
||||
is done in homeassistant.util.color.color_temperature_mired_to_kelvin().
|
||||
|
||||
If the value of mired_temperature is less than or equal to zero, return
|
||||
the original value to avoid a divide by zero.
|
||||
"""
|
||||
if mired_temperature <= 0:
|
||||
return round(mired_temperature)
|
||||
return round(1000000 / mired_temperature)
|
||||
|
||||
|
||||
def _color_mode_to_ha(mode: int) -> str:
|
||||
"""Convert an esphome color mode to a HA color mode constant.
|
||||
|
||||
@ -198,8 +212,9 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
|
||||
# need to convert cw+ww part to white+color_temp
|
||||
white = data["white"] = max(cw, ww)
|
||||
if white != 0:
|
||||
min_ct = self.min_mireds
|
||||
max_ct = self.max_mireds
|
||||
static_info = self._static_info
|
||||
min_ct = static_info.min_mireds
|
||||
max_ct = static_info.max_mireds
|
||||
ct_ratio = ww / (cw + ww)
|
||||
data["color_temperature"] = min_ct + ct_ratio * (max_ct - min_ct)
|
||||
color_modes = _filter_color_modes(
|
||||
@ -216,8 +231,9 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
|
||||
if (transition := kwargs.get(ATTR_TRANSITION)) is not None:
|
||||
data["transition_length"] = transition
|
||||
|
||||
if (color_temp := kwargs.get(ATTR_COLOR_TEMP)) is not None:
|
||||
data["color_temperature"] = color_temp
|
||||
if (color_temp_k := kwargs.get(ATTR_COLOR_TEMP_KELVIN)) is not None:
|
||||
# Do not use kelvin_to_mired here to prevent precision loss
|
||||
data["color_temperature"] = 1000000.0 / color_temp_k
|
||||
if _filter_color_modes(color_modes, LightColorCapability.COLOR_TEMPERATURE):
|
||||
color_modes = _filter_color_modes(
|
||||
color_modes, LightColorCapability.COLOR_TEMPERATURE
|
||||
@ -349,6 +365,12 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
|
||||
"""Return the CT color value in mireds."""
|
||||
return round(self._state.color_temperature)
|
||||
|
||||
@property
|
||||
@esphome_state_property
|
||||
def color_temp_kelvin(self) -> int:
|
||||
"""Return the CT color value in Kelvin."""
|
||||
return _mired_to_kelvin(self._state.color_temperature)
|
||||
|
||||
@property
|
||||
@esphome_state_property
|
||||
def effect(self) -> str | None:
|
||||
@ -385,3 +407,6 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
|
||||
self._attr_effect_list = static_info.effects
|
||||
self._attr_min_mireds = round(static_info.min_mireds)
|
||||
self._attr_max_mireds = round(static_info.max_mireds)
|
||||
if ColorMode.COLOR_TEMP in supported:
|
||||
self._attr_min_color_temp_kelvin = _mired_to_kelvin(static_info.max_mireds)
|
||||
self._attr_max_color_temp_kelvin = _mired_to_kelvin(static_info.min_mireds)
|
||||
|
@ -76,8 +76,8 @@ async def test_light_color_temp(
|
||||
key=1,
|
||||
name="my light",
|
||||
unique_id="my_light",
|
||||
min_mireds=153,
|
||||
max_mireds=400,
|
||||
min_mireds=153.846161,
|
||||
max_mireds=370.370361,
|
||||
supported_color_modes=[
|
||||
LightColorCapability.COLOR_TEMPERATURE
|
||||
| LightColorCapability.ON_OFF
|
||||
@ -90,7 +90,7 @@ async def test_light_color_temp(
|
||||
key=1,
|
||||
state=True,
|
||||
brightness=100,
|
||||
color_temperature=153,
|
||||
color_temperature=153.846161,
|
||||
color_mode=LightColorCapability.COLOR_TEMPERATURE,
|
||||
)
|
||||
]
|
||||
@ -106,10 +106,93 @@ async def test_light_color_temp(
|
||||
assert state.state == STATE_ON
|
||||
attributes = state.attributes
|
||||
|
||||
assert attributes[ATTR_MAX_MIREDS] == 400
|
||||
assert attributes[ATTR_MIN_MIREDS] == 153
|
||||
assert attributes[ATTR_MIN_COLOR_TEMP_KELVIN] == 2500
|
||||
assert attributes[ATTR_MAX_COLOR_TEMP_KELVIN] == 6535
|
||||
assert attributes[ATTR_MAX_MIREDS] == 370
|
||||
|
||||
assert attributes[ATTR_MIN_COLOR_TEMP_KELVIN] == 2700
|
||||
assert attributes[ATTR_MAX_COLOR_TEMP_KELVIN] == 6500
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "light.test_my_light"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_client.light_command.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
key=1,
|
||||
state=True,
|
||||
color_mode=LightColorCapability.COLOR_TEMPERATURE
|
||||
| LightColorCapability.ON_OFF
|
||||
| LightColorCapability.BRIGHTNESS,
|
||||
)
|
||||
]
|
||||
)
|
||||
mock_client.light_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "light.test_my_light"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_client.light_command.assert_has_calls([call(key=1, state=False)])
|
||||
mock_client.light_command.reset_mock()
|
||||
|
||||
|
||||
async def test_light_color_temp_legacy(
|
||||
hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
|
||||
) -> None:
|
||||
"""Test a legacy light entity that does supports color temp."""
|
||||
mock_client.api_version = APIVersion(1, 7)
|
||||
entity_info = [
|
||||
LightInfo(
|
||||
object_id="mylight",
|
||||
key=1,
|
||||
name="my light",
|
||||
unique_id="my_light",
|
||||
min_mireds=153.846161,
|
||||
max_mireds=370.370361,
|
||||
supported_color_modes=[
|
||||
LightColorCapability.COLOR_TEMPERATURE
|
||||
| LightColorCapability.ON_OFF
|
||||
| LightColorCapability.BRIGHTNESS
|
||||
],
|
||||
legacy_supports_brightness=True,
|
||||
legacy_supports_color_temperature=True,
|
||||
)
|
||||
]
|
||||
states = [
|
||||
LightState(
|
||||
key=1,
|
||||
state=True,
|
||||
brightness=100,
|
||||
red=1,
|
||||
green=1,
|
||||
blue=1,
|
||||
white=1,
|
||||
cold_white=1,
|
||||
color_temperature=153.846161,
|
||||
color_mode=19,
|
||||
)
|
||||
]
|
||||
user_service = []
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
user_service=user_service,
|
||||
states=states,
|
||||
)
|
||||
state = hass.states.get("light.test_my_light")
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
attributes = state.attributes
|
||||
|
||||
assert attributes[ATTR_MIN_MIREDS] == 153
|
||||
assert attributes[ATTR_MAX_MIREDS] == 370
|
||||
|
||||
assert attributes[ATTR_MIN_COLOR_TEMP_KELVIN] == 2700
|
||||
assert attributes[ATTR_MAX_COLOR_TEMP_KELVIN] == 6500
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
|
Loading…
x
Reference in New Issue
Block a user