From 1dcecc451915abbea87d049ba691bf65b281de60 Mon Sep 17 00:00:00 2001 From: niccellular <79813408+niccellular@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:42:29 -0500 Subject: [PATCH] TAK Server channel index picker Create a settings picker for the TAK Server's channel index. This allows users to specify TAK traffic to use the non-primary channel to help reduce channel congestion. --- .../Helpers/TAK/TAKMeshtasticBridge.swift | 6 ++- Meshtastic/Helpers/TAK/TAKServerManager.swift | 2 + .../Views/Settings/TAKServerConfig.swift | 38 ++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/Meshtastic/Helpers/TAK/TAKMeshtasticBridge.swift b/Meshtastic/Helpers/TAK/TAKMeshtasticBridge.swift index 9ed42c90..23a08afe 100644 --- a/Meshtastic/Helpers/TAK/TAKMeshtasticBridge.swift +++ b/Meshtastic/Helpers/TAK/TAKMeshtasticBridge.swift @@ -147,6 +147,8 @@ final class TAKMeshtasticBridge { return } + let channel = UInt32(TAKServerManager.shared.channel) + // Determine send method based on CoT type let sendMethod = GenericCoTHandler.shared.classifySendMethod(for: cotMessage) @@ -159,7 +161,7 @@ final class TAKMeshtasticBridge { } do { - try await accessoryManager.sendTAKPacket(takPacket) + try await accessoryManager.sendTAKPacket(takPacket, channel: channel) Logger.tak.info("Sent TAKPacket to mesh: \(cotMessage.type)") } catch { Logger.tak.error("Failed to send TAKPacket to mesh: \(error.localizedDescription)") @@ -169,7 +171,7 @@ final class TAKMeshtasticBridge { // Use EXI compression on ATAK_FORWARDER port (257) GenericCoTHandler.shared.accessoryManager = accessoryManager do { - try await GenericCoTHandler.shared.sendGenericCoT(cotMessage) + try await GenericCoTHandler.shared.sendGenericCoT(cotMessage, channel: channel) Logger.tak.info("Sent generic CoT to mesh via ATAK_FORWARDER: \(cotMessage.type)") } catch { Logger.tak.error("Failed to send generic CoT to mesh: \(error.localizedDescription)") diff --git a/Meshtastic/Helpers/TAK/TAKServerManager.swift b/Meshtastic/Helpers/TAK/TAKServerManager.swift index e3e3caa7..182e47bb 100644 --- a/Meshtastic/Helpers/TAK/TAKServerManager.swift +++ b/Meshtastic/Helpers/TAK/TAKServerManager.swift @@ -26,6 +26,8 @@ final class TAKServerManager: ObservableObject { // MARK: - Configuration (persisted via AppStorage) + @AppStorage("takServerChannel") var channel: Int = 0 + @AppStorage("takServerEnabled") var enabled = false { didSet { Task { diff --git a/Meshtastic/Views/Settings/TAKServerConfig.swift b/Meshtastic/Views/Settings/TAKServerConfig.swift index 749b54fc..09d9d60d 100644 --- a/Meshtastic/Views/Settings/TAKServerConfig.swift +++ b/Meshtastic/Views/Settings/TAKServerConfig.swift @@ -8,6 +8,7 @@ import SwiftUI import UniformTypeIdentifiers import OSLog +import CoreData enum CertificateImportType { case p12 @@ -15,6 +16,15 @@ enum CertificateImportType { } struct TAKServerConfig: View { + @Environment(\.managedObjectContext) var context + @EnvironmentObject var accessoryManager: AccessoryManager + + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \ChannelEntity.index, ascending: true)], + predicate: NSPredicate(format: "role > 0"), + animation: .default + ) private var channels: FetchedResults + @StateObject private var takServer = TAKServerManager.shared @State private var showingFileImporter = false @State private var importType: CertificateImportType = .p12 @@ -140,6 +150,17 @@ struct TAKServerConfig: View { .foregroundColor(.secondary) } + if !channels.isEmpty { + Picker(selection: $takServer.channel) { + ForEach(channels, id: \.index) { channel in + channelLabel(channel) + .tag(Int(channel.index)) + } + } label: { + Label("TAK Channel Index", systemImage: "bubble.left.and.bubble.right") + } + } + if takServer.isRunning { Button { Task { @@ -152,7 +173,7 @@ struct TAKServerConfig: View { } header: { Text("Configuration") } footer: { - Text("Secure mTLS connection on port 8089. Both server and client certificates are required.") + Text("Secure mTLS connection on port 8089. Both server and client certificates are required. TAK Channel Index selects the channel index where TAK messages will be sent.") } } @@ -280,6 +301,21 @@ struct TAKServerConfig: View { } + // MARK: - Channel Label + + @ViewBuilder + private func channelLabel(_ channel: ChannelEntity) -> some View { + if channel.name?.isEmpty ?? false { + if channel.role == 1 { + Text(String("PrimaryChannel").camelCaseToWords()) + } else { + Text(String("Channel \(channel.index)").camelCaseToWords()) + } + } else { + Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()) + } + } + // MARK: - Import Handlers private func handleP12Import(_ result: Result<[URL], Error>) {