fix: improve monitoring functionality with session-reset signal and payload adjustments

This commit is contained in:
2026-04-08 18:29:51 -04:00
parent 16e072698b
commit dda5683572
4 changed files with 59 additions and 16 deletions
+16 -9
View File
@@ -1767,25 +1767,32 @@ def _decode_monitor_status(data: bytes) -> MonitorStatus:
# The data section starts at offset 11 (after the S3 section header).
section = data[11:] if len(data) > 11 else data
# Log the raw payload at WARNING level so we can see it in the server logs
# and confirm the field offsets and is_monitoring detection are correct.
log.warning(
log.debug(
"_decode_monitor_status: total data=%d bytes section=%d bytes hex=%s",
len(data), len(section), section.hex(),
)
# Mode: idle payload is 44 bytes; monitoring is shorter (12 bytes observed)
is_monitoring = len(section) < 20
# Monitoring flag confirmed from 2ndtry 4-8-26 capture:
# section[1] == 0x00 → idle
# section[1] == 0x10 → monitoring (flips exactly at start/stop transitions)
# Payload length is ~46-49 bytes in BOTH states — length alone is unreliable.
is_monitoring = len(section) > 1 and section[1] == 0x10
battery_v = None
memory_total = None
memory_free = None
if not is_monitoring and len(section) >= 0x39:
batt_raw = struct.unpack(">H", section[0x2F:0x31])[0]
# Battery and memory fields confirmed from 2ndtry IDLE S3 frames:
# section[36:38] battery × 100 uint16 BE 0x02A8 = 680 → 6.80 V
# section[38:42] memory_total uint32 BE bytes = total flash
# section[42:46] memory_free uint32 BE bytes = free flash
# These fields appear in BOTH idle and monitoring payloads (battery/memory
# are available regardless of mode).
if len(section) >= 46:
batt_raw = struct.unpack(">H", section[36:38])[0]
battery_v = batt_raw / 100.0
memory_total = struct.unpack(">I", section[0x31:0x35])[0]
memory_free = struct.unpack(">I", section[0x35:0x39])[0]
memory_total = struct.unpack(">I", section[38:42])[0]
memory_free = struct.unpack(">I", section[42:46])[0]
return MonitorStatus(
is_monitoring=is_monitoring,
+8
View File
@@ -438,6 +438,14 @@ def bulk_waveform_term_params(key4: bytes, counter: int) -> bytes:
POLL_PROBE = build_bw_frame(0x5B, 0x00) # length-probe POLL (offset = 0)
POLL_DATA = build_bw_frame(0x5B, 0x30) # data-request POLL (offset = 0x30)
# Session-reset signal (ACK + ETX, no STX/payload).
# Confirmed from 4-8-26 BW TX captures: Blastware sends this 2-byte sequence
# immediately before the first POLL probe, and again between the POLL probe
# and the POLL data request. Required to wake a unit that is actively
# monitoring — without it the unit does not respond to POLL over TCP.
# Harmless for idle units (they respond to POLL regardless).
SESSION_RESET = bytes([0x41, 0x03])
# ── S3 response dataclass ─────────────────────────────────────────────────────
+12
View File
@@ -37,6 +37,7 @@ from .framing import (
bulk_waveform_term_params,
POLL_PROBE,
POLL_DATA,
SESSION_RESET,
)
from .transport import BaseTransport
@@ -190,6 +191,13 @@ class MiniMateProtocol:
log.debug("startup: draining boot string")
self._drain_boot_string()
# Send session-reset signal (ACK+ETX) before the first POLL probe.
# Confirmed from 4-8-26 BW TX captures: Blastware always sends this
# 2-byte signal at session start. Required to wake units that are
# actively monitoring — without it they don't respond to POLL over TCP.
log.debug("startup: session reset signal")
self._send(SESSION_RESET)
log.debug("startup: POLL probe")
self._send(POLL_PROBE)
probe_rsp = self._recv_one(
@@ -200,6 +208,10 @@ class MiniMateProtocol:
"startup: POLL probe response page_key=0x%04X", probe_rsp.page_key
)
# Send another session-reset between probe and data (matches BW behavior).
log.debug("startup: session reset signal (inter-frame)")
self._send(SESSION_RESET)
log.debug("startup: POLL data request")
self._send(POLL_DATA)
data_rsp = self._recv_one(