diff --git a/minimateplus/client.py b/minimateplus/client.py index d1ae279..bc33bde 100644 --- a/minimateplus/client.py +++ b/minimateplus/client.py @@ -727,6 +727,24 @@ class MiniMateClient: notes=notes, ) + def poll(self) -> None: + """ + Perform just the POLL startup handshake — no config reads. + + Opens the connection if not already open. Used by the monitoring + endpoints which need to communicate with the device quickly without + spending 10-15 seconds reading compliance config and event index. + + The POLL establishes the DLE-framed session with the device. + After poll(), the protocol is ready for any command (read_monitor_status, + start_monitoring, stop_monitoring, etc.). + """ + if not self.is_open: + self.open() + proto = self._require_proto() + log.debug("poll: startup handshake") + proto.startup() + # ── Monitoring ──────────────────────────────────────────────────────────── def get_monitor_status(self) -> MonitorStatus: @@ -1748,6 +1766,14 @@ 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( + "_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 diff --git a/sfm/server.py b/sfm/server.py index c34b149..0ae36ba 100644 --- a/sfm/server.py +++ b/sfm/server.py @@ -626,15 +626,20 @@ def device_monitor_status( """ Read monitoring status from the device. - Returns is_monitoring bool, battery voltage, and memory usage (total + free bytes). - Battery and memory are only present when the unit is idle (not monitoring). + Uses poll() (POLL handshake only — no config/compliance reads) so the + request completes in ~2 seconds instead of ~15. The full connect() was + causing false "idle" readings because the compliance+event sequence was + interacting with the device state before the 0x1C read. + + Returns is_monitoring bool, battery voltage, and memory usage (total + free + bytes). Battery and memory are only present when the unit is idle. """ with _build_client(port=port, baud=baud, host=host, tcp_port=tcp_port) as client: try: - client.connect() + client.poll() except Exception as exc: - log.warning("monitor status connect retry: %s", exc) - client.connect() + log.warning("monitor status poll retry: %s", exc) + client.poll() status = client.get_monitor_status() result: dict = {"is_monitoring": status.is_monitoring} @@ -663,10 +668,10 @@ def device_monitor_start( """ with _build_client(port=port, baud=baud, host=host, tcp_port=tcp_port) as client: try: - client.connect() + client.poll() except Exception as exc: - log.warning("start monitoring connect retry: %s", exc) - client.connect() + log.warning("start monitoring poll retry: %s", exc) + client.poll() client.start_monitoring() return {"status": "started"} @@ -685,10 +690,10 @@ def device_monitor_stop( """ with _build_client(port=port, baud=baud, host=host, tcp_port=tcp_port) as client: try: - client.connect() + client.poll() except Exception as exc: - log.warning("stop monitoring connect retry: %s", exc) - client.connect() + log.warning("stop monitoring poll retry: %s", exc) + client.poll() client.stop_monitoring() return {"status": "stopped"}