From 41a14ca4683e1176650e0248f6720707f0c72092 Mon Sep 17 00:00:00 2001 From: Brian Harrison Date: Fri, 10 Apr 2026 01:10:49 -0400 Subject: [PATCH] fix: correct event count field offset and eliminate count_events() walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _decode_event_count: read uint16 BE at offset 10 (confirmed 2026-04-10 from live BE11529 event index — data[10:12]=0x0006=6, matches device LCD). Previous uint32 at offset 3 always returned 1 regardless of event count. ach_server.py: use device_info.event_count (already fetched during connect()) instead of calling count_events() separately. This saves 2*N round-trips and avoids the 1F linked-list walk which was overcounting on some devices. count_events() kept as fallback when connect() is skipped (--events-only). Co-Authored-By: Claude Sonnet 4.6 --- bridges/ach_server.py | 21 ++++++++++++++------- minimateplus/client.py | 31 ++++++++++--------------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/bridges/ach_server.py b/bridges/ach_server.py index 19b7f43..ccaff02 100644 --- a/bridges/ach_server.py +++ b/bridges/ach_server.py @@ -216,13 +216,20 @@ class AchSession: unit_key = serial or self.peer # fall back to IP if no serial last_count = state.get(unit_key, {}).get("event_count", 0) - try: - current_count = client.count_events() - log.info(" Unit has %d stored event(s); last downloaded count: %d", - current_count, last_count) - except Exception as exc: - log.error(" [FAIL] count_events failed: %s", exc) - return + # Use the event count already read from the event index during connect(). + # This is fast (no extra round-trips) and confirmed accurate (matches LCD). + # Falls back to count_events() only if connect() wasn't called. + if device_info is not None: + current_count = device_info.event_count + else: + try: + current_count = client.count_events() + except Exception as exc: + log.error(" [FAIL] count_events failed: %s", exc) + return + + log.info(" Unit has %d stored event(s); last downloaded count: %d", + current_count, last_count) if current_count <= last_count: log.info(" [OK] No new events since last call-home -- nothing to download") diff --git a/minimateplus/client.py b/minimateplus/client.py index 2538f48..47d4a20 100644 --- a/minimateplus/client.py +++ b/minimateplus/client.py @@ -910,36 +910,25 @@ def _decode_event_count(data: bytes) -> int: """ Extract stored event count from SUB F7 (EVENT_INDEX_RESPONSE) payload. - Layout per §7.4 (offsets from data section start): - +00: 00 58 09 — total index size or record count ❓ - +03: 00 00 00 01 — possibly stored event count = 1 ❓ + Confirmed 2026-04-10 from live BE11529 event index (88 bytes): + data[10:12] uint16 BE = stored event count (confirmed: 0x0006 = 6, matches LCD) + data[3:7] uint32 BE = 0x00000001 (NOT the count — meaning TBD) - We use bytes +03..+06 interpreted as uint32 BE as the event count. - This is inferred (🔶) — the exact meaning of the first 3 bytes is unclear. + Previous implementation read uint32 at offset 3, which returned 1 regardless + of how many events were stored. """ - if len(data) < 7: + if len(data) < 12: log.warning("event index payload too short (%d bytes), assuming 0 events", len(data)) return 0 - # Log the full payload so we can reverse-engineer the format - log.warning("event_index raw (%d bytes total):", len(data)) - for off in range(0, len(data), 16): - chunk = data[off:off+16] - hex_part = " ".join(f"{b:02x}" for b in chunk) - asc_part = "".join(chr(b) if 0x20 <= b < 0x7f else "." for b in chunk) - log.warning(" [%04x]: %-47s %s", off, hex_part, asc_part) + count = struct.unpack_from(">H", data, 10)[0] - # Try the uint32 at +3 first - count = struct.unpack_from(">I", data, 3)[0] - - # Sanity check: MiniMate Plus manual says max ~1000 events + # Sanity check: MiniMate Plus max storage is ~1000 events if count > 1000: - log.warning( - "event count %d looks unreasonably large — clamping to 0", count - ) + log.warning("event count %d looks unreasonably large — clamping to 0", count) return 0 - log.warning("event_index decoded count=%d (uint32 BE at offset +3)", count) + log.debug("event_index decoded count=%d (uint16 BE at offset 10)", count) return count