diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 502318f0..e5cfa9bc 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -272,6 +272,13 @@ DOCS[CONF_MULTI_LIGHT_INTERCEPT] = ( "Requires `intercept` to be enabled." ) +CONF_EXPAND_LIGHT_GROUPS, DEFAULT_EXPAND_LIGHT_GROUPS = "expand_light_groups", True +DOCS[CONF_EXPAND_LIGHT_GROUPS] = ( + "Expand light groups to their individual member entities (`true`, default). " + "Set to `false` to send adaptation commands to the group entity directly " + "instead of its members." +) + SLEEP_MODE_SWITCH = "sleep_mode_switch" ADAPT_COLOR_SWITCH = "adapt_color_switch" ADAPT_BRIGHTNESS_SWITCH = "adapt_brightness_switch" @@ -405,6 +412,7 @@ VALIDATION_TUPLES: list[tuple[str, Any, Any]] = [ (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), + (CONF_EXPAND_LIGHT_GROUPS, DEFAULT_EXPAND_LIGHT_GROUPS, bool), ] diff --git a/custom_components/adaptive_lighting/strings.json b/custom_components/adaptive_lighting/strings.json index 4b18ea06..87240d41 100644 --- a/custom_components/adaptive_lighting/strings.json +++ b/custom_components/adaptive_lighting/strings.json @@ -64,7 +64,8 @@ "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.", "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`. 📝" + "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝", + "expand_light_groups": "expand_light_groups: Expand light groups to individual member entities (`true`) or control the group entity directly (`false`)." }, "data_description": { "interval": "Frequency to adapt the lights, in seconds. 🔄", @@ -89,7 +90,8 @@ "take_over_control_mode": "The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed.", "autoreset_control_seconds": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", "send_split_delay": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", - "adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️" + "adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️", + "expand_light_groups": "When set to `false`, adaptation commands are sent to the group entity directly instead of its members." } } }, diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 91049970..5ebcbf81 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -97,6 +97,7 @@ from .const import ( CONF_BRIGHTNESS_MODE_TIME_DARK, CONF_BRIGHTNESS_MODE_TIME_LIGHT, CONF_DETECT_NON_HA_CHANGES, + CONF_EXPAND_LIGHT_GROUPS, CONF_INCLUDE_CONFIG_IN_ATTRIBUTES, CONF_INITIAL_TRANSITION, CONF_INTERCEPT, @@ -239,8 +240,10 @@ def _switches_with_lights( continue switch = data[config.entry_id][SWITCH_DOMAIN] switch._expand_light_groups(hass=hass) - # Check if any of the lights are in the switch's lights - if set(switch.lights) & set(all_check_lights): + # A switch with expand_light_groups=False stores the group entity in + # switch.lights (not its children), so don't expand the incoming lights for it. + check_lights = all_check_lights if switch._expand_light_groups_flag else lights + if set(switch.lights) & set(check_lights): switches.append(switch) return switches @@ -943,6 +946,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): self._name, ) self._multi_light_intercept = False + self._expand_light_groups_flag = data[CONF_EXPAND_LIGHT_GROUPS] self._expand_light_groups() # updates manual control timers location, _ = get_astral_location(self.hass) @@ -1027,7 +1031,10 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): def _expand_light_groups(self, hass: HomeAssistant | None = None) -> None: hass = hass or self.hass - all_lights = _expand_light_groups(hass, self.lights) + if self._expand_light_groups_flag: + all_lights = _expand_light_groups(hass, self.lights) + else: + all_lights = list(self.lights) # keep group entities as-is self.manager.lights.update(all_lights) self.manager.set_auto_reset_manual_control_times( all_lights, @@ -1848,8 +1855,12 @@ class AdaptiveLightingManager: if ( not switch.is_on or not switch._intercept - # Never adapt on light groups, because HA will make a separate light.turn_on - or ((e := self.hass.states.get(entity_id)) and _is_light_group(e)) + # Never adapt on light groups when expanding, because HA will make a separate light.turn_on + or ( + switch._expand_light_groups_flag + and (e := self.hass.states.get(entity_id)) + and _is_light_group(e) + ) # Prevent adaptation of TURN_ON calls when light is already on, # and of TOGGLE calls when toggling off. or self.hass.states.is_state(entity_id, STATE_ON) diff --git a/custom_components/adaptive_lighting/translations/de.json b/custom_components/adaptive_lighting/translations/de.json index c5a0f0e2..5c69e8a9 100644 --- a/custom_components/adaptive_lighting/translations/de.json +++ b/custom_components/adaptive_lighting/translations/de.json @@ -58,7 +58,8 @@ "include_config_in_attributes": "include_config_in_attributes: Alle Optionen als Attribute auf dem Schalter im Home Assistant anzeigen, wenn auf `true` gesetzt. 📝", "multi_light_intercept": "multi_light_intercept: Abfangen und Anpassen von `light.turn_on`-Aufrufen, die auf mehrere Lichter aufrufen. ➗⚠️ Dies kann dazu führen, dass ein einzelner `light.turn_on`-Aufruf in mehrere Aufrufe aufgeteilt wird, z.B. wenn Lichter in verschiedenen Schaltern sind. Erfordert, dass `intercept` aktiviert ist.", "transition_until_sleep": "transition_until_sleep: Wenn diese Option aktiviert ist, behandelt die adaptive Beleuchtung die Schlafeinstellungen als Minimum und geht nach Sonnenuntergang zu diesen Werten über. 🌙", - "intercept": "intercept: Abfangen und Anpassen von `light.turn_on`-Aufrufen, um eine sofortige Anpassung von Farbe und Helligkeit zu ermöglichen. 🏎️ Deaktivieren für Leuchten, die `light.turn_on` mit Farbe und Helligkeit nicht unterstützen." + "intercept": "intercept: Abfangen und Anpassen von `light.turn_on`-Aufrufen, um eine sofortige Anpassung von Farbe und Helligkeit zu ermöglichen. 🏎️ Deaktivieren für Leuchten, die `light.turn_on` mit Farbe und Helligkeit nicht unterstützen.", + "expand_light_groups": "expand_light_groups: Lichtgruppen auf einzelne Mitgliedsentitäten erweitern (`true`) oder die Gruppenentität direkt steuern (`false`)." }, "data_description": { "sunrise_offset": "Anpassung der Sonnenaufgangszeit mit positivem oder negativem Versatz in Sekunden. ⏰", @@ -82,7 +83,8 @@ "initial_transition": "Dauer des ersten Übergangs, wenn das Licht von `off` auf `on` schaltet, in Sekunden. ⏲️", "sleep_rgb_or_color_temp": "Verwende entweder `rgb_color` oder `color_temp` im Schlafmodus. 🌙", "sleep_transition": "Dauer des Übergangs, wenn der \"Schlafmodus\" umgeschaltet wird, in Sekunden. 😴", - "sunrise_time": "Stelle eine feste Zeit (HH:MM:SS) für den Sonnenaufgang ein. 🌅" + "sunrise_time": "Stelle eine feste Zeit (HH:MM:SS) für den Sonnenaufgang ein. 🌅", + "expand_light_groups": "Wenn auf `false` gesetzt, werden Anpassungsbefehle direkt an die Gruppenentität gesendet und nicht an die einzelnen Mitglieder." } } }, diff --git a/custom_components/adaptive_lighting/translations/en.json b/custom_components/adaptive_lighting/translations/en.json index e39898fd..ab8f1cea 100644 --- a/custom_components/adaptive_lighting/translations/en.json +++ b/custom_components/adaptive_lighting/translations/en.json @@ -65,7 +65,8 @@ "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.", "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`. 📝" + "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝", + "expand_light_groups": "expand_light_groups: Expand light groups to individual member entities (`true`) or control the group entity directly (`false`)." }, "data_description": { "interval": "Frequency to adapt the lights, in seconds. 🔄", @@ -90,7 +91,8 @@ "take_over_control_mode": "The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed.", "autoreset_control_seconds": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", "send_split_delay": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", - "adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️" + "adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️", + "expand_light_groups": "When set to `false`, adaptation commands are sent to the group entity directly instead of its members." } } },