@@ -10,7 +10,16 @@ package org.meshtastic.sdk
1010import org.meshtastic.proto.AdminMessage
1111import org.meshtastic.proto.Channel
1212import org.meshtastic.proto.Config
13+ import org.meshtastic.proto.DeviceConnectionStatus
14+ import org.meshtastic.proto.DeviceMetadata
15+ import org.meshtastic.proto.DeviceUIConfig
16+ import org.meshtastic.proto.HamParameters
17+ import org.meshtastic.proto.KeyVerificationAdmin
1318import org.meshtastic.proto.ModuleConfig
19+ import org.meshtastic.proto.NodeRemoteHardwarePinsResponse
20+ import org.meshtastic.proto.Position
21+ import org.meshtastic.proto.SensorConfig
22+ import org.meshtastic.proto.SharedContact
1423import org.meshtastic.proto.User
1524import kotlin.time.Duration
1625import kotlin.time.Instant
@@ -29,6 +38,44 @@ import kotlin.time.Instant
2938 */
3039public interface AdminApi {
3140
41+ // ── Remote targeting ────────────────────────────────────────────────────
42+
43+ /* *
44+ * Return an [AdminApi] instance that targets [dest] instead of the local device.
45+ *
46+ * All calls on the returned instance route admin messages to the specified remote node
47+ * over the mesh. Note: `editSettings`, `batch`, `getDeviceConnectionStatus`, and lifecycle
48+ * commands (`reboot`, `shutdown`, `factoryReset`, `nodeDbReset`) work identically — the
49+ * firmware handles admin-over-mesh transparently.
50+ *
51+ * ```kotlin
52+ * val remoteAdmin = client.admin.forNode(NodeId(0x12345678.toInt()))
53+ * remoteAdmin.setConfig(config) // → sent to remote node
54+ * ```
55+ *
56+ * @param dest the target node's [NodeId]
57+ * @return a remote-targeting [AdminApi] instance
58+ * @since 0.2.0
59+ */
60+ public fun forNode (dest : NodeId ): AdminApi
61+
62+ // ── Device info ─────────────────────────────────────────────────────────
63+
64+ /* *
65+ * Request [DeviceMetadata] from the device (firmware version, hardware model, etc.).
66+ *
67+ * For the local node, this is cached during handshake and available via
68+ * [RadioClient.deviceConfig]. For remote nodes, use [forNode] to target the desired node:
69+ *
70+ * ```kotlin
71+ * val metadata = client.admin.forNode(remoteNodeId).getDeviceMetadata()
72+ * ```
73+ *
74+ * @return the device's metadata
75+ * @since 0.2.0
76+ */
77+ public suspend fun getDeviceMetadata (): AdminResult <DeviceMetadata >
78+
3279 // ── Configs ─────────────────────────────────────────────────────────────
3380
3481 /* * Read a single [Config] section from the device. */
@@ -75,6 +122,130 @@ public interface AdminApi {
75122 /* * Mark [node] as ignored — packets from it are filtered before reaching apps. */
76123 public suspend fun setIgnored (node : NodeId , ignored : Boolean ): AdminResult <Unit >
77124
125+ /* *
126+ * Toggle mute state on [node] — muted nodes do not forward packets.
127+ *
128+ * Note: The firmware uses a toggle primitive (`toggle_muted_node`), so calling this
129+ * always flips the current state. Track local mute state if you need idempotent behavior.
130+ */
131+ public suspend fun toggleMuted (node : NodeId ): AdminResult <Unit >
132+
133+ // ── Position ────────────────────────────────────────────────────────────
134+
135+ /* * Set a fixed GPS position for the device (disables GPS module). */
136+ public suspend fun setFixedPosition (position : Position ): AdminResult <Unit >
137+
138+ /* * Remove the fixed position and re-enable GPS. */
139+ public suspend fun removeFixedPosition (): AdminResult <Unit >
140+
141+ // ── Device UI Config ────────────────────────────────────────────────────
142+
143+ /* * Read the device's UI configuration (display preferences, language, etc.). */
144+ public suspend fun getUIConfig (): AdminResult <DeviceUIConfig >
145+
146+ /* * Write the device's UI configuration. */
147+ public suspend fun storeUIConfig (config : DeviceUIConfig ): AdminResult <Unit >
148+
149+ // ── Canned Messages ─────────────────────────────────────────────────────
150+
151+ /* * Read the canned message module's preset messages. */
152+ public suspend fun getCannedMessages (): AdminResult <String >
153+
154+ /* * Write the canned message module's preset messages (pipe-delimited). */
155+ public suspend fun setCannedMessages (messages : String ): AdminResult <Unit >
156+
157+ // ── Ringtone ────────────────────────────────────────────────────────────
158+
159+ /* * Read the device's ringtone (RTTTL format). */
160+ public suspend fun getRingtone (): AdminResult <String >
161+
162+ /* * Write the device's ringtone (RTTTL format). */
163+ public suspend fun setRingtone (rtttl : String ): AdminResult <Unit >
164+
165+ // ── Device status ───────────────────────────────────────────────────────
166+
167+ /* * Read the device's connection status (WiFi, BLE, Ethernet, MQTT). */
168+ public suspend fun getDeviceConnectionStatus (): AdminResult <DeviceConnectionStatus >
169+
170+ /* * Read the remote hardware pin configuration of [node]. */
171+ public suspend fun getRemoteHardwarePins (): AdminResult <NodeRemoteHardwarePinsResponse >
172+
173+ // ── Ham radio ───────────────────────────────────────────────────────────
174+
175+ /* * Configure the device for amateur radio use (sets call sign, disables encryption). */
176+ public suspend fun setHamMode (params : HamParameters ): AdminResult <Unit >
177+
178+ // ── DFU / file management ───────────────────────────────────────────────
179+
180+ /* *
181+ * Enter DFU (firmware update) mode. The device will reboot into its bootloader.
182+ *
183+ * This is a fire-and-forget admin write; [AdminResult.Success] means the request was queued
184+ * locally, not that the device stayed connected long enough to acknowledge the reboot.
185+ */
186+ public suspend fun enterDfuMode (): AdminResult <Unit >
187+
188+ /* * Delete a file from the device's filesystem. */
189+ public suspend fun deleteFile (path : String ): AdminResult <Unit >
190+
191+ // ── Backup / Restore ────────────────────────────────────────────────────
192+
193+ /* * Back up device preferences to the specified [location]. */
194+ public suspend fun backupPreferences (
195+ location : AdminMessage .BackupLocation = AdminMessage .BackupLocation .FLASH ,
196+ ): AdminResult <Unit >
197+
198+ /* * Restore device preferences from the specified [location]. */
199+ public suspend fun restorePreferences (
200+ location : AdminMessage .BackupLocation = AdminMessage .BackupLocation .FLASH ,
201+ ): AdminResult <Unit >
202+
203+ /* * Remove a stored preference backup from [location]. */
204+ public suspend fun removeBackupPreferences (
205+ location : AdminMessage .BackupLocation = AdminMessage .BackupLocation .FLASH ,
206+ ): AdminResult <Unit >
207+
208+ // ── Node removal ────────────────────────────────────────────────────────
209+
210+ /* * Remove a node from the device's NodeDB by its node number. */
211+ public suspend fun removeNode (node : NodeId ): AdminResult <Unit >
212+
213+ // ── Input / Display ─────────────────────────────────────────────────────
214+
215+ /* * Set the device's scale calibration value (e-ink display DPI). */
216+ public suspend fun setScale (scale : Int ): AdminResult <Unit >
217+
218+ /* * Send a synthetic input event to the device (button press, touch, etc.). */
219+ public suspend fun sendInputEvent (event : AdminMessage .InputEvent ): AdminResult <Unit >
220+
221+ // ── Contacts ────────────────────────────────────────────────────────────
222+
223+ /* * Add a shared contact to the device's contact list. */
224+ public suspend fun addContact (contact : SharedContact ): AdminResult <Unit >
225+
226+ // ── Key verification ────────────────────────────────────────────────────
227+
228+ /* * Initiate or respond to a key verification exchange. */
229+ public suspend fun keyVerification (verification : KeyVerificationAdmin ): AdminResult <Unit >
230+
231+ // ── OTA updates ─────────────────────────────────────────────────────────
232+
233+ /* * Reboot into OTA update mode after [after] (default: immediately). */
234+ public suspend fun rebootOta (after : Duration = Duration .ZERO ): AdminResult <Unit >
235+
236+ /* * Send an OTA event (firmware update control). */
237+ public suspend fun otaRequest (event : AdminMessage .OTAEvent ): AdminResult <Unit >
238+
239+ // ── Sensor ──────────────────────────────────────────────────────────────
240+
241+ /* * Configure a sensor attached to the device. */
242+ public suspend fun setSensorConfig (config : SensorConfig ): AdminResult <Unit >
243+
244+ // ── Simulator ───────────────────────────────────────────────────────────
245+
246+ /* * Exit the firmware simulator mode (development only). */
247+ public suspend fun exitSimulator (): AdminResult <Unit >
248+
78249 // ── Lifecycle ───────────────────────────────────────────────────────────
79250
80251 /* *
@@ -101,14 +272,25 @@ public interface AdminApi {
101272 /* *
102273 * Wipe the device's NodeDB, forcing a fresh discovery cycle on the mesh.
103274 *
104- * @param preserveFavorites when `true` (default), entries marked as favorites are kept. The
105- * firmware does not currently expose a separate flag for this; on devices that always erase
106- * the entire NodeDB, the SDK's [setFavorite] state on local entries is the only persistence.
275+ * The firmware always preserves favorite-marked entries during the wipe (this is
276+ * firmware-enforced behavior). The `nodedb_reset` proto field uses proto3 semantics where
277+ * only `true` can be encoded — a "wipe everything including favorites" mode is not
278+ * available through this command.
279+ *
280+ * The device will reboot after the reset completes.
107281 */
108- public suspend fun nodeDbReset (preserveFavorites : Boolean = true ): AdminResult <Unit >
282+ public suspend fun nodeDbReset (): AdminResult <Unit >
109283
110284 // ── Time ────────────────────────────────────────────────────────────────
111285
286+ /* *
287+ * Set the device's wall clock from raw Unix time seconds without sending position data.
288+ *
289+ * This uses `AdminMessage.set_time_only` directly and is fire-and-forget; [AdminResult.Success]
290+ * means the packet was queued locally.
291+ */
292+ public suspend fun setTimeOnly (unixTime : Int ): AdminResult <Unit >
293+
112294 /* *
113295 * Set the device's wall clock to [at] (default: `Clock.System.now()`).
114296 *
@@ -127,6 +309,14 @@ public interface AdminApi {
127309 * or commit fails, the result reflects that failure and the block's return value is discarded.
128310 */
129311 public suspend fun <T > editSettings (block : suspend AdminEdit .() -> T ): AdminResult <T >
312+
313+ /* *
314+ * Exception-based counterpart to [editSettings] that also exposes batched getter helpers.
315+ *
316+ * Getter failures throw [AdminResultException] via [getOrThrow]. If [block] throws, the SDK
317+ * does not send `commit_edit_settings`; firmware eventually discards the buffered edits.
318+ */
319+ public suspend fun <T > batch (block : suspend AdminBatchScope .() -> T ): T
130320}
131321
132322/* *
@@ -146,3 +336,15 @@ public interface AdminEdit {
146336 public suspend fun setFavorite (node : NodeId , favorite : Boolean )
147337 public suspend fun setIgnored (node : NodeId , ignored : Boolean )
148338}
339+
340+ /* *
341+ * Receiver type for [AdminApi.batch] — combines [AdminEdit] setters with getter helpers.
342+ *
343+ * Getter failures throw [AdminResultException] via [getOrThrow]. Setters share the same deferred
344+ * commit semantics as [AdminApi.editSettings].
345+ */
346+ public interface AdminBatchScope : AdminEdit {
347+ public suspend fun getConfig (type : AdminMessage .ConfigType ): Config
348+ public suspend fun getModuleConfig (type : AdminMessage .ModuleConfigType ): ModuleConfig
349+ public suspend fun listChannels (): List <Channel >
350+ }
0 commit comments