From 3e67d8194c51c59c710d4dbdec7b713ca12bfff7 Mon Sep 17 00:00:00 2001 From: Benjamin Auquite Date: Mon, 10 Apr 2023 07:19:02 -0500 Subject: [PATCH] Revert #568 --- README.md | 64 +++--- custom_components/adaptive_lighting/const.py | 17 -- .../adaptive_lighting/strings.json | 2 - custom_components/adaptive_lighting/switch.py | 196 ++++-------------- .../adaptive_lighting/translations/en.json | 2 - tests/test_switch.py | 141 +++++-------- 6 files changed, 125 insertions(+), 297 deletions(-) diff --git a/README.md b/README.md index 63ab3303..8b52061d 100644 --- a/README.md +++ b/README.md @@ -92,39 +92,37 @@ The YAML and frontend configuration methods support all of the options listed be -| Variable name | Description | Default | Type | -|:-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------|:-------------------------------------| -| `lights` | List of light entity_ids to be controlled (may be empty). 🌟 | `[]` | list of `entity_id`s | -| `watched_lights` | Use this dictionary of lights to check for manually controlled events in addition to the main lights 🌟 Example: {light.watch_light: light.main_light} will fire manually controlled events to light.main_light | `{}` | list of `entity_id`s | -| `prefer_rgb_color` | Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 | `False` | `bool` | -| `include_config_in_attributes` | Show all options as attributes on the switch in Home Assistant when set to `true`. 📝 | `False` | `bool` | -| `initial_transition` | Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️ | `1` | `float` 0-6553 | -| `sleep_transition` | Duration of transition when "sleep mode" is toggled in seconds. 😴 | `1` | `float` 0-6553 | -| `transition` | Duration of transition when lights change, in seconds. 🕑 | `45` | `float` 0-6553 | -| `transition_until_sleep` | When enabled, Adaptive Lighting will treat sleep settings as the minimum, transitioning to these values after sunset. 🌙 | `False` | `bool` | -| `interval` | Frequency to adapt the lights, in seconds. 🔄 | `90` | `int > 0` | -| `min_brightness` | Minimum brightness percentage. 💡 | `1` | `int` 1-100 | -| `max_brightness` | Maximum brightness percentage. 💡 | `100` | `int` 1-100 | -| `min_color_temp` | Warmest color temperature in Kelvin. 🔥 | `2000` | `int` 1000-10000 | -| `max_color_temp` | Coldest color temperature in Kelvin. ❄️ | `5500` | `int` 1000-10000 | -| `sleep_brightness` | Brightness percentage of lights in sleep mode. 😴 | `1` | `int` 1-100 | -| `sleep_rgb_or_color_temp` | Use either `"rgb_color"` or `"color_temp"` in sleep mode. 🌙 | `color_temp` | one of `['color_temp', 'rgb_color']` | -| `sleep_color_temp` | Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴 | `1000` | `int` 1000-10000 | -| `sleep_rgb_color` | RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is "rgb_color"). 🌈 | `[255, 56, 0]` | RGB color | -| `sunrise_time` | Set a fixed time (HH:MM:SS) for sunrise. 🌅 | `None` | `str` | -| `max_sunrise_time` | Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier real sunrises. 🌅 | `None` | `str` | -| `sunrise_offset` | Adjust sunrise time with a positive or negative offset in seconds. ⏰ | `0` | `int` | -| `sunset_time` | Set a fixed time (HH:MM:SS) for sunset. 🌇 | `None` | `str` | -| `min_sunset_time` | Set the earliest virtual sunset time (HH:MM:SS), allowing for later real sunsets. 🌇 | `None` | `str` | -| `sunset_offset` | Adjust sunset time with a positive or negative offset in seconds. ⏰ | `0` | `int` | -| `only_once` | Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄 | `False` | `bool` | -| `take_over_control` | Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒 | `True` | `bool` | -| `alt_detect_method` | alt_detect_method: When true, will check for any significant changes in the opposite direction of where adaptive-lighting tried to adapt last. This is an alternative to 'detect_non_ha_changes' (default: false) | `False` | `bool` | -| `detect_non_ha_changes` | Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️ | `False` | `bool` | -| `separate_turn_on_commands` | Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀 | `False` | `bool` | -| `send_split_delay` | Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️ | `0` | `int` 0-10000 | -| `adapt_delay` | Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️ | `0` | `float > 0` | -| `autoreset_control_seconds` | Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️ | `0` | `int` 0-31536000 | +| Variable name | Description | Default | Type | +|:-------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------|:-------------------------------------| +| `lights` | List of light entity_ids to be controlled (may be empty). 🌟 | `[]` | list of `entity_id`s | +| `prefer_rgb_color` | Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 | `False` | `bool` | +| `include_config_in_attributes` | Show all options as attributes on the switch in Home Assistant when set to `true`. 📝 | `False` | `bool` | +| `initial_transition` | Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️ | `1` | `float` 0-6553 | +| `sleep_transition` | Duration of transition when "sleep mode" is toggled in seconds. 😴 | `1` | `float` 0-6553 | +| `transition` | Duration of transition when lights change, in seconds. 🕑 | `45` | `float` 0-6553 | +| `transition_until_sleep` | When enabled, Adaptive Lighting will treat sleep settings as the minimum, transitioning to these values after sunset. 🌙 | `False` | `bool` | +| `interval` | Frequency to adapt the lights, in seconds. 🔄 | `90` | `int > 0` | +| `min_brightness` | Minimum brightness percentage. 💡 | `1` | `int` 1-100 | +| `max_brightness` | Maximum brightness percentage. 💡 | `100` | `int` 1-100 | +| `min_color_temp` | Warmest color temperature in Kelvin. 🔥 | `2000` | `int` 1000-10000 | +| `max_color_temp` | Coldest color temperature in Kelvin. ❄️ | `5500` | `int` 1000-10000 | +| `sleep_brightness` | Brightness percentage of lights in sleep mode. 😴 | `1` | `int` 1-100 | +| `sleep_rgb_or_color_temp` | Use either `"rgb_color"` or `"color_temp"` in sleep mode. 🌙 | `color_temp` | one of `['color_temp', 'rgb_color']` | +| `sleep_color_temp` | Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴 | `1000` | `int` 1000-10000 | +| `sleep_rgb_color` | RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is "rgb_color"). 🌈 | `[255, 56, 0]` | RGB color | +| `sunrise_time` | Set a fixed time (HH:MM:SS) for sunrise. 🌅 | `None` | `str` | +| `max_sunrise_time` | Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier real sunrises. 🌅 | `None` | `str` | +| `sunrise_offset` | Adjust sunrise time with a positive or negative offset in seconds. ⏰ | `0` | `int` | +| `sunset_time` | Set a fixed time (HH:MM:SS) for sunset. 🌇 | `None` | `str` | +| `min_sunset_time` | Set the earliest virtual sunset time (HH:MM:SS), allowing for later real sunsets. 🌇 | `None` | `str` | +| `sunset_offset` | Adjust sunset time with a positive or negative offset in seconds. ⏰ | `0` | `int` | +| `only_once` | Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄 | `False` | `bool` | +| `take_over_control` | Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒 | `True` | `bool` | +| `detect_non_ha_changes` | Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️ | `False` | `bool` | +| `separate_turn_on_commands` | Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀 | `False` | `bool` | +| `send_split_delay` | Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️ | `0` | `int` 0-10000 | +| `adapt_delay` | Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️ | `0` | `float > 0` | +| `autoreset_control_seconds` | Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️ | `0` | `int` 0-31536000 | diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 66b3f16f..b1f2d5aa 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -36,12 +36,6 @@ "Requires `take_over_control`. 🕵️" ) -CONF_ALT_DETECT_METHOD, DEFAULT_ALT_DETECT_METHOD = "alt_detect_method", False -DOCS[CONF_ALT_DETECT_METHOD] = ( - "alt_detect_method: When true, will check for any significant changes in the opposite direction" - " of where adaptive-lighting tried to adapt last." - " This is an alternative to 'detect_non_ha_changes' (default: false)" -) CONF_INCLUDE_CONFIG_IN_ATTRIBUTES, DEFAULT_INCLUDE_CONFIG_IN_ATTRIBUTES = ( "include_config_in_attributes", False, @@ -208,15 +202,6 @@ "Set to 0 to disable. ⏲️" ) -CONF_WATCHED_LIGHTS, DEFAULT_WATCHED_LIGHTS = "watched_lights", {} -DOCS[CONF_WATCHED_LIGHTS] = ( - "Use this dictionary of lights to check for manually controlled events" - " in addition to the main lights 🌟" - " Requires `alt_detect_method` to be True." - " Example: `watched_lights: {light.watch_light: light.main_light}` will fire" - " manually controlled events to `light.main_light`" -) - SLEEP_MODE_SWITCH = "sleep_mode_switch" ADAPT_COLOR_SWITCH = "adapt_color_switch" ADAPT_BRIGHTNESS_SWITCH = "adapt_brightness_switch" @@ -284,7 +269,6 @@ def replace_zero_with_none(val: int) -> None: VALIDATION_TUPLES = [ (CONF_LIGHTS, DEFAULT_LIGHTS, cv.entity_ids), - (CONF_WATCHED_LIGHTS, DEFAULT_WATCHED_LIGHTS, dict[str]), (CONF_PREFER_RGB_COLOR, DEFAULT_PREFER_RGB_COLOR, bool), (CONF_INCLUDE_CONFIG_IN_ATTRIBUTES, DEFAULT_INCLUDE_CONFIG_IN_ATTRIBUTES, bool), (CONF_INITIAL_TRANSITION, DEFAULT_INITIAL_TRANSITION, VALID_TRANSITION), @@ -325,7 +309,6 @@ def replace_zero_with_none(val: int) -> None: (CONF_ONLY_ONCE, DEFAULT_ONLY_ONCE, bool), (CONF_FLAT_LIMITS, DEFAULT_FLAT_LIMITS, bool), (CONF_TAKE_OVER_CONTROL, DEFAULT_TAKE_OVER_CONTROL, bool), - (CONF_ALT_DETECT_METHOD, DEFAULT_ALT_DETECT_METHOD, bool), (CONF_DETECT_NON_HA_CHANGES, DEFAULT_DETECT_NON_HA_CHANGES, bool), (CONF_SEPARATE_TURN_ON_COMMANDS, DEFAULT_SEPARATE_TURN_ON_COMMANDS, bool), (CONF_SEND_SPLIT_DELAY, DEFAULT_SEND_SPLIT_DELAY, int_between(0, 10000)), diff --git a/custom_components/adaptive_lighting/strings.json b/custom_components/adaptive_lighting/strings.json index 7fb0367a..54af5e11 100644 --- a/custom_components/adaptive_lighting/strings.json +++ b/custom_components/adaptive_lighting/strings.json @@ -20,7 +20,6 @@ "description": "All settings for a Adaptive Lighting component. The option names correspond with the YAML settings. No options are shown if you have this entry defined in YAML.", "data": { "lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟", - "watched_lights": "watched_lights: Use this dictionary of lights to check for manually controlled events in addition to the main lights 🌟 Example: {light.watch_light: light.main_light} will fire manually controlled events to light.main_light", "prefer_rgb_color": "prefer_rgb_color: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝", "initial_transition": "initial_transition: Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️", @@ -44,7 +43,6 @@ "sunset_offset": "sunset_offset: Adjust sunset time with a positive or negative offset in seconds. ⏰", "only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄", "take_over_control": "take_over_control: Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒", - "alt_detect_method": "alt_detect_method: alt_detect_method: When true, will check for any significant changes in the opposite direction of where adaptive-lighting tried to adapt last. This is an alternative to 'detect_non_ha_changes' (default: false)", "detect_non_ha_changes": "detect_non_ha_changes: Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️", "separate_turn_on_commands": "separate_turn_on_commands: Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀", "send_split_delay": "send_split_delay: Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 11d38434..3ba33f22 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -106,7 +106,6 @@ CONF_ADAPT_COLOR_TEMP_UNTIL_SLEEP, CONF_ADAPT_DELAY, CONF_ADAPT_UNTIL_SLEEP, - CONF_ALT_DETECT_METHOD, CONF_AUTORESET_CONTROL, CONF_DETECT_NON_HA_CHANGES, CONF_FLAT_LIMITS, @@ -138,7 +137,6 @@ CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, - CONF_WATCHED_LIGHTS, CONST_COLOR, DOMAIN, ENTITY_LIGHT_TURN_ON_SCHEMA, @@ -722,15 +720,6 @@ def color_difference_redmean( return math.sqrt(red_term + green_term + blue_term) -def check_direction_change(last: int, current: int, last_adapt_value: int) -> bool: - _LOGGER.debug("compare direction: current value %s to last value %s", current, last) - if last_adapt_value < last: # Value adapting down - return current > last or current < last_adapt_value - elif last_adapt_value > last: # Value adapting up - return current < last or current > last_adapt_value - return False - - # All comparisons should be done with RGB since # converting anything to color temp is inaccurate. def _convert_attributes(attributes: dict[str, Any]) -> dict[str, Any]: @@ -773,7 +762,6 @@ def _attributes_have_changed( adapt_brightness: bool, adapt_color: bool, context: Context, - last_adapt_attempt=None, ) -> bool: if adapt_color: old_attributes, new_attributes = _add_missing_attributes( @@ -788,33 +776,15 @@ def _attributes_have_changed( last_brightness = old_attributes[ATTR_BRIGHTNESS] current_brightness = new_attributes[ATTR_BRIGHTNESS] if abs(current_brightness - last_brightness) > BRIGHTNESS_CHANGE: - if last_adapt_attempt: - changed = check_direction_change( - last_brightness, - current_brightness, - last_adapt_attempt[ATTR_BRIGHTNESS], - ) - _LOGGER.debug( - "altdetect: Brightness of '%s' changed from %s to %s intended %s with" - " context.id='%s' Significant? %s", - light, - last_brightness, - current_brightness, - last_adapt_attempt[ATTR_BRIGHTNESS], - context.id, - changed, - ) - return changed - else: - _LOGGER.debug( - "Brightness of '%s' significantly changed from %s to %s with" - " context.id='%s'", - light, - last_brightness, - current_brightness, - context.id, - ) - return True + _LOGGER.debug( + "Brightness of '%s' significantly changed from %s to %s with" + " context.id='%s'", + light, + last_brightness, + current_brightness, + context.id, + ) + return True if ( adapt_color @@ -824,33 +794,15 @@ def _attributes_have_changed( 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: - if last_adapt_attempt: - changed = check_direction_change( - last_color_temp, - current_color_temp, - last_adapt_attempt[ATTR_COLOR_TEMP_KELVIN], - ) - _LOGGER.debug( - "altdetect: Color temperature of '%s' changed from %s to %s intended %s with" - " context.id='%s' Significant? %s", - light, - last_color_temp, - current_color_temp, - last_adapt_attempt[ATTR_COLOR_TEMP_KELVIN], - context.id, - changed, - ) - return changed - else: - _LOGGER.debug( - "Color temperature of '%s' significantly changed from %s to %s with" - " context.id='%s'", - light, - last_color_temp, - current_color_temp, - context.id, - ) - return True + _LOGGER.debug( + "Color temperature of '%s' significantly changed from %s to %s with" + " context.id='%s'", + light, + last_color_temp, + current_color_temp, + context.id, + ) + return True if ( adapt_color @@ -975,20 +927,13 @@ def _set_changeable_settings( self._transition = data[CONF_TRANSITION] self._adapt_delay = data[CONF_ADAPT_DELAY] self._send_split_delay = data[CONF_SEND_SPLIT_DELAY] - self._watched_lights = data[CONF_WATCHED_LIGHTS] self._take_over_control = data[CONF_TAKE_OVER_CONTROL] - self._alt_detect_method = data[CONF_ALT_DETECT_METHOD] self._detect_non_ha_changes = data[CONF_DETECT_NON_HA_CHANGES] - if not data[CONF_TAKE_OVER_CONTROL] and ( - data[CONF_ALT_DETECT_METHOD] or data[CONF_DETECT_NON_HA_CHANGES] - ): - _LOGGER.warn( - "%s: Config mismatch: 'alt_detect_method: true'" - " OR '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.", + if not data[CONF_TAKE_OVER_CONTROL] and data[CONF_DETECT_NON_HA_CHANGES]: + _LOGGER.warning( + "%s: Config mismatch: 'detect_non_ha_changes: true' " + "requires 'take_over_control' to be enabled. Adjusting config " + "and continuing setup with `take_over_control: true`.", self._name, ) self._take_over_control = True @@ -1241,8 +1186,6 @@ async def _update_manual_control_and_maybe_adapt( transition: int | None, force: bool, context: Context | None, - adapt_brightness: bool | None = None, - adapt_color: bool | None = None, ) -> None: assert context is not None _LOGGER.debug( @@ -1257,17 +1200,13 @@ async def _update_manual_control_and_maybe_adapt( adapt_brightness = self.adapt_brightness_switch.is_on adapt_color = self.adapt_color_switch.is_on - all_lights = {k: k for k in lights} - all_lights.update(self._watched_lights) - - for wlight, mlight in all_lights.items(): - if not is_on(self.hass, mlight): + for light in lights: + if not is_on(self.hass, light): continue manually_controlled = self.turn_on_off_listener.is_manually_controlled( self, - wlight, - mlight, + light, force, adapt_brightness, adapt_color, @@ -1278,8 +1217,7 @@ async def _update_manual_control_and_maybe_adapt( and not force and await self.turn_on_off_listener.significant_change( self, - wlight, - mlight, + light, adapt_brightness, adapt_color, context, @@ -1291,15 +1229,13 @@ async def _update_manual_control_and_maybe_adapt( _LOGGER.debug( "%s: '%s' is being manually controlled, stop adapting, context.id=%s.", self._name, - mlight, + light, context.id, ) else: - _fire_manual_control_event(self, mlight, context) + _fire_manual_control_event(self, light, context) else: - await self._adapt_light( - wlight, transition, force=force, context=context - ) + await self._adapt_light(light, transition, force=force, context=context) async def _adapt_light( self, @@ -2103,19 +2039,18 @@ async def state_changed_event_listener(self, event: Event) -> None: def is_manually_controlled( self, switch: AdaptiveSwitch, - wlight: str, - mlight: str, + light: str, force: bool, adapt_brightness: bool, adapt_color: bool, ) -> bool: """Check if the light has been 'on' and is now manually controlled.""" - manual_control = self.manual_control.setdefault(wlight, False) + manual_control = self.manual_control.setdefault(light, False) if manual_control: # Manually controlled until light is turned on and off return True - turn_on_event = self.turn_on_event.get(wlight) + turn_on_event = self.turn_on_event.get(light) if ( turn_on_event is not None and not is_our_context(turn_on_event.context) @@ -2128,13 +2063,13 @@ def is_manually_controlled( # Light was already on and 'light.turn_on' was not called by # the adaptive_lighting integration. manual_control = True - _fire_manual_control_event(switch, wlight, turn_on_event.context) + _fire_manual_control_event(switch, light, turn_on_event.context) _LOGGER.debug( "'%s' was already on and 'light.turn_on' was not called by the" " adaptive_lighting integration (context.id='%s'), the Adaptive" " Lighting will stop adapting the light until the switch or the" " light turns off and then on again.", - mlight, + light, turn_on_event.context.id, ) return manual_control @@ -2142,8 +2077,7 @@ def is_manually_controlled( async def significant_change( self, switch: AdaptiveSwitch, - wlight: str, - mlight: str, + light: str, adapt_brightness: bool, adapt_color: bool, context: Context, @@ -2155,77 +2089,35 @@ async def significant_change( detected, we mark the light as 'manually controlled' until the light or switch is turned 'off' and 'on' again. """ - last_service_data = self.last_service_data.get(wlight) + last_service_data = self.last_service_data.get(light) if last_service_data is None: return compare_to = functools.partial( _attributes_have_changed, - light=wlight, + light=light, adapt_brightness=adapt_brightness, adapt_color=adapt_color, context=context, ) - if switch._alt_detect_method or wlight != mlight: - old_states: list[State] = self.last_state_change[wlight] - _LOGGER.debug("Total state changes detected: %s", len(old_states)) - _LOGGER.debug( - "%s: 'alt_detect_method: true', check all state changes made to light %s", - switch._name, - wlight, - ) - for index, old_state in enumerate(old_states): - # The first entry of old_states should always be the - # same as last_service_data[light], and can be ignored. - if index <= 1: - continue - _LOGGER.debug( - "%s: checking for a manual change between index %s and %s...", - switch._name, - index, - index - 1, - ) - prior_state = old_states[index - 1] - if compare_to( - old_attributes=prior_state.attributes, - new_attributes=old_state.attributes, - last_adapt_attempt=last_service_data, - ): - _LOGGER.info( - "Found unexpected state_change event for %s nr. %s (context.id=%s)" - " old_state=%s\nprior_state=%s", - wlight, - index, - context.id, - old_state, - prior_state, - ) - _LOGGER.info( - "We will now set %s as manually controlled. (context.id=%s)", - wlight, - context.id, - ) - return True # Update state and check for a manual change not done in HA. # Ensure HASS is correctly updating your light's state with # 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: - if wlight != mlight: - return # only supported with alt_detect_method _LOGGER.debug( "%s: 'detect_non_ha_changes: true', calling update_entity(%s)" " and check if it's last adapt succeeded.", switch._name, - wlight, + light, ) # This update_entity probably isn't necessary now that we're checking # if transitions finished from our last adapt. - await self.hass.helpers.entity_component.async_update_entity(wlight) - refreshed_state = self.hass.states.get(wlight) + await self.hass.helpers.entity_component.async_update_entity(light) + refreshed_state = self.hass.states.get(light) _LOGGER.debug( "%s: Current state of %s: %s", switch._name, - wlight, + light, refreshed_state, ) changed = compare_to( @@ -2235,7 +2127,7 @@ async def significant_change( if changed: _LOGGER.debug( "State of '%s' didn't change wrt 'last_service_data' (context.id=%s)", - wlight, + light, context.id, ) return True @@ -2243,7 +2135,7 @@ async def significant_change( "%s: Light '%s' correctly matches our last adapt's service data, continuing..." " context.id=%s.", switch._name, - wlight, + light, context.id, ) return False diff --git a/custom_components/adaptive_lighting/translations/en.json b/custom_components/adaptive_lighting/translations/en.json index 91b59d6a..38fb7b0e 100644 --- a/custom_components/adaptive_lighting/translations/en.json +++ b/custom_components/adaptive_lighting/translations/en.json @@ -21,7 +21,6 @@ "description": "All settings for a Adaptive Lighting component. The option names correspond with the YAML settings. No options are shown if you have the adaptive_lighting entry defined in your YAML configuration.", "data": { "lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟", - "watched_lights": "watched_lights: Use this dictionary of lights to check for manually controlled events in addition to the main lights 🌟 Example: {light.watch_light: light.main_light} will fire manually controlled events to light.main_light", "prefer_rgb_color": "prefer_rgb_color: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝", "initial_transition": "initial_transition: Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️", @@ -45,7 +44,6 @@ "sunset_offset": "sunset_offset: Adjust sunset time with a positive or negative offset in seconds. ⏰", "only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄", "take_over_control": "take_over_control: Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒", - "alt_detect_method": "alt_detect_method: alt_detect_method: When true, will check for any significant changes in the opposite direction of where adaptive-lighting tried to adapt last. This is an alternative to 'detect_non_ha_changes' (default: false)", "detect_non_ha_changes": "detect_non_ha_changes: Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️", "separate_turn_on_commands": "separate_turn_on_commands: Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀", "send_split_delay": "send_split_delay: Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", diff --git a/tests/test_switch.py b/tests/test_switch.py index 770699a0..8b105d44 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -11,7 +11,6 @@ ADAPT_BRIGHTNESS_SWITCH, ADAPT_COLOR_SWITCH, ATTR_TURN_ON_OFF_LISTENER, - CONF_ALT_DETECT_METHOD, CONF_AUTORESET_CONTROL, CONF_DETECT_NON_HA_CHANGES, CONF_INITIAL_TRANSITION, @@ -23,7 +22,6 @@ CONF_SUNRISE_OFFSET, CONF_SUNRISE_TIME, CONF_SUNSET_TIME, - CONF_TAKE_OVER_CONTROL, CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, @@ -214,9 +212,7 @@ 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_ALT_DETECT_METHOD: False, - CONF_DETECT_NON_HA_CHANGES: False, - CONF_TAKE_OVER_CONTROL: True, + CONF_DETECT_NON_HA_CHANGES: True, CONF_PREFER_RGB_COLOR: False, CONF_MIN_COLOR_TEMP: 2500, # to not coincide with sleep_color_temp **(extra_conf or {}), @@ -670,7 +666,7 @@ def increased_color_temp(): @pytest.mark.dependency(depends=[*GLOBAL_TEST_DEPENDENCIES, "test_manual_control"]) async def test_auto_reset_manual_control(hass): switch, (light, *_) = await setup_lights_and_switch( - hass, {CONF_AUTORESET_CONTROL: 0.2} + hass, {CONF_AUTORESET_CONTROL: 0.1} ) context = switch.create_context("test") # needs to be passed to update method manual_control = switch.turn_on_off_listener.manual_control @@ -915,7 +911,7 @@ async def test_state_change_handlers(hass): # [Config options]: transition_used = 2 - total_events = 6 + total_events = 5 async def set_brightness(val: int): # 'Unsafe' set but we know what we're doing. @@ -1036,95 +1032,58 @@ async def update(force: bool = False): assert listener.transition_timers.get(ENTITY_LIGHT) # 5. Execute some checks during a transition - - for i in range(2): - if i == 0: - _LOGGER.debug("Test detect_non_ha_changes before a transition:") - 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 - elif i == 1: - _LOGGER.debug("Test alt_detect_method before a transition:") - switch._take_over_control = True - assert switch._take_over_control - switch._detect_non_ha_changes = False - assert not switch._detect_non_ha_changes - switch._alt_detect_method = True - assert switch._alt_detect_method - await asyncio.sleep(transition_used / 3) - # Ensure the timer still exists - timer = listener.transition_timers.get(ENTITY_LIGHT) - assert timer and timer.is_running() - last_service_data = deepcopy(current_service_data) - await update() - assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT] - await update() - assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT] - timer = listener.transition_timers.get(ENTITY_LIGHT) - assert timer and timer.is_running() - # Ensure the light did not adapt during the transition. - assert last_service_data == current_service_data + _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 + await asyncio.sleep(transition_used / 3) + # Ensure the timer still exists + timer = listener.transition_timers.get(ENTITY_LIGHT) + assert timer and timer.is_running() + last_service_data = deepcopy(current_service_data) + await update() + assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT] + await update() + assert not switch.turn_on_off_listener.manual_control[ENTITY_LIGHT] + timer = listener.transition_timers.get(ENTITY_LIGHT) + assert timer and timer.is_running() + # Ensure the light did not adapt during the transition. + assert last_service_data == current_service_data # 6. Assert everything after the transition finishes. await asyncio.sleep(transition_used) + assert listener.last_state_change.get(ENTITY_LIGHT) + assert len(listener.last_state_change[ENTITY_LIGHT]) == total_events + # Timer should be done and reset now. + # This is the assert that I can't fix. + timer = listener.transition_timers.get(ENTITY_LIGHT) + assert not timer or not timer.is_running() + + # 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] - for i in range(2): - if i == 0: - _LOGGER.debug("Test detect_non_ha_changes after a transition:") - 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 - elif i == 1: - _LOGGER.debug("Test alt_detect_method after a transition:") - switch._take_over_control = True - assert switch._take_over_control - switch._detect_non_ha_changes = False - assert not switch._detect_non_ha_changes - switch._alt_detect_method = True - assert switch._alt_detect_method - - assert listener.last_state_change.get(ENTITY_LIGHT) - if i == 1: - total_events = 2 - assert len(listener.last_state_change[ENTITY_LIGHT]) == total_events - # Timer should be done and reset now. - timer = listener.transition_timers.get(ENTITY_LIGHT) - assert not timer or not timer.is_running() - - # 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] - - await turn_light(True, brightness=50) - _LOGGER.debug("Test: Brightness set to %s", 50) + await turn_light(True, brightness=50) + _LOGGER.debug("Test: Brightness set to %s", 50) - # On next update ENTITY_LIGHT should be marked as manually controlled - await update(force=False) - 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 - ) - assert switch.turn_on_off_listener.manual_control[ENTITY_LIGHT] + # On next update ENTITY_LIGHT should be marked as manually controlled + await update(force=False) + 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 + assert switch.turn_on_off_listener.manual_control[ENTITY_LIGHT] @pytest.mark.dependency(