Merge pull request 'merge protocol-exp 0.12.3 to main' (#5) from protocol-exp into main
Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
@@ -4,6 +4,85 @@ 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
|
||||||
|
|
||||||
|
- **Geophone sensitivity / maximum range field confirmed** — 4-20-26 geo sensitivity
|
||||||
|
captures (1.25 in/s vs 10 in/s) diffed across all three SUB 71 write chunks and both
|
||||||
|
E5 read payloads. The `geo_range` uint8 field per channel is now fully confirmed:
|
||||||
|
- E5 read offset: `channel_label + 33`; SUB 71 write offset: `channel_label + 29`
|
||||||
|
- `0x00` = Normal 10.000 in/s (standard gain); `0x01` = Sensitive 1.250 in/s (high gain)
|
||||||
|
- **Correction:** previous hypothesis (`channel_label+20`, `0x01`=Normal) was wrong.
|
||||||
|
`channel_label+20` reads `0x01` on ALL captures regardless of range — not this field.
|
||||||
|
- `_decode_compliance_config_into`: read offset corrected from `tran_pos+20` → `tran_pos+33`
|
||||||
|
- `_encode_compliance_config`: added `geo_range` parameter; writes to Tran/Vert/Long at `+29`
|
||||||
|
- `apply_config`: added `geo_range` parameter
|
||||||
|
- `POST /device/config`: added `geo_range` to `DeviceConfigBody`
|
||||||
|
- Web UI Config tab: added "Maximum Range — Geo" select (Normal / Sensitive)
|
||||||
|
- Web UI Device tab: added "Max Range (geo)" row to compliance table
|
||||||
|
|
||||||
|
- **`recording_mode` + `histogram_interval_sec` confirmed and implemented** (4-20-26 captures)
|
||||||
|
- `recording_mode`: uint8 at anchor−8 (E5 read) / anchor−7 (write); enum: 0x00=Single Shot,
|
||||||
|
0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous
|
||||||
|
- `histogram_interval_sec`: uint16 BE seconds at anchor−4; same offset in read & write;
|
||||||
|
valid: 2, 5, 15, 60, 300, 900 (matching Blastware dropdown: 2s, 5s, 15s, 1m, 5m, 15m)
|
||||||
|
- Both fields added to `ComplianceConfig`, `_decode_compliance_config_into`,
|
||||||
|
`_encode_compliance_config`, `apply_config`, REST API body, and web UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v0.12.1 — 2026-04-16
|
## v0.12.1 — 2026-04-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for
|
Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for
|
||||||
managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem
|
managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem
|
||||||
(Sierra Wireless RV50 / RV55). Current version: **v0.12.1**.
|
(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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -25,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 |
|
| Step | SUB | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -43,7 +45,8 @@ Full read pipeline + write pipeline + erase pipeline + monitor log working end-t
|
|||||||
| Event advance / next key | 1F | ✅ |
|
| Event advance / next key | 1F | ✅ |
|
||||||
| **Write commands (push config to device)** | **68–83** | ✅ new v0.8.0 |
|
| **Write commands (push config to device)** | **68–83** | ✅ new v0.8.0 |
|
||||||
| **Erase all events** | **0xA3 → 0x1C → 0x06 → 0xA2** | ✅ new v0.9.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`
|
`get_events()` sequence per event: `1E → 0A → 0C → 5A → 1F`
|
||||||
|
|
||||||
@@ -305,10 +308,16 @@ producing only ~1071 bytes instead of ~2126.
|
|||||||
|
|
||||||
### SUB 1A — anchor search range
|
### SUB 1A — anchor search range
|
||||||
|
|
||||||
`_decode_compliance_config_into()` locates sample_rate and record_time via the anchor
|
`_decode_compliance_config_into()` locates fields via the **6-byte stable anchor**
|
||||||
`b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'`. Search range is `cfg[0:150]`.
|
`b'\xbe\x80\x00\x00\x00\x00'`. Search range is `cfg[0:150]`.
|
||||||
|
|
||||||
Do not narrow this to `cfg[40:100]` — the old range was only accidentally correct because
|
**IMPORTANT — the "10-byte anchor" `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` is NOT fully constant.**
|
||||||
|
The first 2 bytes (`\x01\x2c` = 300) are the `histogram_interval_sec` field (uint16 BE, seconds) —
|
||||||
|
the value 300 is just the 5-minute default. When histogram interval is set to a different value
|
||||||
|
(e.g. 15min = 0x0384 = `\x03\x84`), those bytes change. Only the 6-byte suffix
|
||||||
|
`\xbe\x80\x00\x00\x00\x00` is truly constant. The code already uses the 6-byte anchor.
|
||||||
|
|
||||||
|
Do not narrow the search range to `cfg[40:100]` — the old range was only accidentally correct because
|
||||||
the orphaned-send bug was prepending a 44-byte spurious header, pushing the anchor from
|
the orphaned-send bug was prepending a 44-byte spurious header, pushing the anchor from
|
||||||
its real position (cfg[11]) into the 40–100 window.
|
its real position (cfg[11]) into the 40–100 window.
|
||||||
|
|
||||||
@@ -358,15 +367,43 @@ Do NOT use fixed absolute offsets for sample_rate or record_time.
|
|||||||
|
|
||||||
| Field | How to find it |
|
| Field | How to find it |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| **recording_mode** | **uint8 at anchor − 3 (write) / anchor − 4 (read)** ✅ confirmed 2026-04-20 |
|
||||||
| sample_rate | uint16 BE at anchor − 2 |
|
| sample_rate | uint16 BE at anchor − 2 |
|
||||||
|
| **histogram_interval_sec** | **uint16 BE at anchor − 4 (seconds); same offset in read & write** ✅ confirmed 2026-04-20 |
|
||||||
| record_time | float32 BE at anchor + 10 |
|
| record_time | float32 BE at anchor + 10 |
|
||||||
| trigger_level_geo | float32 BE, located in channel block |
|
| trigger_level_geo | float32 BE, located in channel block |
|
||||||
| alarm_level_geo | float32 BE, adjacent to trigger_level_geo |
|
| alarm_level_geo | float32 BE, adjacent to trigger_level_geo |
|
||||||
| max_range_geo | float32 BE, adjacent to alarm_level_geo — **⚠ value and units UNKNOWN** (reads 6.206053 but doesn't match either UI range option; may not be the ADC full-scale — see GitHub issue) |
|
| geo_hardware_constant (adc_scale_factor) | float32 BE at **channel_label+28** in both read (E5) and write (SUB 71) payloads — reads **6.206053** on BOTH tested units (BE11529 and BE18189); identical across all geo channels (Tran/Vert/Long) and all captures. **Confirmed 2026-04-17 from Interface Handbook §4.5**: this is the **ADC-to-velocity scale factor** = 1/sensitivity = (in/s per V). Firmware uses it as: `PPV (in/s) = ADC_voltage × 6.206053`. Cross-check: `1.61133 V (ADC full-scale) × 6.206053 = 10.000 in/s` (Normal range ✅). Do NOT write this field — it is a hardware/firmware constant. |
|
||||||
|
| geo_range (sensitivity selector) | **uint8 at channel_label+33** in both read (E5) and write (SUB 71) payloads — **CONFIRMED 2026-04-20** from 4-20-26 geo sensitivity captures: `0x00` = Normal 10.000 in/s (standard gain), `0x01` = Sensitive 1.250 in/s (high gain). Present in all three geo channel blocks (Tran, Vert, Long). **NOTE: `channel_label+20` reads `0x01` on ALL captures regardless of range setting — it is NOT this field.** Note: the "SUB 71 write offset = +29" that appears in earlier analysis was an artifact of incorrect BW-style destuffing applied to write frame data — write frame data is RAW, so the literal `0x10` bytes in the channel block header are preserved, and the offset is the same as in the E5 read payload. |
|
||||||
| setup_name | ASCII, null-padded, in cfg body |
|
| setup_name | ASCII, null-padded, in cfg body |
|
||||||
| project / client / operator / sensor_location | ASCII, label-value pairs |
|
| project / client / operator / sensor_location | ASCII, label-value pairs |
|
||||||
|
|
||||||
Anchor: `b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'`, search `cfg[0:150]`
|
**True stable anchor: `b'\xbe\x80\x00\x00\x00\x00'` (6-byte suffix), search `cfg[0:150]`.**
|
||||||
|
The old "10-byte anchor" `b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'` is partially variable:
|
||||||
|
bytes `\x01\x2c` = 300 (5-minute default histogram interval); changes when interval changes.
|
||||||
|
|
||||||
|
**Field layout relative to the 6-byte anchor (write payload / E5 read — noted where different):**
|
||||||
|
|
||||||
|
| Offset | Field | Format | Notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| anchor − 7 (write) / anchor − 8 (read) | recording_mode | uint8 | E5 read has extra `0x10` at anchor−7 |
|
||||||
|
| anchor − 6 | sample_rate | uint16 BE | same in read & write |
|
||||||
|
| anchor − 4 | histogram_interval_sec | uint16 BE | seconds; same in read & write ✅ 2026-04-20 |
|
||||||
|
| anchor − 2 | `0x00 0x00` | padding | |
|
||||||
|
| anchor | `\xbe\x80\x00\x00\x00\x00` | anchor | |
|
||||||
|
| anchor + 6 | record_time | float32 BE | same in read & write |
|
||||||
|
|
||||||
|
**recording_mode enum** (confirmed 2026-04-20 from 4-20-26 captures):
|
||||||
|
|
||||||
|
| Value | Mode |
|
||||||
|
|---|---|
|
||||||
|
| `0x00` | Single Shot |
|
||||||
|
| `0x01` | Continuous |
|
||||||
|
| `0x02` | ❓ not observed |
|
||||||
|
| `0x03` | Histogram |
|
||||||
|
| `0x04` | Histogram + Continuous |
|
||||||
|
|
||||||
|
**DLE escaping in write frames — CONFIRMED 2026-04-20:** Write frame data payloads DO escape `0x03` (ETX) bytes with a `0x10` DLE prefix. For histogram_interval = 900 (0x0384), the wire carries `10 03 84` — the `0x03` high byte is preceded by a DLE escape. After DLE destuffing (`10 XX → XX`), the logical field value is correctly `03 84` = 900. The CLAUDE.md claim that write frame data is "written RAW" was incorrect; at minimum ETX (0x03) bytes are escaped. S3FrameParser handles this transparently so the decoded `compliance_raw` always contains logical (destuffed) bytes.
|
||||||
|
|
||||||
### SUB 0C — Waveform Record (210 bytes = data[11:11+0xD2])
|
### SUB 0C — Waveform Record (210 bytes = data[11:11+0xD2])
|
||||||
|
|
||||||
@@ -701,16 +738,16 @@ Fields visible in the Blastware Compliance Setup dialog — most are NOT YET dec
|
|||||||
offsets in the raw 1A/E5 payload. Only fields with `✅` have confirmed offsets in the code.
|
offsets in the raw 1A/E5 payload. Only fields with `✅` have confirmed offsets in the code.
|
||||||
|
|
||||||
**Recording Setup tab:**
|
**Recording Setup tab:**
|
||||||
- Recording Mode: Continuous / Single Shot / Histogram (enum)
|
- Recording Mode: Continuous / Single Shot / Histogram / Histogram+Continuous ✅ (uint8 at anchor−3 in write, anchor−4 in read; 0x00=Single Shot, 0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous) — confirmed 2026-04-20
|
||||||
- Record Stop Mode: Fixed Record Time / Auto / Manual Stop (enum)
|
- Record Stop Mode: Fixed Record Time / Auto / Manual Stop (enum) ❓ (byte near recording_mode; data[40] in E5 sf1 changed 0x01→0x00 alongside Continuous→Single Shot — may be this field)
|
||||||
- Sample Rate: Standard 1024 / Fast 2048 / Faster 4096 sps ✅ (anchor−2)
|
- Sample Rate: Standard 1024 / Fast 2048 / Faster 4096 sps ✅ (anchor−2)
|
||||||
- Record Time: float, seconds ✅ (anchor+10)
|
- Record Time: float, seconds ✅ (anchor+10)
|
||||||
- Histogram Interval: 5 / 15 / 30 / 60 minutes (enum, mode-gated)
|
- Histogram Interval: 2s / 5s / 15s / 1m / 5m / 15m ✅ (uint16 BE seconds at anchor−4, same in read & write; mode-gated to Histogram/Histogram+Continuous) — confirmed 2026-04-20
|
||||||
- Storage Mode: Save All Data / Save Triggered (enum)
|
- Storage Mode: Save All Data / Save Triggered (enum)
|
||||||
- Geophone Type: Standard Triaxial / 4.5 Hz (bool/enum)
|
- Geophone Type: Standard Triaxial / 4.5 Hz (bool/enum)
|
||||||
- Geophone Channels: Enable all geophones (bool), Trigger Source (bool)
|
- Geophone Channels: Enable all geophones (bool), Trigger Source (bool)
|
||||||
- Chan 1-3 Trigger Level (float, in/s) ✅ (`trigger_level_geo`)
|
- Chan 1-3 Trigger Level (float, in/s) ✅ (`trigger_level_geo`)
|
||||||
- Chan 1-3 Maximum Range: Normal 10.000 / 1.25 in/s (enum) ❓ (`max_range_geo` — offset found, reads 6.206053 which matches neither UI value; units and meaning unknown — do NOT use as ADC full-scale)
|
- Chan 1-3 Maximum Range: Normal 10.000 / 1.25 in/s (enum) ✅ (`geo_range` uint8; **CONFIRMED 2026-04-20** from 4-20-26 geo sensitivity captures: offset = `channel_label+33` in both E5 read and SUB 71 write payloads (same bytes, round-tripped verbatim); `0x00` = Normal 10.000 in/s, `0x01` = Sensitive 1.250 in/s; applied to Tran/Vert/Long channel blocks). **IMPORTANT: `channel_label+20` reads `0x01` on ALL captures and is NOT this field** — it is a constant flag. The float32 at `channel_label+28` = 6.206053 is the ADC-to-velocity scale factor (hardware constant, do NOT write).
|
||||||
- Microphone Channels: Enable all microphones (bool), Trigger Source (bool)
|
- Microphone Channels: Enable all microphones (bool), Trigger Source (bool)
|
||||||
- Chan 4 Trigger Level (dB or psi depending on units)
|
- Chan 4 Trigger Level (dB or psi depending on units)
|
||||||
|
|
||||||
@@ -935,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
|
## What's next
|
||||||
|
|
||||||
- **Database** — SQLite store for events + monitor log entries; dedup by key; queryable
|
- **Database** — SQLite store for events + monitor log entries; dedup by key; queryable
|
||||||
- **Histograms** — decode histogram-mode A5 data (noise floor tracking)
|
- **Histograms** — decode histogram-mode A5 data (noise floor tracking)
|
||||||
- Compliance config encoder — build raw write payloads from a `ComplianceConfig` object
|
- 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)
|
- 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
|
- 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
|
- 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)
|
resume monitoring after call-home disconnect (`--restart-monitoring` flag deferred) | ||||||