mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-11 22:34:04 +02:00
Make instantaneous light.turn_on adaptation configurable with intercept (#750)
* Make intercept configurable * Update README.md, strings.json, and services.yaml * import * skip * split * do not pass config_entry * add to conf * Update README.md, strings.json, and services.yaml * spacing --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
f540057093
commit
68feb3d931
6 changed files with 65 additions and 57 deletions
|
|
@ -231,6 +231,13 @@ DOCS[CONF_SKIP_REDUNDANT_COMMANDS] = (
|
|||
"Disable if physical light states get out of sync with HA's recorded state."
|
||||
)
|
||||
|
||||
CONF_INTERCEPT, DEFAULT_INTERCEPT = "intercept", True
|
||||
DOCS[CONF_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."
|
||||
)
|
||||
|
||||
CONF_MULTI_LIGHT_INTERCEPT, DEFAULT_MULTI_LIGHT_INTERCEPT = (
|
||||
"multi_light_intercept",
|
||||
True,
|
||||
|
|
@ -238,7 +245,8 @@ CONF_MULTI_LIGHT_INTERCEPT, DEFAULT_MULTI_LIGHT_INTERCEPT = (
|
|||
DOCS[CONF_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."
|
||||
"into multiple calls, e.g., when lights are in different switches. "
|
||||
"Requires `intercept` to be enabled."
|
||||
)
|
||||
|
||||
SLEEP_MODE_SWITCH = "sleep_mode_switch"
|
||||
|
|
@ -356,6 +364,7 @@ VALIDATION_TUPLES = [
|
|||
DEFAULT_SKIP_REDUNDANT_COMMANDS,
|
||||
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),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@
|
|||
"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.",
|
||||
"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.",
|
||||
"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`. 📝"
|
||||
},
|
||||
"data_description": {
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ from .const import (
|
|||
CONF_DETECT_NON_HA_CHANGES,
|
||||
CONF_INCLUDE_CONFIG_IN_ATTRIBUTES,
|
||||
CONF_INITIAL_TRANSITION,
|
||||
CONF_INTERCEPT,
|
||||
CONF_INTERVAL,
|
||||
CONF_LIGHTS,
|
||||
CONF_MANUAL_CONTROL,
|
||||
|
|
@ -180,11 +181,6 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
SCAN_INTERVAL = timedelta(seconds=10)
|
||||
|
||||
# A (non-user-configurable, thus internal) flag to control the proactive adaptation mode.
|
||||
# This exists to disable the proactive adaptation in the unit tests and enable it
|
||||
# only for specific unit tests and when running as integration."""
|
||||
INTERNAL_CONF_PROACTIVE_SERVICE_CALL_ADAPTATION = "proactive_adaptation"
|
||||
|
||||
# Consider it a significant change when attribute changes more than
|
||||
BRIGHTNESS_CHANGE = 25 # ≈10% of total range
|
||||
COLOR_TEMP_CHANGE = 100 # ≈3% of total range (2000-6500)
|
||||
|
|
@ -427,7 +423,7 @@ async def async_setup_entry( # noqa: PLR0915
|
|||
return
|
||||
|
||||
if (manager := data.get(ATTR_ADAPTIVE_LIGHTING_MANAGER)) is None:
|
||||
manager = AdaptiveLightingManager(hass, config_entry)
|
||||
manager = AdaptiveLightingManager(hass)
|
||||
data[ATTR_ADAPTIVE_LIGHTING_MANAGER] = manager
|
||||
|
||||
sleep_mode_switch = SimpleSwitch(
|
||||
|
|
@ -882,7 +878,16 @@ 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._intercept = data[CONF_INTERCEPT]
|
||||
self._multi_light_intercept = data[CONF_MULTI_LIGHT_INTERCEPT]
|
||||
if not data[CONF_INTERCEPT] and data[CONF_MULTI_LIGHT_INTERCEPT]:
|
||||
_LOGGER.warning(
|
||||
"%s: Config mismatch: `multi_light_intercept` set to `true` requires `intercept`"
|
||||
" to be enabled. Adjusting config and continuing setup with"
|
||||
" `multi_light_intercept: false`.",
|
||||
self._name,
|
||||
)
|
||||
self._multi_light_intercept = False
|
||||
self._expand_light_groups() # updates manual control timers
|
||||
location, _ = get_astral_location(self.hass)
|
||||
|
||||
|
|
@ -1603,11 +1608,10 @@ class SimpleSwitch(SwitchEntity, RestoreEntity):
|
|||
class AdaptiveLightingManager:
|
||||
"""Track 'light.turn_off' and 'light.turn_on' service calls."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize the AdaptiveLightingManager that is shared among all switches."""
|
||||
assert hass is not None
|
||||
self.hass = hass
|
||||
data = validate(config_entry)
|
||||
self.lights: set[str] = set()
|
||||
|
||||
# Tracks 'light.turn_off' service calls
|
||||
|
|
@ -1658,38 +1662,32 @@ class AdaptiveLightingManager:
|
|||
|
||||
self._proactively_adapting_contexts: dict[str, str] = {}
|
||||
|
||||
is_proactive_adaptation_enabled = data.get(
|
||||
INTERNAL_CONF_PROACTIVE_SERVICE_CALL_ADAPTATION,
|
||||
True,
|
||||
)
|
||||
try:
|
||||
self.listener_removers.append(
|
||||
setup_service_call_interceptor(
|
||||
hass,
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
self._service_interceptor_turn_on_handler,
|
||||
),
|
||||
)
|
||||
|
||||
if is_proactive_adaptation_enabled:
|
||||
try:
|
||||
self.listener_removers.append(
|
||||
setup_service_call_interceptor(
|
||||
hass,
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
self._service_interceptor_turn_on_handler,
|
||||
),
|
||||
)
|
||||
self.listener_removers.append(
|
||||
setup_service_call_interceptor(
|
||||
hass,
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TOGGLE,
|
||||
self._service_interceptor_turn_on_handler,
|
||||
),
|
||||
)
|
||||
|
||||
self.listener_removers.append(
|
||||
setup_service_call_interceptor(
|
||||
hass,
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TOGGLE,
|
||||
self._service_interceptor_turn_on_handler,
|
||||
),
|
||||
)
|
||||
|
||||
_LOGGER.debug("Proactive adaptation enabled")
|
||||
except RuntimeError:
|
||||
_LOGGER.warning(
|
||||
"Failed to set up service call interceptors, "
|
||||
"falling back to event-reactive mode",
|
||||
exc_info=True,
|
||||
)
|
||||
_LOGGER.debug("Proactive adaptation enabled")
|
||||
except RuntimeError:
|
||||
_LOGGER.warning(
|
||||
"Failed to set up service call interceptors, "
|
||||
"falling back to event-reactive mode",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
def disable(self):
|
||||
"""Disable the listener by removing all subscribed handlers."""
|
||||
|
|
@ -1812,6 +1810,7 @@ class AdaptiveLightingManager:
|
|||
else:
|
||||
if (
|
||||
not switch.is_on
|
||||
or not switch._intercept
|
||||
# Never adapt on light groups, because HA will make a separate light.turn_on
|
||||
or _is_light_group(self.hass.states.get(entity_id))
|
||||
# Prevent adaptation of TURN_ON calls when light is already on,
|
||||
|
|
@ -1829,12 +1828,13 @@ class AdaptiveLightingManager:
|
|||
):
|
||||
_LOGGER.debug(
|
||||
"Switch is off or light is already on for entity_id='%s', skipped='%s'"
|
||||
" (is_on='%s', is_state='%s', manual_control='%s')",
|
||||
" (is_on='%s', is_state='%s', manual_control='%s', switch._intercept='%s')",
|
||||
entity_id,
|
||||
skipped,
|
||||
switch.is_on,
|
||||
self.hass.states.is_state(entity_id, STATE_ON),
|
||||
self.manual_control.get(entity_id, False),
|
||||
switch._intercept,
|
||||
)
|
||||
skipped.append(entity_id)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@
|
|||
"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.",
|
||||
"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.",
|
||||
"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`. 📝"
|
||||
},
|
||||
"data_description": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue