Use Kelvin instead of Mired, default since core=2022.11 (#375)

* Use Kelvin instead of Mired, default since core=2022.11

* Fix attributes

* Use ATTR_COLOR_TEMP_KELVIN in tests

* use kelvin in tests

* no duplicate

* Round to nearest 5
This commit is contained in:
Bas Nijholt 2022-12-11 16:54:57 -08:00 committed by GitHub
commit 1ef55ca180
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 32 deletions

View file

@ -21,7 +21,7 @@ from homeassistant.components.light import (
ATTR_BRIGHTNESS_STEP,
ATTR_BRIGHTNESS_STEP_PCT,
ATTR_COLOR_NAME,
ATTR_COLOR_TEMP,
ATTR_COLOR_TEMP_KELVIN,
ATTR_HS_COLOR,
ATTR_KELVIN,
ATTR_RGB_COLOR,
@ -85,7 +85,6 @@ from homeassistant.helpers.template import area_entities
from homeassistant.util import slugify
from homeassistant.util.color import (
color_RGB_to_xy,
color_temperature_kelvin_to_mired,
color_temperature_to_rgb,
color_xy_to_hs,
)
@ -155,12 +154,12 @@ SCAN_INTERVAL = timedelta(seconds=10)
# Consider it a significant change when attribute changes more than
BRIGHTNESS_CHANGE = 25 # ≈10% of total range
COLOR_TEMP_CHANGE = 20 # ≈5% of total range
COLOR_TEMP_CHANGE = 100 # ≈3% of total range (2000-6500)
RGB_REDMEAN_CHANGE = 80 # ≈10% of total range
COLOR_ATTRS = { # Should ATTR_PROFILE be in here?
ATTR_COLOR_NAME,
ATTR_COLOR_TEMP,
ATTR_COLOR_TEMP_KELVIN,
ATTR_HS_COLOR,
ATTR_KELVIN,
ATTR_RGB_COLOR,
@ -228,7 +227,7 @@ def _split_service_data(service_data, adapt_brightness, adapt_color):
if adapt_brightness:
service_data_brightness = service_data.copy()
service_data_brightness.pop(ATTR_RGB_COLOR, None)
service_data_brightness.pop(ATTR_COLOR_TEMP, None)
service_data_brightness.pop(ATTR_COLOR_TEMP_KELVIN, None)
service_datas.append(service_data_brightness)
if not service_datas: # neither adapt_brightness nor adapt_color
@ -491,11 +490,11 @@ def _attributes_have_changed(
if (
adapt_color
and ATTR_COLOR_TEMP in old_attributes
and ATTR_COLOR_TEMP in new_attributes
and ATTR_COLOR_TEMP_KELVIN in old_attributes
and ATTR_COLOR_TEMP_KELVIN in new_attributes
):
last_color_temp = old_attributes[ATTR_COLOR_TEMP]
current_color_temp = new_attributes[ATTR_COLOR_TEMP]
last_color_temp = old_attributes[ATTR_COLOR_TEMP_KELVIN]
current_color_temp = new_attributes[ATTR_COLOR_TEMP_KELVIN]
if abs(current_color_temp - last_color_temp) > COLOR_TEMP_CHANGE:
_LOGGER.debug(
"Color temperature of '%s' significantly changed from %s to %s with"
@ -530,7 +529,8 @@ def _attributes_have_changed(
ATTR_RGB_COLOR in old_attributes and ATTR_RGB_COLOR not in new_attributes
)
switched_to_rgb_color = (
ATTR_COLOR_TEMP in old_attributes and ATTR_COLOR_TEMP not in new_attributes
ATTR_COLOR_TEMP_KELVIN in old_attributes
and ATTR_COLOR_TEMP_KELVIN not in new_attributes
)
if switched_color_temp or switched_to_rgb_color:
# Light switched from RGB mode to color_temp or visa versa
@ -824,10 +824,11 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
):
_LOGGER.debug("%s: Setting color_temp of light %s", self._name, light)
attributes = self.hass.states.get(light).attributes
min_mireds, max_mireds = attributes["min_mireds"], attributes["max_mireds"]
color_temp_mired = self._settings["color_temp_mired"]
color_temp_mired = max(min(color_temp_mired, max_mireds), min_mireds)
service_data[ATTR_COLOR_TEMP] = color_temp_mired
min_kelvin = attributes["min_color_temp_kelvin"]
max_kelvin = attributes["max_color_temp_kelvin"]
color_temp_kelvin = self._settings["color_temp_kelvin"]
color_temp_kelvin = max(min(color_temp_kelvin, max_kelvin), min_kelvin)
service_data[ATTR_COLOR_TEMP_KELVIN] = color_temp_kelvin
elif "color" in features and adapt_color:
_LOGGER.debug("%s: Setting rgb_color of light %s", self._name, light)
service_data[ATTR_RGB_COLOR] = self._settings["rgb_color"]
@ -1229,16 +1230,17 @@ class SunLightSettings:
percent = 1 + percent
return (delta_brightness * percent) + self.min_brightness
def calc_color_temp_kelvin(self, percent: float) -> float:
def calc_color_temp_kelvin(self, percent: float) -> int:
"""Calculate the color temperature in Kelvin."""
if percent > 0:
delta = self.max_color_temp - self.min_color_temp
return (delta * percent) + self.min_color_temp
ct = (delta * percent) + self.min_color_temp
return 5 * round(ct / 5) # round to nearest 5
return self.min_color_temp
def get_settings(
self, is_sleep, transition
) -> dict[str, float | tuple[float, float] | tuple[float, float, float]]:
) -> dict[str, float | int | tuple[float, float] | tuple[float, float, float]]:
"""Get all light settings.
Calculating all values takes <0.5ms.
@ -1257,13 +1259,11 @@ class SunLightSettings:
rgb_color: tuple[float, float, float] = color_temperature_to_rgb(
color_temp_kelvin
)
color_temp_mired: float = color_temperature_kelvin_to_mired(color_temp_kelvin)
xy_color: tuple[float, float] = color_RGB_to_xy(*rgb_color)
hs_color: tuple[float, float] = color_xy_to_hs(*xy_color)
return {
"brightness_pct": brightness_pct,
"color_temp_kelvin": color_temp_kelvin,
"color_temp_mired": color_temp_mired,
"rgb_color": rgb_color,
"xy_color": xy_color,
"hs_color": hs_color,
@ -1397,7 +1397,7 @@ class TurnOnOffListener:
# settings the light will be later *or* the second event might indicate a
# final state. The latter case happens for example when a light was
# called with a color_temp outside of its range (and HA reports the
# incorrect 'min_mireds' and 'max_mireds', which happens e.g., for
# incorrect 'min_kelvin' and 'max_kelvin', which happens e.g., for
# Philips Hue White GU10 Bluetooth lights).
old_state: list[State] | None = self.last_state_change.get(entity_id)
if (

View file

@ -41,7 +41,7 @@ from homeassistant.components.demo.light import DemoLight
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_BRIGHTNESS_PCT,
ATTR_COLOR_TEMP,
ATTR_COLOR_TEMP_KELVIN,
ATTR_RGB_COLOR,
)
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
@ -62,6 +62,7 @@ from homeassistant.const import (
from homeassistant.core import Context, State
from homeassistant.helpers import entity_registry
from homeassistant.setup import async_setup_component
from homeassistant.util.color import color_temperature_mired_to_kelvin
import homeassistant.util.dt as dt_util
import pytest
@ -338,7 +339,10 @@ async def test_light_settings(hass):
state.entity_id
]
assert state.attributes[ATTR_BRIGHTNESS] == last_service_data[ATTR_BRIGHTNESS]
assert state.attributes[ATTR_COLOR_TEMP] == last_service_data[ATTR_COLOR_TEMP]
assert (
state.attributes[ATTR_COLOR_TEMP_KELVIN]
== last_service_data[ATTR_COLOR_TEMP_KELVIN]
)
# Turn off "sleep mode"
await hass.services.async_call(
@ -371,7 +375,10 @@ async def test_light_settings(hass):
last_service_data = switch.turn_on_off_listener.last_service_data[
state.entity_id
]
assert state.attributes[ATTR_COLOR_TEMP] == last_service_data[ATTR_COLOR_TEMP]
assert (
state.attributes[ATTR_COLOR_TEMP_KELVIN]
== last_service_data[ATTR_COLOR_TEMP_KELVIN]
)
# At sunset the brightness should be max and color_temp at the smallest value
light_states = await patch_time_and_get_updated_states(sunset)
@ -481,7 +488,9 @@ async def test_manual_control(hass):
return (light._brightness + 100) % 255
def increased_color_temp():
return max((light._ct + 100) % light.max_mireds, light.min_mireds)
return max(
(light._ct + 100) % light.max_color_temp_kelvin, light.min_color_temp_kelvin
)
# Nothing is manually controlled
await update()
@ -520,7 +529,13 @@ async def test_manual_control(hass):
await switch.adapt_brightness_switch.async_turn_off()
await turn_light(True, brightness=increased_brightness())
assert not manual_control[ENTITY_LIGHT]
await turn_light(True, color_temp=(light._ct + 100) % 500)
mired_range = (light.min_color_temp_kelvin, light.max_color_temp_kelvin)
kelvin_range = (
color_temperature_mired_to_kelvin(mired_range[1]),
color_temperature_mired_to_kelvin(mired_range[0]),
)
ptp_kelvin = kelvin_range[1] - kelvin_range[0]
await turn_light(True, color_temp_kelvin=(light._ct + 100) % ptp_kelvin)
assert manual_control[ENTITY_LIGHT]
await switch.adapt_brightness_switch.async_turn_on() # turn on again
@ -573,7 +588,9 @@ async def test_apply_service(hass):
return (light._brightness + 100) % 255
def increased_color_temp():
return max((light._ct + 100) % light.max_mireds, light.min_mireds)
return max(
(light._ct + 100) % light.max_color_temp_kelvin, light.min_color_temp_kelvin
)
async def change_light():
await hass.services.async_call(
@ -582,7 +599,7 @@ async def test_apply_service(hass):
{
ATTR_ENTITY_ID: entity_id,
ATTR_BRIGHTNESS: increased_brightness(),
ATTR_COLOR_TEMP: increased_color_temp(),
ATTR_COLOR_TEMP_KELVIN: increased_color_temp(),
},
blocking=True,
)
@ -613,7 +630,7 @@ async def test_apply_service(hass):
await apply(adapt_color=True, adapt_brightness=False)
new_state = hass.states.get(entity_id).attributes
assert old_state[ATTR_BRIGHTNESS] == new_state[ATTR_BRIGHTNESS]
assert old_state[ATTR_COLOR_TEMP] != new_state[ATTR_COLOR_TEMP]
assert old_state[ATTR_COLOR_TEMP_KELVIN] != new_state[ATTR_COLOR_TEMP_KELVIN]
# Test only changing brightness
await change_light()
@ -621,7 +638,7 @@ async def test_apply_service(hass):
await apply(adapt_color=False, adapt_brightness=True)
new_state = hass.states.get(entity_id).attributes
assert old_state[ATTR_BRIGHTNESS] != new_state[ATTR_BRIGHTNESS]
assert old_state[ATTR_COLOR_TEMP] == new_state[ATTR_COLOR_TEMP]
assert old_state[ATTR_COLOR_TEMP_KELVIN] == new_state[ATTR_COLOR_TEMP_KELVIN]
async def test_switch_off_on_off(hass):
@ -733,11 +750,15 @@ def test_is_our_context():
def test_attributes_have_changed():
"""Test _attributes_have_changed function."""
attributes_1 = {ATTR_BRIGHTNESS: 1, ATTR_RGB_COLOR: (0, 0, 0), ATTR_COLOR_TEMP: 100}
attributes_1 = {
ATTR_BRIGHTNESS: 1,
ATTR_RGB_COLOR: (0, 0, 0),
ATTR_COLOR_TEMP_KELVIN: 100,
}
attributes_2 = {
ATTR_BRIGHTNESS: 100,
ATTR_RGB_COLOR: (255, 0, 0),
ATTR_COLOR_TEMP: 300,
ATTR_COLOR_TEMP_KELVIN: 300,
}
kwargs = dict(
light="light.test",
@ -756,7 +777,7 @@ def test_attributes_have_changed():
)
# Switch from rgb_color to color_temp
assert _attributes_have_changed(
old_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP: 100},
old_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP_KELVIN: 100},
new_attributes={ATTR_BRIGHTNESS: 1, ATTR_RGB_COLOR: (0, 0, 0)},
**kwargs,
)