feat(call-home): Implement Auto Call Home configuration management
- Added `CallHomeConfig` model to represent the Auto Call Home settings. - Introduced methods in `MiniMateClient` for reading (`get_call_home_config`) and writing (`set_call_home_config`) the call home configuration. - Updated `MiniMateProtocol` with new commands for call home operations (SUB 0x2C for read, SUB 0x7E for write, and SUB 0x7F for confirm). - Created API endpoints for retrieving and updating call home settings in the server. - Enhanced the web interface with a new "Call Home" tab for user interaction with call home settings. - Implemented JavaScript functions for reading and writing call home configurations from the web app.
This commit is contained in:
@@ -65,6 +65,7 @@ SUB_WAVEFORM_HEADER = 0x0A
|
||||
SUB_WAVEFORM_RECORD = 0x0C
|
||||
SUB_BULK_WAVEFORM = 0x5A
|
||||
SUB_COMPLIANCE = 0x1A
|
||||
SUB_CALL_HOME = 0x2C # Call home config read → response 0xD3 ✅
|
||||
SUB_UNKNOWN_2E = 0x2E
|
||||
|
||||
# Write command SUBs (= Read SUB + 0x60, confirmed from BW captures 3-11-26)
|
||||
@@ -78,6 +79,10 @@ SUB_WRITE_CONFIRM_C = 0x74 # Confirm C — sent after 69 ✅
|
||||
SUB_TRIGGER_CONFIG_WRITE = 0x82 # Write trigger config (0x22 + 0x60) ✅
|
||||
SUB_TRIGGER_CONFIRM = 0x83 # Confirm trigger write ✅
|
||||
|
||||
# Call home write SUBs (confirmed from 4-20-26 call home settings captures)
|
||||
SUB_CALL_HOME_WRITE = 0x7E # Write call home config → response 0x81 ✅
|
||||
SUB_CALL_HOME_CONFIRM = 0x7F # Confirm call home write → response 0x80 ✅
|
||||
|
||||
# Monitoring control SUBs (confirmed from 4-8-26/2ndtry BW TX capture)
|
||||
SUB_START_MONITORING = 0x96 # Start monitoring → response 0x69 ✅
|
||||
SUB_STOP_MONITORING = 0x97 # Stop monitoring → response 0x68 ✅
|
||||
@@ -109,6 +114,7 @@ DATA_LENGTHS: dict[int, int] = {
|
||||
# SUB_WAVEFORM_HEADER (0x0A) is VARIABLE — length read from probe response
|
||||
# data[4]. Do NOT add it here; use read_waveform_header() instead. ✅
|
||||
SUB_WAVEFORM_RECORD: 0xD2, # 210-byte waveform/histogram record ✅
|
||||
SUB_CALL_HOME: 0x7C, # 124-byte call home config ✅ (confirmed 4-20-26)
|
||||
SUB_UNKNOWN_2E: 0x1A, # 26 bytes, purpose TBD 🔶
|
||||
0x09: 0xCA, # 202 bytes, purpose TBD 🔶
|
||||
# SUB_COMPLIANCE (0x1A) uses a multi-step sequence with a 2090-byte total;
|
||||
@@ -1087,6 +1093,89 @@ class MiniMateProtocol:
|
||||
self._send(frame)
|
||||
return self.recv_write_ack(expected_sub=rsp_sub)
|
||||
|
||||
# ── Call home config (SUBs 0x2C / 0x7E / 0x7F) ──────────────────────────
|
||||
|
||||
def read_call_home_config(self) -> bytes:
|
||||
"""
|
||||
Read the auto call home configuration (SUB 0x2C → response 0xD3).
|
||||
|
||||
Standard two-step read: probe (offset=0x00) then data (offset=0x7C=124).
|
||||
Returns the raw 125-byte payload (data[11:] of the data response).
|
||||
|
||||
Confirmed from 4-20-26 call home settings capture:
|
||||
- Probe response: data[4]=0x7C (confirms data length = 124)
|
||||
- Data response: 136 bytes total (11-byte echo header + 125 bytes payload)
|
||||
- Payload[0:3] = 0x00 0x7C 0xDC (header: zero, inner-length, constant)
|
||||
- Payload[5] = auto_call_home_enabled
|
||||
- Payload[6:46] = dial_string (40-byte null-padded ASCII "RADIO RING")
|
||||
|
||||
Returns:
|
||||
Raw 125-byte call home config payload (data[11:]).
|
||||
Suitable for round-trip write (append \\x00\\x00 → 127-byte write payload).
|
||||
|
||||
Raises:
|
||||
ProtocolError: on timeout or wrong response SUB.
|
||||
"""
|
||||
rsp_sub = _expected_rsp_sub(SUB_CALL_HOME) # 0xFF - 0x2C = 0xD3
|
||||
length = DATA_LENGTHS[SUB_CALL_HOME] # 0x7C = 124
|
||||
|
||||
log.debug("read_call_home_config: 0x2C probe")
|
||||
self._send(build_bw_frame(SUB_CALL_HOME, 0))
|
||||
self._recv_one(expected_sub=rsp_sub)
|
||||
|
||||
log.debug("read_call_home_config: 0x2C data request offset=0x%02X", length)
|
||||
self._send(build_bw_frame(SUB_CALL_HOME, length))
|
||||
data_rsp = self._recv_one(expected_sub=rsp_sub)
|
||||
|
||||
payload = data_rsp.data[11:]
|
||||
log.debug("read_call_home_config: received %d payload bytes", len(payload))
|
||||
return payload
|
||||
|
||||
def write_call_home_config(self, data: bytes) -> None:
|
||||
"""
|
||||
Write the auto call home configuration (SUB 0x7E → 0x7F confirm).
|
||||
|
||||
Write sequence (confirmed from 4-20-26 call home settings captures):
|
||||
SUB 0x7E write 127-byte payload → device acks SUB 0x81
|
||||
SUB 0x7F confirm (no data) → device acks SUB 0x80
|
||||
|
||||
The 127-byte write payload = 125-byte read payload + b'\\x00\\x00'.
|
||||
The offset field = data[1] + 2 = 0x7C + 2 = 0x7E = 126.
|
||||
|
||||
Write frame format: build_bw_write_frame (minimal DLE stuffing — only
|
||||
BW_CMD is doubled; all other bytes are RAW). The \\x10\\x03 sequence
|
||||
within the payload is preserved as-is (device interprets DLE+ETX as the
|
||||
literal value 0x03 per the inner-frame terminator convention).
|
||||
|
||||
Args:
|
||||
data: 127-byte write payload (read payload + \\x00\\x00 footer).
|
||||
Must start with [0x00][0x7C][...] (standard header).
|
||||
|
||||
Raises:
|
||||
ValueError: if data is not exactly 127 bytes or lacks expected header.
|
||||
ProtocolError: on timeout or wrong response SUB.
|
||||
"""
|
||||
if len(data) < 2:
|
||||
raise ValueError(f"call home write payload must be at least 2 bytes, got {len(data)}")
|
||||
rsp_sub_write = _expected_rsp_sub(SUB_CALL_HOME_WRITE) # 0xFF - 0x7E = 0x81
|
||||
rsp_sub_confirm = _expected_rsp_sub(SUB_CALL_HOME_CONFIRM) # 0xFF - 0x7F = 0x80
|
||||
|
||||
# Offset formula: data[1] + 2 (same pattern as other single-chunk writes)
|
||||
offset = data[1] + 2 # 0x7C + 2 = 0x7E = 126
|
||||
frame = build_bw_write_frame(SUB_CALL_HOME_WRITE, data, offset=offset)
|
||||
log.debug(
|
||||
"write_call_home_config: %d bytes data[1]=0x%02X offset=0x%04X",
|
||||
len(data), data[1], offset,
|
||||
)
|
||||
self._send(frame)
|
||||
self.recv_write_ack(expected_sub=rsp_sub_write)
|
||||
log.debug("write_call_home_config: write acked; sending confirm 0x7F")
|
||||
|
||||
confirm_frame = build_bw_write_frame(SUB_CALL_HOME_CONFIRM, b"")
|
||||
self._send(confirm_frame)
|
||||
self.recv_write_ack(expected_sub=rsp_sub_confirm)
|
||||
log.debug("write_call_home_config: confirm acked — done")
|
||||
|
||||
# ── Monitoring ────────────────────────────────────────────────────────────
|
||||
|
||||
def read_monitor_status(self) -> S3Frame:
|
||||
|
||||
Reference in New Issue
Block a user