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.
This commit is contained in:
niccellular 2026-02-19 13:42:29 -05:00
parent 24fe868a95
commit 1dcecc4519
3 changed files with 43 additions and 3 deletions

View file

@ -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)")

View file

@ -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 {

View file

@ -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<ChannelEntity>
@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>) {