Merge pull request #70 from meshtastic/feature/refactor_blemanager

Feature/refactor blemanager
This commit is contained in:
Garth Vander Houwen 2022-05-28 00:00:10 -07:00 committed by GitHub
commit d9c5fbd417
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 979 additions and 2858 deletions

View file

@ -37,10 +37,10 @@
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; };
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */; };
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */; };
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5226EB1DF10058C060 /* BLEManager.swift */; };
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */; };
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5C26ED09490058C060 /* portnums.pb.swift */; };
DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5E26ED09B50058C060 /* radioconfig.pb.swift */; };
DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6026ED0A230058C060 /* mqtt.pb.swift */; };
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6126ED0A230058C060 /* admin.pb.swift */; };
DDAF8C6526ED0A490058C060 /* channel.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6426ED0A490058C060 /* channel.pb.swift */; };
@ -109,10 +109,10 @@
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreDataSample.xcdatamodel; sourceTree = "<group>"; };
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshPackets.swift; sourceTree = "<group>"; };
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = "<group>"; };
DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mesh.pb.swift; sourceTree = "<group>"; };
DDAF8C5C26ED09490058C060 /* portnums.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = portnums.pb.swift; sourceTree = "<group>"; };
DDAF8C5E26ED09B50058C060 /* radioconfig.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = radioconfig.pb.swift; sourceTree = "<group>"; };
DDAF8C6026ED0A230058C060 /* mqtt.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mqtt.pb.swift; sourceTree = "<group>"; };
DDAF8C6126ED0A230058C060 /* admin.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = admin.pb.swift; sourceTree = "<group>"; };
DDAF8C6426ED0A490058C060 /* channel.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = channel.pb.swift; sourceTree = "<group>"; };
@ -237,7 +237,6 @@
DD4C158B2824A91E0032668E /* module_config.pb.swift */,
DDAF8C6026ED0A230058C060 /* mqtt.pb.swift */,
DDAF8C5C26ED09490058C060 /* portnums.pb.swift */,
DDAF8C5E26ED09B50058C060 /* radioconfig.pb.swift */,
DDAF8C6626ED0C8C0058C060 /* remote_hardware.pb.swift */,
DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */,
DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */,
@ -371,6 +370,7 @@
DDAF8C6D26ED19040058C060 /* Extensions.swift */,
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */,
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */,
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -547,7 +547,6 @@
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */,
DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */,
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */,
DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */,
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */,
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */,
DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */,
@ -579,6 +578,7 @@
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */,
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */,
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */,
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */,
C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */,
DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */,
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */,
@ -751,7 +751,7 @@
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
ENABLE_PREVIEWS = YES;
@ -783,7 +783,7 @@
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
ENABLE_PREVIEWS = YES;

View file

