From 3273c3489ce3538c171ccd21a31bfe5f06f55912 Mon Sep 17 00:00:00 2001 From: Matthew Wolter Date: Sat, 11 Apr 2026 18:19:52 -0700 Subject: [PATCH] =?UTF-8?q?G1:=20N07=20=E2=80=94=20tighten=20BATTERY=20sto?= =?UTF-8?q?rage-field=20length=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why: The BATTERY handler previously gated the used_kb / total_kb reads on `len(data) > 3`, which is wrong. The full RESP_CODE_BATT_AND_STORAGE frame is 11 bytes (1 type + 2 level + 4 used_kb + 4 total_kb), so a 4-10 byte truncated frame would pass the guard, and io.BytesIO.read(4) silently returns short bytes instead of raising. int.from_bytes(b"", ...) returns 0, so HA sensor telemetry silently reports zero storage on a truncated frame. Tighten the guard to `len(data) >= 11` so the storage fields are only parsed when the full frame is present. Inline comment added to document the expected frame layout. Note: the unconditional 2-byte `level` read at the top of the handler has the same class of issue (no guard, silent zero on a 1-byte frame). That is out of scope for finding N07 and has been logged in issues_log.md as a separate item. Refs: Forensics report finding N07 (S3) --- src/meshcore/reader.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/meshcore/reader.py b/src/meshcore/reader.py index 581853a..a9b2f4c 100644 --- a/src/meshcore/reader.py +++ b/src/meshcore/reader.py @@ -343,7 +343,12 @@ class MessageReader: elif packet_type_value == PacketType.BATTERY.value: battery_level = int.from_bytes(dbuf.read(2), byteorder="little") result = {"level": battery_level} - if len(data) > 3: # has storage info as well + # Full RESP_CODE_BATT_AND_STORAGE frame is 11 bytes: + # 1 type + 2 level + 4 used_kb + 4 total_kb. The previous + # `len(data) > 3` guard let 4-10 byte truncated frames through, + # producing silent zero values for used_kb/total_kb because + # io.BytesIO.read() returns short data without raising. + if len(data) >= 11: # has storage info as well result["used_kb"] = int.from_bytes(dbuf.read(4), byteorder="little") result["total_kb"] = int.from_bytes(dbuf.read(4), byteorder="little") await self.dispatcher.dispatch(Event(EventType.BATTERY, result))