Part of #670
Summary
Reduce redundant state-event volume after Phase A by suppressing entity writes when
availability/value/attributes are unchanged. This is one sub-issue rather than nine platform
sub-issues so the base helper contract and per-platform signatures stay coherent.
Scope reframing (2026-07-07, per @raman325): This is a performance optimization, not part of the guard fix. The guard is fixed by Phase A deferral (#676) + per-lock sharding (#680–#684). Verified in HA 2026.7.0 core.py async_set_internal: an unchanged async_write_ha_state() still fires EVENT_STATE_REPORTED (HA only skips the State allocation), so HA's built-in dedup does not prevent the guard and does not make the write free of an event fire. The only thing this task adds above HA is deciding whether to call async_write_ha_state() at all. Do not reimplement HA's state/attribute comparison; gate the write on the entity's own derived value/availability and let HA dedupe underneath. Because HA already makes unchanged writes cheap, treat this as lower priority and measure the benefit rather than assume it.
Scope / changes
- Add a base helper in
entity.py near KeymasterEntity (entity.py:28-72) that records the
last published state signature and calls async_write_ha_state() only when changed, with a
force=True escape hatch.
- Convert platform coordinator handlers that currently write unconditionally:
binary_sensor.py:105-108, button.py:85-101, datetime.py:91-120, event.py:137-152,
number.py:124-154, sensor.py:120-136 and sensor.py:167-204, switch.py:188-278,
text.py:100-136, and time.py:96-142.
- Preserve actual event entity writes for real lock/reset events at
event.py:99-134 by using
force=True or bypassing the dedupe helper.
- Preserve optimistic immediate local feedback where it already exists, such as
number.py:187.
- Add per-platform tests in existing files:
tests/test_switch.py, tests/test_text.py,
tests/test_sensor.py, tests/test_number.py, tests/test_datetime.py,
tests/test_time.py, tests/test_button.py, tests/test_binary_sensor.py, and
tests/test_event.py.
Plan reference
§5.9, §7.4, §8 PR 3; decisions 9 and 10.
Dependencies
Depends on: #676
Acceptance criteria
Risk / notes
Watch for signatures containing mutable dicts; normalize attributes into stable
sorted tuples. Event entities are the main false-dedupe risk.
Size
L
Availability-only fan-out note (2026-07-07)
Change-detection also neutralizes availability-only entities (buttons and code-slot events): they
should no-op unless availability flips. always_update=False is inert on the current global
coordinator because updates use a fresh dict(self.kmlocks) after in-place mutation, so
entity-level change-detection is the real lever until per-lock coordinators pass stable data. Full
detachment of availability-only entities is tracked separately in #686.
Part of #670
Summary
Reduce redundant state-event volume after Phase A by suppressing entity writes when
availability/value/attributes are unchanged. This is one sub-issue rather than nine platform
sub-issues so the base helper contract and per-platform signatures stay coherent.
Scope / changes
entity.pynearKeymasterEntity(entity.py:28-72) that records thelast published state signature and calls
async_write_ha_state()only when changed, with aforce=Trueescape hatch.binary_sensor.py:105-108,button.py:85-101,datetime.py:91-120,event.py:137-152,number.py:124-154,sensor.py:120-136andsensor.py:167-204,switch.py:188-278,text.py:100-136, andtime.py:96-142.event.py:99-134by usingforce=Trueor bypassing the dedupe helper.number.py:187.tests/test_switch.py,tests/test_text.py,tests/test_sensor.py,tests/test_number.py,tests/test_datetime.py,tests/test_time.py,tests/test_button.py,tests/test_binary_sensor.py, andtests/test_event.py.Plan reference
§5.9, §7.4, §8 PR 3; decisions 9 and 10.
Dependencies
Depends on: #676
Acceptance criteria
async_write_ha_state()for representativeentities from every platform.
pytest tests/test_switch.py tests/test_text.py tests/test_sensor.py tests/test_number.py tests/test_datetime.py tests/test_time.py tests/test_event.py tests/test_binary_sensor.py tests/test_button.py.ruff check custom_components/ tests/,ruff format --check custom_components/ tests/,mypy custom_components/keymaster/.Risk / notes
Watch for signatures containing mutable dicts; normalize attributes into stable
sorted tuples. Event entities are the main false-dedupe risk.
Size
L
Availability-only fan-out note (2026-07-07)
Change-detection also neutralizes availability-only entities (buttons and code-slot events): they
should no-op unless availability flips.
always_update=Falseis inert on the current globalcoordinator because updates use a fresh
dict(self.kmlocks)after in-place mutation, soentity-level change-detection is the real lever until per-lock coordinators pass stable data. Full
detachment of availability-only entities is tracked separately in #686.