@ -124,14 +124,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
self.isConnected = false
self.lastConnectionError = "🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name)."
print("🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name).")
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(String(name)). This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer.") }
if meshLoggingEnabled { MeshLogger.log(self.lastConnectionError + " This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer.") }
self.timeoutTimerCount = 0
self.timeoutTimer?.invalidate()
} else {
print("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)")
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)") }
}
}
@ -140,13 +140,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
func connectTo(peripheral: CBPeripheral) {
if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") }
print("✅ BLE Connecting: \(peripheral.name ?? "Unknown")")
stopScanning()
if self.connectedPeripheral != nil {
if meshLoggingEnabled { MeshLogger.log(" BLE Disconnecting from: \(self.connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")") }
print(" BLE Disconnecting from: \(self.connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")")
self.disconnectPeripheral()
}
@ -171,12 +170,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
var peripheralName: String = peripheral.name ?? "Unknown"
let last4Code: String = (peripheral.name != nil ? String(peripheral.name!.suffix(4)) : "Unknown")
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
}
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, bitrate: nil, channelUtilization: nil, airTime: nil, lastUpdate: Date(), subscribed: false, peripheral: peripheral)
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, lastFourCode: last4Code, firmwareVersion: "Unknown", rssi: RSSI.intValue, bitrate: nil, channelUtilization: nil, airTime: nil, lastUpdate: Date(), subscribed: false, peripheral: peripheral)
let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id })
if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected {
@ -195,8 +195,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
let today = Date()
let fiveMinutesAgo = Calendar.current.date(byAdding: .minute, value: -5, to: today)!
peripherals.removeAll(where: { $0.lastUpdate <= fiveMinutesAgo})
let oneMinuteAgo = Calendar.current.date(byAdding: .minute, value: -1, to: today)!
peripherals.removeAll(where: { $0.lastUpdate <= oneMinuteAgo})
}
// Called when a peripheral is connected
@ -216,7 +216,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// Discover Services
peripheral.discoverServices([meshtasticServiceCBUUID])
if meshLoggingEnabled { MeshLogger.log("✅ BLE Connected: \(peripheral.name ?? "Unknown")") }
print("✅ BLE Connected: \(peripheral.name ?? "Unknown")")
}
@ -224,7 +223,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Failed to Connect: \(peripheral.name ?? "Unknown")") }
print("🚫 BLE Failed to Connect: \(peripheral.name ?? "Unknown")")
disconnectPeripheral()
}
@ -247,27 +245,27 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
lastConnectionError = "🚨 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds."
if peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
if meshLoggingEnabled { MeshLogger.log(" BLE Reconnecting: \(peripheral.name ?? "Unknown")") }
print(" BLE Reconnecting: \(peripheral.name ?? "Unknown")")
self.connectTo(peripheral: peripheral)
}
} else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us.
// Seems to be what is received when a tbeam sleeps, immediately recconnecting does not work.
lastConnectionError = e.localizedDescription
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") }
} else if errorCode == 14 { // Peer removed pairing information
// Forgetting and reconnecting seems to be necessary so we need to show the user an error telling them to do that
lastConnectionError = "🚨 \(e.localizedDescription) This error usually cannot be fixed without forgetting the device unders Settings > Bluetooth and re-connecting to the radio."
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(lastConnectionError)") }
} else {
lastConnectionError = e.localizedDescription
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") }
}
} else {
@ -275,7 +273,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// Disconnected without error which indicates user intent to disconnect
// Happens when swiping to disconnect
if meshLoggingEnabled { MeshLogger.log(" BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect") }
print(" BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect")
}
}
@ -292,7 +289,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
for service in services {
if service.uuid == meshtasticServiceCBUUID {
print("✅ Meshtastic service discovered OK")
if meshLoggingEnabled { MeshLogger.log("✅ BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")") }
//peripheral.discoverCharacteristics(nil, for: service)
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID], for: service)
@ -304,7 +301,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let e = error {
print("🚫 Discover Characteristics error \(e)")
if meshLoggingEnabled { MeshLogger.log("🚫 BLE didDiscoverCharacteristicsFor error by \(peripheral.name ?? "Unknown") \(e)") }
}
@ -314,7 +310,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
switch characteristic.uuid {
case TORADIO_UUID:
print("✅ TORADIO characteristic OK")
if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover TORADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
TORADIO_characteristic = characteristic
var toRadio: ToRadio = ToRadio()
@ -323,13 +319,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
peripheral.writeValue(binaryData, for: characteristic, type: .withResponse)
case FROMRADIO_UUID:
print("✅ FROMRADIO characteristic OK")
if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover FROMRADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
FROMRADIO_characteristic = characteristic
peripheral.readValue(for: FROMRADIO_characteristic)
case FROMNUM_UUID:
print("✅ FROMNUM (Notify) characteristic OK")
if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
FROMNUM_characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
@ -343,18 +339,15 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print(" didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)")
if meshLoggingEnabled { MeshLogger.log(" didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)") }
if let errorText = error?.localizedDescription {
print("🚫 didUpdateNotificationStateFor error: \(errorText)")
if meshLoggingEnabled { MeshLogger.log("🚫 didUpdateNotificationStateFor error: \(errorText)") }
}
}
// MARK: Data Read / Update Characteristic Event
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("🚫 didUpdateValueFor Characteristic error \(e)")
@ -364,7 +357,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if errorCode == 5 { // CBATTErrorDomain Code=5 "Authentication is insufficient."
// BLE Pin connection error
// We will try and re-connect to this device
lastConnectionError = "🚫 BLE \(e.localizedDescription) Please try connecting again and check the PIN carefully."
if meshLoggingEnabled { MeshLogger.log("🚫 BLE \(e.localizedDescription) Please try connecting again and check the PIN carefully.") }
self.centralManager?.cancelPeripheralConnection(peripheral)
@ -373,7 +365,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if errorCode == 15 { // CBATTErrorDomain Code=15 "Encryption is insufficient."
// BLE Pin connection error
// We will try and re-connect to this device
lastConnectionError = "🚫 BLE \(e.localizedDescription) This may be a Meshtastic Firmware bug affecting BLE 4.0 devices."
if meshLoggingEnabled { MeshLogger.log("🚫 BLE \(e.localizedDescription) Please try connecting again. You may need to forget the device under Settings > General > Bluetooth.") }
self.centralManager?.cancelPeripheralConnection(peripheral)
@ -390,9 +381,50 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
var decodedInfo = FromRadio()
decodedInfo = try! FromRadio(serializedData: characteristic.value!)
switch decodedInfo.packet.decoded.portnum {
case .unknownApp:
print("MyInfo or NodeInfo")
case .textMessageApp:
textMessageAppPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), meshLogging: meshLoggingEnabled, context: context!)
case .remoteHardwareApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Remote Hardware App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .positionApp:
positionPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .nodeinfoApp:
nodeInfoPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .routingApp:
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .adminApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Admin App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .replyApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Reply App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .ipTunnelApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for IP Tunnel App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .serialApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Serial App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .storeForwardApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Store Forward App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .rangeTestApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Range Test App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .telemetryApp:
telemetryPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .textMessageCompressedApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Text Message Compressed App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .zpsApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for ZPS App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .privateApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Private App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .atakForwarder:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for ATAK Forwarder App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .UNRECOGNIZED(_):
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Other App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .max:
print("MAX PORT NUM OF 511")
}
// MARK: Incoming MyInfo Packet
if decodedInfo.myInfo.myNodeNum != 0 {
@ -440,18 +472,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
print("💾 Saved the All - Broadcast User")
}
//var settingsCalled = self.getSettings()
if false {
print("💾 Called Get Settings")
} else {
print("💥 Get Settings Call Failed")
}
} catch {
print("💥 Error Saving the All - Broadcast User")
@ -471,6 +491,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec)
fetchedMyInfo[0].minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion)
fetchedMyInfo[0].maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels)
self.connectedPeripheral.num = fetchedMyInfo[0].myNodeNum
self.connectedPeripheral.firmwareVersion = fetchedMyInfo[0].firmwareVersion ?? "Unknown"
self.connectedPeripheral.name = fetchedMyInfo[0].bleName ?? "Unknown"
@ -480,7 +501,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
do {
try context!.save()
print("💾 Saved a myInfo for \(decodedInfo.myInfo.myNodeNum)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a myInfo for \(peripheral.name ?? String(decodedInfo.myInfo.myNodeNum))") }
} catch {
@ -681,333 +701,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if decodedInfo.nodeInfo.hasUser {
print("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)")
if meshLoggingEnabled { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") }
} else {
print("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)")
if meshLoggingEnabled { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)") }
}
}
// Handle other packet types
if decodedInfo.packet.id != 0 {
do {
// MARK: Incoming Packet from the TEXTMESSAGE_APP
if decodedInfo.packet.decoded.portnum == PortNum.textMessageApp {
if let messageText = String(bytes: decodedInfo.packet.decoded.payload, encoding: .utf8) {
print("💬 BLE FROMRADIO received for text message app \(messageText)")
if meshLoggingEnabled { MeshLogger.log("💬 BLE FROMRADIO received for text message app \(messageText)") }
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
messageUsers.predicate = NSPredicate(format: "num IN %@", [decodedInfo.packet.to, decodedInfo.packet.from])
do {
let fetchedUsers = try context?.fetch(messageUsers) as! [UserEntity]
let newMessage = MessageEntity(context: context!)
newMessage.messageId = Int64(decodedInfo.packet.id)
newMessage.messageTimestamp = Int32(bitPattern: decodedInfo.packet.rxTime)
newMessage.receivedACK = false
newMessage.direction = "IN"
newMessage.isEmoji = decodedInfo.packet.decoded.emoji == 1
if decodedInfo.packet.decoded.replyID > 0 {
newMessage.replyID = Int64(decodedInfo.packet.decoded.replyID)
}
if decodedInfo.packet.to == broadcastNodeNum && fetchedUsers.count == 1 {
// Save the broadcast user if it does not exist
let bcu: UserEntity = UserEntity(context: context!)
bcu.shortName = "ALL"
bcu.longName = "All - Broadcast"
bcu.hwModel = "UNSET"
bcu.num = Int64(broadcastNodeNum)
bcu.userId = "BROADCASTNODE"
newMessage.toUser = bcu
} else {
newMessage.toUser = fetchedUsers.first(where: { $0.num == decodedInfo.packet.to })
}
newMessage.fromUser = fetchedUsers.first(where: { $0.num == decodedInfo.packet.from })
newMessage.messagePayload = messageText
do {
try context!.save()
print("💾 Saved a new message for \(decodedInfo.packet.id)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new message for \(newMessage.messageId)") }
if newMessage.toUser != nil && newMessage.toUser!.num == self.broadcastNodeNum || self.connectedPeripheral != nil && self.connectedPeripheral.num == newMessage.toUser!.num {
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? "Unknown")",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
if meshLoggingEnabled { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
}
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Failed to save new MessageEntity \(nsError)")
}
} catch {
print("💥 Fetch Message To and From Users Error")
}
}
// MARK: Incoming NODEINFO_APP Packet
} else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
let fetchNodeInfoAppRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from))
do {
let fetchedNode = try context?.fetch(fetchNodeInfoAppRequest) as! [NodeInfoEntity]
if fetchedNode.count == 1 {
fetchedNode[0].id = Int64(decodedInfo.packet.from)
fetchedNode[0].num = Int64(decodedInfo.packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime)))
fetchedNode[0].snr = decodedInfo.packet.rxSnr
} else {
return
}
do {
try context!.save()
if meshLoggingEnabled { MeshLogger.log("💾 Updated NodeInfo SNR \(decodedInfo.packet.rxSnr) and Time from Node Info App Packet For: \(fetchedNode[0].num)")}
print("💾 Updated NodeInfo SNR \(decodedInfo.packet.rxSnr) and Time from Packet For: \(fetchedNode[0].num)")
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
}
} catch {
print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP")
}
// MARK: Incoming Packet from the POSITION_APP
} else if decodedInfo.packet.decoded.portnum == PortNum.positionApp {
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from))
do {
let fetchedNode = try context?.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
if fetchedNode.count == 1 {
fetchedNode[0].id = Int64(decodedInfo.packet.from)
fetchedNode[0].num = Int64(decodedInfo.packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime)))
fetchedNode[0].snr = decodedInfo.packet.rxSnr
if let positionMessage = try? Position(serializedData: decodedInfo.packet.decoded.payload) {
let position = PositionEntity(context: context!)
position.latitudeI = positionMessage.latitudeI
position.longitudeI = positionMessage.longitudeI
position.altitude = positionMessage.altitude
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
mutablePositions.add(position)
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
}
} else {
return
}
do {
try context!.save()
if meshLoggingEnabled {
MeshLogger.log("💾 Updated NodeInfo Position Coordinates, SNR \(decodedInfo.packet.rxSnr) and Time from Position App Packet For: \(fetchedNode[0].num)")
}
print("💾 Updated NodeInfo Position Coordinates, SNR \(decodedInfo.packet.rxSnr) and Time from Position App Packet For:: \(fetchedNode[0].num)")
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)")
}
} catch {
print("💥 Error Fetching NodeInfoEntity for POSITION_APP")
}
// MARK: Incoming ROUTING_APP Packet
} else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
if let routingMessage = try? Routing(serializedData: decodedInfo.packet.decoded.payload) {
print(decodedInfo.packet.decoded.requestID)
print(routingMessage)
let error = routingMessage.errorReason
var errorExplanation = "Unknown Routing Error"
switch error {
case Routing.Error.none:
errorExplanation = "This message is not a failure"
case Routing.Error.noRoute:
errorExplanation = "Our node doesn't have a route to the requested destination anymore."
case Routing.Error.gotNak:
errorExplanation = "We received a nak while trying to forward on your behalf"
case Routing.Error.timeout:
errorExplanation = "Timeout"
case Routing.Error.noInterface:
errorExplanation = "No suitable interface could be found for delivering this packet"
case Routing.Error.maxRetransmit:
errorExplanation = "We reached the max retransmission count (typically for naive flood routing)"
case Routing.Error.noChannel:
errorExplanation = "No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)"
case Routing.Error.tooLarge:
errorExplanation = "The packet was too big for sending (exceeds interface MTU after encoding)"
case Routing.Error.noResponse:
errorExplanation = "The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions)"
case Routing.Error.badRequest:
errorExplanation = "The application layer service on the remote node received your request, but considered your request somehow invalid"
case Routing.Error.notAuthorized:
errorExplanation = "The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel)"
fallthrough
default:
print(error)
}
if meshLoggingEnabled { MeshLogger.log("🕸️ ROUTING PACKET received for RequestID: \(decodedInfo.packet.decoded.requestID) Error: \(errorExplanation)") }
print("🕸️ ROUTING PACKET received for RequestID: \(decodedInfo.packet.decoded.requestID) Error: \(errorExplanation)")
if routingMessage.errorReason == Routing.Error.none {
print("Priority ACK no Error")
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(decodedInfo.packet.decoded.requestID))
do {
let fetchedMessage = try context?.fetch(fetchMessageRequest)[0] as? MessageEntity
if fetchedMessage != nil {
fetchedMessage!.receivedACK = true
fetchedMessage!.ackSNR = decodedInfo.packet.rxSnr
fetchedMessage!.ackTimestamp = Int32(decodedInfo.packet.rxTime)
fetchedMessage!.objectWillChange.send()
}
try context!.save()
if meshLoggingEnabled {
MeshLogger.log("💾 ACK Received and saved for MessageID \(decodedInfo.packet.decoded.requestID)")
}
print("💾 ACK Received and saved for MessageID \(decodedInfo.packet.decoded.requestID)")
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Error Saving ACK for message MessageID \(decodedInfo.packet.id) Error: \(nsError)")
}
}
}
// MARK: Incoming TELEMETRY_APP Packet
} else if decodedInfo.packet.decoded.portnum == PortNum.telemetryApp {
if let telemetryMessage = try? Telemetry(serializedData: decodedInfo.packet.decoded.payload) {
let telemetry = TelemetryEntity(context: context!)
print(decodedInfo.packet.decoded.requestID)
print(telemetryMessage)
}
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Telemetry App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print(" MESH PACKET received for Telemetry App UNHANDLED \(try decodedInfo.packet.jsonString())")
} else if decodedInfo.packet.decoded.portnum == PortNum.storeForwardApp {
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Store Forward App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print(" MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())")
} else if decodedInfo.packet.decoded.portnum == PortNum.adminApp {
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print(" MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())")
} else {
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print(" MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())")
}
} catch {
if meshLoggingEnabled { MeshLogger.log("⚰️ Fatal Error: Failed to decode json") }
print("⚰️ Fatal Error: Failed to decode json")
}
}
if decodedInfo.configCompleteID != 0 {
if meshLoggingEnabled { MeshLogger.log("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)") }
print("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)")
self.connectedPeripheral.subscribed = true
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
}
case FROMNUM_UUID :
print("🚨 FROMNUM Characteristic UUID: \(characteristic.uuid)")
if characteristic.value == nil || characteristic.value!.isEmpty {
// Config conplete returns so we don't read the characteristic again
return
}
var decodedInfo = FromRadio()
case FROMNUM_UUID :
print("🗞️ FROMNUM Notification, value will be read below")
//decodedInfo = try! FromRadio(serializedData: characteristic.value!)
default:
print("🚨 Unhandled Characteristic UUID: \(characteristic.uuid)")
}
peripheral.readValue(for: FROMRADIO_characteristic)
}
// Either Read the config complete value or from num notify value
peripheral.readValue(for: FROMRADIO_characteristic)
}
// Send Message
public func sendMessage(message: String, toUserNum: Int64, isEmoji: Bool, replyID: Int64) -> Bool {
@ -1025,7 +747,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil {
connectTo(peripheral: preferredPeripheral!.peripheral)
}
print("🚫 Message Send Failed, not properly connected to \(preferredPeripheral?.name ?? "Unknown")")
if meshLoggingEnabled { MeshLogger.log("🚫 Message Send Failed, not properly connected to \(preferredPeripheral?.name ?? "Unknown")") }
success = false
@ -1106,14 +827,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
let binaryData: Data = try! toRadio.serializedData()
if meshLoggingEnabled { MeshLogger.log("📲 New messageId \(newMessage.messageId) sent to \(newMessage.toUser?.longName! ?? "Unknown")") }
print("📲 New messageId \(newMessage.messageId) sent to \(newMessage.toUser?.longName! ?? "Unknown")")
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
do {
try context!.save()
print("💾 Saved a new sent message to \(toUserNum)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new sent message from \(connectedPeripheral.num) to \(toUserNum)") }
success = true
@ -1122,8 +842,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
context!.rollback()
let nsError = error as NSError
print("💥 Unresolved Core Data error in Send Message Function it is likely that your database is corrupted deleting and re-installing the app should clear the corrupted data. Error: \(nsError)")
if meshLoggingEnabled { MeshLogger.log("💥 Unresolved Core Data error \(nsError)") }
if meshLoggingEnabled { MeshLogger.log("💥 Unresolved Core Data error in Send Message Function it is likely that your database is corrupted deleting and re-installing the app should clear the corrupted data. Error: \(nsError)") }
}
}
}
@ -1165,7 +884,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
var meshPacket = MeshPacket()
meshPacket.to = UInt32(destNum)
meshPacket.from = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.wantAck = wantResponse
var dataMessage = DataMessage()
@ -1179,13 +898,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet from the phone to the device for node: \(fromNodeNum)") }
print("📍 Sent a Position Packet from the phone to the device for node: \(fromNodeNum)")
if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet from the Apple device GPS to node: \(fromNodeNum)") }
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
}
}
@ -1218,7 +937,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
public func getSettings() -> Bool {
var adminPacket = AdminMessage()
//adminPacket.getRadioRequest = true
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
@ -1249,4 +967,41 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
// Send Position
public func sendShutdown(destNum: Int64, wantResponse: Bool) -> Bool {
var adminPacket = AdminMessage()
adminPacket.rebootSeconds = 30
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = false
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
return true
}
return false
}
}

View file

@ -17,6 +17,7 @@ class MeshLogger {
formatter.dateFormat = "M/d/yy h:mm:ss.SSSS"
let timestamp = formatter.string(from: Date())
guard let data = (message + " - " + timestamp + "\n").data(using: String.Encoding.utf8) else { return }
print(message)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {

View file

@ -0,0 +1,278 @@
//
// MeshPackets.swift
// Meshtastic Apple
//
// Created by Garth Vander Houwen on 5/27/22.
//
import Foundation
import CoreData
func nodeInfoPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
let fetchNodeInfoAppRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
do {
let fetchedNode = try context.fetch(fetchNodeInfoAppRequest) as! [NodeInfoEntity]
if fetchedNode.count == 1 {
fetchedNode[0].id = Int64(packet.from)
fetchedNode[0].num = Int64(packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
fetchedNode[0].snr = packet.rxSnr
} else {
return
}
do {
try context.save()
if meshLogging { MeshLogger.log("💾 Updated NodeInfo SNR \(packet.rxSnr) and Time from Node Info App Packet For: \(fetchedNode[0].num)")}
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
}
} catch {
print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP")
}
}
func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
do {
let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
if fetchedNode.count == 1 {
fetchedNode[0].id = Int64(packet.from)
fetchedNode[0].num = Int64(packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
fetchedNode[0].snr = packet.rxSnr
if let positionMessage = try? Position(serializedData: packet.decoded.payload) {
let position = PositionEntity(context: context)
position.latitudeI = positionMessage.latitudeI
position.longitudeI = positionMessage.longitudeI
position.altitude = positionMessage.altitude
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
mutablePositions.add(position)
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
}
} else {
return
}
do {
try context.save()
if meshLogging {
MeshLogger.log("💾 Updated Node Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)")
}
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)")
}
} catch {
print("💥 Error Fetching NodeInfoEntity for POSITION_APP")
}
}
func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
if let routingMessage = try? Routing(serializedData: packet.decoded.payload) {
print(packet.decoded.requestID)
print(routingMessage)
let error = routingMessage.errorReason
var errorExplanation = "Unknown Routing Error"
switch error {
case Routing.Error.none:
errorExplanation = "This message is not a failure"
case Routing.Error.noRoute:
errorExplanation = "Our node doesn't have a route to the requested destination anymore."
case Routing.Error.gotNak:
errorExplanation = "We received a nak while trying to forward on your behalf"
case Routing.Error.timeout:
errorExplanation = "Timeout"
case Routing.Error.noInterface:
errorExplanation = "No suitable interface could be found for delivering this packet"
case Routing.Error.maxRetransmit:
errorExplanation = "We reached the max retransmission count (typically for naive flood routing)"
case Routing.Error.noChannel:
errorExplanation = "No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)"
case Routing.Error.tooLarge:
errorExplanation = "The packet was too big for sending (exceeds interface MTU after encoding)"
case Routing.Error.noResponse:
errorExplanation = "The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions)"
case Routing.Error.badRequest:
errorExplanation = "The application layer service on the remote node received your request, but considered your request somehow invalid"
case Routing.Error.notAuthorized:
errorExplanation = "The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel)"
fallthrough
default:
print(error)
}
if meshLogging { MeshLogger.log("🕸️ ROUTING PACKET received for RequestID: \(packet.decoded.requestID) Error: \(errorExplanation)") }
if routingMessage.errorReason == Routing.Error.none {
print("Priority ACK no Error")
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
do {
let fetchedMessage = try context.fetch(fetchMessageRequest)[0] as? MessageEntity
if fetchedMessage != nil {
fetchedMessage!.receivedACK = true
fetchedMessage!.ackSNR = packet.rxSnr
fetchedMessage!.ackTimestamp = Int32(packet.rxTime)
fetchedMessage!.objectWillChange.send()
}
try context.save()
if meshLogging {
MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)")
}
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)")
}
}
}
}
func telemetryPacket(packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) {
let telemetry = TelemetryEntity(context: context)
if meshLogging { MeshLogger.log(" MESH PACKET received for Telemetry App UNHANDLED \(telemetryMessage)") }
} else {
}
}
func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging: Bool, context: NSManagedObjectContext) {
let broadcastNodeNum: UInt32 = 4294967295
if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) {
if meshLogging { MeshLogger.log("💬 Message received for text message app \(messageText)") }
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
do {
let fetchedUsers = try context.fetch(messageUsers) as! [UserEntity]
let newMessage = MessageEntity(context: context)
newMessage.messageId = Int64(packet.id)
newMessage.messageTimestamp = Int32(bitPattern: packet.rxTime)
newMessage.receivedACK = false
newMessage.direction = "IN"
newMessage.isEmoji = packet.decoded.emoji == 1
if packet.decoded.replyID > 0 {
newMessage.replyID = Int64(packet.decoded.replyID)
}
if packet.to == broadcastNodeNum && fetchedUsers.count == 1 {
// Save the broadcast user if it does not exist
let bcu: UserEntity = UserEntity(context: context)
bcu.shortName = "ALL"
bcu.longName = "All - Broadcast"
bcu.hwModel = "UNSET"
bcu.num = Int64(broadcastNodeNum)
bcu.userId = "BROADCASTNODE"
newMessage.toUser = bcu
} else {
newMessage.toUser = fetchedUsers.first(where: { $0.num == packet.to })
}
newMessage.fromUser = fetchedUsers.first(where: { $0.num == packet.from })
newMessage.messagePayload = messageText
do {
try context.save()
print("💾 Saved a new message for \(packet.id)")
if meshLogging { MeshLogger.log("💾 Saved a new message for \(newMessage.messageId)") }
if newMessage.toUser != nil && newMessage.toUser!.num == broadcastNodeNum || connectedNode == newMessage.toUser!.num {
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? "Unknown")",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
}
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Failed to save new MessageEntity \(nsError)")
}
} catch {
print("💥 Fetch Message To and From Users Error")
}
}
}

