diff --git a/README.md b/README.md index be753a9f..56d9f05d 100644 --- a/README.md +++ b/README.md @@ -97,45 +97,45 @@ 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 | -| `interval` | Frequency to adapt the lights, in seconds. πŸ”„ | `90` | `int > 0` | -| `transition` | Duration of transition when lights change, in seconds. πŸ•‘ | `45` | `float` 0-6553 | -| `initial_transition` | Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️ | `1` | `float` 0-6553 | -| `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 | -| `prefer_rgb_color` | Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 | `False` | `bool` | -| `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 | -| `sleep_transition` | Duration of transition when "sleep mode" is toggled in seconds. 😴 | `1` | `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` | -| `sunrise_time` | Set a fixed time (HH:MM:SS) for sunrise. πŸŒ… | `None` | `str` | -| `min_sunrise_time` | Set the earliest virtual sunrise time (HH:MM:SS), allowing for later sunrises. πŸŒ… | `None` | `str` | -| `max_sunrise_time` | Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier 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 sunsets. πŸŒ‡ | `None` | `str` | -| `max_sunset_time` | Set the latest virtual sunset time (HH:MM:SS), allowing for earlier sunsets. πŸŒ‡ | `None` | `str` | -| `sunset_offset` | Adjust sunset time with a positive or negative offset in seconds. ⏰ | `0` | `int` | -| `brightness_mode` | Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). πŸ“ˆ | `default` | one of `['default', 'linear', 'tanh']` | -| `brightness_mode_time_dark` | (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. πŸ“ˆπŸ“‰ | `900` | `int` | -| `brightness_mode_time_light` | (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. πŸ“ˆπŸ“‰. | `3600` | `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` | Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. πŸ•΅οΈ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable this feature if you encounter such issues. | `False` | `bool` | -| `autoreset_control_seconds` | Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️ | `0` | `int` 0-31536000 | -| `only_once` | Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). πŸ”„ | `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` | -| `skip_redundant_commands` | Skip sending adaptation commands whose target state already equals the light's known state. Minimizes network traffic and improves the adaptation responsivity in some situations. πŸ“‰Disable if physical light states get out of sync with HA's recorded state. | `False` | `bool` | -| `multi_light_intercept` | Intercept and adapt `light.turn_on` calls that target multiple lights. βž—βš οΈ This might result in splitting up a single `light.turn_on` call into multiple calls, e.g., when lights are in different switches. | `True` | `bool` | -| `include_config_in_attributes` | Show all options as attributes on the switch in Home Assistant when set to `true`. πŸ“ | `False` | `bool` | +| Variable name | Description | Default | Type | +|:-------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------|:---------------------------------------| +| `lights` | List of light entity_ids to be controlled (may be empty). 🌟 | `[]` | list of `entity_id`s | +| `interval` | Frequency to adapt the lights, in seconds. πŸ”„ | `90` | `int > 0` | +| `transition` | Duration of transition when lights change, in seconds. πŸ•‘ | `45` | `float` 0-6553 | +| `initial_transition` | Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️ | `1` | `float` 0-6553 | +| `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 | +| `prefer_rgb_color` | Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 | `False` | `bool` | +| `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 | +| `sleep_transition` | Duration of transition when "sleep mode" is toggled in seconds. 😴 | `1` | `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` | +| `sunrise_time` | Set a fixed time (HH:MM:SS) for sunrise. πŸŒ… | `None` | `str` | +| `min_sunrise_time` | Set the earliest virtual sunrise time (HH:MM:SS), allowing for later sunrises. πŸŒ… | `None` | `str` | +| `max_sunrise_time` | Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier 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 sunsets. πŸŒ‡ | `None` | `str` | +| `max_sunset_time` | Set the latest virtual sunset time (HH:MM:SS), allowing for earlier sunsets. πŸŒ‡ | `None` | `str` | +| `sunset_offset` | Adjust sunset time with a positive or negative offset in seconds. ⏰ | `0` | `int` | +| `brightness_mode` | Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). πŸ“ˆ | `default` | one of `['default', 'linear', 'tanh']` | +| `brightness_mode_time_dark` | (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. πŸ“ˆπŸ“‰ | `900` | `int` | +| `brightness_mode_time_light` | (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. πŸ“ˆπŸ“‰. | `3600` | `int` | +| `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` | Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. πŸ•΅οΈ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable this feature if you encounter such issues. | `False` | `bool` | +| `autoreset_control_seconds` | Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️ | `0` | `int` 0-31536000 | +| `only_once` | Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). πŸ”„ | `False` | `bool` | +| `adapt_only_on_bare_turn_on` | When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is invoked without specifying color or brightness. ❌🌈 This e.g., prevents adaptation when activating a scene. If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. Needs `take_over_control` enabled. πŸ•΅οΈ | `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` | +| `skip_redundant_commands` | Skip sending adaptation commands whose target state already equals the light's known state. Minimizes network traffic and improves the adaptation responsivity in some situations. πŸ“‰Disable if physical light states get out of sync with HA's recorded state. | `False` | `bool` | +| `multi_light_intercept` | Intercept and adapt `light.turn_on` calls that target multiple lights. βž—βš οΈ This might result in splitting up a single `light.turn_on` call into multiple calls, e.g., when lights are in different switches. | `True` | `bool` | +| `include_config_in_attributes` | Show all options as attributes on the switch in Home Assistant when set to `true`. πŸ“ | `False` | `bool` | diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 1d060c56..f3dc628d 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -77,6 +77,18 @@ DOCS[CONF_ONLY_ONCE] = ( "(`false`). πŸ”„" ) +CONF_ADAPT_ONLY_ON_BARE_TURN_ON, DEFAULT_ADAPT_ONLY_ON_BARE_TURN_ON = ( + "adapt_only_on_bare_turn_on", + False, +) +DOCS[CONF_ADAPT_ONLY_ON_BARE_TURN_ON] = ( + "When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is " + "invoked without specifying color or brightness. ❌🌈 " + "This e.g., prevents adaptation when activating a scene. " + "If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. " + "Needs `take_over_control` enabled. πŸ•΅οΈ " +) + CONF_PREFER_RGB_COLOR, DEFAULT_PREFER_RGB_COLOR = "prefer_rgb_color", False DOCS[CONF_PREFER_RGB_COLOR] = ( "Whether to prefer RGB color adjustment over " @@ -329,7 +341,6 @@ VALIDATION_TUPLES = [ ), (CONF_BRIGHTNESS_MODE_TIME_DARK, DEFAULT_BRIGHTNESS_MODE_TIME_DARK, int), (CONF_BRIGHTNESS_MODE_TIME_LIGHT, DEFAULT_BRIGHTNESS_MODE_TIME_LIGHT, int), - (CONF_ONLY_ONCE, DEFAULT_ONLY_ONCE, bool), (CONF_TAKE_OVER_CONTROL, DEFAULT_TAKE_OVER_CONTROL, bool), (CONF_DETECT_NON_HA_CHANGES, DEFAULT_DETECT_NON_HA_CHANGES, bool), ( @@ -338,6 +349,7 @@ VALIDATION_TUPLES = [ int_between(0, 365 * 24 * 60 * 60), # 1 year max ), (CONF_ONLY_ONCE, DEFAULT_ONLY_ONCE, bool), + (CONF_ADAPT_ONLY_ON_BARE_TURN_ON, DEFAULT_ADAPT_ONLY_ON_BARE_TURN_ON, bool), (CONF_SEPARATE_TURN_ON_COMMANDS, DEFAULT_SEPARATE_TURN_ON_COMMANDS, bool), (CONF_SEND_SPLIT_DELAY, DEFAULT_SEND_SPLIT_DELAY, int_between(0, 10000)), (CONF_ADAPT_DELAY, DEFAULT_ADAPT_DELAY, cv.positive_float), diff --git a/custom_components/adaptive_lighting/strings.json b/custom_components/adaptive_lighting/strings.json index cece3da7..5e60ed79 100644 --- a/custom_components/adaptive_lighting/strings.json +++ b/custom_components/adaptive_lighting/strings.json @@ -45,10 +45,11 @@ "brightness_mode": "brightness_mode: Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). πŸ“ˆ", "brightness_mode_time_dark": "brightness_mode_time_dark: (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. πŸ“ˆπŸ“‰", "brightness_mode_time_light": "brightness_mode_time_light: (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. πŸ“ˆπŸ“‰.", - "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`! πŸ”’", "detect_non_ha_changes": "detect_non_ha_changes: Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. πŸ•΅οΈ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable this feature if you encounter such issues.", "autoreset_control_seconds": "autoreset_control_seconds: Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", + "only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). πŸ”„", + "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is invoked without specifying color or brightness. ❌🌈 This e.g., prevents adaptation when activating a scene. If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. Needs `take_over_control` enabled. πŸ•΅οΈ ", "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. ⏲️", "adapt_delay": "adapt_delay: Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️", diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index b5bf65de..49d886fa 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -103,6 +103,7 @@ from .const import ( ATTR_ADAPT_COLOR, ATTR_ADAPTIVE_LIGHTING_MANAGER, CONF_ADAPT_DELAY, + CONF_ADAPT_ONLY_ON_BARE_TURN_ON, CONF_ADAPT_UNTIL_SLEEP, CONF_AUTORESET_CONTROL, CONF_BRIGHTNESS_MODE, @@ -876,15 +877,18 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): self._adapt_delay = data[CONF_ADAPT_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]: + if not data[CONF_TAKE_OVER_CONTROL] and ( + data[CONF_DETECT_NON_HA_CHANGES] or data[CONF_ADAPT_ONLY_ON_BARE_TURN_ON] + ): _LOGGER.warning( - "%s: Config mismatch: 'detect_non_ha_changes: true' " - "requires 'take_over_control' to be enabled. Adjusting config " + "%s: Config mismatch: `detect_non_ha_changes` or `adapt_only_on_bare_turn_on` " + "set to `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 + self._detect_non_ha_changes = data[CONF_DETECT_NON_HA_CHANGES] + self._adapt_only_on_bare_turn_on = data[CONF_ADAPT_ONLY_ON_BARE_TURN_ON] self._auto_reset_manual_control_time = data[CONF_AUTORESET_CONTROL] self._skip_redundant_commands = data[CONF_SKIP_REDUNDANT_COMMANDS] self._multi_light_intercept = data[CONF_MULTI_LIGHT_INTERCEPT] @@ -1446,13 +1450,14 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): async def _respond_to_off_to_on_event(self, entity_id: str, event: Event) -> None: assert not self.manager.is_proactively_adapting(event.context.id) + from_turn_on = self.manager._off_to_on_state_event_is_from_turn_on( + entity_id, + event, + ) if ( self._take_over_control and not self._detect_non_ha_changes - and not self.manager._off_to_on_state_event_is_from_turn_on( - entity_id, - event, - ) + and not from_turn_on ): # There is an edge case where 2 switches control the same light, e.g., # one for brightness and one for color. Now we will mark both switches @@ -1468,6 +1473,25 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): self.manager.mark_as_manual_control(entity_id) return + if ( + self._take_over_control + and self._adapt_only_on_bare_turn_on + and from_turn_on + ): + service_data = self.manager.turn_on_event[entity_id].data[ATTR_SERVICE_DATA] + if self.manager._mark_manual_control_if_non_bare_turn_on( + entity_id, + service_data, + ): + _LOGGER.debug( + "Skipping responding to 'off' β†’ 'on' event for '%s' with context.id='%s' because" + " we only adapt on bare `light.turn_on` events and not on service_data: '%s'", + entity_id, + event.context.id, + service_data, + ) + return + if self._adapt_delay > 0: await asyncio.sleep(self._adapt_delay) @@ -2070,6 +2094,14 @@ class AdaptiveLightingManager: # and of TOGGLE calls when toggling off. or self.hass.states.is_state(entity_id, STATE_ON) or self.manual_control.get(entity_id, False) + or ( + switch._take_over_control + and switch._adapt_only_on_bare_turn_on + and self._mark_manual_control_if_non_bare_turn_on( + entity_id, + data[CONF_PARAMS], + ) + ) ): _LOGGER.debug( "Switch is off or light is already on for entity_id='%s', skipped='%s'" @@ -2622,6 +2654,7 @@ class AdaptiveLightingManager: turn_on_event = self.turn_on_event.get(light) if ( turn_on_event is not None + and not self.is_proactively_adapting(turn_on_event.context.id) and not is_our_context(turn_on_event.context) and not force ): @@ -2855,6 +2888,21 @@ class AdaptiveLightingManager: ) return False + def _mark_manual_control_if_non_bare_turn_on( + self, + entity_id: str, + service_data: ServiceData, + ) -> bool: + _LOGGER.debug( + "_mark_manual_control_if_non_bare_turn_on: entity_id='%s', service_data='%s'", + entity_id, + service_data, + ) + if any(attr in service_data for attr in COLOR_ATTRS | BRIGHTNESS_ATTRS): + self.mark_as_manual_control(entity_id) + return True + return False + class _AsyncSingleShotTimer: def __init__(self, delay, callback) -> None: diff --git a/custom_components/adaptive_lighting/translations/en.json b/custom_components/adaptive_lighting/translations/en.json index c8e2c413..cbf87f96 100644 --- a/custom_components/adaptive_lighting/translations/en.json +++ b/custom_components/adaptive_lighting/translations/en.json @@ -46,10 +46,11 @@ "brightness_mode": "brightness_mode: Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). πŸ“ˆ", "brightness_mode_time_dark": "brightness_mode_time_dark: (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. πŸ“ˆπŸ“‰", "brightness_mode_time_light": "brightness_mode_time_light: (Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. πŸ“ˆπŸ“‰.", - "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`! πŸ”’", "detect_non_ha_changes": "detect_non_ha_changes: Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. πŸ•΅οΈ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable this feature if you encounter such issues.", "autoreset_control_seconds": "autoreset_control_seconds: Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", + "only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). πŸ”„", + "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is invoked without specifying color or brightness. ❌🌈 This e.g., prevents adaptation when activating a scene. If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. Needs `take_over_control` enabled. πŸ•΅οΈ ", "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. ⏲️", "adapt_delay": "adapt_delay: Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️", diff --git a/tests/test_switch.py b/tests/test_switch.py index 1a25c7a1..692f0fde 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -88,6 +88,7 @@ from custom_components.adaptive_lighting.const import ( SERVICE_CHANGE_SWITCH_SETTINGS, SERVICE_SET_MANUAL_CONTROL, SLEEP_MODE_SWITCH, + CONF_ADAPT_ONLY_ON_BARE_TURN_ON, UNDO_UPDATE_LISTENER, ) from custom_components.adaptive_lighting.switch import ( @@ -561,9 +562,19 @@ async def test_manager_not_tracking_untracked_lights(hass): assert light not in switch.manager.lights -async def test_manual_control(hass): +@pytest.mark.parametrize("adapt_only_on_bare_turn_on", [True, False]) +@pytest.mark.parametrize("proactive_service_call_adaptation", [True, False]) +async def test_manual_control( + hass, adapt_only_on_bare_turn_on, proactive_service_call_adaptation +): """Test the 'manual control' tracking.""" - switch, (light, *_) = await setup_lights_and_switch(hass) + switch, (light, *_) = await setup_lights_and_switch( + hass, + { + CONF_ADAPT_ONLY_ON_BARE_TURN_ON: adapt_only_on_bare_turn_on, + INTERNAL_CONF_PROACTIVE_SERVICE_CALL_ADAPTATION: proactive_service_call_adaptation, + }, + ) assert switch._take_over_control assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON @@ -639,9 +650,14 @@ async def test_manual_control(hass): await change_manual_control(True) assert manual_control[ENTITY_LIGHT_1] await turn_light(False) + assert not manual_control[ENTITY_LIGHT_1], manual_control await turn_light(True, brightness=increased_brightness()) assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON - assert not manual_control[ENTITY_LIGHT_1], manual_control + if adapt_only_on_bare_turn_on: + # Marks as manually controlled beacuse we turned it on with brightness + assert manual_control[ENTITY_LIGHT_1], manual_control + else: + assert not manual_control[ENTITY_LIGHT_1], manual_control # Check that toggling (sleep mode) switch resets manual control for entity_id in [ENTITY_SWITCH, ENTITY_SLEEP_MODE_SWITCH]: