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:
@@ -4,6 +4,57 @@ All notable changes to seismo-relay are documented here.
|
||||
|
||||
---
|
||||
|
||||
## v0.12.3 — 2026-04-20
|
||||
|
||||
### Added
|
||||
|
||||
- **Auto Call Home config protocol** — Full read/write/decode/encode pipeline for the
|
||||
device's Remote Access → Setup Unit ACH settings, confirmed from 4-20-26 call home
|
||||
settings captures.
|
||||
|
||||
**Protocol (new):**
|
||||
- `SUB 0x2C` — Call Home Config READ (response `0xD3`); two-step read; data offset
|
||||
`0x7C` = 124; raw payload 125 bytes (1-byte longer than DATA_LENGTH due to DLE-escaped
|
||||
`\x10\x03` at raw[117:119] representing num_retries = 3)
|
||||
- `SUB 0x7E` — Call Home Config WRITE (response `0x81`); 127-byte payload (125-byte read
|
||||
payload + `\x00\x00`); offset = `data[1]+2 = 0x7E`; write format (DLE-aware checksum)
|
||||
- `SUB 0x7F` — Call Home WRITE CONFIRM (response `0x80`); no data
|
||||
|
||||
**Field map (confirmed from 10-frame BW TX diff):**
|
||||
- `raw[5]` — auto_call_home_enabled (bool)
|
||||
- `raw[6:46]` — dial_string (40-byte null-padded ASCII)
|
||||
- `raw[87]` — after_event_recorded (bool)
|
||||
- `raw[91]` — at_specified_times (bool)
|
||||
- `raw[93]` — time1_enabled / `raw[101]` — time1_hour / `raw[102]` — time1_min
|
||||
- `raw[95]` — time2_enabled / `raw[105]` — time2_hour / `raw[106]` — time2_min
|
||||
- `raw[117:119]` — `\x10\x03` (DLE-escaped 0x03 = num_retries value 3)
|
||||
- `raw[120]` — time_between_retries_sec / `raw[122]` — wait_for_connection_sec / `raw[124]` — warm_up_time_sec
|
||||
|
||||
**Library (`minimateplus/`):**
|
||||
- `models.py` — `CallHomeConfig` dataclass (14 fields; `raw` bytes preserved for
|
||||
round-trip writes)
|
||||
- `protocol.py` — `SUB_CALL_HOME = 0x2C`, `SUB_CALL_HOME_WRITE = 0x7E`,
|
||||
`SUB_CALL_HOME_CONFIRM = 0x7F`; `read_call_home_config()`, `write_call_home_config()`
|
||||
- `client.py` — `get_call_home_config()`, `set_call_home_config()`,
|
||||
`_decode_call_home_config()` (handles DLE prefix at raw[117]),
|
||||
`_encode_call_home_config()` (patches in-place; raises `ValueError` if hour/min = 3)
|
||||
|
||||
**REST API (`sfm/server.py`):**
|
||||
- `GET /device/call_home` — reads and decodes call home config from device
|
||||
- `POST /device/call_home` — reads, patches specified fields, writes back to device
|
||||
- `CallHomeConfigBody` Pydantic model with 9 optional writable fields
|
||||
|
||||
**Web UI (`sfm/sfm_webapp.html`):**
|
||||
- New "Call Home" tab with enable flag, dial string (read-only), after-event trigger,
|
||||
at-specified-times flag, two time slots (enable + HH:MM each), and read-only retry
|
||||
settings (num_retries, time_between_retries_sec, wait_for_connection_sec,
|
||||
warm_up_time_sec)
|
||||
- "Read from Device", "Write to Device", "Clear Form" action buttons
|
||||
- Client-side guard: rejects hour or minute value equal to 3 with a clear message
|
||||
explaining the DLE-encoding limitation
|
||||
|
||||
---
|
||||
|
||||
## v0.12.2 — 2026-04-20
|
||||
|
||||
### Added / Fixed
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for
|
||||
managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem
|
||||
(Sierra Wireless RV50 / RV55). Current version: **v0.12.2**.
|
||||
(Sierra Wireless RV50 / RV55). Current version: **v0.12.3**.
|
||||
|
||||
When new information about the protocol is discovered, please update the instantel_protocol_reference.md with the findings in addition to this document
|
||||
|
||||
@@ -27,9 +27,9 @@ CHANGELOG.md ← version history
|
||||
|
||||
---
|
||||
|
||||
## Current implementation state (v0.10.0)
|
||||
## Current implementation state (v0.12.3)
|
||||
|
||||
Full read pipeline + write pipeline + erase pipeline + monitor log working end-to-end over TCP/cellular:
|
||||
Full read pipeline + write pipeline + erase pipeline + monitor log + call home config working end-to-end over TCP/cellular:
|
||||
|
||||
| Step | SUB | Status |
|
||||
|---|---|---|
|
||||
@@ -45,7 +45,8 @@ Full read pipeline + write pipeline + erase pipeline + monitor log working end-t
|
||||
| Event advance / next key | 1F | ✅ |
|
||||
| **Write commands (push config to device)** | **68–83** | ✅ new v0.8.0 |
|
||||
| **Erase all events** | **0xA3 → 0x1C → 0x06 → 0xA2** | ✅ new v0.9.0 |
|
||||
| **Monitor log entries (partial 0x2C records)** | **0A browse** | ✅ **new v0.10.0** |
|
||||
| **Monitor log entries (partial 0x2C records)** | **0A browse** | ✅ new v0.10.0 |
|
||||
| **Auto Call Home config (read + write)** | **2C → 7E → 7F** | ✅ **new v0.12.3** |
|
||||
|
||||
`get_events()` sequence per event: `1E → 0A → 0C → 5A → 1F`
|
||||
|
||||
@@ -971,12 +972,104 @@ call-home.
|
||||
|
||||
---
|
||||
|
||||
## Auto Call Home config (SUBs 0x2C / 0x7E / 0x7F) — confirmed 2026-04-20
|
||||
|
||||
Full read/write pipeline confirmed from `bridges/captures/4-20-26/call home settings/`
|
||||
(10 BW TX write frames diffed against the S3 read response).
|
||||
|
||||
Accessible in Blastware: **Remote Access → Setup Unit**.
|
||||
|
||||
### Protocol
|
||||
|
||||
**SUB 0x2C — Call Home Config READ (response 0xD3)**
|
||||
|
||||
Standard two-step read: probe offset `0x0000`, data offset `0x007C` (124).
|
||||
Returns 125 raw bytes (one more than DATA_LENGTH) because the device encodes
|
||||
num_retries value `3` as `\x10\x03` on the wire — S3FrameParser preserves both
|
||||
bytes literally, shifting all subsequent field positions by +1.
|
||||
|
||||
**SUB 0x7E — Call Home Config WRITE (response 0x81)**
|
||||
|
||||
Write format (only BW_CMD `0x10` doubled on wire; DLE-aware checksum).
|
||||
Payload = 125-byte read payload + `\x00\x00` = 127 bytes.
|
||||
Offset = `data[1] + 2 = 0x7C + 2 = 0x7E`.
|
||||
|
||||
**SUB 0x7F — Call Home WRITE CONFIRM (response 0x80)**
|
||||
|
||||
Confirm frame, no data payload. Required after SUB 0x7E.
|
||||
|
||||
### Field map (raw 125-byte array from `data_rsp.data[11:]`)
|
||||
|
||||
| Raw Offset | Field | Notes |
|
||||
|---|---|---|
|
||||
| `[5]` | `auto_call_home_enabled` | `0x00`=off, `0x01`=on |
|
||||
| `[6:46]` | `dial_string` | 40-byte null-padded ASCII |
|
||||
| `[87]` | `after_event_recorded` | bool |
|
||||
| `[91]` | `at_specified_times` | bool |
|
||||
| `[93]` | `time1_enabled` | bool |
|
||||
| `[101]` | `time1_hour` | 0–23 |
|
||||
| `[102]` | `time1_min` | 0–59 |
|
||||
| `[95]` | `time2_enabled` | bool |
|
||||
| `[105]` | `time2_hour` | 0–23 |
|
||||
| `[106]` | `time2_min` | 0–59 |
|
||||
| `[117]` | DLE prefix `0x10` | Part of `\x10\x03` (DLE-escaped ETX encoding value 3) |
|
||||
| `[118]` | `num_retries` | Value = 3; detect via `raw[117] == 0x10` |
|
||||
| `[120]` | `time_between_retries_sec` | Shifted +1 from logical 119 |
|
||||
| `[122]` | `wait_for_connection_sec` | Shifted +1 from logical 121 |
|
||||
| `[124]` | `warm_up_time_sec` | Shifted +1 from logical 123 |
|
||||
|
||||
**DLE-escaped 0x03 at raw[117:119]:** The byte value `0x03` is indistinguishable from the
|
||||
frame ETX terminator, so the device encodes it as `\x10\x03` (DLE + ETX inner-terminator).
|
||||
S3FrameParser in `STATE_AFTER_DLE` on ETX appends both bytes as literal payload. The write
|
||||
frame sends them verbatim — device accepts `\x10\x03` and interprets it as value 3.
|
||||
|
||||
**Unconfirmed fields:** time slots 3 and 4 (offsets unknown), `modem_power_relay_enabled`.
|
||||
|
||||
### `CallHomeConfig` model — models.py
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class CallHomeConfig:
|
||||
raw: Optional[bytes] = None # 125-byte raw read payload
|
||||
auto_call_home_enabled: Optional[bool] = None # raw[5]
|
||||
dial_string: Optional[str] = None # raw[6:46]
|
||||
after_event_recorded: Optional[bool] = None # raw[87]
|
||||
at_specified_times: Optional[bool] = None # raw[91]
|
||||
time1_enabled: Optional[bool] = None # raw[93]
|
||||
time1_hour: Optional[int] = None # raw[101]
|
||||
time1_min: Optional[int] = None # raw[102]
|
||||
time2_enabled: Optional[bool] = None # raw[95]
|
||||
time2_hour: Optional[int] = None # raw[105]
|
||||
time2_min: Optional[int] = None # raw[106]
|
||||
num_retries: Optional[int] = None # raw[118] (DLE-prefixed)
|
||||
time_between_retries_sec: Optional[int] = None # raw[120] (shifted +1)
|
||||
wait_for_connection_sec: Optional[int] = None # raw[122] (shifted +1)
|
||||
warm_up_time_sec: Optional[int] = None # raw[124] (shifted +1)
|
||||
```
|
||||
|
||||
### SFM REST API — sfm/server.py
|
||||
|
||||
```
|
||||
GET /device/call_home?host=1.2.3.4&tcp_port=9034 ← read call home config
|
||||
POST /device/call_home?host=1.2.3.4&tcp_port=9034 ← write call home config
|
||||
```
|
||||
|
||||
POST body fields (all optional): `auto_call_home_enabled`, `after_event_recorded`,
|
||||
`at_specified_times`, `time1_enabled`, `time1_hour`, `time1_min`, `time2_enabled`,
|
||||
`time2_hour`, `time2_min`.
|
||||
|
||||
**Note:** `dial_string` is read-only in the current implementation (omitted from POST
|
||||
body) because writing a dial string may require DLE escaping for embedded control characters.
|
||||
|
||||
---
|
||||
|
||||
## What's next
|
||||
|
||||
- **Database** — SQLite store for events + monitor log entries; dedup by key; queryable
|
||||
- **Histograms** — decode histogram-mode A5 data (noise floor tracking)
|
||||
- Compliance config encoder — build raw write payloads from a `ComplianceConfig` object
|
||||
- Locate "Sensor Check" byte in compliance config (need capture with Disabled vs Before-monitoring)
|
||||
- Call Home — map time slots 3/4 offsets; add dial_string write support; confirm `modem_power_relay_enabled`
|
||||
- Modem manager — push RV50/RV55 configs via Sierra Wireless API
|
||||
- RV55 DCD/DTR issue — newer RV55 firmware doesn't assert DCD by default; units don't
|
||||
resume monitoring after call-home disconnect (`--restart-monitoring` flag deferred) | ||||