View file

@ -7,6 +7,7 @@ struct Peripheral: Identifiable {
var name: String
var shortName: String
var longName: String
var lastFourCode: String
var firmwareVersion: String
var rssi: Int
var bitrate: Float?
@ -16,12 +17,13 @@ struct Peripheral: Identifiable {
var subscribed: Bool
var peripheral: CBPeripheral
init(id: String, num: Int64, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, bitrate: Float?, channelUtilization: Float?, airTime: Float?, lastUpdate: Date, subscribed: Bool, peripheral: CBPeripheral) {
init(id: String, num: Int64, name: String, shortName: String, longName: String, lastFourCode: String, firmwareVersion: String, rssi: Int, bitrate: Float?, channelUtilization: Float?, airTime: Float?, lastUpdate: Date, subscribed: Bool, peripheral: CBPeripheral) {
self.id = id
self.num = num
self.name = name
self.shortName = shortName
self.longName = longName
self.lastFourCode = lastFourCode
self.firmwareVersion = firmwareVersion
self.rssi = rssi
self.bitrate = bitrate

View file

@ -257,7 +257,7 @@ struct Config {
var positionBroadcastSecs: UInt32 = 0
///
/// We should send our position this often (but only if it has changed significantly)
/// Disable adaptive position braoadcast, which is now the default.
var positionBroadcastSmartDisabled: Bool = false
///
@ -266,10 +266,6 @@ struct Config {
/// The lat/lon/alt can be set by an internal GPS or with the help of the app.
var fixedPosition: Bool = false
///
/// Should we disbale location sharing with other nodes (or the local phone)
var locationShareDisabled: Bool = false
///
/// Should the GPS be disabled for this node?
var gpsDisabled: Bool = false
@ -618,6 +614,10 @@ struct Config {
/// will be a station
var apMode: Bool = false
///
/// If set, the node AP will broadcast as a hidden SSID
var apHidden: Bool = false
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -738,7 +738,7 @@ struct Config {
/// Because protobufs take ZERO space when the value is zero this works out nicely.
/// This value is replaced by bandwidth/spread_factor/coding_rate.
/// If you'd like to experiment with other options add them to MeshRadio.cpp in the device code.
var modemPreset: Config.LoRaConfig.ModemPreset = .vlongSlow
var modemPreset: Config.LoRaConfig.ModemPreset = .longFast
///
/// Bandwidth in MHz
@ -898,7 +898,7 @@ struct Config {
///
/// TODO: REPLACE
case vlongSlow // = 0
case longFast // = 0
///
/// TODO: REPLACE
@ -906,7 +906,7 @@ struct Config {
///
/// TODO: REPLACE
case longFast // = 2
case vlongSlow // = 2
///
/// TODO: REPLACE
@ -926,14 +926,14 @@ struct Config {
case UNRECOGNIZED(Int)
init() {
self = .vlongSlow
self = .longFast
}
init?(rawValue: Int) {
switch rawValue {
case 0: self = .vlongSlow
case 0: self = .longFast
case 1: self = .longSlow
case 2: self = .longFast
case 2: self = .vlongSlow
case 3: self = .midSlow
case 4: self = .midFast
case 5: self = .shortSlow
@ -944,9 +944,9 @@ struct Config {
var rawValue: Int {
switch self {
case .vlongSlow: return 0
case .longFast: return 0
case .longSlow: return 1
case .longFast: return 2
case .vlongSlow: return 2
case .midSlow: return 3
case .midFast: return 4
case .shortSlow: return 5
@ -1047,9 +1047,9 @@ extension Config.LoRaConfig.RegionCode: CaseIterable {
extension Config.LoRaConfig.ModemPreset: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [Config.LoRaConfig.ModemPreset] = [
.vlongSlow,
.longSlow,
.longFast,
.longSlow,
.vlongSlow,
.midSlow,
.midFast,
.shortSlow,
@ -1291,7 +1291,6 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
1: .standard(proto: "position_broadcast_secs"),
2: .standard(proto: "position_broadcast_smart_disabled"),
3: .standard(proto: "fixed_position"),
4: .standard(proto: "location_share_disabled"),
5: .standard(proto: "gps_disabled"),
6: .standard(proto: "gps_update_interval"),
7: .standard(proto: "gps_attempt_time"),
@ -1309,7 +1308,6 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.positionBroadcastSecs) }()
case 2: try { try decoder.decodeSingularBoolField(value: &self.positionBroadcastSmartDisabled) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.fixedPosition) }()
case 4: try { try decoder.decodeSingularBoolField(value: &self.locationShareDisabled) }()
case 5: try { try decoder.decodeSingularBoolField(value: &self.gpsDisabled) }()
case 6: try { try decoder.decodeSingularUInt32Field(value: &self.gpsUpdateInterval) }()
case 7: try { try decoder.decodeSingularUInt32Field(value: &self.gpsAttemptTime) }()
@ -1331,9 +1329,6 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
if self.fixedPosition != false {
try visitor.visitSingularBoolField(value: self.fixedPosition, fieldNumber: 3)
}
if self.locationShareDisabled != false {
try visitor.visitSingularBoolField(value: self.locationShareDisabled, fieldNumber: 4)
}
if self.gpsDisabled != false {
try visitor.visitSingularBoolField(value: self.gpsDisabled, fieldNumber: 5)
}
@ -1359,7 +1354,6 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
if lhs.positionBroadcastSecs != rhs.positionBroadcastSecs {return false}
if lhs.positionBroadcastSmartDisabled != rhs.positionBroadcastSmartDisabled {return false}
if lhs.fixedPosition != rhs.fixedPosition {return false}
if lhs.locationShareDisabled != rhs.locationShareDisabled {return false}
if lhs.gpsDisabled != rhs.gpsDisabled {return false}
if lhs.gpsUpdateInterval != rhs.gpsUpdateInterval {return false}
if lhs.gpsAttemptTime != rhs.gpsAttemptTime {return false}
@ -1511,6 +1505,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
1: .same(proto: "ssid"),
2: .same(proto: "psk"),
3: .standard(proto: "ap_mode"),
4: .standard(proto: "ap_hidden"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1522,6 +1517,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
case 1: try { try decoder.decodeSingularStringField(value: &self.ssid) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.psk) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.apMode) }()
case 4: try { try decoder.decodeSingularBoolField(value: &self.apHidden) }()
default: break
}
}
@ -1537,6 +1533,9 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if self.apMode != false {
try visitor.visitSingularBoolField(value: self.apMode, fieldNumber: 3)
}
if self.apHidden != false {
try visitor.visitSingularBoolField(value: self.apHidden, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1544,6 +1543,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if lhs.ssid != rhs.ssid {return false}
if lhs.psk != rhs.psk {return false}
if lhs.apMode != rhs.apMode {return false}
if lhs.apHidden != rhs.apHidden {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -1644,7 +1644,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if self.txPower != 0 {
try visitor.visitSingularInt32Field(value: self.txPower, fieldNumber: 1)
}
if self.modemPreset != .vlongSlow {
if self.modemPreset != .longFast {
try visitor.visitSingularEnumField(value: self.modemPreset, fieldNumber: 2)
}
if self.bandwidth != 0 {
@ -1710,9 +1710,9 @@ extension Config.LoRaConfig.RegionCode: SwiftProtobuf._ProtoNameProviding {
extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "VLongSlow"),
0: .same(proto: "LongFast"),
1: .same(proto: "LongSlow"),
2: .same(proto: "LongFast"),
2: .same(proto: "VLongSlow"),
3: .same(proto: "MidSlow"),
4: .same(proto: "MidFast"),
5: .same(proto: "ShortSlow"),

View file

@ -215,11 +215,180 @@ struct OEMStore {
init() {}
}
struct LocalConfig {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// TODO: REPLACE
var device: Config.DeviceConfig {
get {return _storage._device ?? Config.DeviceConfig()}
set {_uniqueStorage()._device = newValue}
}
/// Returns true if `device` has been explicitly set.
var hasDevice: Bool {return _storage._device != nil}
/// Clears the value of `device`. Subsequent reads from it will return its default value.
mutating func clearDevice() {_uniqueStorage()._device = nil}
///
/// TODO: REPLACE
var position: Config.PositionConfig {
get {return _storage._position ?? Config.PositionConfig()}
set {_uniqueStorage()._position = newValue}
}
/// Returns true if `position` has been explicitly set.
var hasPosition: Bool {return _storage._position != nil}
/// Clears the value of `position`. Subsequent reads from it will return its default value.
mutating func clearPosition() {_uniqueStorage()._position = nil}
///
/// TODO: REPLACE
var power: Config.PowerConfig {
get {return _storage._power ?? Config.PowerConfig()}
set {_uniqueStorage()._power = newValue}
}
/// Returns true if `power` has been explicitly set.
var hasPower: Bool {return _storage._power != nil}
/// Clears the value of `power`. Subsequent reads from it will return its default value.
mutating func clearPower() {_uniqueStorage()._power = nil}
///
/// TODO: REPLACE
var wifi: Config.WiFiConfig {
get {return _storage._wifi ?? Config.WiFiConfig()}
set {_uniqueStorage()._wifi = newValue}
}
/// Returns true if `wifi` has been explicitly set.
var hasWifi: Bool {return _storage._wifi != nil}
/// Clears the value of `wifi`. Subsequent reads from it will return its default value.
mutating func clearWifi() {_uniqueStorage()._wifi = nil}
///
/// TODO: REPLACE
var display: Config.DisplayConfig {
get {return _storage._display ?? Config.DisplayConfig()}
set {_uniqueStorage()._display = newValue}
}
/// Returns true if `display` has been explicitly set.
var hasDisplay: Bool {return _storage._display != nil}
/// Clears the value of `display`. Subsequent reads from it will return its default value.
mutating func clearDisplay() {_uniqueStorage()._display = nil}
///
/// TODO: REPLACE
var lora: Config.LoRaConfig {
get {return _storage._lora ?? Config.LoRaConfig()}
set {_uniqueStorage()._lora = newValue}
}
/// Returns true if `lora` has been explicitly set.
var hasLora: Bool {return _storage._lora != nil}
/// Clears the value of `lora`. Subsequent reads from it will return its default value.
mutating func clearLora() {_uniqueStorage()._lora = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
struct LocalModuleConfig {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// TODO: REPLACE
var mqtt: ModuleConfig.MQTTConfig {
get {return _storage._mqtt ?? ModuleConfig.MQTTConfig()}
set {_uniqueStorage()._mqtt = newValue}
}
/// Returns true if `mqtt` has been explicitly set.
var hasMqtt: Bool {return _storage._mqtt != nil}
/// Clears the value of `mqtt`. Subsequent reads from it will return its default value.
mutating func clearMqtt() {_uniqueStorage()._mqtt = nil}
///
/// TODO: REPLACE
var serial: ModuleConfig.SerialConfig {
get {return _storage._serial ?? ModuleConfig.SerialConfig()}
set {_uniqueStorage()._serial = newValue}
}
/// Returns true if `serial` has been explicitly set.
var hasSerial: Bool {return _storage._serial != nil}
/// Clears the value of `serial`. Subsequent reads from it will return its default value.
mutating func clearSerial() {_uniqueStorage()._serial = nil}
///
/// TODO: REPLACE
var externalNotification: ModuleConfig.ExternalNotificationConfig {
get {return _storage._externalNotification ?? ModuleConfig.ExternalNotificationConfig()}
set {_uniqueStorage()._externalNotification = newValue}
}
/// Returns true if `externalNotification` has been explicitly set.
var hasExternalNotification: Bool {return _storage._externalNotification != nil}
/// Clears the value of `externalNotification`. Subsequent reads from it will return its default value.
mutating func clearExternalNotification() {_uniqueStorage()._externalNotification = nil}
///
/// TODO: REPLACE
var storeForward: ModuleConfig.StoreForwardConfig {
get {return _storage._storeForward ?? ModuleConfig.StoreForwardConfig()}
set {_uniqueStorage()._storeForward = newValue}
}
/// Returns true if `storeForward` has been explicitly set.
var hasStoreForward: Bool {return _storage._storeForward != nil}
/// Clears the value of `storeForward`. Subsequent reads from it will return its default value.
mutating func clearStoreForward() {_uniqueStorage()._storeForward = nil}
///
/// TODO: REPLACE
var rangeTest: ModuleConfig.RangeTestConfig {
get {return _storage._rangeTest ?? ModuleConfig.RangeTestConfig()}
set {_uniqueStorage()._rangeTest = newValue}
}
/// Returns true if `rangeTest` has been explicitly set.
var hasRangeTest: Bool {return _storage._rangeTest != nil}
/// Clears the value of `rangeTest`. Subsequent reads from it will return its default value.
mutating func clearRangeTest() {_uniqueStorage()._rangeTest = nil}
///
/// TODO: REPLACE
var telemetry: ModuleConfig.TelemetryConfig {
get {return _storage._telemetry ?? ModuleConfig.TelemetryConfig()}
set {_uniqueStorage()._telemetry = newValue}
}
/// Returns true if `telemetry` has been explicitly set.
var hasTelemetry: Bool {return _storage._telemetry != nil}
/// Clears the value of `telemetry`. Subsequent reads from it will return its default value.
mutating func clearTelemetry() {_uniqueStorage()._telemetry = nil}
///
/// TODO: REPLACE
var cannedMessage: ModuleConfig.CannedMessageConfig {
get {return _storage._cannedMessage ?? ModuleConfig.CannedMessageConfig()}
set {_uniqueStorage()._cannedMessage = newValue}
}
/// Returns true if `cannedMessage` has been explicitly set.
var hasCannedMessage: Bool {return _storage._cannedMessage != nil}
/// Clears the value of `cannedMessage`. Subsequent reads from it will return its default value.
mutating func clearCannedMessage() {_uniqueStorage()._cannedMessage = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
#if swift(>=5.5) && canImport(_Concurrency)
extension ScreenFonts: @unchecked Sendable {}
extension DeviceState: @unchecked Sendable {}
extension ChannelFile: @unchecked Sendable {}
extension OEMStore: @unchecked Sendable {}
extension LocalConfig: @unchecked Sendable {}
extension LocalModuleConfig: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
@ -443,3 +612,227 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
return true
}
}
extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "LocalConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "device"),
2: .same(proto: "position"),
3: .same(proto: "power"),
4: .same(proto: "wifi"),
5: .same(proto: "display"),
6: .same(proto: "lora"),
]
fileprivate class _StorageClass {
var _device: Config.DeviceConfig? = nil
var _position: Config.PositionConfig? = nil
var _power: Config.PowerConfig? = nil
var _wifi: Config.WiFiConfig? = nil
var _display: Config.DisplayConfig? = nil
var _lora: Config.LoRaConfig? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_device = source._device
_position = source._position
_power = source._power
_wifi = source._wifi
_display = source._display
_lora = source._lora
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularMessageField(value: &_storage._device) }()
case 2: try { try decoder.decodeSingularMessageField(value: &_storage._position) }()
case 3: try { try decoder.decodeSingularMessageField(value: &_storage._power) }()
case 4: try { try decoder.decodeSingularMessageField(value: &_storage._wifi) }()
case 5: try { try decoder.decodeSingularMessageField(value: &_storage._display) }()
case 6: try { try decoder.decodeSingularMessageField(value: &_storage._lora) }()
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = _storage._device {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
} }()
try { if let v = _storage._position {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try { if let v = _storage._power {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
try { if let v = _storage._wifi {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
} }()
try { if let v = _storage._display {
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
} }()
try { if let v = _storage._lora {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: LocalConfig, rhs: LocalConfig) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._device != rhs_storage._device {return false}
if _storage._position != rhs_storage._position {return false}
if _storage._power != rhs_storage._power {return false}
if _storage._wifi != rhs_storage._wifi {return false}
if _storage._display != rhs_storage._display {return false}
if _storage._lora != rhs_storage._lora {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "LocalModuleConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "mqtt"),
2: .same(proto: "serial"),
3: .standard(proto: "external_notification"),
4: .standard(proto: "store_forward"),
5: .standard(proto: "range_test"),
6: .same(proto: "telemetry"),
7: .standard(proto: "canned_message"),
]
fileprivate class _StorageClass {
var _mqtt: ModuleConfig.MQTTConfig? = nil
var _serial: ModuleConfig.SerialConfig? = nil
var _externalNotification: ModuleConfig.ExternalNotificationConfig? = nil
var _storeForward: ModuleConfig.StoreForwardConfig? = nil
var _rangeTest: ModuleConfig.RangeTestConfig? = nil
var _telemetry: ModuleConfig.TelemetryConfig? = nil
var _cannedMessage: ModuleConfig.CannedMessageConfig? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_mqtt = source._mqtt
_serial = source._serial
_externalNotification = source._externalNotification
_storeForward = source._storeForward
_rangeTest = source._rangeTest
_telemetry = source._telemetry
_cannedMessage = source._cannedMessage
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularMessageField(value: &_storage._mqtt) }()
case 2: try { try decoder.decodeSingularMessageField(value: &_storage._serial) }()
case 3: try { try decoder.decodeSingularMessageField(value: &_storage._externalNotification) }()
case 4: try { try decoder.decodeSingularMessageField(value: &_storage._storeForward) }()
case 5: try { try decoder.decodeSingularMessageField(value: &_storage._rangeTest) }()
case 6: try { try decoder.decodeSingularMessageField(value: &_storage._telemetry) }()
case 7: try { try decoder.decodeSingularMessageField(value: &_storage._cannedMessage) }()
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = _storage._mqtt {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
} }()
try { if let v = _storage._serial {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try { if let v = _storage._externalNotification {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
try { if let v = _storage._storeForward {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
} }()
try { if let v = _storage._rangeTest {
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
} }()
try { if let v = _storage._telemetry {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
try { if let v = _storage._cannedMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
} }()
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: LocalModuleConfig, rhs: LocalModuleConfig) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._mqtt != rhs_storage._mqtt {return false}
if _storage._serial != rhs_storage._serial {return false}
if _storage._externalNotification != rhs_storage._externalNotification {return false}
if _storage._storeForward != rhs_storage._storeForward {return false}
if _storage._rangeTest != rhs_storage._rangeTest {return false}
if _storage._telemetry != rhs_storage._telemetry {return false}
if _storage._cannedMessage != rhs_storage._cannedMessage {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -1037,27 +1037,9 @@ struct DataMessage {
/// Formerly named typ and of type Type
var portnum: PortNum = .unknownApp
var payloadVariant: DataMessage.OneOf_PayloadVariant? = nil
///
/// TODO: REPLACE
var payload: Data {
get {
if case .payload(let v)? = payloadVariant {return v}
return Data()
}
set {payloadVariant = .payload(newValue)}
}
///
/// TODO: REPLACE
var payloadCompressed: Data {
get {
if case .payloadCompressed(let v)? = payloadVariant {return v}
return Data()
}
set {payloadVariant = .payloadCompressed(newValue)}
}
var payload: Data = Data()
///
/// Not normally used, but for testing a sender can request that recipient
@ -1106,34 +1088,6 @@ struct DataMessage {
var unknownFields = SwiftProtobuf.UnknownStorage()
enum OneOf_PayloadVariant: Equatable {
///
/// TODO: REPLACE
case payload(Data)
///
/// TODO: REPLACE
case payloadCompressed(Data)
#if !swift(>=4.1)
static func ==(lhs: DataMessage.OneOf_PayloadVariant, rhs: DataMessage.OneOf_PayloadVariant) -> Bool {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch (lhs, rhs) {
case (.payload, .payload): return {
guard case .payload(let l) = lhs, case .payload(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.payloadCompressed, .payloadCompressed): return {
guard case .payloadCompressed(let l) = lhs, case .payloadCompressed(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
init() {}
fileprivate var _location: Location? = nil
@ -2142,7 +2096,6 @@ extension Routing: @unchecked Sendable {}
extension Routing.OneOf_Variant: @unchecked Sendable {}
extension Routing.Error: @unchecked Sendable {}
extension DataMessage: @unchecked Sendable {}
extension DataMessage.OneOf_PayloadVariant: @unchecked Sendable {}
extension Location: @unchecked Sendable {}
extension MeshPacket: @unchecked Sendable {}
extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {}
@ -2684,7 +2637,6 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "portnum"),
2: .same(proto: "payload"),
10: .standard(proto: "payload_compressed"),
3: .standard(proto: "want_response"),
4: .same(proto: "dest"),
5: .same(proto: "source"),
@ -2701,14 +2653,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.portnum) }()
case 2: try {
var v: Data?
try decoder.decodeSingularBytesField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .payload(v)
}
}()
case 2: try { try decoder.decodeSingularBytesField(value: &self.payload) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.wantResponse) }()
case 4: try { try decoder.decodeSingularFixed32Field(value: &self.dest) }()
case 5: try { try decoder.decodeSingularFixed32Field(value: &self.source) }()
@ -2716,14 +2661,6 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 7: try { try decoder.decodeSingularFixed32Field(value: &self.replyID) }()
case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }()
case 9: try { try decoder.decodeSingularMessageField(value: &self._location) }()
case 10: try {
var v: Data?
try decoder.decodeSingularBytesField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .payloadCompressed(v)
}
}()
default: break
}
}
@ -2737,9 +2674,9 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if self.portnum != .unknownApp {
try visitor.visitSingularEnumField(value: self.portnum, fieldNumber: 1)
}
try { if case .payload(let v)? = self.payloadVariant {
try visitor.visitSingularBytesField(value: v, fieldNumber: 2)
} }()
if !self.payload.isEmpty {
try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 2)
}
if self.wantResponse != false {
try visitor.visitSingularBoolField(value: self.wantResponse, fieldNumber: 3)
}
@ -2761,15 +2698,12 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
try { if let v = self._location {
try visitor.visitSingularMessageField(value: v, fieldNumber: 9)
} }()
try { if case .payloadCompressed(let v)? = self.payloadVariant {
try visitor.visitSingularBytesField(value: v, fieldNumber: 10)
} }()
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: DataMessage, rhs: DataMessage) -> Bool {
if lhs.portnum != rhs.portnum {return false}
if lhs.payloadVariant != rhs.payloadVariant {return false}
if lhs.payload != rhs.payload {return false}
if lhs.wantResponse != rhs.wantResponse {return false}
if lhs.dest != rhs.dest {return false}
if lhs.source != rhs.source {return false}

View file

@ -253,21 +253,21 @@ struct ModuleConfig {
enum Serial_Baud: SwiftProtobuf.Enum {
typealias RawValue = Int
case baudDefault // = 0
case baud2400 // = 1
case baud4800 // = 2
case baud9600 // = 3
case baud19200 // = 4
case baud38400 // = 5
case baud57600 // = 6
case baud115200 // = 7
case baud230400 // = 8
case baud460800 // = 9
case baud576000 // = 10
case baud921600 // = 11
case baud110 // = 12
case baud300 // = 13
case baud600 // = 14
case baud1200 // = 15
case baud110 // = 1
case baud300 // = 2
case baud600 // = 3
case baud1200 // = 4
case baud2400 // = 5
case baud4800 // = 6
case baud9600 // = 7
case baud19200 // = 8
case baud38400 // = 9
case baud57600 // = 10
case baud115200 // = 11
case baud230400 // = 12
case baud460800 // = 13
case baud576000 // = 14
case baud921600 // = 15
case UNRECOGNIZED(Int)
init() {
@ -277,21 +277,21 @@ struct ModuleConfig {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .baudDefault
case 1: self = .baud2400
case 2: self = .baud4800
case 3: self = .baud9600
case 4: self = .baud19200
case 5: self = .baud38400
case 6: self = .baud57600
case 7: self = .baud115200
case 8: self = .baud230400
case 9: self = .baud460800
case 10: self = .baud576000
case 11: self = .baud921600
case 12: self = .baud110
case 13: self = .baud300
case 14: self = .baud600
case 15: self = .baud1200
case 1: self = .baud110
case 2: self = .baud300
case 3: self = .baud600
case 4: self = .baud1200
case 5: self = .baud2400
case 6: self = .baud4800
case 7: self = .baud9600
case 8: self = .baud19200
case 9: self = .baud38400
case 10: self = .baud57600
case 11: self = .baud115200
case 12: self = .baud230400
case 13: self = .baud460800
case 14: self = .baud576000
case 15: self = .baud921600
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -299,21 +299,21 @@ struct ModuleConfig {
var rawValue: Int {
switch self {
case .baudDefault: return 0
case .baud2400: return 1
case .baud4800: return 2
case .baud9600: return 3
case .baud19200: return 4
case .baud38400: return 5
case .baud57600: return 6
case .baud115200: return 7
case .baud230400: return 8
case .baud460800: return 9
case .baud576000: return 10
case .baud921600: return 11
case .baud110: return 12
case .baud300: return 13
case .baud600: return 14
case .baud1200: return 15
case .baud110: return 1
case .baud300: return 2
case .baud600: return 3
case .baud1200: return 4
case .baud2400: return 5
case .baud4800: return 6
case .baud9600: return 7
case .baud19200: return 8
case .baud38400: return 9
case .baud57600: return 10
case .baud115200: return 11
case .baud230400: return 12
case .baud460800: return 13
case .baud576000: return 14
case .baud921600: return 15
case .UNRECOGNIZED(let i): return i
}
}
@ -643,6 +643,10 @@ extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [
.baudDefault,
.baud110,
.baud300,
.baud600,
.baud1200,
.baud2400,
.baud4800,
.baud9600,
@ -654,10 +658,6 @@ extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable {
.baud460800,
.baud576000,
.baud921600,
.baud110,
.baud300,
.baud600,
.baud1200,
]
}
@ -990,21 +990,21 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
extension ModuleConfig.SerialConfig.Serial_Baud: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "BAUD_Default"),
1: .same(proto: "BAUD_2400"),
2: .same(proto: "BAUD_4800"),
3: .same(proto: "BAUD_9600"),
4: .same(proto: "BAUD_19200"),
5: .same(proto: "BAUD_38400"),
6: .same(proto: "BAUD_57600"),
7: .same(proto: "BAUD_115200"),
8: .same(proto: "BAUD_230400"),
9: .same(proto: "BAUD_460800"),
10: .same(proto: "BAUD_576000"),
11: .same(proto: "BAUD_921600"),
12: .same(proto: "BAUD_110"),
13: .same(proto: "BAUD_300"),
14: .same(proto: "BAUD_600"),
15: .same(proto: "BAUD_1200"),
1: .same(proto: "BAUD_110"),
2: .same(proto: "BAUD_300"),
3: .same(proto: "BAUD_600"),
4: .same(proto: "BAUD_1200"),
5: .same(proto: "BAUD_2400"),
6: .same(proto: "BAUD_4800"),
7: .same(proto: "BAUD_9600"),
8: .same(proto: "BAUD_19200"),
9: .same(proto: "BAUD_38400"),
10: .same(proto: "BAUD_57600"),
11: .same(proto: "BAUD_115200"),
12: .same(proto: "BAUD_230400"),
13: .same(proto: "BAUD_460800"),
14: .same(proto: "BAUD_576000"),
15: .same(proto: "BAUD_921600"),
]
}

View file

@ -73,6 +73,10 @@ enum PortNum: SwiftProtobuf.Enum {
/// Payload is a [AdminMessage](/docs/developers/protobufs/api#adminmessage) message
case adminApp // = 6
///
/// Compressed TEXT_MESSAGE payloads.
case textMessageCompressedApp // = 7
///
/// Provides a 'ping' service that replies to any packet it receives.
/// Also serves as a small example module.
@ -110,10 +114,6 @@ enum PortNum: SwiftProtobuf.Enum {
/// Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS
case zpsApp // = 68
///
/// Compressed payloads.
case compressionApp // = 69
///
/// Private applications should use portnums >= 256.
/// To simplify initial development and testing you can use "PRIVATE_APP"
@ -142,6 +142,7 @@ enum PortNum: SwiftProtobuf.Enum {
case 4: self = .nodeinfoApp
case 5: self = .routingApp
case 6: self = .adminApp
case 7: self = .textMessageCompressedApp
case 32: self = .replyApp
case 33: self = .ipTunnelApp
case 64: self = .serialApp
@ -149,7 +150,6 @@ enum PortNum: SwiftProtobuf.Enum {
case 66: self = .rangeTestApp
case 67: self = .telemetryApp
case 68: self = .zpsApp
case 69: self = .compressionApp
case 256: self = .privateApp
case 257: self = .atakForwarder
case 511: self = .max
@ -166,6 +166,7 @@ enum PortNum: SwiftProtobuf.Enum {
case .nodeinfoApp: return 4
case .routingApp: return 5
case .adminApp: return 6
case .textMessageCompressedApp: return 7
case .replyApp: return 32
case .ipTunnelApp: return 33
case .serialApp: return 64
@ -173,7 +174,6 @@ enum PortNum: SwiftProtobuf.Enum {
case .rangeTestApp: return 66
case .telemetryApp: return 67
case .zpsApp: return 68
case .compressionApp: return 69
case .privateApp: return 256
case .atakForwarder: return 257
case .max: return 511
@ -195,6 +195,7 @@ extension PortNum: CaseIterable {
.nodeinfoApp,
.routingApp,
.adminApp,
.textMessageCompressedApp,
.replyApp,
.ipTunnelApp,
.serialApp,
@ -202,7 +203,6 @@ extension PortNum: CaseIterable {
.rangeTestApp,
.telemetryApp,
.zpsApp,
.compressionApp,
.privateApp,
.atakForwarder,
.max,
@ -226,6 +226,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
4: .same(proto: "NODEINFO_APP"),
5: .same(proto: "ROUTING_APP"),
6: .same(proto: "ADMIN_APP"),
7: .same(proto: "TEXT_MESSAGE_COMPRESSED_APP"),
32: .same(proto: "REPLY_APP"),
33: .same(proto: "IP_TUNNEL_APP"),
64: .same(proto: "SERIAL_APP"),
@ -233,7 +234,6 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
66: .same(proto: "RANGE_TEST_APP"),
67: .same(proto: "TELEMETRY_APP"),
68: .same(proto: "ZPS_APP"),
69: .same(proto: "COMPRESSION_APP"),
256: .same(proto: "PRIVATE_APP"),
257: .same(proto: "ATAK_FORWARDER"),
511: .same(proto: "MAX"),

File diff suppressed because it is too large Load diff

View file

@ -265,8 +265,8 @@ struct Connect: View {
ConnectedDevice(
bluetoothOn: self.bleManager.isSwitchedOn,
deviceConnected: self.bleManager.connectedPeripheral != nil,
name: (bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.shortName :
"???")
name: (bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.lastFourCode :
"????")
}
)
}

View file

@ -7,6 +7,7 @@ import SwiftUI
//
struct LastHeardText: View {
var lastHeard: Date?
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
var body: some View {
@ -16,7 +17,7 @@ struct LastHeardText: View {
} else {
Text("Last Heard: Unknown Age")
Text("Unknown Age")
}
}
}

View file

@ -470,7 +470,7 @@ struct UserMessageList: View {
ConnectedDevice(
bluetoothOn: bleManager.isSwitchedOn,
deviceConnected: bleManager.connectedPeripheral != nil,
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???")
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
}
}
}

View file

@ -37,17 +37,33 @@ struct NodeDetail: View {
},
set: { _ in }
)
let annotations = [MapLocation(name: node.user!.shortName ?? "???", coordinate: mostRecent.coordinate!)]
Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in
MapAnnotation(
coordinate: location.coordinate,
content: {
CircleText(text: node.user!.shortName ?? "???", color: .accentColor, circleSize: 32, fontSize: 14)
}
ZStack {
let annotations = node.positions?.array as! [PositionEntity]
Map(coordinateRegion: regionBinding,
interactionModes: [.all],
showsUserLocation: true,
userTrackingMode: .constant(.follow),
annotationItems: annotations
)
{ location in
return MapAnnotation(
coordinate: location.coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0),
content: {
CircleText(text: node.user!.shortName ?? "???", color: .accentColor)
}
)
}
.frame(idealWidth: bounds.size.width, maxHeight: bounds.size.height / 2)
.ignoresSafeArea(.all, edges: [.leading, .trailing])
}
.frame(idealWidth: bounds.size.width, maxHeight: bounds.size.height / 3)
} else {
Image(node.user?.hwModel ?? "UNSET")
@ -74,7 +90,6 @@ struct NodeDetail: View {
.symbolRenderingMode(.hierarchical)
LastHeardText(lastHeard: node.lastHeard).font(.title3)
}
.padding()
Divider()
@ -133,16 +148,18 @@ struct NodeDetail: View {
BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .title, color: .accentColor)
.padding(.bottom)
Text(String(mostRecent.batteryLevel) + "%")
.font(.title3)
.foregroundColor(.gray)
.fixedSize()
if mostRecent.batteryLevel > 0 {
Text(String(mostRecent.batteryLevel) + "%")
.font(.title3)
.foregroundColor(.gray)
.fixedSize()
}
Text(String(format: "%.2f", mostRecent.voltage) + " V")
.font(.title3)
.foregroundColor(.gray)
.fixedSize()
}
.padding(5)
}
@ -201,70 +218,50 @@ struct NodeDetail: View {
.padding()
Divider()
ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
if mappin.coordinate != nil {
VStack {
HStack {
Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline)
Text("Lat/Long:").font(.caption)
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
.foregroundColor(.gray)
.font(.caption)
Image(systemName: "arrow.up.arrow.down.circle")
.font(.subheadline)
.foregroundColor(.accentColor)
.symbolRenderingMode(.hierarchical)
Text("Alt:")
.font(.caption)
Text("\(String(mappin.altitude))m")
.foregroundColor(.gray)
.font(.caption)
}
HStack {
Image(systemName: "clock.badge.checkmark.fill")
.font(.subheadline)
.foregroundColor(.accentColor)
.symbolRenderingMode(.hierarchical)
Text("Time:")
.font(.caption)
Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)")
.foregroundColor(.gray)
.font(.caption)
Divider()
}
}
}
}
// ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
//
// if mappin.coordinate != nil {
//
// VStack {
//
// HStack {
//
// Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline)
// Text("Lat/Long:").font(.caption)
// Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
// .foregroundColor(.gray)
// .font(.caption)
//
// Image(systemName: "arrow.up.arrow.down.circle")
// .font(.subheadline)
// .foregroundColor(.accentColor)
// .symbolRenderingMode(.hierarchical)
//
// Text("Alt:")
// .font(.caption)
//
// Text("\(String(mappin.altitude))m")
// .foregroundColor(.gray)
// .font(.caption)
// }
// HStack {
//
// Image(systemName: "clock.badge.checkmark.fill")
// .font(.subheadline)
// .foregroundColor(.accentColor)
// .symbolRenderingMode(.hierarchical)
// Text("Time:")
// .font(.caption)
// Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)")
// .foregroundColor(.gray)
// .font(.caption)
// Divider()
//
// HStack {
//
// BatteryIcon(batteryLevel: mappin.batteryLevel, font: .subheadline, color: .accentColor)
//
// if mappin.batteryLevel > 0 {
//
// Text(String(mappin.batteryLevel) + "%")
// .font(.caption2)
// .foregroundColor(.gray)
// }
// }
// }
// }
// .padding(1)
// Divider()
// }
// }
}
}
}
@ -281,7 +278,7 @@ struct NodeDetail: View {
ConnectedDevice(
bluetoothOn: bleManager.isSwitchedOn,
deviceConnected: bleManager.connectedPeripheral != nil,
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???")
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
}
)
.onAppear(perform: {

View file

@ -68,7 +68,7 @@ struct NodeList: View {
HStack(alignment: .bottom) {
Image(systemName: "repeat.circle.fill").font(.title3)
Image(systemName: "repeat.circle.fill").font(.title2)
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
@ -83,7 +83,8 @@ struct NodeList: View {
HStack(alignment: .bottom) {
Image(systemName: "clock.badge.checkmark.fill").font(.title3).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
Image(systemName: "clock.badge.checkmark.fill").font(.title3)
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
LastHeardText(lastHeard: node.lastHeard).font(.subheadline).foregroundColor(.gray)

View file

@ -126,8 +126,8 @@ struct NodeMap: View {
ConnectedDevice(
bluetoothOn: bleManager.isSwitchedOn,
deviceConnected: bleManager.connectedPeripheral != nil,
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName :
"???")
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode :
"????")
})
.onAppear(perform: {

View file

@ -251,7 +251,7 @@ struct AppSettings: View {
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???")
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
})
.onAppear {

View file

@ -81,7 +81,7 @@ struct ShareChannel: View {
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???")
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
})
.onAppear {

View file

@ -15,7 +15,7 @@ fi
pdir=$(realpath "../Meshtastic-protobufs")
sdir=$(realpath "./MeshtasticClient/Protobufs")
echo "pdir:$pdir sdir:$sdir"
pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto config.proto deviceonly.proto mesh.proto module_config.proto mqtt.proto portnums.proto radioconfig.proto remote_hardware.proto storeforward.proto telemetry.proto"
pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto config.proto deviceonly.proto mesh.proto module_config.proto mqtt.proto portnums.proto remote_hardware.proto storeforward.proto telemetry.proto"
for pf in $pfiles
do
echo "Generating $pf..."