Add adapt_only_on_bare_turn_on which instantly triggers manual_control when turning on with brightness or color (#709)

This commit is contained in:
Bas Nijholt 2023-08-05 20:56:32 -07:00 • committed by GitHub
commit d44cb66148
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 131 additions and 53 deletions

View file

@ -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),

View file

@ -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. ⏲️",

View file

@ -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:

View file

@ -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. ⏲️",