mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-12 06:44:04 +02:00
With transition_until_sleep and sleep_rgb, after sunset, use RGB colors (#656)
* With transition_until_sleep and sleep_rgb, after sunset, use RGB colors Closes #624 * Add comment * Add test
This commit is contained in:
parent
c1528ec10a
commit
97f388608a
2 changed files with 125 additions and 5 deletions
|
|
@ -1123,6 +1123,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
and adapt_color
|
||||
and not (prefer_rgb_color and "color" in features)
|
||||
and not (sleep_rgb and "color" in features)
|
||||
and not (self._settings["force_rgb_color"] and "color" in features)
|
||||
):
|
||||
_LOGGER.debug("%s: Setting color_temp of light %s", self._name, light)
|
||||
attributes = self.hass.states.get(light).attributes
|
||||
|
|
@ -1296,9 +1297,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
else:
|
||||
filtered_lights.append(light)
|
||||
|
||||
_LOGGER.debug("%s: filtered_lights: '%s'", self._name, filtered_lights)
|
||||
if not filtered_lights:
|
||||
return
|
||||
|
||||
adapt_brightness = self.adapt_brightness_switch.is_on
|
||||
adapt_color = self.adapt_color_switch.is_on
|
||||
|
||||
|
|
@ -1337,6 +1338,13 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
else:
|
||||
_fire_manual_control_event(self, light, context)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s: Calling _adapt_light from _update_attrs_and_maybe_adapt_lights:"
|
||||
" '%s' with transition %s",
|
||||
self._name,
|
||||
light,
|
||||
transition,
|
||||
)
|
||||
await self._adapt_light(light, transition, context=context)
|
||||
|
||||
async def _sleep_mode_switch_state_event_action(self, event: Event) -> None:
|
||||
|
|
@ -1487,6 +1495,17 @@ class SimpleSwitch(SwitchEntity, RestoreEntity):
|
|||
self._state = False
|
||||
|
||||
|
||||
def lerp_color(
|
||||
rgb1: tuple[int, int, int], rgb2: tuple[int, int, int], t: float
|
||||
) -> tuple[int, int, int]:
|
||||
"""Linearly interpolate between two RGB colors."""
|
||||
return (
|
||||
int(rgb1[0] + t * (rgb2[0] - rgb1[0])),
|
||||
int(rgb1[1] + t * (rgb2[1] - rgb1[1])),
|
||||
int(rgb1[2] + t * (rgb2[2] - rgb1[2])),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SunLightSettings:
|
||||
"""Track the state of the sun and associated light settings."""
|
||||
|
|
@ -1654,15 +1673,29 @@ class SunLightSettings:
|
|||
if transition is not None
|
||||
else self.calc_percent(0)
|
||||
)
|
||||
rgb_color: tuple[float, float, float]
|
||||
# Variable `force_rgb_color` is needed for RGB color after sunset (if enabled)
|
||||
force_rgb_color = False
|
||||
brightness_pct = self.calc_brightness_pct(percent, is_sleep)
|
||||
if is_sleep:
|
||||
color_temp_kelvin = self.sleep_color_temp
|
||||
rgb_color: tuple[float, float, float] = self.sleep_rgb_color
|
||||
rgb_color = self.sleep_rgb_color
|
||||
elif (
|
||||
self.sleep_rgb_or_color_temp == "rgb_color"
|
||||
and self.adapt_until_sleep
|
||||
and percent < 0
|
||||
):
|
||||
# Feature requested in
|
||||
# https://github.com/basnijholt/adaptive-lighting/issues/624
|
||||
# This will result in a perceptible jump in color at sunset and sunrise
|
||||
# because the `color_temperature_to_rgb` function is not 100% accurate.
|
||||
min_color_rgb = color_temperature_to_rgb(self.min_color_temp)
|
||||
rgb_color = lerp_color(min_color_rgb, self.sleep_rgb_color, percent)
|
||||
color_temp_kelvin = self.calc_color_temp_kelvin(percent)
|
||||
force_rgb_color = True
|
||||
else:
|
||||
color_temp_kelvin = self.calc_color_temp_kelvin(percent)
|
||||
rgb_color: tuple[float, float, float] = color_temperature_to_rgb(
|
||||
color_temp_kelvin
|
||||
)
|
||||
rgb_color = color_temperature_to_rgb(color_temp_kelvin)
|
||||
# backwards compatibility for versions < 1.3.1 - see #403
|
||||
color_temp_mired: float = math.floor(1000000 / color_temp_kelvin)
|
||||
xy_color: tuple[float, float] = color_RGB_to_xy(*rgb_color)
|
||||
|
|
@ -1675,6 +1708,7 @@ class SunLightSettings:
|
|||
"xy_color": xy_color,
|
||||
"hs_color": hs_color,
|
||||
"sun_position": percent,
|
||||
"force_rgb_color": force_rgb_color,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ from custom_components.adaptive_lighting.const import (
|
|||
ADAPT_BRIGHTNESS_SWITCH,
|
||||
ADAPT_COLOR_SWITCH,
|
||||
ATTR_ADAPTIVE_LIGHTING_MANAGER,
|
||||
CONF_ADAPT_UNTIL_SLEEP,
|
||||
CONF_AUTORESET_CONTROL,
|
||||
CONF_DETECT_NON_HA_CHANGES,
|
||||
CONF_INITIAL_TRANSITION,
|
||||
|
|
@ -64,6 +65,7 @@ from custom_components.adaptive_lighting.const import (
|
|||
CONF_MIN_COLOR_TEMP,
|
||||
CONF_PREFER_RGB_COLOR,
|
||||
CONF_SEPARATE_TURN_ON_COMMANDS,
|
||||
CONF_SLEEP_RGB_OR_COLOR_TEMP,
|
||||
CONF_SUNRISE_OFFSET,
|
||||
CONF_SUNRISE_TIME,
|
||||
CONF_SUNSET_TIME,
|
||||
|
|
@ -74,6 +76,7 @@ from custom_components.adaptive_lighting.const import (
|
|||
DEFAULT_NAME,
|
||||
DEFAULT_SLEEP_BRIGHTNESS,
|
||||
DEFAULT_SLEEP_COLOR_TEMP,
|
||||
DEFAULT_SLEEP_RGB_COLOR,
|
||||
DOMAIN,
|
||||
SERVICE_APPLY,
|
||||
SERVICE_CHANGE_SWITCH_SETTINGS,
|
||||
|
|
@ -1430,6 +1433,7 @@ async def test_proactive_adaptation(hass):
|
|||
{
|
||||
ATTR_BRIGHTNESS_PCT: 67,
|
||||
ATTR_COLOR_TEMP_KELVIN: 3448,
|
||||
"force_rgb_color": False,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -1464,6 +1468,7 @@ async def test_proactive_adaptation_with_separate_commands(hass):
|
|||
{
|
||||
ATTR_BRIGHTNESS_PCT: 67,
|
||||
ATTR_COLOR_TEMP_KELVIN: 3448,
|
||||
"force_rgb_color": False,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -1615,3 +1620,84 @@ async def test_two_switches_for_single_light(hass):
|
|||
after_color_temp = attrs[ATTR_COLOR_TEMP_KELVIN]
|
||||
assert before_brightness != after_brightness
|
||||
assert before_color_temp != after_color_temp
|
||||
|
||||
|
||||
async def test_adapt_until_sleep_and_rgb_colors(hass):
|
||||
"""Test setting up the Adaptive Lighting switches with different timezones.
|
||||
|
||||
Also test the (sleep) brightness and color temperature settings.
|
||||
"""
|
||||
lat, long, timezone = (32.87336, -117.22743, "US/Pacific")
|
||||
await config_util.async_process_ha_core_config(
|
||||
hass,
|
||||
{"latitude": lat, "longitude": long, "time_zone": timezone},
|
||||
)
|
||||
switch, lights = await setup_lights_and_switch(
|
||||
hass,
|
||||
{
|
||||
CONF_SUNRISE_TIME: datetime.time(SUNRISE.hour),
|
||||
CONF_SUNSET_TIME: datetime.time(SUNSET.hour),
|
||||
CONF_ADAPT_UNTIL_SLEEP: True,
|
||||
CONF_SLEEP_RGB_OR_COLOR_TEMP: "rgb_color",
|
||||
},
|
||||
)
|
||||
|
||||
context = switch.create_context("test") # needs to be passed to update method
|
||||
min_color_temp = switch._sun_light_settings.min_color_temp
|
||||
|
||||
sunset = SUNSET.replace(tzinfo=dt_util.DEFAULT_TIME_ZONE).astimezone(dt_util.UTC)
|
||||
before_sunset = sunset - datetime.timedelta(hours=1)
|
||||
after_sunset = sunset + datetime.timedelta(hours=1)
|
||||
sunrise = SUNRISE.replace(tzinfo=dt_util.DEFAULT_TIME_ZONE).astimezone(dt_util.UTC)
|
||||
before_sunrise = sunrise - datetime.timedelta(hours=1)
|
||||
after_sunrise = sunrise + datetime.timedelta(hours=1)
|
||||
|
||||
async def patch_time_and_update(time):
|
||||
with patch("homeassistant.util.dt.utcnow", return_value=time):
|
||||
await switch._update_attrs_and_maybe_adapt_lights(context=context)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# At sunset the brightness should be max and color_temp at the smallest value
|
||||
await patch_time_and_update(sunset)
|
||||
assert not switch._settings["force_rgb_color"]
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] == DEFAULT_MAX_BRIGHTNESS
|
||||
assert switch._settings["color_temp_kelvin"] == min_color_temp
|
||||
|
||||
# One hour before sunset the brightness should be max and color_temp
|
||||
# not at the smallest value yet.
|
||||
await patch_time_and_update(before_sunset)
|
||||
assert not switch._settings["force_rgb_color"]
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] == DEFAULT_MAX_BRIGHTNESS
|
||||
assert switch._settings["color_temp_kelvin"] > min_color_temp
|
||||
assert "color_temp_kelvin" in switch.manager.last_service_data[ENTITY_LIGHT]
|
||||
|
||||
# One hour after sunset the brightness should be down
|
||||
await patch_time_and_update(after_sunset)
|
||||
assert switch._settings["force_rgb_color"]
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] < DEFAULT_MAX_BRIGHTNESS
|
||||
assert "rgb_color" in switch.manager.last_service_data[ENTITY_LIGHT]
|
||||
|
||||
# At sunrise the brightness should be max and color_temp at the smallest value
|
||||
await patch_time_and_update(sunrise)
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] == DEFAULT_MAX_BRIGHTNESS
|
||||
assert switch._settings["color_temp_kelvin"] == min_color_temp
|
||||
assert "color_temp_kelvin" in switch.manager.last_service_data[ENTITY_LIGHT]
|
||||
|
||||
# One hour before sunrise the brightness should smaller than max
|
||||
# and color_temp at the min value.
|
||||
await patch_time_and_update(before_sunrise)
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] < DEFAULT_MAX_BRIGHTNESS
|
||||
assert "rgb_color" in switch.manager.last_service_data[ENTITY_LIGHT]
|
||||
|
||||
# One hour after sunrise the brightness should be up
|
||||
await patch_time_and_update(after_sunrise)
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] == DEFAULT_MAX_BRIGHTNESS
|
||||
assert switch._settings["color_temp_kelvin"] > min_color_temp
|
||||
assert "color_temp_kelvin" in switch.manager.last_service_data[ENTITY_LIGHT]
|
||||
|
||||
# Turn on sleep mode which make the brightness and color_temp
|
||||
# deterministic regardless of the time
|
||||
await switch.sleep_mode_switch.async_turn_on()
|
||||
await switch._update_attrs_and_maybe_adapt_lights(context=context)
|
||||
assert switch._settings[ATTR_BRIGHTNESS_PCT] == DEFAULT_SLEEP_BRIGHTNESS
|
||||
assert switch._settings["rgb_color"] == DEFAULT_SLEEP_RGB_COLOR
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue