Skip to content

feat: optimistic light state for instant UI feedback - #684

Draft
wolfgang-steinberg wants to merge 2 commits into
asantaga:devfrom
wolfgang-steinberg:feat/optimistic-light-state
Draft

feat: optimistic light state for instant UI feedback#684
wolfgang-steinberg wants to merge 2 commits into
asantaga:devfrom
wolfgang-steinberg:feat/optimistic-light-state

Conversation

@wolfgang-steinberg

@wolfgang-steinberg wolfgang-steinberg commented Aug 8, 2026

Copy link
Copy Markdown

Problem

After turning a Wiser light on/off (or dimming it), Home Assistant only updates
the entity state ~2–3 seconds later. async_turn_on/async_turn_off send
the command and then call async_force_update(2), which deliberately waits 2 s
before re-polling the hub. The lamp reacts instantly, but the UI lags behind
that whole delay, which feels unresponsive.

Change

Reflect the just-sent command in the entity state immediately, then confirm
it against real hub data:

  • store an optimistic override (_optimistic_is_on / _optimistic_percentage)
    after the command is sent and async_write_ha_state() at once, so the UI
    updates without waiting for the poll;
  • is_on / brightness return the override while it is set.

Clearing the override robustly (this is what keeps rapid toggles from
misbehaving — see the note below):

  • a per-entity generation counter: each command bumps it, and a command's
    delayed try/finally cleanup only clears the override if it is still the
    latest command. So when commands overlap (fast toggling / dragging a dim
    slider), an earlier command's cleanup can no longer wipe the value a newer
    command just set;
  • _handle_coordinator_update additionally drops the on/off override as
    soon as the hub reports the commanded state. Clearing on a match never changes
    what is shown, so a slow hub can't revert the UI to the pre-command value
    between the command and its confirmation. Brightness keeps the (now
    generation-guarded) timed cleanup, because the hub may legitimately report a
    remapped percentage that never equals the set value exactly.

Only light.py is touched. Behaviour is unchanged when no command is pending
(override is None), and physical changes (wall switch, app) still come through
the normal coordinator poll.

Why the extra guards

The first version cleared the override unconditionally after each command's
2 s refresh. With overlapping commands, the earlier command's cleanup fired
mid-flight and wiped the newer command's optimistic value, so the UI briefly
snapped back to the stale hub state before settling ~2–3 s later. Reported
during testing on rapid on/off and rapid dim changes; the two guards above fix
it at the root.

Testing

  • tests/test_light_optimistic_state.py: a superseded command does not clobber
    a newer command's override (generation guard); the on/off override is dropped
    only once the hub confirms the state; a single command still clears normally.
    Full unittest suite green.
  • Live hub: on/off and brightness show in the UI within a fraction of a second
    instead of ~2–3 s. Firing overlapping on/off and dim commands (fired
    concurrently inside the 2 s window) and sampling the state every 0.25 s, the
    entity now tracks the last command and holds it — no revert to the previous
    value across the 2 s marks — then settles on the real hub value seamlessly.

Independent of #683 (multi-gang fix): that PR changes __init__,
_handle_coordinator_update, name and unique_id; this one changes
__init__, is_on, async_turn_on/off, brightness and (now)
_handle_coordinator_update. The overlaps are clean insertions that merge
without conflict.

turn_on/off and brightness are reflected in Home Assistant immediately
instead of only after the ~2 s hub poll that follows each command. The
optimistic value is held until the follow-up refresh returns real hub
data and is always cleared afterwards (try/finally), so a failed command
self-corrects on the next refresh and there is no stale state or flicker.
@wolfgang-steinberg
wolfgang-steinberg marked this pull request as draft August 8, 2026 16:12
The optimistic override was cleared unconditionally after each command's fixed
2s refresh. With overlapping commands (rapid on/off or dim changes) an earlier
command's cleanup wiped the optimistic value a later command had just set, so
the UI briefly reverted to the stale hub value before settling ~2-3s later.

Guard the cleanup with a per-entity generation counter so only the latest
command clears the optimistic state, and additionally drop the on/off override
from _handle_coordinator_update once the hub confirms the commanded state
(clearing on a match never changes what is shown, so a slow hub can no longer
revert the UI). Brightness keeps the timed cleanup, now generation-guarded.

Adds tests/test_light_optimistic_state.py.
@wolfgang-steinberg

Copy link
Copy Markdown
Author

Pushed a stability fix (2e6b758). Testing surfaced that rapid on/off and rapid dim changes made the UI briefly snap back to the old value before settling: the override was cleared unconditionally after each command's 2 s refresh, so with overlapping commands an earlier command's cleanup wiped the value a newer command had just set.

Fixed at the root with (a) a per-entity generation counter, so only the latest command clears the override, and (b) dropping the on/off override from _handle_coordinator_update once the hub confirms the state (clearing on a match never changes what's shown, so a slow hub can't revert the UI). Added tests/test_light_optimistic_state.py; verified on a live hub with concurrent on/off and dim bursts — the entity now tracks the last command with no revert. Kept as draft for a bit more real-world testing.

@asantaga

Copy link
Copy Markdown
Owner

@LGO44 Do you have lights? Can u test?

@LGO44

LGO44 commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

I will try it next week

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants