Skip to content

perf(entity): scope availability subscription to the lock entities#1306

Open
terafin wants to merge 1 commit into
raman325:mainfrom
terafin:perf/availability-subscription-scope
Open

perf(entity): scope availability subscription to the lock entities#1306
terafin wants to merge 1 commit into
raman325:mainfrom
terafin:perf/availability-subscription-scope

Conversation

@terafin

@terafin terafin commented Jun 27, 2026

Copy link
Copy Markdown

Proposed change

The availability binary_sensor registers its state-change handler with all_states=True:

async_track_state_change_filtered(
    self.hass, TrackStates(True, set(), set()), self._handle_available_state_update
)

TrackStates(True, ...) subscribes to every state change in the entire Home Assistant instance. _handle_available_state_update then filters in Python and early-returns for any entity that isn't one of the slot's locks.

On a production instance with ~6–7 of these entities, cProfile measured the handler running ~86,580 times / 30s (~2,900/sec) and its inner generator ~640,100 times / 30s — the single largest application-level CPU consumer, doing no useful work.

Fix

Scope the subscription to just the lock entities:

TrackStates(False, {lock.lock.entity_id for lock in self.locks}, set())

self.locks is mutated at runtime (_handle_add_locks / _handle_remove_lock), so the tracked set is re-scoped via async_update_listeners() whenever locks change, and availability is recomputed at that point — previously the all-states firehose recomputed it implicitly, so this preserves behavior for the dynamic add/remove-lock case. The initial availability computation and the async_on_remove cleanup are unchanged.

Pure performance fix; no behavior change. Verified: the full test_binary_sensor.py, test_event.py, and test_callbacks.py suites pass (48 tests), and a new regression test asserts the subscription is scoped to the locks and re-scopes on lock add/remove.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (which adds functionality)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • Performance regression measured via cProfile on a production HA instance running 4.2.0.
  • No existing issue; filing this PR directly.

🤖 Generated with Claude Code

The availability binary_sensor registered its state-change handler with
`async_track_state_change_filtered(hass, TrackStates(True, set(), set()), ...)`.
`all_states=True` subscribes to EVERY state change in the Home Assistant
instance; the handler then filters in Python and early-returns for any entity
that isn't one of the slot's locks.

On a production instance with ~6-7 of these entities, cProfile measured the
handler running ~86,580 times / 30s (~2,900/sec) and its inner generator
~640,100 times / 30s — the single largest application-level CPU consumer,
doing no useful work.

Scope the subscription to just the lock entities via
`TrackStates(False, {lock.lock.entity_id for lock in self.locks}, set())`.
Because `self.locks` is mutated at runtime by `_handle_add_locks` /
`_handle_remove_lock`, the tracked set is re-scoped with
`async_update_listeners` whenever locks change, and availability is recomputed
at that point (previously the all-states firehose recomputed it implicitly).

Pure performance fix; no behavior change. Adds a regression test asserting the
subscription is scoped to the locks and re-scopes on lock add/remove.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added python Pull requests that update Python code bug Something isn't working labels Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant