11"""Fire-and-forget client for the bm_sbc_gateway IPC socket.
22
33The gateway binds a Unix-domain `SOCK_DGRAM` listener at
4- `/run/bm_sbc/gateway_ipc.sock` and accepts CBOR-encoded datagrams matching
5- the v1 schema documented in the gateway app. This module wraps each supported
6- message type as a single function call.
4+ `/run/bm_sbc/gateway_ipc.sock` and accepts CBOR-encoded datagrams
5+ matching the v1 schema documented in `docs/ gateway-ipc.md`.
6+ This module wraps each supported message type as a single function call.
77
88Example::
99
10- from bm_sbc_gateway import sensor_data, spotter_tx, spotter_log
10+ from bm_sbc_gateway import (
11+ config_set,
12+ replay_caught_up,
13+ sensor_data,
14+ spotter_log,
15+ spotter_tx,
16+ )
1117
1218 sensor_data("temperature", cbor2.dumps({"t_c": 21.4}))
1319 spotter_tx(payload_bytes, iridium_fallback=True)
1420 spotter_log("boot complete", file_name="system.log", print_timestamp=True)
21+ config_set("wifi_ssid", "mynet")
22+ replay_caught_up()
1523"""
1624
1725from __future__ import annotations
4048class Client :
4149 """Reusable client holding a connected `SOCK_DGRAM` socket.
4250
43- Prefer a single `Client` instance over the module-level helpers when
44- sending many messages — it avoids re-opening the socket every call.
51+ Prefer a single `Client` instance over the module-level helpers
52+ when sending many messages —
53+ it avoids re-opening the socket every call.
4554 """
4655
4756 def __init__ (self , socket_path : str = DEFAULT_SOCKET_PATH ) -> None :
4857 self ._path = socket_path
4958 self ._sock = socket .socket (socket .AF_UNIX , socket .SOCK_DGRAM )
5059
5160 def close (self ) -> None :
61+ """Close the underlying socket."""
5262 self ._sock .close ()
5363
5464 def __enter__ (self ) -> "Client" :
@@ -62,6 +72,7 @@ def _send(self, message: dict[str, Any]) -> None:
6272 self ._sock .sendto (cbor2 .dumps (message ), self ._path )
6373
6474 def replay_caught_up (self ) -> None :
75+ """Signal that the upstream replay has caught up."""
6576 self ._send ({"type" : "replay_caught_up" })
6677
6778 def spotter_log (
@@ -70,6 +81,11 @@ def spotter_log(
7081 file_name : Optional [str ] = None ,
7182 print_timestamp : Optional [bool ] = None ,
7283 ) -> None :
84+ """Append a line to a Spotter log, either SD card file or console.
85+
86+ `data` is bounded by the gateway at 1024 bytes.
87+ `file_name` is bounded at 63 bytes; omit it for the console log.
88+ """
7389 msg : dict [str , Any ] = {"type" : "spotter_log" , "data" : data }
7490 if file_name is not None :
7591 msg ["file_name" ] = file_name
@@ -82,22 +98,38 @@ def spotter_tx(
8298 data : bytes ,
8399 iridium_fallback : Optional [bool ] = None ,
84100 ) -> None :
101+ """Transmit a payload over the Spotter cell/satellite link.
102+
103+ `iridium_fallback=True` enables Iridium fallback on top of cellular;
104+ omit (or `False`) for cellular-only.
105+ """
85106 msg : dict [str , Any ] = {"type" : "spotter_tx" , "data" : data }
86107 if iridium_fallback is not None :
87108 msg ["iridium_fallback" ] = iridium_fallback
88109 self ._send (msg )
89110
90111 def config_set (self , config_key : str , config_value : Any ) -> None :
91- # The gateway dispatches on the CBOR wire type of config_value
92- # (text → STR, uint → UINT32, neg int → INT32, float → FLOAT),
93- # so just pass the native Python value.
94- self ._send ({
95- "type" : "config_set" ,
96- "config_key" : config_key ,
97- "config_value" : config_value ,
98- })
112+ """Write a key-value pair into the local system config partition.
113+
114+ The gateway infers the stored type from the CBOR wire type of `config_value`:
115+ text → STR, unsigned int → UINT32, negative int → INT32, float/double → FLOAT.
116+ Strings are capped at 48 bytes by the gateway.
117+ """
118+ self ._send (
119+ {
120+ "type" : "config_set" ,
121+ "config_key" : config_key ,
122+ "config_value" : config_value ,
123+ }
124+ )
99125
100126 def sensor_data (self , topic_suffix : str , data : bytes ) -> None :
127+ """Publish sensor data on the Bristlemouth pub-sub network.
128+
129+ Published topic is `sensor/<node_id_hex16>/<topic_suffix>`.
130+ `topic_suffix` must not begin with `/`;
131+ the gateway inserts the separator.
132+ """
101133 if topic_suffix .startswith ("/" ):
102134 raise ValueError (
103135 "topic_suffix must not begin with '/'; the gateway inserts "
0 commit comments