merge from working wait_for_transitions

This commit is contained in:
Benjamin Auquite 2023-04-02 23:48:04 -05:00
commit 9eb56463ec
2 changed files with 114 additions and 46 deletions

View file

@ -1135,6 +1135,16 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
if not len(lights):
return
if not force:
if self._only_once:
return
for light in lights:
# Don't adapt lights that haven't finished prior transitions.
if self.turn_on_off_listener.transition_timers.get(light):
lights.remove(light)
await self._adapt_lights(lights, transition, force, context)
if not force:
if self._only_once:
return
@ -1568,6 +1578,9 @@ class TurnOnOffListener:
self.auto_reset_manual_control_timers: dict[str, _AsyncSingleShotTimer] = {}
self.auto_reset_manual_control_times: dict[str, float] = {}
# Track light transitions
self.transition_timers: dict[str, _AsyncSingleShotTimer] = {}
# When a state is different `max_cnt_significant_changes` times in a row,
# mark it as manually_controlled.
self.max_cnt_significant_changes = 2
@ -1898,6 +1911,8 @@ class TurnOnOffListener:
light,
context.id,
)
self.manual_control[light] = True
_fire_manual_control_event(switch, light, context, is_async=False)
return True
_LOGGER.debug(
"%s: Light '%s' correctly matches our last adapt's service data, continuing..."

View file

@ -21,6 +21,7 @@ from homeassistant.components.adaptive_lighting.const import (
CONF_SUNRISE_OFFSET,
CONF_SUNRISE_TIME,
CONF_SUNSET_TIME,
CONF_TAKE_OVER_CONTROL,
CONF_TRANSITION,
CONF_TURN_ON_LIGHTS,
CONF_USE_DEFAULTS,
@ -56,6 +57,7 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
ATTR_AREA_ID,
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
CONF_LIGHTS,
CONF_NAME,
CONF_PLATFORM,
@ -197,7 +199,8 @@ async def setup_lights_and_switch(hass, extra_conf=None):
CONF_SUNSET_TIME: datetime.time(SUNSET.hour),
CONF_INITIAL_TRANSITION: 0,
CONF_TRANSITION: 0,
CONF_DETECT_NON_HA_CHANGES: True,
CONF_DETECT_NON_HA_CHANGES: False,
CONF_TAKE_OVER_CONTROL: True,
CONF_PREFER_RGB_COLOR: False,
CONF_MIN_COLOR_TEMP: 2500, # to not coincide with sleep_color_temp
**(extra_conf or {}),
@ -692,6 +695,101 @@ async def test_apply_service(hass):
assert old_state[ATTR_COLOR_TEMP_KELVIN] == new_state[ATTR_COLOR_TEMP_KELVIN]
async def test_significant_change(hass):
"""Test significant change."""
async def turn_light(state, **kwargs):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON if state else SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: ENTITY_LIGHT, **kwargs},
blocking=True,
)
await hass.async_block_till_done()
async def update(force):
await switch._update_attrs_and_maybe_adapt_lights(
transition=0,
context=switch.create_context("test"),
force=force,
)
await hass.async_block_till_done()
async def change_switch_settings(**kwargs):
await hass.services.async_call(
DOMAIN,
SERVICE_CHANGE_SWITCH_SETTINGS,
{
ATTR_ENTITY_ID: ENTITY_SWITCH,
**kwargs,
},
blocking=True,
)
await hass.async_block_till_done()
async def set_brightness(val: int):
hass.states.async_set(
ENTITY_LIGHT, "on", {ATTR_BRIGHTNESS: val, ATTR_SUPPORTED_FEATURES: 1}
)
await hass.async_block_till_done()
async def do_nothing(entity_id):
_LOGGER.debug("update entity successfully replaced for %s", entity_id)
return None
switch, (bed_light_instance, *_) = await setup_lights_and_switch(hass)
_LOGGER.debug("Test detect_non_ha_changes:")
switch._take_over_control = True
assert switch._take_over_control
switch._detect_non_ha_changes = True
assert switch._detect_non_ha_changes
switch._alt_detect_method = False
assert not switch._alt_detect_method
# build last service data
await update(force=False)
# force=True should not reset manual control.
await turn_light(True, brightness=40)
await turn_light(True, brightness=20)
await update(force=False)
assert switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
await update(force=True)
assert switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
# turn light off then on should reset manual control.
await turn_light(False)
await turn_light(True)
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
# Assert last_service_data got filled from update()
# Assert last_state_change got filled from update()
await update(force=True)
assert switch.turn_on_off_listener.last_service_data.get(ENTITY_LIGHT) is not None
assert switch.turn_on_off_listener.last_state_change.get(ENTITY_LIGHT) is not None
# Simulate a transition to 255 where the update() is already using brightness 255.
await set_brightness(240)
await set_brightness(244)
await set_brightness(247)
await set_brightness(250)
# last_state_change should have our state changes.
# Change brightness by async_set (not using 'light.turn_on')
new_brightness = 50
await set_brightness(new_brightness)
_LOGGER.debug("Test: Brightness set to %s", new_brightness)
# Override update_entity() to do nothing. Otherwise what happens is
# update_entity() refreshes the state to the last call of
# light.turn_on().
switch.hass.helpers.entity_component.async_update_entity = do_nothing
# On next update ENTITY_LIGHT should be marked as manually controlled
await update(force=False)
assert ENTITY_LIGHT in switch.turn_on_off_listener.last_state_change
assert switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
async def test_switch_off_on_off(hass):
"""Test switch rapid off_on_off."""
@ -742,51 +840,6 @@ async def test_switch_off_on_off(hass):
assert state == STATE_OFF
async def test_significant_change(hass):
"""Test significant change."""
async def turn_light(state, **kwargs):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON if state else SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: ENTITY_LIGHT, **kwargs},
blocking=True,
)
await hass.async_block_till_done()
async def update(force):
await switch._update_attrs_and_maybe_adapt_lights(
transition=0,
context=switch.create_context("test"),
force=force,
)
await hass.async_block_till_done()
switch, (bed_light_instance, *_) = await setup_lights_and_switch(hass)
await turn_light(True)
await update(force=True) # removes manual control
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
# Change brightness by setting state (not using 'light.turn_on')
attributes = hass.states.get(ENTITY_LIGHT).attributes
new_attributes = attributes.copy()
new_brightness = (attributes[ATTR_BRIGHTNESS] + 100) % 255
new_attributes[ATTR_BRIGHTNESS] = new_brightness
bed_light_instance._brightness = new_brightness
assert switch.turn_on_off_listener.last_service_data.get(ENTITY_LIGHT) is not None
for _ in range(switch.turn_on_off_listener.max_cnt_significant_changes):
await update(force=False)
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
# Check that the brightness was not changed since setting it manually
assert (
new_brightness == hass.states.get(ENTITY_LIGHT).attributes[ATTR_BRIGHTNESS]
)
# On next update the light should be marked as manually controlled
await update(force=False)
# TODO: the state should be `bool(manual_control) is True`
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
def test_color_difference_redmean():
"""Test color_difference_redmean function."""
for _ in range(10):