From 2965c9ec9cf8e1f56b52b49de07610d6da4c840e Mon Sep 17 00:00:00 2001 From: Tim Stallmann Date: Wed, 26 Mar 2025 08:06:15 -0400 Subject: [PATCH] Add skip_brightness_increases conf option --- README.md | 3 +- .../adaptive_lighting/adaptation_utils.py | 33 ++++++++++++++++--- custom_components/adaptive_lighting/const.py | 12 +++++++ .../adaptive_lighting/strings.json | 1 + custom_components/adaptive_lighting/switch.py | 3 ++ .../adaptive_lighting/translations/en.json | 1 + 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7a7ee9d9..7e25493a 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,8 @@ The YAML and frontend configuration methods support all of the options listed be | `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` | +| `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` +| `skip_brightness_increases` | Skip sending adaptation commands when the light entity is already dimmed below the the current target brightness. Lights will only ever get dimmer with this command enabled. | `False` | `bool` || `bool` | | `intercept` | Intercept and adapt `light.turn_on` calls to enabling instantaneous color and brightness adaptation. 🏎️ Disable for lights that do not support `light.turn_on` with color and brightness. | `True` | `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. Requires `intercept` to be enabled. | `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/adaptation_utils.py b/custom_components/adaptive_lighting/adaptation_utils.py index 26acd92b..76682413 100644 --- a/custom_components/adaptive_lighting/adaptation_utils.py +++ b/custom_components/adaptive_lighting/adaptation_utils.py @@ -130,6 +130,18 @@ def _remove_redundant_attributes( } +def _remove_brightness_increases( + service_data: ServiceData, + state: State, +) -> ServiceData: + """Filter service data by removing brightness increases.""" + return { + k: v + for k, v in service_data.items() + if k != ATTR_BRIGHTNESS or k not in state.attributes or v <= state.attributes[k] + } + + def _has_relevant_service_data_attributes(service_data: ServiceData) -> bool: """Determines whether the service data justifies an adaptation service call. @@ -145,6 +157,7 @@ async def _create_service_call_data_iterator( hass: HomeAssistant, service_datas: list[ServiceData], filter_by_state: bool, + skip_brightness_increases: bool, ) -> AsyncGenerator[ServiceData]: """Enumerates and filters a list of service datas on the fly. @@ -156,15 +169,23 @@ async def _create_service_call_data_iterator( flexibility because entity states can change while the items are iterated. """ for service_data in service_datas: - if filter_by_state and (entity_id := service_data.get(ATTR_ENTITY_ID)): + if (filter_by_state or skip_brightness_increases) and ( + entity_id := service_data.get(ATTR_ENTITY_ID) + ): current_entity_state = hass.states.get(entity_id) # Filter data to remove attributes that equal the current state if current_entity_state is not None: - service_data = _remove_redundant_attributes( # noqa: PLW2901 - service_data, - state=current_entity_state, - ) + if filter_by_state: + service_data = _remove_redundant_attributes( # noqa: PLW2901 + service_data, + state=current_entity_state, + ) + if skip_brightness_increases: + service_data = _remove_brightness_increases( # noqa: PLW2901 + service_data, + state=current_entity_state, + ) # Emit service data if it still contains relevant attributes (else try next) if _has_relevant_service_data_attributes(service_data): @@ -240,6 +261,7 @@ def prepare_adaptation_data( split: bool, filter_by_state: bool, force: bool, + skip_brightness_increases: bool, ) -> AdaptationData: """Prepares a data object carrying all data required to execute an adaptation.""" _LOGGER.debug( @@ -261,6 +283,7 @@ def prepare_adaptation_data( hass, service_datas, filter_by_state, + skip_brightness_increases, ) attributes = _identify_light_control_attributes(service_data) diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 502318f0..2e271256 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -254,6 +254,17 @@ DOCS[CONF_SKIP_REDUNDANT_COMMANDS] = ( "Disable if physical light states get out of sync with HA's recorded state." ) +CONF_SKIP_BRIGHTNESS_INCREASES, DEFAULT_SKIP_BRIGHTNESS_INCREASES = ( + "skip_brightness_increases", + False, +) +DOCS[CONF_SKIP_BRIGHTNESS_INCREASES] = ( + "Skip sending adaptation commands when the target brightness " + "is greater than the current entity brightness. Will " + "result in lights which have been manually dimmed " + "not increasing in brightness." +) + CONF_INTERCEPT, DEFAULT_INTERCEPT = "intercept", True DOCS[CONF_INTERCEPT] = ( "Intercept and adapt `light.turn_on` calls to enabling instantaneous color " @@ -402,6 +413,7 @@ VALIDATION_TUPLES: list[tuple[str, Any, Any]] = [ DEFAULT_SKIP_REDUNDANT_COMMANDS, bool, ), + (CONF_SKIP_BRIGHTNESS_INCREASES, DEFAULT_SKIP_BRIGHTNESS_INCREASES, bool), (CONF_INTERCEPT, DEFAULT_INTERCEPT, bool), (CONF_MULTI_LIGHT_INTERCEPT, DEFAULT_MULTI_LIGHT_INTERCEPT, bool), (CONF_INCLUDE_CONFIG_IN_ATTRIBUTES, DEFAULT_INCLUDE_CONFIG_IN_ATTRIBUTES, bool), diff --git a/custom_components/adaptive_lighting/strings.json b/custom_components/adaptive_lighting/strings.json index 4b18ea06..3af4fc7c 100644 --- a/custom_components/adaptive_lighting/strings.json +++ b/custom_components/adaptive_lighting/strings.json @@ -62,6 +62,7 @@ "send_split_delay": "send_split_delay", "adapt_delay": "adapt_delay", "skip_redundant_commands": "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.", + "skip_brightness_increases": "skip_brightness_increases: Never increase brightness on a light which has been dimmed below the current target brightness.", "intercept": "intercept: Intercept and adapt `light.turn_on` calls to enabling instantaneous color and brightness adaptation. 🏎️ Disable for lights that do not support `light.turn_on` with color and brightness.", "multi_light_intercept": "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. Requires `intercept` to be enabled.", "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝" diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 3923e056..e8725721 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -116,6 +116,7 @@ from .const import ( CONF_PREFER_RGB_COLOR, CONF_SEND_SPLIT_DELAY, CONF_SEPARATE_TURN_ON_COMMANDS, + CONF_SKIP_BRIGHTNESS_INCREASES, CONF_SKIP_REDUNDANT_COMMANDS, CONF_SLEEP_BRIGHTNESS, CONF_SLEEP_COLOR_TEMP, @@ -933,6 +934,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): 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._skip_brightness_increases = data[CONF_SKIP_BRIGHTNESS_INCREASES] self._intercept = data[CONF_INTERCEPT] self._multi_light_intercept = data[CONF_MULTI_LIGHT_INTERCEPT] if not data[CONF_INTERCEPT] and data[CONF_MULTI_LIGHT_INTERCEPT]: @@ -1286,6 +1288,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): split=self._separate_turn_on_commands, filter_by_state=self._skip_redundant_commands, force=force, + skip_brightness_increases=self._skip_brightness_increases, ) async def _adapt_light( diff --git a/custom_components/adaptive_lighting/translations/en.json b/custom_components/adaptive_lighting/translations/en.json index e39898fd..006d30af 100644 --- a/custom_components/adaptive_lighting/translations/en.json +++ b/custom_components/adaptive_lighting/translations/en.json @@ -63,6 +63,7 @@ "send_split_delay": "send_split_delay", "adapt_delay": "adapt_delay", "skip_redundant_commands": "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.", + "skip_brightness_increases": "skip_brightness_increases: Never increase brightness on a light which has been dimmed below the current target brightness.", "intercept": "intercept: Intercept and adapt `light.turn_on` calls to enabling instantaneous color and brightness adaptation. 🏎️ Disable for lights that do not support `light.turn_on` with color and brightness.", "multi_light_intercept": "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. Requires `intercept` to be enabled.", "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝"