Checklist
Battery Notes Version
3.6.3
Describe the issue
battery_last_reported freezes permanently at the HA restart timestamp for
devices whose reported battery percentage never changes (e.g. always
battery: 100). After days_last_reported days, the daily
check_battery_last_reported automation fires a false "Battery Not
Reported" notification, even though the device is alive and actively
publishing.
Verified on a production install with 16 affected battery-powered devices:
sensor.motion_sensor_garden_battery: state frozen for 90 hours (exact
HA restart moment), while the sibling entity from the same MQTT
device/topic (sensor.motion_sensor_garden_illuminance) updated 0.1
hours ago. Zigbee2MQTT logs show 1732 publishes in 24h with the
battery field present.
check_battery_last_reported (with days_last_reported: 2) reported
all 16 devices as "not reported"; 15/16 were demonstrably alive and
publishing.
Root cause (two parts)
- Startup stamp —
_ensure_last_reported (coordinator.py) sets
last_reported = utcnow() for every device without a stored value, at
every HA start. This suggests a fresh report that never happened.
- No events for unchanged values — HA's MQTT integration does not
write a new state when the payload is identical. battery: 100 stays
100 on every publish → no state_changed and no
state_reported event ever fires again → both listeners
(async_track_state_change_event + async_track_state_report_event,
sensor.py L667-680 / L715-730) never run again → last_reported stays
frozen at the restart timestamp indefinitely.
Note: this is not fixable inside async_state_reported_listener (e.g.
re-stamping after the 1-hour throttle on same value) — for MQTT-backed
entities the event never arrives in the first place because of state
de-duplication. The 1-hour throttle (STATE_WRITE_INTERVAL_SECONDS) is
irrelevant here; the listener is simply never invoked.
Reproduction steps
- Have a battery device that reports an identical percentage every
report cycle (very common: many Zigbee sensors keep reporting
battery: 100 until the battery actually degrades)
- Restart Home Assistant →
_ensure_last_reported stamps
last_reported = now for all devices
- Let the device keep reporting the same value (nothing changes on the
HA side: no state writes, no events)
- Wait more than
days_last_reported days
- Run
battery_notes.check_battery_last_reported (daily automation):
the device is falsely reported as "not reported", while it is alive
and publishing (provable in MQTT logs and via fresh sibling entities)
Observed false alarm (15 of 16 devices were false positives)
- Motion Sensor Garden: 1732 MQTT publishes/24h with battery field,
battery sensor state frozen 90h
- Motion Sensor Kitchen: 652 publishes/24h, same
- Environment/leak sensors: 14-56 publishes/24h, same pattern
- The only genuinely dead device (replaced hardware, 0 publishes in 7+
days) was correctly reported — the check itself works, the clock just
freezes for constant-value devices
Suggested fix direction
Treat device liveness separately from battery value changes. In
_async_battery_last_reported (services.py), before raising the event:
if any non-battery-notes entity of the same device (or the
source_entity_id for entity-based coordinators) had a state write within
the threshold window, the device is alive and reporting — refresh
last_reported instead of raising a false alarm. Truly dead devices (all
entities frozen) are still reported. No storage schema changes, no new
events, no recorder impact.
Happy to submit a PR implementing this (with the same-value/throttle
listener case handled too for integrations that do re-write identical
states).
System Health details
| field |
value |
| version |
core-2026.9.1 |
| installation_type |
Docker (homeassistant/home-assistant:latest) |
| os |
Linux (Docker host) |
| timezone |
Europe/Amsterdam |
Debug logs
Debug logging was enabled during the investigation. The relevant
observation is not log noise but the absence of it: for affected devices
Entity id %s has been reported. (sensor.py) never appears after the
restart, because no state events arrive for the unchanged battery value —
while MQTT publishing of the same battery value continues every few
seconds (verified on the broker).
Checklist
Battery Notes Version
3.6.3
Describe the issue
battery_last_reportedfreezes permanently at the HA restart timestamp fordevices whose reported battery percentage never changes (e.g. always
battery: 100). Afterdays_last_reporteddays, the dailycheck_battery_last_reportedautomation fires a false "Battery NotReported" notification, even though the device is alive and actively
publishing.
Verified on a production install with 16 affected battery-powered devices:
sensor.motion_sensor_garden_battery: state frozen for 90 hours (exactHA restart moment), while the sibling entity from the same MQTT
device/topic (
sensor.motion_sensor_garden_illuminance) updated 0.1hours ago. Zigbee2MQTT logs show 1732 publishes in 24h with the
battery field present.
check_battery_last_reported(withdays_last_reported: 2) reportedall 16 devices as "not reported"; 15/16 were demonstrably alive and
publishing.
Root cause (two parts)
_ensure_last_reported(coordinator.py) setslast_reported = utcnow()for every device without a stored value, atevery HA start. This suggests a fresh report that never happened.
write a new state when the payload is identical.
battery: 100stays100on every publish → nostate_changedand nostate_reportedevent ever fires again → both listeners(
async_track_state_change_event+async_track_state_report_event,sensor.py L667-680 / L715-730) never run again →
last_reportedstaysfrozen at the restart timestamp indefinitely.
Note: this is not fixable inside
async_state_reported_listener(e.g.re-stamping after the 1-hour throttle on same value) — for MQTT-backed
entities the event never arrives in the first place because of state
de-duplication. The 1-hour throttle (
STATE_WRITE_INTERVAL_SECONDS) isirrelevant here; the listener is simply never invoked.
Reproduction steps
report cycle (very common: many Zigbee sensors keep reporting
battery: 100until the battery actually degrades)_ensure_last_reportedstampslast_reported = nowfor all devicesHA side: no state writes, no events)
days_last_reporteddaysbattery_notes.check_battery_last_reported(daily automation):the device is falsely reported as "not reported", while it is alive
and publishing (provable in MQTT logs and via fresh sibling entities)
Observed false alarm (15 of 16 devices were false positives)
battery sensor state frozen 90h
days) was correctly reported — the check itself works, the clock just
freezes for constant-value devices
Suggested fix direction
Treat device liveness separately from battery value changes. In
_async_battery_last_reported(services.py), before raising the event:if any non-battery-notes entity of the same device (or the
source_entity_idfor entity-based coordinators) had a state write withinthe threshold window, the device is alive and reporting — refresh
last_reportedinstead of raising a false alarm. Truly dead devices (allentities frozen) are still reported. No storage schema changes, no new
events, no recorder impact.
Happy to submit a PR implementing this (with the same-value/throttle
listener case handled too for integrations that do re-write identical
states).
System Health details
Debug logs
Debug logging was enabled during the investigation. The relevant
observation is not log noise but the absence of it: for affected devices
Entity id %s has been reported.(sensor.py) never appears after therestart, because no state events arrive for the unchanged battery value —
while MQTT publishing of the same battery value continues every few
seconds (verified on the broker).