socket.accept() on Windows blocks indefinitely and ignores KeyboardInterrupt.
Setting a 1-second timeout on the server socket causes the accept loop to wake
up every second and re-check, so Ctrl-C is handled within ~1 second.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Session directory and log file are now created ONLY after startup() succeeds.
Internet scanners and dropped connections no longer litter the output folder.
Raw bytes are buffered in memory until startup succeeds, then flushed to disk.
- Add --allow-ip IP flag (repeatable) to allowlist specific source IPs.
Connections from un-listed IPs are rejected immediately (socket closed, no log).
If no --allow-ip flags are given, all IPs are still accepted (original behavior).
Usage: --allow-ip 63.43.212.232 --allow-ip 152.1.2.3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_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 <noreply@anthropic.com>
- Replace all Unicode arrows/checkmarks (-> [OK] [FAIL]) in ach_server.py
and client.py log calls — Windows cp1252 console can't encode them
- Fix DeviceInfo attribute: serial_number -> serial
- Fix _device_info_to_dict key: serial_number -> serial
- Demote count_events 1E/1F per-key log lines from WARNING to DEBUG
(they were flooding the console on devices with many stored events)
- FileHandler now opens with encoding='utf-8' so session log files
can hold any characters without codec errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ach_server.py:
- Add ach_state.json per-unit state tracking (keyed by serial number)
- count_events() before any download; skip session if no new events since last call-home
- Download only events beyond the previous high-water mark (all_events[last_count:])
- --max-events N safety cap for first-run units with many stored events
- state_path and max_events wired through AchSession constructor and serve()
client.py (_decode_monitor_status):
- Revert monitoring flag to section[1] == 0x10 (was incorrectly changed to section[6])
- Fix battery/memory offsets to section[-10:-8], [-8:-4], [-4:] (no trailing checksum byte)
- Both confirmed by full byte diff of all 144 0xE3 data frames in 4-8-26/2ndtry capture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
minimateplus/transport.py:
- Add SocketTransport(TcpTransport) — wraps an already-accepted inbound
socket; connect() is a no-op; everything else inherited from TcpTransport.
Enables the ACH server to reuse all existing protocol/client code without
any changes.
bridges/ach_server.py:
- Minimal inbound ACH server — listens on port 12345, accepts call-home
connections from MiniMate Plus units, runs the full BW protocol:
startup handshake → get_device_info → get_events(full_waveform=True)
- Saves device_info.json + events.json + raw_rx_<ts>.bin + session log
per connection to bridges/captures/ach_inbound_<ts>/
- raw_rx.bin is byte-compatible with existing Analyzer tooling
- Taps transport.read() to capture raw S3 bytes alongside parsed output
- Each connection runs in its own daemon thread
- Clearly distinguishes push vs pull protocol in the startup log
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a production-safe headphone-splitter mode:
- Device bytes tee'd to both --upstream (primary/prod) and --mirror (new server)
- Only primary server responses are returned to the device
- Mirror connect/write failures are non-fatal and logged; prod is unaffected
- New raw_mirror_<ts>.bin capture file alongside raw_client/raw_server
Three modes: standalone (capture only), bridge (one upstream), splitter (two).
Default listen port changed to 12345 to match project ACH setup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bridges/ach_bridge.py: transparent TCP bridge that MITMs the MiniMate Plus
call-home connection — forwards to real ACH server while logging all frames
to raw_client/raw_server .bin files compatible with parse_capture.py;
standalone capture mode for lab use without a real server
- bridges/serial_watch.py: RS-232 serial monitor with live S3 frame parsing;
taps the line between MiniMate and modem (RV50/RV55); captures raw bytes,
.log and .jsonl; --ack-ok mode auto-replies to AT commands; fixed fatal
indentation bug in the original that silently prevented any data capture
- seismo_lab.py: new "Serial Watch" fourth tab (SerialWatchPanel) wrapping
serial_watch.py functionality; COM port picker with refresh, baud config,
ack-ok toggle, colour-coded live frame log (teal frames / yellow ctrl /
blue AT), raw .bin capture auto-fed into Analyzer tab on stop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bridges/tcp_serial_bridge.py: increase default boot_delay 2s → 8s to
cover MiniMate Plus cold-start time (unit wakes from RS-232 line
assertion but takes 5-10s to be ready for POLL_PROBE).
- sfm/server.py: add _run_with_retry() — on TCP connections only, retries
once on ProtocolError. Serial timeouts are not retried (usually a real
fault). Confirmed behaviour: unit wakes purely from RS-232 line voltage,
no software wake-up frame needed.