mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-11 22:34:04 +02:00
Compare commits
20 commits
main
...
wait_for_t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96f59f0900 | ||
|
|
a649198cf4 | ||
|
|
26c32ac3d8 |
||
|
|
3731c99936 | ||
|
|
b4c6bf8a25 | ||
|
|
b986b3f77c | ||
|
|
4479761ad7 | ||
|
|
2f0bad82ff | ||
|
|
cebf31a203 | ||
|
|
836595051d | ||
|
|
3aa2f3242b | ||
|
|
732fc17c7f | ||
|
|
1571dfaeda | ||
|
|
120efff71e | ||
|
|
30c62bf759 | ||
|
|
8e847e71eb | ||
|
|
22bae9ed84 | ||
|
|
8b2fcf93be | ||
|
|
d7aecc9e7f |
||
|
|
52275bfee3 |
3 changed files with 200 additions and 81 deletions
|
|
@ -96,10 +96,10 @@ The YAML and frontend configuration methods support all of the options listed be
|
||||||
| `lights` | List of light entities to be controlled by Adaptive Lighting (may be empty). 🌟 | `[]` | list of `entity_id`s |
|
| `lights` | List of light entities to be controlled by Adaptive Lighting (may be empty). 🌟 | `[]` | list of `entity_id`s |
|
||||||
| `prefer_rgb_color` | Use RGB color adjustment instead of native light color temperature. 🌈 | `False` | `bool` |
|
| `prefer_rgb_color` | Use RGB color adjustment instead of native light color temperature. 🌈 | `False` | `bool` |
|
||||||
| `include_config_in_attributes` | Show all options as attributes on the switch in Home Assistant when set to `true`. 📝 | `False` | `bool` |
|
| `include_config_in_attributes` | Show all options as attributes on the switch in Home Assistant when set to `true`. 📝 | `False` | `bool` |
|
||||||
| `adapt_until_sleep` | When `true`, Adaptive Lighting will use the sleep settings as the minimum, and transition to these values past the sunset | `False` | `bool` |
|
|
||||||
| `initial_transition` | Duration of the first transition when lights turn from `off` to `on`. ⏲️ | `1` | `float` 0-6553 |
|
| `initial_transition` | Duration of the first transition when lights turn from `off` to `on`. ⏲️ | `1` | `float` 0-6553 |
|
||||||
| `sleep_transition` | Duration of transition when 'sleep mode' is toggled. 😴 | `1` | `float` 0-6553 |
|
| `sleep_transition` | Duration of transition when 'sleep mode' is toggled. 😴 | `1` | `float` 0-6553 |
|
||||||
| `transition` | Duration of transition when lights change, in seconds. 🕑 | `45` | `float` 0-6553 |
|
| `transition` | Duration of transition when lights change, in seconds. 🕑 | `45` | `float` 0-6553 |
|
||||||
|
| `transition_until_sleep` | When checked, Adaptive Lighting will use the sleep settings as the minimum, and transition to these values past the sunset | `False` | `bool` |
|
||||||
| `interval` | Frequency to adapt the lights, in seconds. 🔄 | `90` | `int > 0` |
|
| `interval` | Frequency to adapt the lights, in seconds. 🔄 | `90` | `int > 0` |
|
||||||
| `min_brightness` | Minimum brightness percentage. 💡 | `1` | `int` 1-100 |
|
| `min_brightness` | Minimum brightness percentage. 💡 | `1` | `int` 1-100 |
|
||||||
| `max_brightness` | Maximum brightness percentage. 💡 | `100` | `int` 1-100 |
|
| `max_brightness` | Maximum brightness percentage. 💡 | `100` | `int` 1-100 |
|
||||||
|
|
|
||||||
|
|
@ -818,10 +818,22 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
||||||
self._only_once = data[CONF_ONLY_ONCE]
|
self._only_once = data[CONF_ONLY_ONCE]
|
||||||
self._prefer_rgb_color = data[CONF_PREFER_RGB_COLOR]
|
self._prefer_rgb_color = data[CONF_PREFER_RGB_COLOR]
|
||||||
self._separate_turn_on_commands = data[CONF_SEPARATE_TURN_ON_COMMANDS]
|
self._separate_turn_on_commands = data[CONF_SEPARATE_TURN_ON_COMMANDS]
|
||||||
self._take_over_control = data[CONF_TAKE_OVER_CONTROL]
|
|
||||||
self._transition = data[CONF_TRANSITION]
|
self._transition = data[CONF_TRANSITION]
|
||||||
self._adapt_delay = data[CONF_ADAPT_DELAY]
|
self._adapt_delay = data[CONF_ADAPT_DELAY]
|
||||||
self._send_split_delay = data[CONF_SEND_SPLIT_DELAY]
|
self._send_split_delay = data[CONF_SEND_SPLIT_DELAY]
|
||||||
|
self._take_over_control = data[CONF_TAKE_OVER_CONTROL]
|
||||||
|
self._detect_non_ha_changes = data[CONF_DETECT_NON_HA_CHANGES]
|
||||||
|
if not data[CONF_TAKE_OVER_CONTROL] and (data[CONF_DETECT_NON_HA_CHANGES]):
|
||||||
|
_LOGGER.warn(
|
||||||
|
"%s: Config mismatch: 'detect_non_ha_changes: true' "
|
||||||
|
" are set in config, however required"
|
||||||
|
" variable 'take_over_control' is turned off. Please check your"
|
||||||
|
" configuration to ensure desired functionality. We will now"
|
||||||
|
" enable 'take_over_control' and continue setting up the"
|
||||||
|
" adaptive-lighting integration normally.",
|
||||||
|
self._name,
|
||||||
|
)
|
||||||
|
self._take_over_control = True
|
||||||
self._auto_reset_manual_control_time = data[CONF_AUTORESET_CONTROL]
|
self._auto_reset_manual_control_time = data[CONF_AUTORESET_CONTROL]
|
||||||
self._expand_light_groups() # updates manual control timers
|
self._expand_light_groups() # updates manual control timers
|
||||||
_loc = get_astral_location(self.hass)
|
_loc = get_astral_location(self.hass)
|
||||||
|
|
@ -1132,10 +1144,21 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = self._lights
|
lights = self._lights
|
||||||
if (self._only_once and not force) or not lights:
|
|
||||||
|
if not force:
|
||||||
|
if self._only_once:
|
||||||
return
|
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)
|
||||||
|
|
||||||
|
if not len(lights):
|
||||||
|
return
|
||||||
|
|
||||||
await self._adapt_lights(lights, transition, force, context)
|
await self._adapt_lights(lights, transition, force, context)
|
||||||
|
|
||||||
async def _adapt_lights(
|
async def _adapt_lights(
|
||||||
|
|
@ -1544,6 +1567,9 @@ class TurnOnOffListener:
|
||||||
self.auto_reset_manual_control_timers: dict[str, _AsyncSingleShotTimer] = {}
|
self.auto_reset_manual_control_timers: dict[str, _AsyncSingleShotTimer] = {}
|
||||||
self.auto_reset_manual_control_times: dict[str, float] = {}
|
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,
|
# When a state is different `max_cnt_significant_changes` times in a row,
|
||||||
# mark it as manually_controlled.
|
# mark it as manually_controlled.
|
||||||
self.max_cnt_significant_changes = 2
|
self.max_cnt_significant_changes = 2
|
||||||
|
|
@ -1555,6 +1581,49 @@ class TurnOnOffListener:
|
||||||
EVENT_STATE_CHANGED, self.state_changed_event_listener
|
EVENT_STATE_CHANGED, self.state_changed_event_listener
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def start_transition_timer(self, light: str) -> None:
|
||||||
|
"""Mark a light as manually controlled."""
|
||||||
|
_LOGGER.debug("Start transition timer for %s", light)
|
||||||
|
last_service_data = self.last_service_data
|
||||||
|
if (
|
||||||
|
not last_service_data
|
||||||
|
or light not in last_service_data
|
||||||
|
or ATTR_TRANSITION not in last_service_data[light]
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
delay = last_service_data[light][ATTR_TRANSITION]
|
||||||
|
timer = self.transition_timers.get(light)
|
||||||
|
if timer is not None:
|
||||||
|
if delay is None: # Timer object exists, but should not anymore
|
||||||
|
timer.cancel()
|
||||||
|
self.transition_timers.pop(light)
|
||||||
|
else: # Timer object already exists, just update the delay and restart it
|
||||||
|
timer.delay = delay
|
||||||
|
timer.start()
|
||||||
|
elif delay is not None: # Timer object does not exist, create it
|
||||||
|
|
||||||
|
async def reset():
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Transition finished for light %s",
|
||||||
|
light,
|
||||||
|
)
|
||||||
|
# This part is optional, we could just wait for the next interval.
|
||||||
|
switches = _get_switches_with_lights(self.hass, [light])
|
||||||
|
for switch in switches:
|
||||||
|
if not switch.is_on:
|
||||||
|
continue
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
await switch._update_attrs_and_maybe_adapt_lights(
|
||||||
|
[light],
|
||||||
|
force=False,
|
||||||
|
context=switch.create_context("transit"),
|
||||||
|
)
|
||||||
|
|
||||||
|
timer = _AsyncSingleShotTimer(delay, reset)
|
||||||
|
self.transition_timers[light] = timer
|
||||||
|
timer.start()
|
||||||
|
|
||||||
def set_auto_reset_manual_control_times(self, lights: list[str], time: float):
|
def set_auto_reset_manual_control_times(self, lights: list[str], time: float):
|
||||||
"""Set the time after which the lights are automatically reset."""
|
"""Set the time after which the lights are automatically reset."""
|
||||||
if time == 0:
|
if time == 0:
|
||||||
|
|
@ -1622,7 +1691,6 @@ class TurnOnOffListener:
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
self.last_state_change.pop(light, None)
|
self.last_state_change.pop(light, None)
|
||||||
self.last_service_data.pop(light, None)
|
self.last_service_data.pop(light, None)
|
||||||
self.cnt_significant_changes[light] = 0
|
|
||||||
|
|
||||||
async def turn_on_off_event_listener(self, event: Event) -> None:
|
async def turn_on_off_event_listener(self, event: Event) -> None:
|
||||||
"""Track 'light.turn_off' and 'light.turn_on' service calls."""
|
"""Track 'light.turn_off' and 'light.turn_on' service calls."""
|
||||||
|
|
@ -1701,11 +1769,7 @@ class TurnOnOffListener:
|
||||||
new_state.context.id,
|
new_state.context.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if new_state is not None and new_state.state == STATE_ON:
|
||||||
new_state is not None
|
|
||||||
and new_state.state == STATE_ON
|
|
||||||
and is_our_context(new_state.context)
|
|
||||||
):
|
|
||||||
# It is possible to have multiple state change events with the same context.
|
# It is possible to have multiple state change events with the same context.
|
||||||
# This can happen because a `turn_on.light(brightness_pct=100, transition=30)`
|
# This can happen because a `turn_on.light(brightness_pct=100, transition=30)`
|
||||||
# event leads to an instant state change of
|
# event leads to an instant state change of
|
||||||
|
|
@ -1718,21 +1782,29 @@ class TurnOnOffListener:
|
||||||
# incorrect 'min_kelvin' and 'max_kelvin', which happens e.g., for
|
# incorrect 'min_kelvin' and 'max_kelvin', which happens e.g., for
|
||||||
# Philips Hue White GU10 Bluetooth lights).
|
# Philips Hue White GU10 Bluetooth lights).
|
||||||
old_state: list[State] | None = self.last_state_change.get(entity_id)
|
old_state: list[State] | None = self.last_state_change.get(entity_id)
|
||||||
|
if is_our_context(new_state.context):
|
||||||
if (
|
if (
|
||||||
old_state is not None
|
old_state is not None
|
||||||
and old_state[0].context.id == new_state.context.id
|
and old_state[0].context.id == new_state.context.id
|
||||||
):
|
):
|
||||||
# If there is already a state change event from this event (with this
|
|
||||||
# context) then append it to the already existing list.
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"State change event of '%s' is already in 'self.last_state_change' (%s)"
|
"TurnOnOffListener: State change event of '%s' is already"
|
||||||
|
" in 'self.last_state_change' (%s)"
|
||||||
" adding this state also",
|
" adding this state also",
|
||||||
entity_id,
|
entity_id,
|
||||||
new_state.context.id,
|
new_state.context.id,
|
||||||
)
|
)
|
||||||
self.last_state_change[entity_id].append(new_state)
|
self.last_state_change[entity_id].append(new_state)
|
||||||
else:
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"TurnOnOffListener: New adapt '%s' found for %s",
|
||||||
|
new_state,
|
||||||
|
entity_id,
|
||||||
|
)
|
||||||
self.last_state_change[entity_id] = [new_state]
|
self.last_state_change[entity_id] = [new_state]
|
||||||
|
self.start_transition_timer(entity_id)
|
||||||
|
elif old_state is not None:
|
||||||
|
self.last_state_change[entity_id].append(new_state)
|
||||||
|
|
||||||
def is_manually_controlled(
|
def is_manually_controlled(
|
||||||
self,
|
self,
|
||||||
|
|
@ -1787,64 +1859,58 @@ class TurnOnOffListener:
|
||||||
detected, we mark the light as 'manually controlled' until the light
|
detected, we mark the light as 'manually controlled' until the light
|
||||||
or switch is turned 'off' and 'on' again.
|
or switch is turned 'off' and 'on' again.
|
||||||
"""
|
"""
|
||||||
if light not in self.last_state_change:
|
last_service_data = self.last_service_data.get(light)
|
||||||
return False
|
if last_service_data is None:
|
||||||
old_states: list[State] = self.last_state_change[light]
|
return
|
||||||
await self.hass.helpers.entity_component.async_update_entity(light)
|
|
||||||
new_state = self.hass.states.get(light)
|
|
||||||
compare_to = functools.partial(
|
compare_to = functools.partial(
|
||||||
_attributes_have_changed,
|
_attributes_have_changed,
|
||||||
light=light,
|
light=light,
|
||||||
new_attributes=new_state.attributes,
|
|
||||||
adapt_brightness=adapt_brightness,
|
adapt_brightness=adapt_brightness,
|
||||||
adapt_color=adapt_color,
|
adapt_color=adapt_color,
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
for index, old_state in enumerate(old_states):
|
# Update state and check for a manual change not done in HA.
|
||||||
changed = compare_to(old_attributes=old_state.attributes)
|
# Ensure HASS is correctly updating your light's state with
|
||||||
if not changed:
|
# light.turn_on calls if any problems arise. This
|
||||||
|
# can happen e.g. using zigbee2mqtt with 'report: false' in device settings.
|
||||||
|
if switch._detect_non_ha_changes:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"State of '%s' didn't change wrt change event nr. %s (context.id=%s)",
|
"%s: 'detect_non_ha_changes: true', calling update_entity(%s)"
|
||||||
|
" and check if it's last adapt succeeded.",
|
||||||
|
switch._name,
|
||||||
light,
|
light,
|
||||||
index,
|
|
||||||
context.id,
|
|
||||||
)
|
)
|
||||||
break
|
# This update_entity probably isn't necessary now that we're checking
|
||||||
|
# if transitions finished from our last adapt.
|
||||||
last_service_data = self.last_service_data.get(light)
|
await self.hass.helpers.entity_component.async_update_entity(light)
|
||||||
if changed and last_service_data is not None:
|
refreshed_state = self.hass.states.get(light)
|
||||||
# It can happen that the state change events that are associated
|
_LOGGER.debug(
|
||||||
# with the last 'light.turn_on' call by this integration were not
|
"%s: Current state of %s: %s",
|
||||||
# final states. Possibly a later EVENT_STATE_CHANGED happened, where
|
switch._name,
|
||||||
# the correct target brightness/color was reached.
|
light,
|
||||||
changed = compare_to(old_attributes=last_service_data)
|
refreshed_state,
|
||||||
if not changed:
|
)
|
||||||
|
changed = compare_to(
|
||||||
|
old_attributes=last_service_data,
|
||||||
|
new_attributes=refreshed_state.attributes,
|
||||||
|
)
|
||||||
|
if changed:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"State of '%s' didn't change wrt 'last_service_data' (context.id=%s)",
|
"State of '%s' didn't change wrt 'last_service_data' (context.id=%s)",
|
||||||
light,
|
light,
|
||||||
context.id,
|
context.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
n_changes = self.cnt_significant_changes[light]
|
|
||||||
if changed:
|
|
||||||
self.cnt_significant_changes[light] += 1
|
|
||||||
if n_changes >= self.max_cnt_significant_changes:
|
|
||||||
# Only mark a light as significantly changing, if changed==True
|
|
||||||
# N times in a row. We do this because sometimes a state changes
|
|
||||||
# happens only *after* a new update interval has already started.
|
|
||||||
self.mark_as_manual_control(light)
|
self.mark_as_manual_control(light)
|
||||||
_fire_manual_control_event(switch, light, context, is_async=False)
|
_fire_manual_control_event(switch, light, context, is_async=False)
|
||||||
else:
|
return True
|
||||||
if n_changes > 1:
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"State of '%s' had 'cnt_significant_changes=%s' but the state"
|
"%s: Light '%s' correctly matches our last adapt's service data, continuing..."
|
||||||
" changed to the expected settings now",
|
" context.id=%s.",
|
||||||
|
switch._name,
|
||||||
light,
|
light,
|
||||||
n_changes,
|
context.id,
|
||||||
)
|
)
|
||||||
self.cnt_significant_changes[light] = 0
|
return False
|
||||||
|
|
||||||
return changed
|
|
||||||
|
|
||||||
async def maybe_cancel_adjusting(
|
async def maybe_cancel_adjusting(
|
||||||
self, entity_id: str, off_to_on_event: Event, on_to_off_event: Event | None
|
self, entity_id: str, off_to_on_event: Event, on_to_off_event: Event | None
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ from homeassistant.components.adaptive_lighting.const import (
|
||||||
CONF_SUNRISE_OFFSET,
|
CONF_SUNRISE_OFFSET,
|
||||||
CONF_SUNRISE_TIME,
|
CONF_SUNRISE_TIME,
|
||||||
CONF_SUNSET_TIME,
|
CONF_SUNSET_TIME,
|
||||||
|
CONF_TAKE_OVER_CONTROL,
|
||||||
CONF_TRANSITION,
|
CONF_TRANSITION,
|
||||||
CONF_TURN_ON_LIGHTS,
|
CONF_TURN_ON_LIGHTS,
|
||||||
CONF_USE_DEFAULTS,
|
CONF_USE_DEFAULTS,
|
||||||
|
|
@ -56,6 +57,7 @@ from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_AREA_ID,
|
ATTR_AREA_ID,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_SUPPORTED_FEATURES,
|
||||||
CONF_LIGHTS,
|
CONF_LIGHTS,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
|
|
@ -197,7 +199,8 @@ async def setup_lights_and_switch(hass, extra_conf=None):
|
||||||
CONF_SUNSET_TIME: datetime.time(SUNSET.hour),
|
CONF_SUNSET_TIME: datetime.time(SUNSET.hour),
|
||||||
CONF_INITIAL_TRANSITION: 0,
|
CONF_INITIAL_TRANSITION: 0,
|
||||||
CONF_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_PREFER_RGB_COLOR: False,
|
||||||
CONF_MIN_COLOR_TEMP: 2500, # to not coincide with sleep_color_temp
|
CONF_MIN_COLOR_TEMP: 2500, # to not coincide with sleep_color_temp
|
||||||
**(extra_conf or {}),
|
**(extra_conf or {}),
|
||||||
|
|
@ -762,25 +765,75 @@ async def test_significant_change(hass):
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
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)
|
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
|
||||||
|
|
||||||
|
# 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)
|
await turn_light(True)
|
||||||
await update(force=True) # removes manual control
|
|
||||||
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
|
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
|
||||||
|
|
||||||
# Change brightness by setting state (not using 'light.turn_on')
|
# Assert last_service_data got filled from update()
|
||||||
attributes = hass.states.get(ENTITY_LIGHT).attributes
|
await update(force=True)
|
||||||
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
|
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):
|
|
||||||
|
# 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)
|
await update(force=False)
|
||||||
assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
|
assert switch.turn_on_off_listener.last_service_data.get(ENTITY_LIGHT) is not None
|
||||||
# On next update the light should be marked as manually controlled
|
assert switch.turn_on_off_listener.manual_control[ENTITY_LIGHT]
|
||||||
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():
|
def test_color_difference_redmean():
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue