meshtastic 0.0.182 → 0.0.184

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/README.md +5 -0
  3. data/documentation/admin-backup.md +369 -0
  4. data/documentation/admin-channel.md +6 -6
  5. data/documentation/admin-config.md +5 -5
  6. data/documentation/admin-firmware-hex.md +57 -0
  7. data/documentation/admin-firmware-serial.md +1 -1
  8. data/documentation/admin-firmware-uf2.md +94 -0
  9. data/documentation/admin-firmware.md +52 -10
  10. data/documentation/admin.md +12 -2
  11. data/documentation/module-config.md +8 -4
  12. data/documentation/rtttl.md +8 -4
  13. data/lib/meshtastic/admin/backup.rb +560 -0
  14. data/lib/meshtastic/admin/channel.rb +6 -3
  15. data/lib/meshtastic/admin/config.rb +14 -14
  16. data/lib/meshtastic/admin/firmware/hex.rb +209 -0
  17. data/lib/meshtastic/admin/firmware/uf2.rb +166 -0
  18. data/lib/meshtastic/admin/firmware.rb +55 -13
  19. data/lib/meshtastic/admin.rb +60 -23
  20. data/lib/meshtastic/config_pb.rb +1 -1
  21. data/lib/meshtastic/mesh_pb.rb +1 -1
  22. data/lib/meshtastic/module_config.rb +22 -4
  23. data/lib/meshtastic/module_config_pb.rb +2 -1
  24. data/lib/meshtastic/rtttl.rb +22 -4
  25. data/lib/meshtastic/version.rb +1 -1
  26. data/spec/lib/meshtastic/admin/backup_spec.rb +704 -0
  27. data/spec/lib/meshtastic/admin/channel_spec.rb +23 -9
  28. data/spec/lib/meshtastic/admin/config_spec.rb +28 -12
  29. data/spec/lib/meshtastic/admin/firmware/hex_spec.rb +160 -0
  30. data/spec/lib/meshtastic/admin/firmware/uf2_spec.rb +234 -0
  31. data/spec/lib/meshtastic/admin/firmware_spec.rb +106 -3
  32. data/spec/lib/meshtastic/admin_spec.rb +106 -40
  33. data/spec/lib/meshtastic/module_config_spec.rb +47 -0
  34. data/spec/lib/meshtastic/rtttl_spec.rb +47 -0
  35. metadata +10 -1
@@ -7,11 +7,12 @@
7
7
  | Operation | What the Ruby implementation actually does |
8
8
  | --- | --- |
9
9
  | `sha256(firmware: ... \| bytes: ...)` | Returns a raw 32-byte SHA-256 digest. Exactly one nonempty image source is required. |
10
- | `request_ota(...)` | Sends the real ESP32 `OTAEvent` admin request: raw SHA-256 plus `:OTA_BLE` (default) or `:OTA_WIFI`. This pins the image hash and requests a reboot into an **already installed compatible loader**; it does not upload firmware or prove that the loader started. |
10
+ | `request_ota(...)` | Sends the real ESP32 `OTAEvent` admin request: raw SHA-256 plus an explicit `transfer: :ble` or `transfer: :wifi` (or the documented handle inference below). This pins the image hash and requests a reboot into an **already installed compatible loader**; it does not upload firmware or prove that the loader started. |
11
11
  | `install(protocol: :unified_wifi, host: ..., firmware: ... \| bytes: ...)` | Separate ESP32 unified-loader TCP protocol, normally port 3232. Requires the image hash to have been provisioned using `request_ota`. |
12
12
  | `install(protocol: :unified_ble, address: ..., firmware: ... \| bytes: ...)` | ESP32 unified-loader custom GATT protocol, with a native Ruby BlueZ backend and application ACK flow control. Not the old BLE-only firmware-ota protocol. |
13
13
  | `install(protocol: :nordic_dfu, address: ..., package: ...)` | Adafruit SDK11 legacy Nordic BLE DFU for application-only legacy ZIP packages. [Exact scope and options](admin-firmware-nordic.md). Not Nordic Secure DFU or UF2. |
14
14
  | `install(protocol: :esp_rom, ...)` | Native ESP ROM serial flashing with explicit chip, flash geometry and offset, ROM acknowledgements and flash MD5 verification. [Exact scope and options](admin-firmware-serial.md). |
15
+ | `install(protocol: :uf2, format: :uf2, ...)` | Validate and copy a UF2 image to an explicitly selected mounted bootloader volume. Copy completion is not flash or boot confirmation. See `UF2.help` for volume and image validation options. |
15
16
  | `verify_reboot(...)` or `install(..., verify: {...})` | Fresh application connection, matching configuration handshake and a new request-ID/source-correlated Admin device-metadata reply. Checks exact firmware version and optional node identity. |
16
17
  | `enter_dfu(...)` | Sends `enter_dfu_mode_request`; current upstream handles entry on nRF52/RP2040. It neither transfers a DFU package nor copies a UF2 image. |
17
18
  | `reboot_ota`, `xmodem_blocks`, `send_xmodem` | Raise `NotImplementedError`. The legacy reboot field has no handler in the inspected firmware; XModem is filesystem transfer, not firmware installation. |
@@ -19,6 +20,46 @@
19
20
 
20
21
  Admin commands accept the transport, addressing, and authentication options documented in [Admin](admin.md). Use exactly one transport. Successful submission is **not confirmation that hardware supports or performed the operation**. A routing acknowledgement alone cannot prove an ESP32 OTA loader/partition exists. No automatic board detection is performed.
21
22
 
23
+ ## Control connection, transfer protocol, and image format
24
+
25
+ These are separate choices:
26
+
27
+ - **Control connection** (`transport_obj: connection`, the actual connected Serial, Bluetooth, TCP, or MQTT handle) carries an authenticated Admin preparation request to the running application. It does not carry the subsequent firmware bytes.
28
+ - **OTA transfer** (`request_ota(transfer: :wifi | :ble)`) chooses the ESP32 loader mode after reboot. A serial control request with `transfer: :wifi` is intentional: USB carries the request, then a separate WiFi loader endpoint receives the image. There is no `transfer: :serial` or `:mqtt` unified updater.
29
+ - **Installer protocol** (`install(protocol: ...)`) selects the actual data endpoint and wire protocol, not a PhoneAPI handle. Choose the matching unified WiFi/BLE loader, ESP serial ROM, legacy Nordic BLE DFU, or UF2 mounted-volume writer explicitly.
30
+ - **Image format** (`install(format: :bin | :zip | :uf2)`) describes the artifact. It defaults to the selected protocol's format for compatibility, not to the filename. It never silently selects a different protocol.
31
+
32
+ `request_ota` no longer silently defaults to BLE. With exactly one non-nil control handle, a Bluetooth handle infers `transfer: :ble` and a TCP handle infers `transfer: :wifi` as conveniences; these are not capability detection. Serial or MQTT handles require an explicit transfer or valid legacy `mode:`. A connected handle is required for the Admin request. Explicit transfer overrides handle inference (for example, a Bluetooth preparation request can select a WiFi loader). `mode: :OTA_WIFI` / `:OTA_BLE` remains supported; if both `mode` and `transfer` are supplied they must agree. Supply only `transport_obj:`; legacy transport-specific Admin keywords and ambiguous multi-transport handles are rejected.
33
+
34
+ ### Supported artifact inventory
35
+
36
+ | Format | Installer protocol | Scope |
37
+ | --- | --- | --- |
38
+ | `:bin` | `:unified_wifi`, `:unified_ble` | Matching ESP32 application-update binary; not a merged factory image or bootloader. |
39
+ | `:bin` | `:esp_rom` | ESP image with explicit supported chip, flash geometry and offset; see the serial installer documentation. |
40
+ | `:zip` | `:nordic_dfu` | Application-only legacy Adafruit SDK11 Nordic DFU ZIP supplied through `package:` or `package_bytes:`; not any arbitrary ZIP or Secure DFU bundle. |
41
+ | `:uf2` | `:uf2` | UF2 block image supplied through `firmware:` or `bytes:` to the separately selected mounted bootloader volume. |
42
+ | `:hex` | `:swd` | nRF52840 Intel HEX through an explicitly configured OpenOCD/SWD programmer; [options and safety restrictions](admin-firmware-hex.md). |
43
+ | Standalone DAT / arbitrary DFU | None | Unsupported as standalone install inputs; no universal conversion or board support is claimed. |
44
+
45
+ A declared format/protocol mismatch is rejected before backend invocation. Binary routes also reject known UF2/ZIP/Intel HEX signatures and `.uf2`, `.zip`, `.hex`, `.dfu` filenames before connecting or erasing, including UF2 bytes renamed to `.bin`. Binary files are read once and the inspected bytes are passed to the backend. These guards are not a universal image validator: selecting `:bin` does not prove board, partition, signature, or bootloader compatibility. UF2 and Nordic package validation belongs to their respective backends. Entering DFU/UF2 bootloader mode and selecting/mounting its volume are separate preparation steps; `install` never automatically reboots the application.
46
+
47
+ ### MQTT scope
48
+
49
+ `request_ota(transport_obj: connection, transfer: :wifi, ...)` can submit an authorized Admin preparation request through MQTT, subject to device routing/authentication support. Supply an explicit unicast `to:` and a valid `session_passkey:` obtained through an authenticated radio session; MQTT has no receive queue for automatic Admin session acquisition. That is not an MQTT firmware upload or confirmation that preparation succeeded. The image still needs a reachable, compatible loader data endpoint; `install(protocol: :mqtt, ...)` is unsupported. MQTT is also not a synchronous post-reboot verifier. Do not infer flashing support from broker publish success or an XModem protobuf field.
50
+
51
+ Stock firmware rejects decoded/plaintext MQTT Admin messages. Encrypted traffic
52
+ still requires the target's routing, downlink and Admin authorization settings;
53
+ this client's channel-PSK MQTT publisher does not implement host-side PKI.
54
+ The updater's complete transport selection is WiFi or BLE, not MQTT. Therefore
55
+ an MQTT-only connection cannot deliver an image. Adding such a path would require
56
+ device-side updater changes or an external bridge, not merely publishing image
57
+ chunks from Ruby. A hybrid preparation-through-MQTT followed by direct TCP/BLE
58
+ transfer is not an MQTT image uploader.
59
+
60
+ Sources: [MQTT receive filtering](https://github.com/meshtastic/firmware/blob/6d41e279f1f51bd59f687b9d441c1bf47b1594fc/src/mqtt/MQTT.cpp#L129-L151)
61
+ and [loader transport selection](https://github.com/meshtastic/esp32-unified-ota/blob/e7c0b95e14b6a1ffeca81b71c1ac477593911213/src/main.cpp#L54-L75).
62
+
22
63
  ## ESP32 unified WiFi example
23
64
 
24
65
  Only use a matching application update `.bin`, not a merged full-flash image, UF2, ZIP, or bootloader. Confirm board, flash layout, power, WiFi configuration, and loader compatibility yourself. Wrong images or interrupted writes may leave the device unbootable; keep a recovery method available.
@@ -28,15 +69,16 @@ image = File.binread('firmware-matching-board-update.bin')
28
69
 
29
70
  # Phase 1: use an existing authenticated Admin connection to pin this image.
30
71
  Meshtastic::Admin::Firmware.request_ota(
31
- serial_obj: serial_obj,
72
+ transport_obj: connection,
32
73
  bytes: image,
33
- mode: :OTA_WIFI
74
+ transfer: :wifi
34
75
  )
35
76
 
36
77
  # Phase 2: connect to the separate loader after it reboots and joins WiFi.
37
- # Not the Meshtastic TCP PhoneAPI port 4403, nor an existing tcp_obj.
78
+ # Not the Meshtastic TCP PhoneAPI port 4403, nor an existing TCP PhoneAPI handle.
38
79
  result = Meshtastic::Admin::Firmware.install(
39
80
  protocol: :unified_wifi,
81
+ format: :bin,
40
82
  host: '192.0.2.10',
41
83
  bytes: image,
42
84
  port: 3232,
@@ -48,9 +90,9 @@ result = Meshtastic::Admin::Firmware.install(
48
90
  # loader_version: 'hardware firmware reboot_count loader_version' }
49
91
  ```
50
92
 
51
- `request_ota(ota_hash: ...)` also accepts an explicitly supplied **raw** 32-byte digest (not hex). When an image is supplied alongside the digest they must match. It rejects unknown modes. The loader itself checks that the upload hash equals its provisioned NVS hash and verifies the downloaded bytes.
93
+ `request_ota(ota_hash: ...)` also accepts an explicitly supplied **raw** 32-byte digest (not hex). When an image is supplied alongside the digest they must match. It rejects unknown transfers/modes and contradictory aliases before sending. The loader itself checks that the upload hash equals its provisioned NVS hash and verifies the downloaded bytes.
52
94
 
53
- `install` intentionally rejects `serial_obj`, `tcp_obj`, `bluetooth_obj`, `mqtt_obj`, `mode`, mesh destinations, and other unknown options. These are not the unified WiFi transport. `host` must address the actual prepared loader. Protocol selection is explicit: no guessing or fallback to a different flasher.
95
+ `install` intentionally rejects `transport_obj`, `serial_obj`, `tcp_obj`, `bluetooth_obj`, `mqtt_obj`, `mode`, mesh destinations, and other unknown options. These are not the unified WiFi transport. `host` must address the actual prepared loader. Protocol selection is explicit: no guessing or fallback to a different flasher.
54
96
 
55
97
  ### Wire behavior and failure handling
56
98
 
@@ -66,7 +108,7 @@ result = Meshtastic::Admin::Firmware.install(
66
108
 
67
109
  ## ESP32 unified BLE example
68
110
 
69
- The compatible unified loader must already be installed and its NVS hash pinned with `request_ota(mode: :OTA_BLE, bytes: image, ...)`. Close the application transport before opening the loader; never open serial and BLE on the same radio concurrently. Identify the bootloader's **actual** address explicitly (it can differ from the application's). The backend never guesses an incremented MAC, discovers/selects another device, or pairs automatically. If BlueZ does not know the address, discover that loader explicitly before calling `install`. Application reconnection still requires normal Meshtastic BLE pairing.
111
+ The compatible unified loader must already be installed and its NVS hash pinned with `request_ota(transfer: :ble, bytes: image, ...)`. Close the application transport before opening the loader; never open serial and BLE on the same radio concurrently. Identify the bootloader's **actual** address explicitly (it can differ from the application's). The backend never guesses an incremented MAC, discovers/selects another device, or pairs automatically. If BlueZ does not know the address, discover that loader explicitly before calling `install`. Application reconnection still requires normal Meshtastic BLE pairing.
70
112
 
71
113
  ```ruby
72
114
  result = Meshtastic::Admin::Firmware.install(
@@ -92,7 +134,7 @@ For tests or an alternative Ruby GATT implementation, unified BLE accepts `backe
92
134
 
93
135
  ## Post-reboot verification
94
136
 
95
- `verify:` is an **optional Hash** of `verify_reboot` options, validated before transfer. Omitting it preserves loader-only `:verified` results; it never silently claims boot health. Providing it runs verification only after the installer returns and closes its loader connection. You can also call `verify_reboot` independently.
137
+ `verify:` is an **optional Hash** of `verify_reboot` options, validated before transfer. Omitting it preserves the backend result (`:verified` for the native loaders; a UF2 copy alone is not verified flash); it never silently claims boot health. Providing it runs verification only after the installer returns and closes its loader connection. You can also call `verify_reboot` independently.
96
138
 
97
139
  - Required: `transport: :tcp | :bluetooth | :serial`, `expected_version:` (exact nonempty String).
98
140
  - Production default: `connection:` Hash for a **new** application connection, explicitly specifying `host`, `address`, or `block_dev`, respectively. TCP uses PhoneAPI port **4403**, not updater port 3232; set `connection[:port]` only for a custom PhoneAPI port. Do not supply an existing socket/handle.
@@ -100,13 +142,13 @@ For tests or an alternative Ruby GATT implementation, unified BLE accepts `backe
100
142
  - Optional advanced `reconnect:` callable receives `{transport:, connection:, timeout:}` and must return a newly connected handle of the selected transport, with configuration requested. The verifier still waits for configuration and issues a fresh Admin metadata request; a callback cannot substitute a cached metadata Hash. The returned handle is closed afterward.
101
143
  - Pre-metadata connection/configuration I/O failures retry every 0.25 seconds within the total deadline. Metadata errors/version mismatches do not cause a reflash or get converted to success.
102
144
  - A fresh source/request-ID-matched `get_device_metadata_response` is required. Cached `handle[:metadata]`, configuration metadata, loader VERSION, a routing ACK or successful port open cannot satisfy verification.
103
- - Success merges `status: :boot_verified`, `loader_status: :verified`, `boot_verified: true`, `reboot_verified: true`, current firmware version, node number and metadata into the transfer result. Failure raises; firmware might already have been written, so inspect the device rather than blindly rerunning the installer.
145
+ - Success merges `status: :boot_verified`, `loader_status:` preserving the backend status (`:verified` or UF2 `:copied`), `boot_verified: true`, `reboot_verified: true`, current firmware version, node number and metadata into the transfer result. Failure raises; firmware might already have been written, so inspect the device rather than blindly rerunning the installer.
104
146
 
105
147
  This proves that the selected application responds and reports the expected version/optional identity. It does **not** cryptographically attest the running image, establish board compatibility, or test radio/RF operation. Use the correct release artifact and retain a recovery path.
106
148
 
107
149
  ## Explicit gaps
108
150
 
109
- - No legacy BLE-only updater, ArduinoOTA/espota WiFi updater, Nordic Secure DFU, serial Nordic DFU, RP2040 UF2 filesystem copying, or automatic bootloader installation. Native protocol support is deliberately scoped; a board name alone does not establish its installed bootloader or transport capabilities.
151
+ - No legacy BLE-only updater, ArduinoOTA/espota WiFi updater, Nordic Secure DFU, serial Nordic DFU, universal HEX conversion, or automatic bootloader installation. Native protocol support is deliberately scoped; a board name alone does not establish its installed bootloader or transport capabilities.
110
152
  - No automatic discovery, board/image compatibility parser, OTA partition creation, or firmware downloads. ESP32 unified source targets ESP32/ESP32-S3; this is not a claim that every ESP32 variant or every Meshtastic board has that loader.
111
153
  - MQTT can carry an authorized preparation request; it is not an image transport or synchronous post-reboot verifier.
112
154
  - Tests exercise fake GATT loaders, real Ruby D-Bus signal marshalling over UNIX sockets, loopback TCP PhoneAPI/configuration/Admin exchanges and the retained TCP uploader. No hardware was contacted or flashed; physical device compatibility and reboot behavior remain hardware-unverified.
@@ -1,6 +1,12 @@
1
1
  # Meshtastic::Admin
2
2
 
3
- Build and send `AdminMessage` on `ADMIN_APP` through a connected `serial_obj`, `bluetooth_obj`, `tcp_obj`, or `mqtt_obj`. These operations can change configuration, reboot, erase files, or reset a device. Sending is not confirmation of successful execution.
3
+ Build and send `AdminMessage` on `ADMIN_APP` through `transport_obj: connection`, where `connection` is the actual connected Serial, Bluetooth, TCP, or MQTT handle. These operations can change configuration, reboot, erase files, or reset a device. Sending is not confirmation of successful execution.
4
+
5
+ ## Connection handle
6
+
7
+ All Admin send/request helpers and nested Channel, Config, and Firmware preparation APIs use `transport_obj: connection`. Pass the handle returned by `Meshtastic::Serial.connect`, `Meshtastic::Bluetooth.connect`, `Meshtastic::TCP.connect`, or `Meshtastic::MQTT.connect`, not a transport name, endpoint string, or wrapper containing `serial_obj:`. Admin detects the transport from the handle; `Admin.transport_type(transport_obj: connection)` returns its type. Missing, unsupported, or ambiguous multi-transport handles are rejected. The old Admin keywords `serial_obj:`, `bluetooth_obj:`, `tcp_obj:`, and `mqtt_obj:` are rejected rather than silently selected.
8
+
9
+ The low-level transport APIs are unchanged: connect using their documented endpoint arguments and disconnect with their original transport-specific keyword (for example, `Meshtastic::Serial.disconnect(serial_obj: connection)`). Firmware `install` uses a separate loader endpoint (`host:`, `address:`, `port:`, or mounted volume/programmer options); it does not accept the application `transport_obj:`. Post-reboot verification likewise opens a new application connection using its documented endpoint options.
4
10
 
5
11
  ## Addressing and defaults
6
12
 
@@ -18,6 +24,10 @@ Every generated payload field is available through `encode` and `send`, includin
18
24
 
19
25
  ## Operation catalog
20
26
 
27
+ For host-side configuration files, use [Admin::Backup](admin-backup.md).
28
+ The `backup_preferences` / `restore_preferences` operations below instead manage
29
+ the device's own FLASH/SD preference backup; they do not export a file to the host.
30
+
21
31
  | Group | Methods and payload arguments |
22
32
  | --- | --- |
23
33
  | Identity | `get_owner`; `set_owner(owner:)` or `set_owner(long_name:, short_name:)`; `set_ham_mode(ham:)` or callsign/frequency/power/name fields |
@@ -59,7 +69,7 @@ Unrelated ports, non-response variants, encrypted/absent data, and mismatched ID
59
69
 
60
70
  ```ruby
61
71
  reply = Meshtastic::Admin.request(
62
- serial_obj: serial_obj, # alternatively bluetooth_obj: or tcp_obj:
72
+ transport_obj: connection, # a connected Serial, Bluetooth, or TCP handle
63
73
  to: '!aabbccdd',
64
74
  message: Meshtastic::AdminMessage.new(get_device_metadata_request: true),
65
75
  timeout: 10
@@ -4,20 +4,24 @@ Reopens the generated `Meshtastic::ModuleConfig` protobuf class. Get/set module
4
4
 
5
5
  ## Methods
6
6
 
7
- - `get(serial_obj:, module_config_type: :MQTT_CONFIG)`
8
- - `set(serial_obj:, module_config:)`
7
+ - `get(transport_obj:, module_config_type: :MQTT_CONFIG)`
8
+ - `set(transport_obj:, module_config:)`
9
9
  - `help` / `authors`
10
10
 
11
11
  `module_config_type` values include `:MQTT_CONFIG`, `:SERIAL_CONFIG`, `:STOREFORWARD_CONFIG`, `:TELEMETRY_CONFIG`, `:REMOTEHARDWARE_CONFIG`, `:CANNEDMSG_CONFIG`, `:AUDIO_CONFIG`, `:PAXCOUNTER_CONFIG`, `:NEIGHBORINFO_CONFIG`, `:DETECTIONSENSOR_CONFIG`, `:EXTNOTIF_CONFIG`, `:RANGETEST_CONFIG`, `:AMBIENTLIGHTING_CONFIG`, `:STATUSMESSAGE_CONFIG`, `:MESHBEACON_CONFIG`, `:TAK_CONFIG`, `:TRAFFICMANAGEMENT_CONFIG`.
12
12
 
13
+ Supply `transport_obj: connection` with an actual connected Serial, Bluetooth, TCP, or MQTT handle. These non-Admin wrappers also retain `serial_obj:`, `bluetooth_obj:`, `tcp_obj:`, and `mqtt_obj:` for existing callers, translating them internally to Admin's `transport_obj:`. Supply exactly one non-nil connection option; mixed aliases are rejected even when they refer to the same handle. Nil aliases are ignored, and caller options are not mutated.
14
+
15
+ Routing, validation, and automatic remote session-key acquisition follow [Admin](admin.md). Setters may wait for session acquisition, but their return value is transport submission, not confirmed persistence. MQTT requires an explicit authorized passkey for remote writes; these helpers do not add synchronous MQTT readback.
16
+
13
17
  ## Example
14
18
 
15
19
  ```ruby
16
- Meshtastic::ModuleConfig.get(serial_obj: serial_obj, module_config_type: :MQTT_CONFIG)
20
+ Meshtastic::ModuleConfig.get(transport_obj: connection, module_config_type: :MQTT_CONFIG)
17
21
 
18
22
  mod = Meshtastic::ModuleConfig.new
19
23
  mod.mqtt = Meshtastic::ModuleConfig::MQTTConfig.new(enabled: true, address: 'mqtt.example.test')
20
- Meshtastic::ModuleConfig.set(serial_obj: serial_obj, module_config: mod)
24
+ Meshtastic::ModuleConfig.set(transport_obj: connection, module_config: mod)
21
25
  ```
22
26
 
23
27
  ## Related
@@ -5,18 +5,22 @@ Ringtone helpers. `encode` builds `RTTTLConfig`. `set` / `get` go through [Admin
5
5
  ## Methods
6
6
 
7
7
  - `encode(ringtone:)`
8
- - `set(serial_obj:, ringtone:)`
9
- - `get(serial_obj:)`
8
+ - `set(transport_obj:, ringtone:)`
9
+ - `get(transport_obj:)`
10
10
  - `help` / `authors`
11
11
 
12
+ Supply `transport_obj: connection` with an actual connected Serial, Bluetooth, TCP, or MQTT handle. These non-Admin wrappers also retain `serial_obj:`, `bluetooth_obj:`, `tcp_obj:`, and `mqtt_obj:` for existing callers, translating them internally to Admin's `transport_obj:`. Supply exactly one non-nil connection option; mixed aliases are rejected even when they refer to the same handle. Nil aliases are ignored, and caller options are not mutated.
13
+
14
+ Routing, validation, and automatic remote session-key acquisition follow [Admin](admin.md). Setters may wait for session acquisition, but their return value is transport submission, not confirmed persistence. MQTT requires an explicit authorized passkey for remote writes; these helpers do not add synchronous MQTT readback.
15
+
12
16
  ## Example
13
17
 
14
18
  ```ruby
15
19
  Meshtastic::RTTTL.set(
16
- serial_obj: serial_obj,
20
+ transport_obj: connection,
17
21
  ringtone: 'Mario:d=4,o=5,b=125:16e6'
18
22
  )
19
- Meshtastic::RTTTL.get(serial_obj: serial_obj)
23
+ Meshtastic::RTTTL.get(transport_obj: connection)
20
24
  ```
21
25
 
22
26
  ## Related