feat: instant context-based manual-control marking#1470
Draft
jaredjxyz wants to merge 2 commits into
Draft
Conversation
Adds an immediate, context-driven path for marking lights manually
controlled when a non-AL service call changes a tracked light's
brightness or color, in addition to the existing periodic
significant_change comparison.
## Background
AL currently detects manual control of in-HA changes via the
significant_change comparison in update_manually_controlled_from_untracked_change,
which only runs on the adapt cycle (every interval seconds, default 90s).
This produces a race window: while a non-AL service call is in flight
(e.g. a continuous dim, a brightness ramp, etc.), AL's adapt tick can
fire and re-assert its own values before the cumulative drift becomes
significant enough for significant_change to flag manual.
The threshold-based path is also necessary for true detect_non_ha_changes
use (state drift caused by non-HA actors like the Hue app or physical
button cycling), which has no service-call context to inspect.
## What this PR adds
In state_changed_event_listener, when the new_state's context.id does
not carry AL's :al: marker (i.e. an external service call) and at least
one of {brightness, color_temp_kelvin, rgb_color} differs from
old_state, the affected light is added to manual_control on the
relevant axes immediately.
To avoid double-marking during a single service call's transition
(which emits multiple state_changed events with the same context.id),
a per-light last_external_marking_context dict dedupes by context.id.
The dict is cleared in reset() alongside the other per-light tracking
dicts.
The threshold-based check (still firing on the adapt tick when
detect_non_ha_changes is enabled) is preserved unchanged: it now
serves its original niche of catching truly non-HA state drift.
## Effects
- Eliminates the 'AL re-adapts mid-dim' race for in-HA service calls.
- Per-axis precision: if only brightness changed, only BRIGHTNESS is
flagged, leaving color free to keep adapting (matches pause_changed
intent).
- No threshold tuning required for the deliberate-user-action case.
- detect_non_ha_changes remains the safety net for out-of-HA drift.
## Backward compatibility
Marking happens earlier than before, but the resulting state of
manual_control is identical to what would eventually be produced by
the periodic check. Automations driven by adaptive_lighting.manual_control
events will fire sooner; automations reading the manual_control
state attribute will see flags appear without the up-to-90-second
delay. No config flag is added; the new path is gated by the existing
take_over_control switch, which any user already needs enabled to
have manual_control behavior at all.
for more information, see https://pre-commit.ci
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an immediate, context-driven path for marking lights manually controlled when a non-AL service call changes a tracked light's brightness or color, complementing the existing periodic `significant_change` comparison.
Problem
AL currently detects manual control of in-HA changes via the threshold-based `significant_change` comparison inside `update_manually_controlled_from_untracked_change`, which only runs on the adapt cycle (every `interval` seconds, default 90 s).
This produces a race window: while a non-AL service call is in flight (e.g. a continuous dim, a brightness ramp from a remote dimmer blueprint, an automation that incrementally adjusts brightness), AL's adapt tick can fire and re-assert its own values before the cumulative drift crosses the brightness/kelvin/rgb thresholds. Each individual sub-threshold tick is correctly ignored, but the cumulative operation is no longer in flight by the time the periodic check sees it.
In practice this manifests as 'AL fights my dim' for users with continuous-adjustment automations, even though the eventual end-state (after the next adapt tick) is correctly flagged manual.
The threshold-based path is still necessary for true `detect_non_ha_changes` use cases (state drift caused by non-HA actors like the Hue companion app or physical bulb buttons), which have no service-call context to inspect.
Change
In `state_changed_event_listener`, when:
the affected light is added to `manual_control` on the relevant axes via `add_manual_control_attributes`, and `adaptive_lighting.manual_control` is fired with the new flags. The marking is gated on the existing `take_over_control` config switch.
To avoid double-marking during a single service call's transition (which emits multiple `state_changed` events that share a `context.id`), a new per-light dict `last_external_marking_context` dedupes by `context.id`. It is cleared in `reset()` alongside the other per-light tracking dicts.
The threshold-based path (firing on the adapt tick) is preserved unchanged: it now serves its original niche of catching truly non-HA state drift.
Effects
Compatibility
Marking happens earlier in time than before, but the resulting state of `manual_control` is identical to what would eventually be produced by the periodic check. Effects on consumers:
Test plan
Status
Draft. Posting for design feedback before adding test coverage and polish. Specifically interested in:
🤖 Generated with Claude Code