diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkRadioController.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkRadioController.kt index 5ad90a19ad..b0a389c361 100644 --- a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkRadioController.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkRadioController.kt @@ -173,38 +173,22 @@ class SdkRadioController( override suspend fun setOwner(destNum: Int, user: User, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.setOwner(user) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_owner = user)) - } + c.admin.forNode(NodeId(destNum)).setOwner(user) } override suspend fun setConfig(destNum: Int, config: Config, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.setConfig(config) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_config = config)) - } + c.admin.forNode(NodeId(destNum)).setConfig(config) } override suspend fun setModuleConfig(destNum: Int, config: ModuleConfig, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.setModuleConfig(config) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_module_config = config)) - } + c.admin.forNode(NodeId(destNum)).setModuleConfig(config) } override suspend fun setRemoteChannel(destNum: Int, channel: Channel, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.setChannel(channel) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_channel = channel)) - } + c.admin.forNode(NodeId(destNum)).setChannel(channel) } override suspend fun setFixedPosition(destNum: Int, position: Position) { @@ -215,162 +199,88 @@ class SdkRadioController( altitude = position.altitude, time = position.time, ) - if (isLocalNode(destNum)) { - c.admin.setFixedPosition(protoPos) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_fixed_position = protoPos)) - } + c.admin.forNode(NodeId(destNum)).setFixedPosition(protoPos) } override suspend fun setRingtone(destNum: Int, ringtone: String) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.setRingtone(ringtone) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_ringtone_message = ringtone)) - } + c.admin.forNode(NodeId(destNum)).setRingtone(ringtone) } override suspend fun setCannedMessages(destNum: Int, messages: String) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.setCannedMessages(messages) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(set_canned_message_module_messages = messages)) - } + c.admin.forNode(NodeId(destNum)).setCannedMessages(messages) } // ── Remote admin (getters) ────────────────────────────────────────────── override suspend fun getOwner(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.getOwner() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(get_owner_request = true), wantResponse = true) - } + c.admin.forNode(NodeId(destNum)).getOwner() } override suspend fun getConfig(destNum: Int, configType: Int, packetId: Int) { val c = requireClient() val type = AdminMessage.ConfigType.fromValue(configType) ?: return - if (isLocalNode(destNum)) { - c.admin.getConfig(type) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(get_config_request = type), wantResponse = true) - } + c.admin.forNode(NodeId(destNum)).getConfig(type) } override suspend fun getModuleConfig(destNum: Int, moduleConfigType: Int, packetId: Int) { val c = requireClient() val type = AdminMessage.ModuleConfigType.fromValue(moduleConfigType) ?: return - if (isLocalNode(destNum)) { - c.admin.getModuleConfig(type) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(get_module_config_request = type), wantResponse = true) - } + c.admin.forNode(NodeId(destNum)).getModuleConfig(type) } override suspend fun getChannel(destNum: Int, index: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.getChannel(ChannelIndex(index)) - } else { - sendRemoteAdmin(c, destNum, AdminMessage(get_channel_request = index + 1), wantResponse = true) - } + c.admin.forNode(NodeId(destNum)).getChannel(ChannelIndex(index)) } override suspend fun getRingtone(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.getRingtone() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(get_ringtone_request = true), wantResponse = true) - } + c.admin.forNode(NodeId(destNum)).getRingtone() } override suspend fun getCannedMessages(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.getCannedMessages() - } else { - sendRemoteAdmin( - c, - destNum, - AdminMessage(get_canned_message_module_messages_request = true), - wantResponse = true, - ) - } + c.admin.forNode(NodeId(destNum)).getCannedMessages() } override suspend fun getDeviceConnectionStatus(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.getDeviceConnectionStatus() - } else { - sendRemoteAdmin( - c, - destNum, - AdminMessage(get_device_connection_status_request = true), - wantResponse = true, - ) - } + c.admin.forNode(NodeId(destNum)).getDeviceConnectionStatus() } // ── Lifecycle commands ─────────────────────────────────────────────────── override suspend fun reboot(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.reboot() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(reboot_seconds = 0)) - } + c.admin.forNode(NodeId(destNum)).reboot() } override suspend fun rebootToDfu(nodeNum: Int) { val c = requireClient() - if (isLocalNode(nodeNum)) { - c.admin.enterDfuMode() - } else { - sendRemoteAdmin(c, nodeNum, AdminMessage(enter_dfu_mode_request = true)) - } + c.admin.forNode(NodeId(nodeNum)).enterDfuMode() } override suspend fun requestRebootOta(requestId: Int, destNum: Int, mode: Int, hash: ByteArray?) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.rebootOta() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(reboot_ota_seconds = 0)) - } + c.admin.forNode(NodeId(destNum)).rebootOta() } override suspend fun shutdown(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.shutdown() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(shutdown_seconds = 0)) - } + c.admin.forNode(NodeId(destNum)).shutdown() } override suspend fun factoryReset(destNum: Int, packetId: Int) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.factoryReset() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(factory_reset_config = 1)) - } + c.admin.forNode(NodeId(destNum)).factoryReset() } override suspend fun nodedbReset(destNum: Int, packetId: Int, preserveFavorites: Boolean) { val c = requireClient() - if (isLocalNode(destNum)) { - c.admin.nodeDbReset() - } else { - sendRemoteAdmin(c, destNum, AdminMessage(nodedb_reset = true)) - } + c.admin.forNode(NodeId(destNum)).nodeDbReset() } override suspend fun removeByNodenum(packetId: Int, nodeNum: Int) { @@ -459,17 +369,27 @@ class SdkRadioController( // ── Edit settings (transactional) ─────────────────────────────────────── override suspend fun beginEditSettings(destNum: Int) { - val c = client ?: return - val msg = AdminMessage(begin_edit_settings = true) - val target = if (isLocalNode(destNum)) NodeId(c.ownNode.value?.num ?: 0) else NodeId(destNum) - sendRemoteAdmin(c, target.raw, msg) + val c = requireClient() + val target = resolveTarget(c, destNum) + val payload = AdminMessage.ADAPTER.encode(AdminMessage(begin_edit_settings = true)) + c.send( + portnum = PortNum.ADMIN_APP, + payload = payload, + to = target, + wantAck = false, + ) } override suspend fun commitEditSettings(destNum: Int) { - val c = client ?: return - val msg = AdminMessage(commit_edit_settings = true) - val target = if (isLocalNode(destNum)) NodeId(c.ownNode.value?.num ?: 0) else NodeId(destNum) - sendRemoteAdmin(c, target.raw, msg) + val c = requireClient() + val target = resolveTarget(c, destNum) + val payload = AdminMessage.ADAPTER.encode(AdminMessage(commit_edit_settings = true)) + c.send( + portnum = PortNum.ADMIN_APP, + payload = payload, + to = target, + wantAck = true, + ) } // ── Utility ───────────────────────────────────────────────────────────── @@ -490,34 +410,8 @@ class SdkRadioController( // ── Private helpers ───────────────────────────────────────────────────── - private fun isLocalNode(destNum: Int): Boolean { - if (destNum == 0) return true - val ownNum = client?.ownNode?.value?.num ?: return true - return destNum == ownNum - } - - private suspend fun sendRemoteAdmin( - c: RadioClient, - destNum: Int, - adminMsg: AdminMessage, - wantResponse: Boolean = false, - ) { - val payload = AdminMessage.ADAPTER.encode(adminMsg).toByteString() - try { - c.send( - MeshPacket( - to = destNum, - want_ack = true, - decoded = Data( - portnum = PortNum.ADMIN_APP, - payload = payload, - want_response = wantResponse, - ), - ), - ) - } catch (e: Exception) { - Logger.e(e) { "sendRemoteAdmin to $destNum failed" } - throw e - } + private fun resolveTarget(c: RadioClient, destNum: Int): NodeId { + if (destNum == 0) return NodeId(c.ownNode.value?.num ?: 0) + return NodeId(destNum) } } diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkStateBridge.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkStateBridge.kt index d696871f90..4a6e2594dd 100644 --- a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkStateBridge.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/radio/SdkStateBridge.kt @@ -27,7 +27,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import okio.ByteString.Companion.toByteString import org.koin.core.annotation.Single import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.di.CoroutineDispatchers @@ -42,13 +41,11 @@ import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.ServiceRepository import org.meshtastic.core.repository.UiPrefs -import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.ClientNotification -import org.meshtastic.proto.Data -import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.PortNum import org.meshtastic.proto.User import org.meshtastic.sdk.AdminResult +import org.meshtastic.sdk.ChannelIndex import org.meshtastic.sdk.ConnectionState as SdkConnectionState import org.meshtastic.sdk.MeshEvent import org.meshtastic.sdk.NodeChange @@ -330,18 +327,11 @@ class SdkStateBridge( val channel = action.contactKey[0].digitToInt() val destId = action.contactKey.substring(1) val destNum = runCatching { DataPacket.parseNodeNum(destId) }.getOrDefault(DataPacket.BROADCAST) - client.send( - MeshPacket( - to = destNum, - channel = channel, - want_ack = true, - decoded = Data( - portnum = PortNum.TEXT_MESSAGE_APP, - payload = action.emoji.encodeToByteArray().toByteString(), - emoji = EMOJI_INDICATOR, - reply_id = action.replyId, - ), - ), + client.sendReaction( + emoji = action.emoji, + to = NodeId(destNum), + channel = ChannelIndex(channel), + replyId = action.replyId, ) } @@ -365,27 +355,12 @@ class SdkStateBridge( } is ServiceAction.GetDeviceMetadata -> { - val payload = AdminMessage.ADAPTER.encode( - AdminMessage(get_device_metadata_request = true), - ).toByteString() - client.send( - MeshPacket( - to = action.destNum, - want_ack = true, - decoded = Data( - portnum = PortNum.ADMIN_APP, - payload = payload, - want_response = true, - ), - ), - ) + client.admin.forNode(NodeId(action.destNum)).getDeviceMetadata() } } } companion object { - private const val EMOJI_INDICATOR = 1 - private fun mapConnectionState(sdkState: SdkConnectionState): AppConnectionState = when (sdkState) { is SdkConnectionState.Disconnected -> AppConnectionState.Disconnected is SdkConnectionState.Connecting -> AppConnectionState.Connecting(attempt = sdkState.attempt)