mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
fixes
This commit is contained in:
parent
239f1ac5cc
commit
67ecc493e8
5 changed files with 58 additions and 38 deletions
|
|
@ -17821,7 +17821,7 @@
|
|||
}
|
||||
},
|
||||
"Fix Channel" : {
|
||||
"comment" : "The text on a button that, when pressed, will attempt to fix the primary LoRaWAN channel.",
|
||||
"comment" : "The text on a button that, when pressed, will attempt to fix the primary LoRa channel.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Fix Primary Channel?" : {
|
||||
|
|
|
|||
|
|
@ -516,16 +516,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
if let text = String(bytes: data.payload, encoding: .utf8) {
|
||||
Logger.tak.debug("Text message received, calling broadcast")
|
||||
let server = TAKServerManager.shared
|
||||
if server.meshToCotEnabled && server.isRunning && !server.connectedClients.isEmpty {
|
||||
if server.bridge == nil {
|
||||
Logger.tak.info("Initializing bridge for text message")
|
||||
let bridge = TAKMeshtasticBridge(
|
||||
accessoryManager: AccessoryManager.shared,
|
||||
takServerManager: server
|
||||
)
|
||||
bridge.context = AccessoryManager.shared.context
|
||||
server.bridge = bridge
|
||||
}
|
||||
if server.ensureBridgeReadyForMeshToCot() {
|
||||
await server.bridge?.broadcastMeshTextMessageToTAK(text: text, from: packet.from, channel: packet.channel, to: packet.to)
|
||||
}
|
||||
}
|
||||
|
|
@ -537,16 +528,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
if let position = try? Position(serializedBytes: data.payload) {
|
||||
Logger.tak.debug("Position received, calling broadcast")
|
||||
let server = TAKServerManager.shared
|
||||
if server.meshToCotEnabled && server.isRunning && !server.connectedClients.isEmpty {
|
||||
if server.bridge == nil {
|
||||
Logger.tak.info("Initializing bridge for position")
|
||||
let bridge = TAKMeshtasticBridge(
|
||||
accessoryManager: AccessoryManager.shared,
|
||||
takServerManager: server
|
||||
)
|
||||
bridge.context = AccessoryManager.shared.context
|
||||
server.bridge = bridge
|
||||
}
|
||||
if server.ensureBridgeReadyForMeshToCot() {
|
||||
await server.bridge?.broadcastMeshPositionToTAK(position: position, from: packet.from)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import Foundation
|
|||
import MeshtasticProtobufs
|
||||
import CoreLocation
|
||||
import OSLog
|
||||
import Combine
|
||||
|
||||
/// Converts Meshtastic packets to CoT format for bridging to TAK Server
|
||||
final class MeshToCoTConverter: ObservableObject {
|
||||
|
|
@ -123,12 +124,28 @@ final class MeshToCoTConverter: ObservableObject {
|
|||
// Get emoji based on waypoint icon/expire time
|
||||
let iconEmoji = getEmojiForWaypoint(waypoint)
|
||||
|
||||
// Handle expiry - if expire is 0, never expire. Otherwise use the expire time as Unix timestamp
|
||||
let stale: Date
|
||||
if waypoint.expire == 0 {
|
||||
// Never expire - set to 1 year from now
|
||||
stale = Date().addingTimeInterval(365 * 24 * 60 * 60)
|
||||
} else {
|
||||
// expire is Unix timestamp when waypoint expires
|
||||
let expireDate = Date(timeIntervalSince1970: TimeInterval(waypoint.expire))
|
||||
if expireDate > Date() {
|
||||
stale = expireDate
|
||||
} else {
|
||||
// Already expired, don't broadcast
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return CoTMessage(
|
||||
uid: uid,
|
||||
type: "b-ttf-ff", // Point feature friend
|
||||
time: Date(),
|
||||
start: Date(),
|
||||
stale: Date().addingTimeInterval(TimeInterval(waypoint.expire * 60)),
|
||||
stale: stale,
|
||||
how: "m-g",
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
|
|
@ -156,9 +173,8 @@ final class MeshToCoTConverter: ObservableObject {
|
|||
return CoTMessage.chat(
|
||||
senderUid: senderUid,
|
||||
senderCallsign: senderName,
|
||||
messageId: messageId,
|
||||
message: text,
|
||||
channelName: "Primary"
|
||||
chatroom: "Primary"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ final class TAKMeshtasticBridge {
|
|||
|
||||
/// Send all known mesh node positions to TAK clients
|
||||
/// Useful when a new TAK client connects
|
||||
/// Only sends nodes with positions updated within the last hour
|
||||
/// Only sends nodes with positions updated within the last 2 hours
|
||||
/// Excludes the node we're currently connected to
|
||||
func broadcastAllNodesToTAK() async {
|
||||
guard let takServerManager, takServerManager.isRunning else { return }
|
||||
|
|
@ -844,7 +844,7 @@ final class TAKMeshtasticBridge {
|
|||
case 0x1F681, 11: // 🚁
|
||||
return ("a-u-G", "\(googleUUID)/Google/heliport.png", "-16776961")
|
||||
// ⛵ Boat - Google marina
|
||||
case 0x1F6B5, 12: // ⛵
|
||||
case 0x26F5, 12: // ⛵
|
||||
return ("a-u-G", "\(googleUUID)/Google/marina.png", "-16776961")
|
||||
// 🚢 Ship - Google marina
|
||||
case 0x1F6A2: // 🚢
|
||||
|
|
@ -853,7 +853,7 @@ final class TAKMeshtasticBridge {
|
|||
case 0x1F680: // 🚀
|
||||
return ("a-u-G", "\(googleUUID)/Google/target.png", "-16776961")
|
||||
// 🛸 UFO - Generic purple pushpin
|
||||
case 0x1F6B5, 13: // 🛸
|
||||
case 0x1F6B8, 13: // 🛸
|
||||
return ("a-u-G", "\(genericUUID)/Tacks/purple-pushpin.png", "-65281")
|
||||
// 🚲 Bicycle - Google cycling
|
||||
case 0x1F6B2: // 🚲
|
||||
|
|
@ -1357,13 +1357,13 @@ final class TAKMeshtasticBridge {
|
|||
case 0x1F6A6: // 🚩
|
||||
return ("a-u-G", "\(googleUUID)/Google/flag.png", "-16776961")
|
||||
// ⛔ No Entry - Google caution
|
||||
case 0x1F6D1: // ⛔
|
||||
case 0x26D4: // ⛔
|
||||
return ("a-u-G", "\(googleUUID)/Google/caution.png", "-16776961")
|
||||
// 🛑 Stop - Google caution
|
||||
case 0x1F6D1: // 🛑
|
||||
return ("a-u-G", "\(googleUUID)/Google/caution.png", "-16776961")
|
||||
// 🏕️ Base Camp - Google campground
|
||||
case 0x1F6D1: // 🏕️
|
||||
case 0x1F3D5: // 🏕️
|
||||
return ("a-u-G", "\(googleUUID)/Google/campground.png", "-16776961")
|
||||
// 🏢 Office Building - Google homegardenbusiness
|
||||
case 0x1F3E2: // 🏢
|
||||
|
|
|
|||
|
|
@ -159,10 +159,10 @@ final class TAKServerManager: ObservableObject {
|
|||
return
|
||||
}
|
||||
|
||||
let channelName = primaryChannel.name ?? ""
|
||||
let channelPsk = primaryChannel.psk ?? Data()
|
||||
let pskBase64 = channelPsk.base64EncodedString()
|
||||
|
||||
let channelName = primaryChannel.name ?? ""
|
||||
let channelPsk = primaryChannel.psk ?? Data()
|
||||
let pskBase64 = channelPsk.base64EncodedString()
|
||||
|
||||
if channelName.isEmpty {
|
||||
issues.append(PrimaryChannelIssue(
|
||||
title: "Unnamed Primary Channel",
|
||||
|
|
@ -172,22 +172,25 @@ final class TAKServerManager: ObservableObject {
|
|||
isValid = false
|
||||
}
|
||||
|
||||
let pskLength = pskBase64.count
|
||||
if pskLength == 0 {
|
||||
// Use byte length for encryption strength checks (not Base64 string length)
|
||||
let pskBytes = channelPsk.count
|
||||
if pskBytes == 0 {
|
||||
issues.append(PrimaryChannelIssue(
|
||||
title: "Public Channel Not Supported",
|
||||
description: "TAK Server requires a private channel with encryption. Public channels expose your location and messages. Tap the button below to set up a private TAK channel.",
|
||||
canAutoFix: true
|
||||
))
|
||||
isValid = false
|
||||
} else if pskBase64 == "AQ==" {
|
||||
} else if channelPsk == Data([0x01]) {
|
||||
// Default key is single byte 0x01
|
||||
issues.append(PrimaryChannelIssue(
|
||||
title: "Default Encryption Key",
|
||||
description: "TAK Server requires a unique private channel key. The default key is not secure. Tap the button below to set up a proper private TAK channel.",
|
||||
canAutoFix: true
|
||||
))
|
||||
isValid = false
|
||||
} else if pskLength == 4 {
|
||||
} else if pskBytes < 16 {
|
||||
// Less than 128-bit (16 bytes)
|
||||
issues.append(PrimaryChannelIssue(
|
||||
title: "Weak Encryption Key",
|
||||
description: "TAK Server requires at least 128-bit encryption for your privacy. Tap the button below to set up a secure private TAK channel.",
|
||||
|
|
@ -527,6 +530,25 @@ final class TAKServerManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure bridge is initialized and ready for mesh-to-CoT broadcasting
|
||||
/// Returns true if broadcasting is possible (meshToCotEnabled, server running, clients connected)
|
||||
/// Call this before any mesh-to-CoT broadcast operations
|
||||
func ensureBridgeReadyForMeshToCot() -> Bool {
|
||||
guard meshToCotEnabled, isRunning, !connectedClients.isEmpty else { return false }
|
||||
|
||||
if bridge == nil {
|
||||
Logger.tak.info("Initializing bridge for mesh-to-CoT broadcast")
|
||||
let accessoryManager = AccessoryManager.shared
|
||||
let newBridge = TAKMeshtasticBridge(
|
||||
accessoryManager: accessoryManager,
|
||||
takServerManager: self
|
||||
)
|
||||
newBridge.context = accessoryManager.context
|
||||
bridge = newBridge
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/// Send a CoT message to a specific client
|
||||
func send(_ cotMessage: CoTMessage, to clientId: UUID) async throws {
|
||||
|
|
@ -548,7 +570,7 @@ final class TAKServerManager: ObservableObject {
|
|||
// MARK: - Auto-fix Primary Channel
|
||||
|
||||
/// Automatically fix the primary channel to TAK-compatible settings
|
||||
/// Sets: Name="TAK", 256-bit AES key, LoRa channel=0
|
||||
/// Sets: Name="TAK", 256-bit AES key, preserves existing LoRa channel
|
||||
/// Returns true if successful
|
||||
func autoFixPrimaryChannel() async -> Bool {
|
||||
let accessoryManager = AccessoryManager.shared
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue