Checklist
Battery Notes Version
3.4.8
Describe the issue
What I think I'm seeing
While looking at custom_components/battery_notes/sensor.py:600-604, something caught my eye that I can't quite reconcile — flagging in case it's actually a bug or in case I'm reading the types wrong.
if (
self.coordinator.last_reported_level == wrapped_battery_state.state
and delta.total_seconds() < STATE_WRITE_INTERVAL_SECONDS
):
self._attr_available = True
return
self.coordinator.last_reported_level looks like it's stored as a float.
wrapped_battery_state.state is the state on an HA State object, so a str at runtime.
If both of those are right, then when the source's raw value is 100, this reduces to 100.0 == "100", which is False in Python. If that's happening, the short-circuiting and would make the early return never fire while the source's value is a percentage. I'm not sure whether there's something upstream that normalizes the types and I'm just missing it.
Behavior on my install that seems consistent with the guard not skipping
On one of my devices, the derived binary_sensor.<device>_battery_plus_low is showing ~8,500 recorder rows over 24 h. My rough expectation, if the 1-hour rate limit (STATE_WRITE_INTERVAL_SECONDS = 3600) were skipping the way I think it's meant to, would be closer to ~24 rows/day for a stable battery.
With custom_components.battery_notes: debug, I also see the _LOGGER.debug("Entity id %s has been reported.", …) line (sensor.py:613) — which sits after the guard — firing on what looks like every source event, which again seems consistent with the guard not skipping. But I could be misreading the flow.
A possible change, if this is actually a bug
--- a/custom_components/battery_notes/sensor.py
+++ b/custom_components/battery_notes/sensor.py
@@ -598,7 +598,7 @@
# Don't update if battery level same and it's been < 1 hour
delta = dt_util.utcnow() - self.coordinator.last_wrapped_battery_state_write
if (
- self.coordinator.last_reported_level == wrapped_battery_state.state
+ self.coordinator.last_reported_level == float(wrapped_battery_state.state)
and delta.total_seconds() < STATE_WRITE_INTERVAL_SECONDS
):
self._attr_available = True
The float(...) cast should be safe because validate_is_float(wrapped_battery_state.state) is asserted a few lines above at sensor.py:591. On my install this appears to bring the same entity down to something in the range of the once-per-hour cadence, but I only measured briefly and I might not be catching all the code paths.
Questions
- Am I reading the types correctly, or is there something upstream that would make
last_reported_level a string in the state I'm not seeing?
- If the mismatch is real, is a
float(...) cast the right shape of fix, or would you prefer both sides stringified, or the comparison moved earlier before the setter converts?
Thank you again for the integration I really appreciate it
Reproduction steps
Please see details above.
System Health details
System Information
| version |
core-2026.7.1 |
| installation_type |
Home Assistant OS |
| dev |
false |
| hassio |
true |
| docker |
true |
| container_arch |
amd64 |
| user |
root |
| virtualenv |
false |
| python_version |
3.14.6 |
| os_name |
Linux |
| os_version |
6.18.37-haos |
| arch |
x86_64 |
| timezone |
America/New_York |
| config_dir |
/config |
Home Assistant Cloud
| logged_in |
false |
| can_reach_cert_server |
ok |
| can_reach_cloud_auth |
ok |
| can_reach_cloud |
ok |
HACS
| GitHub API |
ok |
| GitHub Content |
ok |
| GitHub Web |
ok |
| HACS Data |
ok |
| GitHub API Calls Remaining |
4998 |
| Installed Version |
2.0.5 |
| Stage |
running |
| Available Repositories |
3476 |
| Downloaded Repositories |
105 |
Home Assistant Supervisor
| host_os |
Home Assistant OS 18.1 |
| update_channel |
stable |
| supervisor_version |
supervisor-2026.06.2 |
| agent_version |
1.9.0 |
| docker_version |
29.5.3 |
| disk_total |
916.2 GB |
| disk_used |
373.0 GB |
| nameservers |
. |
| healthy |
true |
| supported |
true |
| host_connectivity |
true |
| supervisor_connectivity |
true |
| ntp_synchronized |
true |
| virtualization |
|
| board |
generic-x86-64 |
| disk_life_time |
4 % |
| supervisor_api |
ok |
| version_api |
ok |
| installed_addons |
. |
Dashboards
| dashboards |
49 |
| resources |
53 |
| views |
90 |
| mode |
storage |
Network Configuration
| adapters |
lo (disabled), . |
| ipv4_addresses |
lo (127.0.0.1/8), . |
| ipv6_addresses |
lo (::1/128), . |
| announce_addresses |
. |
Recorder
| oldest_recorder_run |
June 5, 2026 at 9:28 PM |
| current_recorder_run |
July 6, 2026 at 9:07 PM |
| estimated_db_size |
27590.08 MiB |
| database_engine |
mysql |
| database_version |
11.4.10 |
Debug logs
2026-07-06 19:24:05.301 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:24:05.829 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:24:06.069 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:25:06.255 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:25:06.852 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:25:06.855 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:26:07.455 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:26:08.164 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:26:08.167 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:27:08.220 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:27:08.746 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:27:08.971 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:28:09.205 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:28:09.709 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:28:09.854 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:29:10.233 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:29:10.748 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
2026-07-06 19:29:10.866 DEBUG (MainThread) [custom_components.battery_notes.sensor] Entity id sensor.<device>_battery_ac has been reported.
Diagnostics dump
No response
Checklist
Battery Notes Version
3.4.8
Describe the issue
What I think I'm seeing
While looking at
custom_components/battery_notes/sensor.py:600-604, something caught my eye that I can't quite reconcile — flagging in case it's actually a bug or in case I'm reading the types wrong.self.coordinator.last_reported_levellooks like it's stored as afloat.wrapped_battery_state.stateis thestateon an HAStateobject, so astrat runtime.If both of those are right, then when the source's raw value is
100, this reduces to100.0 == "100", which isFalsein Python. If that's happening, the short-circuitingandwould make the early return never fire while the source's value is a percentage. I'm not sure whether there's something upstream that normalizes the types and I'm just missing it.Behavior on my install that seems consistent with the guard not skipping
On one of my devices, the derived
binary_sensor.<device>_battery_plus_lowis showing ~8,500 recorder rows over 24 h. My rough expectation, if the 1-hour rate limit (STATE_WRITE_INTERVAL_SECONDS = 3600) were skipping the way I think it's meant to, would be closer to ~24 rows/day for a stable battery.With
custom_components.battery_notes: debug, I also see the_LOGGER.debug("Entity id %s has been reported.", …)line (sensor.py:613) — which sits after the guard — firing on what looks like every source event, which again seems consistent with the guard not skipping. But I could be misreading the flow.A possible change, if this is actually a bug
The
float(...)cast should be safe becausevalidate_is_float(wrapped_battery_state.state)is asserted a few lines above atsensor.py:591. On my install this appears to bring the same entity down to something in the range of the once-per-hour cadence, but I only measured briefly and I might not be catching all the code paths.Questions
last_reported_levela string in the state I'm not seeing?float(...)cast the right shape of fix, or would you prefer both sides stringified, or the comparison moved earlier before the setter converts?Thank you again for the integration I really appreciate it
Reproduction steps
Please see details above.
System Health details
System Information
Home Assistant Cloud
HACS
Home Assistant Supervisor
Dashboards
Network Configuration
Recorder
Debug logs
Diagnostics dump
No response