diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 58b7e8c2..b007418b 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553562855B02500E55709 /* LoRaConfig.swift */; }; DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; }; DD2AD8A8296D2DF9001FF0E7 /* MapViewSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */; }; + DD2DC2C029BCD8AB003B383C /* HardwareModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */; }; DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; }; DD3501892852FC3B000FC853 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3501882852FC3B000FC853 /* Settings.swift */; }; DD35018B2852FC79000FC853 /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD35018A2852FC79000FC853 /* UserSettings.swift */; }; @@ -110,6 +111,7 @@ DDCDC6CB29481FCC004C1DDA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDCDC6CD29481FCC004C1DDA /* Localizable.strings */; }; DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; }; DDD3BBD5292D763200D609B3 /* MeshtasticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */; }; + DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD6EEAE29BC024700383354 /* Firmware.swift */; }; DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; }; DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; }; DDDE59F529AF163D00490C6C /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41A61C29AE7E8E003C5A37 /* WidgetKit.framework */; }; @@ -177,6 +179,7 @@ DD2553562855B02500E55709 /* LoRaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaConfig.swift; sourceTree = ""; }; DD2553582855B52700E55709 /* PositionConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfig.swift; sourceTree = ""; }; DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewSwiftUI.swift; sourceTree = ""; }; + DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardwareModels.swift; sourceTree = ""; }; DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; DD35018A2852FC79000FC853 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = ""; }; @@ -284,6 +287,7 @@ DDCDC6CE294821AD004C1DDA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserConfig.swift; sourceTree = ""; }; DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeshtasticTests.swift; sourceTree = ""; }; + DDD6EEAE29BC024700383354 /* Firmware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firmware.swift; sourceTree = ""; }; DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = ""; }; DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = ""; }; DDDD527729B5B83F0045BC3C /* MeshtasticDataModelV9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV9.xcdatamodel; sourceTree = ""; }; @@ -385,6 +389,7 @@ DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */, DD4A911D2708C65400501B7E /* AppSettings.swift */, DDA0B6B1294CDC55001356EC /* Channels.swift */, + DDD6EEAE29BC024700383354 /* Firmware.swift */, DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */, DD3501882852FC3B000FC853 /* Settings.swift */, @@ -483,6 +488,7 @@ DD1925B828CDA93900720036 /* SerialConfigEnums.swift */, DD994B68295F88B60013760A /* IntervalEnums.swift */, DD5E5239298EFA5300D21B61 /* TelemetryWeather.swift */, + DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */, ); path = Enums; sourceTree = ""; @@ -891,6 +897,7 @@ DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, DDC4D568275499A500A4208E /* Persistence.swift in Sources */, + DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */, DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */, DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */, DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */, @@ -926,6 +933,7 @@ DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */, + DD2DC2C029BCD8AB003B383C /* HardwareModels.swift in Sources */, DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */, DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */, DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, @@ -1180,7 +1188,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.22; + MARKETING_VERSION = 2.1.0; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1214,7 +1222,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.22; + MARKETING_VERSION = 2.1.0; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift new file mode 100644 index 00000000..de6e9331 --- /dev/null +++ b/Meshtastic/Enums/HardwareModels.swift @@ -0,0 +1,262 @@ +// +// HardwareModels.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 3/11/23. +// + +import Foundation + +enum HardwareModels: String, CaseIterable, Identifiable { + + case UNSET + case TLORA_V2 + case TLORA_V1 + case TLORA_V2_1_1P6 + case TBEAM + case HELTEC_V2_0 + case TBEAM_V0P7 + case T_ECHO + case TLORA_V1_1P3 + case RAK4631 + case HELTEC_V2_1 + case HELTEC_V1 + case LILYGO_TBEAM_S3_CORE + case RAK11200 + case NANO_G1 + case TLORA_V2_1_1P8 + case TLORA_T3_S3 + case NANO_G1_EXPLORER + case STATION_G1 + case M5STACK + case HELTEC_V3 + case HELTEC_WSL_V3 + + var id: String { self.rawValue } + var description: String { + switch self { + + case .UNSET: + return NSLocalizedString("unset", comment: "UNSET") + case .TLORA_V2: + return "TLoRa V2" + case .TLORA_V1: + return "TLoRa V1" + case .TLORA_V2_1_1P6: + return "TLoRa V2.1.1.6" + case .TBEAM: + return "TBeam" + case .HELTEC_V2_0: + return "HELTEC V2.0" + case .TBEAM_V0P7: + return "TBeam 0.7" + case .T_ECHO: + return "TEcho" + case .TLORA_V1_1P3: + return "TLORA V1.1.3" + case .RAK4631: + return "RAK 4631 NRF" + case .HELTEC_V2_1: + return "HELTEC V2.1" + case .HELTEC_V1: + return "HELTEC V1" + case .LILYGO_TBEAM_S3_CORE: + return "TBEAM S3" + case .RAK11200: + return "RAK 11200 ESP32" + case .NANO_G1: + return "Nano G1" + case .TLORA_V2_1_1P8: + return "TLoRa V2.1.1.8" + case .TLORA_T3_S3: + return "TLoRa T3 S3" + case .NANO_G1_EXPLORER: + return "Nano G1 Explorer" + case .STATION_G1: + return "Station G1" + case .M5STACK: + return "M5 Stack" + case .HELTEC_V3: + return "Heltec V3" + case .HELTEC_WSL_V3: + return "Heltec wireless stick lite V3" + } + + } + var firmwareStrings: [String] { + switch self { + + case .UNSET: + return [] + case .TLORA_V2: + return ["firmware-tlora-v2-"] + case .TLORA_V1: + return ["firmware-tlora-v1-"] + case .TLORA_V2_1_1P6: + return ["firmware-tlora-v2-1-1.6-"] + case .TBEAM: + return ["firmware-tbeam-"] + case .HELTEC_V2_0: + return ["firmware-heltec-v2.0-"] + case .TBEAM_V0P7: + return ["firmware-tbeam0.7-"] + case .T_ECHO: + return ["firmware-t-echo-"] + case .TLORA_V1_1P3: + return ["firmware-tlora_v1_3-"] + case .RAK4631: + return ["firmware-rak4631-", "firmware-rak4631_eink-"] + case .HELTEC_V2_1: + return ["firmware-heltec-v2.1-"] + case .HELTEC_V1: + return ["firmware-heltec-v1-"] + case .LILYGO_TBEAM_S3_CORE: + return ["firmware-tbeam-s3-core-"] + case .RAK11200: + return ["firmware-rak11200-"] + case .NANO_G1: + return ["firmware-nano-g1-"] + case .TLORA_V2_1_1P8: + return ["firmware-tlora-v2-1-1.8-"] + case .TLORA_T3_S3: + return ["firmware-tlora-t3s3-v1-"] + case .NANO_G1_EXPLORER: + return ["firmware-nano-g1-explorer-"] + case .STATION_G1: + return ["firmware-station-g1-"] + case .M5STACK: + return ["firmware-m5stack-core-", "firmware-m5stack-coreink-"] + case .HELTEC_V3: + return ["firmware-heltec-v3-"] + case .HELTEC_WSL_V3: + return ["firmware-heltec-wsl-v3-"] + } + + } + func platform() -> HardwarePlatforms { + + switch self { + + case .UNSET: + return HardwarePlatforms.NONE + case .TLORA_V2: + return HardwarePlatforms.ESP32 + case .TLORA_V1: + return HardwarePlatforms.ESP32 + case .TLORA_V2_1_1P6: + return HardwarePlatforms.ESP32 + case .TBEAM: + return HardwarePlatforms.ESP32 + case .HELTEC_V2_0: + return HardwarePlatforms.ESP32 + case .TBEAM_V0P7: + return HardwarePlatforms.ESP32 + case .T_ECHO: + return HardwarePlatforms.NRF52 + case .TLORA_V1_1P3: + return HardwarePlatforms.ESP32 + case .RAK4631: + return HardwarePlatforms.NRF52 + case .HELTEC_V2_1: + return HardwarePlatforms.ESP32 + case .HELTEC_V1: + return HardwarePlatforms.ESP32 + case .LILYGO_TBEAM_S3_CORE: + return HardwarePlatforms.ESP32 + case .RAK11200: + return HardwarePlatforms.ESP32 + case .NANO_G1: + return HardwarePlatforms.ESP32 + case .TLORA_V2_1_1P8: + return HardwarePlatforms.ESP32 + case .TLORA_T3_S3: + return HardwarePlatforms.ESP32 + case .NANO_G1_EXPLORER: + return HardwarePlatforms.ESP32 + case .STATION_G1: + return HardwarePlatforms.ESP32 + case .M5STACK: + return HardwarePlatforms.ESP32 + case .HELTEC_V3: + return HardwarePlatforms.ESP32 + case .HELTEC_WSL_V3: + return HardwarePlatforms.ESP32 + } + } + func protoEnumValue() -> HardwareModel { + + switch self { + + case .UNSET: + return HardwareModel.unset + case .TLORA_V2: + return HardwareModel.tloraV2 + case .TLORA_V1: + return HardwareModel.tloraV1 + case .TLORA_V2_1_1P6: + return HardwareModel.tloraV211P6 + case .TBEAM: + return HardwareModel.tbeam + case .HELTEC_V2_0: + return HardwareModel.heltecV20 + case .TBEAM_V0P7: + return HardwareModel.tbeamV0P7 + case .T_ECHO: + return HardwareModel.tEcho + case .TLORA_V1_1P3: + return HardwareModel.tloraV11P3 + case .RAK4631: + return HardwareModel.rak4631 + case .HELTEC_V2_1: + return HardwareModel.heltecV21 + case .HELTEC_V1: + return HardwareModel.heltecV1 + case .LILYGO_TBEAM_S3_CORE: + return HardwareModel.lilygoTbeamS3Core + case .RAK11200: + return HardwareModel.rak11200 + case .NANO_G1: + return HardwareModel.nanoG1 + case .TLORA_V2_1_1P8: + return HardwareModel.tloraV211P8 + case .TLORA_T3_S3: + return HardwareModel.tloraT3S3 + case .NANO_G1_EXPLORER: + return HardwareModel.nanoG1Explorer + case .STATION_G1: + return HardwareModel.stationG1 + case .M5STACK: + return HardwareModel.m5Stack + case .HELTEC_V3: + return HardwareModel.heltecV3 + case .HELTEC_WSL_V3: + return HardwareModel.heltecWslV3 + } + } +} + + +enum HardwarePlatforms: String, CaseIterable, Identifiable { + + case NONE + case ESP32 + case NRF52 + case STM32 + case PIPICO + var id: String { self.rawValue } + var description: String { + switch self { + + case .NONE: + return "None" + case .ESP32: + return "Expressif ESP 32" + case .NRF52: + return "Nordic NRF52" + case .STM32: + return "ARM STM 32" + case .PIPICO: + return "Raspberrry Pi Pico" + } + } +} diff --git a/Meshtastic/Enums/IntervalEnums.swift b/Meshtastic/Enums/IntervalEnums.swift index 06087893..8a36933a 100644 --- a/Meshtastic/Enums/IntervalEnums.swift +++ b/Meshtastic/Enums/IntervalEnums.swift @@ -92,6 +92,7 @@ enum UpdateIntervals: Int, CaseIterable, Identifiable { case fifteenSeconds = 15 case thirtySeconds = 30 case oneMinute = 60 + case twoMinutes = 120 case fiveMinutes = 300 case tenMinutes = 600 case fifteenMinutes = 900 @@ -121,6 +122,8 @@ enum UpdateIntervals: Int, CaseIterable, Identifiable { return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: return NSLocalizedString("interval.one.minute", comment: "One Minute") + case .twoMinutes: + return NSLocalizedString("interval.two.minutes", comment: "Two Minutes") case .fiveMinutes: return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") case .tenMinutes: diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 3375e012..c5471674 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -158,12 +158,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { isConnected = true if userSettings?.preferredPeripheralId.count ?? 0 < 1 { userSettings?.preferredPeripheralId = peripheral.identifier.uuidString - // preferredPeripheral = true - } else if userSettings!.preferredPeripheralId == peripheral.identifier.uuidString { - // preferredPeripheral = true - } else { - // preferredPeripheral = false - print("Trying to connect a non prefered peripheral") } UserDefaults.standard.synchronize() // Invalidate and reset connection timer count @@ -426,7 +420,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } else { let version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))] nowKnown = true - connectedVersion = String(version) + connectedVersion = String(version.dropLast()) } let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame @@ -446,6 +440,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { connectedPeripheral.longName = myInfo?.bleName ?? NSLocalizedString("unknown", comment: "Unknown") } } + tryClearExistingChannels() } // NodeInfo if decodedInfo.nodeInfo.num > 0 && !invalidVersion { @@ -502,7 +497,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { case .waypointApp: waypointPacket(packet: decodedInfo.packet, context: context!) case .nodeinfoApp: - if !invalidVersion { nodeInfoAppPacket(packet: decodedInfo.packet, context: context!) } + if !invalidVersion { upsertNodeInfoPacket(packet: decodedInfo.packet, context: context!) } case .routingApp: if !invalidVersion { routingPacket(packet: decodedInfo.packet, connectedNodeNum: self.connectedPeripheral.num, context: context!) } case .adminApp: @@ -873,6 +868,27 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { return false } + public func sendRebootOta(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + var adminPacket = AdminMessage() + adminPacket.rebootOtaSeconds = 5 + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { var adminPacket = AdminMessage() adminPacket.factoryReset = 5 @@ -982,25 +998,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num)) - do { - guard let fetchedMyInfo = try context!.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { - return false - } - if fetchedMyInfo.count == 1 { - guard let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as? NSMutableOrderedSet else { - return false - } - mutableChannels.removeAllObjects() - fetchedMyInfo[0].channels = mutableChannels - do { - try context!.save() - } catch { - print("Failed to clear existing channels from local app database") - } - } - } catch { - print("Failed to find a node MyInfo to save these channels to") - } + tryClearExistingChannels() let decodedString = base64UrlString.base64urlToBase64() if let decodedData = Data(base64Encoded: decodedString) { do { @@ -1129,7 +1127,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "🛟 Saved Bluetooth Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertBluetoothConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertBluetoothConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } @@ -1154,7 +1152,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { meshPacket.decoded = dataMessage let messageDescription = "🛟 Saved Device Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertDeviceConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertDeviceConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } return 0 @@ -1172,14 +1170,13 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. = NSFetchRequest.init(entityName: "MyInfoEntity") + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num)) + + do { + let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as? [MyInfoEntity] ?? [] + if fetchedMyInfo.count == 1 { + let mutableChannels = fetchedMyInfo[0].channels?.mutableCopy() as? NSMutableOrderedSet + mutableChannels?.removeAllObjects() + fetchedMyInfo[0].channels = mutableChannels + do { + try context!.save() + } catch { + print("Failed to clear existing channels from local app database") + } + } + } catch { + print("Failed to find a node MyInfo to save these channels to") + } + } } // MARK: - CB Central Manager implmentation diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index c5c6a377..81f0caa4 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -13,13 +13,11 @@ import ActivityKit #endif func generateMessageMarkdown (message: String) -> String { - let types: NSTextCheckingResult.CheckingType = [.address, .link, .phoneNumber] let detector = try! NSDataDetector(types: types.rawValue) let matches = detector.matches(in: message, options: [], range: NSRange(location: 0, length: message.utf16.count)) var messageWithMarkdown = message if matches.count > 0 { - for match in matches { guard let range = Range(match.range, in: message) else { continue } if match.resultType == .address { @@ -40,7 +38,6 @@ func generateMessageMarkdown (message: String) -> String { } func localConfig (config: Config, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { - // We don't care about any of the Power settings, config is available for everything else if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: nodeNum, context: context) @@ -342,7 +339,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newTelemetry.voltage = nodeInfo.deviceMetrics.voltage newTelemetry.channelUtilization = nodeInfo.deviceMetrics.channelUtilization newTelemetry.airUtilTx = nodeInfo.deviceMetrics.airUtilTx - let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet + guard let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as? NSMutableOrderedSet else { + return nil + } fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet } @@ -356,7 +355,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje position.altitude = nodeInfo.position.altitude position.satsInView = Int32(nodeInfo.position.satsInView) position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time))) - let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet + guard let mutablePositions = fetchedNode[0].positions!.mutableCopy() as? NSMutableOrderedSet else { + return nil + } fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet } @@ -392,63 +393,6 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje return nil } -func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(packet.from)) - MeshLogger.log("📟 \(logString)") - - guard packet.from > 0 else { return } - - let fetchNodeInfoAppRequest: NSFetchRequest = 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 - fetchedNode[0].channel = Int32(packet.channel) - - if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) { - if nodeInfoMessage.hasDeviceMetrics { - let telemetry = TelemetryEntity(context: context) - telemetry.batteryLevel = Int32(nodeInfoMessage.deviceMetrics.batteryLevel) - telemetry.voltage = nodeInfoMessage.deviceMetrics.voltage - telemetry.channelUtilization = nodeInfoMessage.deviceMetrics.channelUtilization - telemetry.airUtilTx = nodeInfoMessage.deviceMetrics.airUtilTx - var newTelemetries = [TelemetryEntity]() - newTelemetries.append(telemetry) - fetchedNode[0].telemetries? = NSOrderedSet(array: newTelemetries) - } - if nodeInfoMessage.hasUser { - fetchedNode[0].user!.userId = nodeInfoMessage.user.id - fetchedNode[0].user!.num = Int64(nodeInfoMessage.num) - fetchedNode[0].user!.longName = nodeInfoMessage.user.longName - fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName - fetchedNode[0].user!.macaddr = nodeInfoMessage.user.macaddr - fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased() - } - } - do { - try context.save() - print("💾 Updated NodeInfo 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)") - } - } else { - // New node info not from device but potentially from another network - } - } catch { - print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP") - } -} - func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) { @@ -617,7 +561,7 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] if fetchedMyInfo?.count ?? 0 > 0 { - for ch in fetchedMyInfo![0].channels!.array as! [ChannelEntity] { + for ch in fetchedMyInfo![0].channels!.array as? [ChannelEntity] ?? [] { if ch.index == packet.channel { ch.objectWillChange.send() @@ -685,7 +629,9 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage telemetry.metricsType = 1 } telemetry.time = Date(timeIntervalSince1970: TimeInterval(Int64(telemetryMessage.time))) - let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet + guard let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as? NSMutableOrderedSet else { + return + } mutableTelemetries.add(telemetry) fetchedNode[0].lastHeard = telemetry.time fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet @@ -716,7 +662,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage } } // Update our live activity if there is one running, not available on mac iOS >= 16.2 - #if !targetEnvironment(macCatalyst) +#if !targetEnvironment(macCatalyst) if #available(iOS 16.2, *) { let oneMinuteLater = Calendar.current.date(byAdding: .minute, value: (Int(1) ), to: Date())! @@ -734,7 +680,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage } } } - #endif +#endif } } catch { context.rollback() diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 6ba49d8e..9763b0e7 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -97,6 +97,79 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext) { } } +func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(packet.from)) + MeshLogger.log("📟 \(logString)") + + guard packet.from > 0 else { return } + + let fetchNodeInfoAppRequest: NSFetchRequest = 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 == 0 { + // Not Found Insert + let newNode = NodeInfoEntity(context: context) + newNode.id = Int64(packet.from) + newNode.num = Int64(packet.from) + newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime))) + newNode.snr = packet.rxSnr + newNode.channel = Int32(packet.channel) + if let newUserMessage = try? User(serializedData: packet.decoded.payload) { + let newUser = UserEntity(context: context) + newUser.userId = newUserMessage.id + newUser.num = Int64(packet.from) + newUser.longName = newUserMessage.longName + newUser.shortName = newUserMessage.shortName + newUser.macaddr = newUserMessage.macaddr + newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased() + newNode.user = newUser + } + } else { + // Update an existing node + 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 + fetchedNode[0].channel = Int32(packet.channel) + + if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) { + if nodeInfoMessage.hasDeviceMetrics { + let telemetry = TelemetryEntity(context: context) + telemetry.batteryLevel = Int32(nodeInfoMessage.deviceMetrics.batteryLevel) + telemetry.voltage = nodeInfoMessage.deviceMetrics.voltage + telemetry.channelUtilization = nodeInfoMessage.deviceMetrics.channelUtilization + telemetry.airUtilTx = nodeInfoMessage.deviceMetrics.airUtilTx + var newTelemetries = [TelemetryEntity]() + newTelemetries.append(telemetry) + fetchedNode[0].telemetries? = NSOrderedSet(array: newTelemetries) + } + if nodeInfoMessage.hasUser { + fetchedNode[0].user!.userId = nodeInfoMessage.user.id + fetchedNode[0].user!.num = Int64(nodeInfoMessage.num) + fetchedNode[0].user!.longName = nodeInfoMessage.user.longName + fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName + fetchedNode[0].user!.macaddr = nodeInfoMessage.user.macaddr + fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased() + } + } + do { + try context.save() + print("💾 Updated NodeInfo 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 upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.received %@", comment: "Position Packet received from node: %@"), String(packet.from)) @@ -119,7 +192,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) // Unset the current latest position for this node let fetchCurrentLatestPositionsRequest: NSFetchRequest = NSFetchRequest.init(entityName: "PositionEntity") fetchCurrentLatestPositionsRequest.predicate = NSPredicate(format: "nodePosition.num == %lld && latest = true", Int64(packet.from)) - + guard let fetchedPositions = try context.fetch(fetchCurrentLatestPositionsRequest) as? [PositionEntity] else { return } @@ -164,8 +237,13 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) } } } else { - print("💥 Empty POSITION_APP Packet") - print((try? packet.jsonString()) ?? "JSON Decode Failure") + + if (try? NodeInfo(serializedData: packet.decoded.payload)) != nil { + upsertNodeInfoPacket(packet: packet, context: context) + } else { + print("💥 Empty POSITION_APP Packet") + print((try? packet.jsonString()) ?? "JSON Decode Failure") + } } } } catch { @@ -355,6 +433,7 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64 newLoRaConfig.txPower = Int32(config.txPower) newLoRaConfig.txEnabled = config.txEnabled newLoRaConfig.channelNum = Int32(config.channelNum) + newLoRaConfig.sx126xRxBoostedGain = config.sx126XRxBoostedGain fetchedNode[0].loRaConfig = newLoRaConfig } else { fetchedNode[0].loRaConfig?.regionCode = Int32(config.region.rawValue) @@ -370,6 +449,7 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64 fetchedNode[0].loRaConfig?.txPower = Int32(config.txPower) fetchedNode[0].loRaConfig?.txEnabled = config.txEnabled fetchedNode[0].loRaConfig?.channelNum = Int32(config.channelNum) + fetchedNode[0].loRaConfig?.sx126xRxBoostedGain = config.sx126XRxBoostedGain } do { try context.save() diff --git a/Meshtastic/Protobufs/meshtastic/admin.pb.swift b/Meshtastic/Protobufs/meshtastic/admin.pb.swift index 8deabc66..aa4a6481 100644 --- a/Meshtastic/Protobufs/meshtastic/admin.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/admin.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -31,7 +31,7 @@ struct AdminMessage { /// /// TODO: REPLACE - var payloadVariant: AdminMessage.OneOf_PayloadVariant? + var payloadVariant: AdminMessage.OneOf_PayloadVariant? = nil /// /// Send the specified channel in the response to this message @@ -301,7 +301,7 @@ struct AdminMessage { } /// - /// This message is only supported for the simulator porduino build. + /// This message is only supported for the simulator Portduino build. /// If received the simulator will exit successfully. var exitSimulator: Bool { get { @@ -442,7 +442,7 @@ struct AdminMessage { /// Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. case rebootOtaSeconds(Int32) /// - /// This message is only supported for the simulator porduino build. + /// This message is only supported for the simulator Portduino build. /// If received the simulator will exit successfully. case exitSimulator(Bool) /// @@ -752,7 +752,7 @@ extension AdminMessage.ConfigType: CaseIterable { .networkConfig, .displayConfig, .loraConfig, - .bluetoothConfig + .bluetoothConfig, ] } @@ -767,7 +767,7 @@ extension AdminMessage.ModuleConfigType: CaseIterable { .telemetryConfig, .cannedmsgConfig, .audioConfig, - .remotehardwareConfig + .remotehardwareConfig, ] } @@ -813,7 +813,7 @@ extension HamParameters: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".AdminMessage" @@ -848,7 +848,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 97: .standard(proto: "reboot_seconds"), 98: .standard(proto: "shutdown_seconds"), 99: .standard(proto: "factory_reset"), - 100: .standard(proto: "nodedb_reset") + 100: .standard(proto: "nodedb_reset"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1315,7 +1315,7 @@ extension AdminMessage.ConfigType: SwiftProtobuf._ProtoNameProviding { 3: .same(proto: "NETWORK_CONFIG"), 4: .same(proto: "DISPLAY_CONFIG"), 5: .same(proto: "LORA_CONFIG"), - 6: .same(proto: "BLUETOOTH_CONFIG") + 6: .same(proto: "BLUETOOTH_CONFIG"), ] } @@ -1329,7 +1329,7 @@ extension AdminMessage.ModuleConfigType: SwiftProtobuf._ProtoNameProviding { 5: .same(proto: "TELEMETRY_CONFIG"), 6: .same(proto: "CANNEDMSG_CONFIG"), 7: .same(proto: "AUDIO_CONFIG"), - 8: .same(proto: "REMOTEHARDWARE_CONFIG") + 8: .same(proto: "REMOTEHARDWARE_CONFIG"), ] } @@ -1339,7 +1339,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa 1: .standard(proto: "call_sign"), 2: .standard(proto: "tx_power"), 3: .same(proto: "frequency"), - 4: .standard(proto: "short_name") + 4: .standard(proto: "short_name"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/apponly.pb.swift b/Meshtastic/Protobufs/meshtastic/apponly.pb.swift index 03d3695a..dfa98782 100644 --- a/Meshtastic/Protobufs/meshtastic/apponly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/apponly.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -50,7 +50,7 @@ struct ChannelSet { init() {} - fileprivate var _loraConfig: Config.LoRaConfig? + fileprivate var _loraConfig: Config.LoRaConfig? = nil } #if swift(>=5.5) && canImport(_Concurrency) @@ -59,13 +59,13 @@ extension ChannelSet: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".ChannelSet" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "settings"), - 2: .standard(proto: "lora_config") + 2: .standard(proto: "lora_config"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/cannedmessages.pb.swift b/Meshtastic/Protobufs/meshtastic/cannedmessages.pb.swift index 9b01a442..7d70da3b 100644 --- a/Meshtastic/Protobufs/meshtastic/cannedmessages.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/cannedmessages.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -42,12 +42,12 @@ extension CannedMessageModuleConfig: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".CannedMessageModuleConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "messages") + 1: .same(proto: "messages"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/channel.pb.swift b/Meshtastic/Protobufs/meshtastic/channel.pb.swift index 8daac7aa..f635dddd 100644 --- a/Meshtastic/Protobufs/meshtastic/channel.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/channel.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -183,7 +183,7 @@ struct Channel { init() {} - fileprivate var _settings: ChannelSettings? + fileprivate var _settings: ChannelSettings? = nil } #if swift(>=4.2) @@ -193,7 +193,7 @@ extension Channel.Role: CaseIterable { static var allCases: [Channel.Role] = [ .disabled, .primary, - .secondary + .secondary, ] } @@ -207,7 +207,7 @@ extension Channel.Role: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".ChannelSettings" @@ -217,7 +217,7 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen 3: .same(proto: "name"), 4: .same(proto: "id"), 5: .standard(proto: "uplink_enabled"), - 6: .standard(proto: "downlink_enabled") + 6: .standard(proto: "downlink_enabled"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -276,7 +276,7 @@ extension Channel: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "index"), 2: .same(proto: "settings"), - 3: .same(proto: "role") + 3: .same(proto: "role"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -323,6 +323,6 @@ extension Channel.Role: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "DISABLED"), 1: .same(proto: "PRIMARY"), - 2: .same(proto: "SECONDARY") + 2: .same(proto: "SECONDARY"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/config.pb.swift b/Meshtastic/Protobufs/meshtastic/config.pb.swift index 3707b384..04f17cdb 100644 --- a/Meshtastic/Protobufs/meshtastic/config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/config.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -27,7 +27,7 @@ struct Config { /// /// Payload Variant - var payloadVariant: Config.OneOf_PayloadVariant? + var payloadVariant: Config.OneOf_PayloadVariant? = nil var device: Config.DeviceConfig { get { @@ -632,7 +632,7 @@ struct Config { init() {} - fileprivate var _ipv4Config: Config.NetworkConfig.IpV4Config? + fileprivate var _ipv4Config: Config.NetworkConfig.IpV4Config? = nil } /// @@ -891,7 +891,7 @@ struct Config { // methods supported on all messages. /// - /// When enabled, the `modem_preset` fields will be adheared to, else the `bandwidth`/`spread_factor`/`coding_rate` + /// When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate` /// will be taked from their respective manually defined fields var usePreset: Bool = false @@ -940,14 +940,14 @@ struct Config { var txEnabled: Bool = false /// - /// If zero then, use default max legal continuous power (ie. something that won't + /// If zero, then use default max legal continuous power (ie. something that won't /// burn out the radio hardware) /// In most cases you should use zero here. /// Units are in dBm. var txPower: Int32 = 0 /// - /// This is controlling the actual hardware frequency the radio is transmitting on. + /// This controls the actual hardware frequency the radio transmits on. /// Most users should never need to be exposed to this field/concept. /// A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region). /// If ZERO then the rule is "use the old channel name hash based @@ -977,7 +977,7 @@ struct Config { /// /// For testing it is useful sometimes to force a node to never listen to /// particular other nodes (simulating radio out of range). All nodenums listed - /// in ignore_incoming will have packets they send droped on receive (by router.cpp) + /// in ignore_incoming will have packets they send dropped on receive (by router.cpp) var ignoreIncoming: [UInt32] = [] var unknownFields = SwiftProtobuf.UnknownStorage() @@ -998,7 +998,7 @@ struct Config { case eu433 // = 2 /// - /// European Union 433mhz + /// European Union 868mhz case eu868 // = 3 /// @@ -1190,7 +1190,7 @@ struct Config { var mode: Config.BluetoothConfig.PairingMode = .randomPin /// - /// Specified pin for PairingMode.FixedPin + /// Specified PIN for PairingMode.FixedPin var fixedPin: UInt32 = 0 var unknownFields = SwiftProtobuf.UnknownStorage() @@ -1199,15 +1199,15 @@ struct Config { typealias RawValue = Int /// - /// Device generates a random pin that will be shown on the screen of the device for pairing + /// Device generates a random PIN that will be shown on the screen of the device for pairing case randomPin // = 0 /// - /// Device requires a specified fixed pin for pairing + /// Device requires a specified fixed PIN for pairing case fixedPin // = 1 /// - /// Device requires no pin for pairing + /// Device requires no PIN for pairing case noPin // = 2 case UNRECOGNIZED(Int) @@ -1252,7 +1252,7 @@ extension Config.DeviceConfig.Role: CaseIterable { .routerClient, .repeater, .tracker, - .sensor + .sensor, ] } @@ -1261,7 +1261,7 @@ extension Config.DeviceConfig.RebroadcastMode: CaseIterable { static var allCases: [Config.DeviceConfig.RebroadcastMode] = [ .all, .allSkipDecoding, - .localOnly + .localOnly, ] } @@ -1278,7 +1278,7 @@ extension Config.PositionConfig.PositionFlags: CaseIterable { .seqNo, .timestamp, .heading, - .speed + .speed, ] } @@ -1286,7 +1286,7 @@ extension Config.NetworkConfig.AddressMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [Config.NetworkConfig.AddressMode] = [ .dhcp, - .static + .static, ] } @@ -1298,7 +1298,7 @@ extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable { .utm, .mgrs, .olc, - .osgr + .osgr, ] } @@ -1306,7 +1306,7 @@ extension Config.DisplayConfig.DisplayUnits: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [Config.DisplayConfig.DisplayUnits] = [ .metric, - .imperial + .imperial, ] } @@ -1316,7 +1316,7 @@ extension Config.DisplayConfig.OledType: CaseIterable { .oledAuto, .oledSsd1306, .oledSh1106, - .oledSh1107 + .oledSh1107, ] } @@ -1326,7 +1326,7 @@ extension Config.DisplayConfig.DisplayMode: CaseIterable { .default, .twocolor, .inverted, - .color + .color, ] } @@ -1348,7 +1348,7 @@ extension Config.LoRaConfig.RegionCode: CaseIterable { .th, .lora24, .ua433, - .ua868 + .ua868, ] } @@ -1362,7 +1362,7 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable { .mediumFast, .shortSlow, .shortFast, - .longModerate + .longModerate, ] } @@ -1371,7 +1371,7 @@ extension Config.BluetoothConfig.PairingMode: CaseIterable { static var allCases: [Config.BluetoothConfig.PairingMode] = [ .randomPin, .fixedPin, - .noPin + .noPin, ] } @@ -1403,7 +1403,7 @@ extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".Config" @@ -1414,7 +1414,7 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas 4: .same(proto: "network"), 5: .same(proto: "display"), 6: .same(proto: "lora"), - 7: .same(proto: "bluetooth") + 7: .same(proto: "bluetooth"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1574,7 +1574,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl 4: .standard(proto: "button_gpio"), 5: .standard(proto: "buzzer_gpio"), 6: .standard(proto: "rebroadcast_mode"), - 7: .standard(proto: "node_info_broadcast_secs") + 7: .standard(proto: "node_info_broadcast_secs"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1641,7 +1641,7 @@ extension Config.DeviceConfig.Role: SwiftProtobuf._ProtoNameProviding { 3: .same(proto: "ROUTER_CLIENT"), 4: .same(proto: "REPEATER"), 5: .same(proto: "TRACKER"), - 6: .same(proto: "SENSOR") + 6: .same(proto: "SENSOR"), ] } @@ -1649,7 +1649,7 @@ extension Config.DeviceConfig.RebroadcastMode: SwiftProtobuf._ProtoNameProviding static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "ALL"), 1: .same(proto: "ALL_SKIP_DECODING"), - 2: .same(proto: "LOCAL_ONLY") + 2: .same(proto: "LOCAL_ONLY"), ] } @@ -1664,7 +1664,7 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm 6: .standard(proto: "gps_attempt_time"), 7: .standard(proto: "position_flags"), 8: .standard(proto: "rx_gpio"), - 9: .standard(proto: "tx_gpio") + 9: .standard(proto: "tx_gpio"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1745,7 +1745,7 @@ extension Config.PositionConfig.PositionFlags: SwiftProtobuf._ProtoNameProviding 64: .same(proto: "SEQ_NO"), 128: .same(proto: "TIMESTAMP"), 256: .same(proto: "HEADING"), - 512: .same(proto: "SPEED") + 512: .same(proto: "SPEED"), ] } @@ -1759,7 +1759,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple 5: .standard(proto: "mesh_sds_timeout_secs"), 6: .standard(proto: "sds_secs"), 7: .standard(proto: "ls_secs"), - 8: .standard(proto: "min_wake_secs") + 8: .standard(proto: "min_wake_secs"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1833,7 +1833,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp 6: .standard(proto: "eth_enabled"), 7: .standard(proto: "address_mode"), 8: .standard(proto: "ipv4_config"), - 9: .standard(proto: "rsyslog_server") + 9: .standard(proto: "rsyslog_server"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1904,7 +1904,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp extension Config.NetworkConfig.AddressMode: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "DHCP"), - 1: .same(proto: "STATIC") + 1: .same(proto: "STATIC"), ] } @@ -1914,7 +1914,7 @@ extension Config.NetworkConfig.IpV4Config: SwiftProtobuf.Message, SwiftProtobuf. 1: .same(proto: "ip"), 2: .same(proto: "gateway"), 3: .same(proto: "subnet"), - 4: .same(proto: "dns") + 4: .same(proto: "dns"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1969,7 +1969,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp 6: .same(proto: "units"), 7: .same(proto: "oled"), 8: .same(proto: "displaymode"), - 9: .standard(proto: "heading_bold") + 9: .standard(proto: "heading_bold"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2045,14 +2045,14 @@ extension Config.DisplayConfig.GpsCoordinateFormat: SwiftProtobuf._ProtoNameProv 2: .same(proto: "UTM"), 3: .same(proto: "MGRS"), 4: .same(proto: "OLC"), - 5: .same(proto: "OSGR") + 5: .same(proto: "OSGR"), ] } extension Config.DisplayConfig.DisplayUnits: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "METRIC"), - 1: .same(proto: "IMPERIAL") + 1: .same(proto: "IMPERIAL"), ] } @@ -2061,7 +2061,7 @@ extension Config.DisplayConfig.OledType: SwiftProtobuf._ProtoNameProviding { 0: .same(proto: "OLED_AUTO"), 1: .same(proto: "OLED_SSD1306"), 2: .same(proto: "OLED_SH1106"), - 3: .same(proto: "OLED_SH1107") + 3: .same(proto: "OLED_SH1107"), ] } @@ -2070,7 +2070,7 @@ extension Config.DisplayConfig.DisplayMode: SwiftProtobuf._ProtoNameProviding { 0: .same(proto: "DEFAULT"), 1: .same(proto: "TWOCOLOR"), 2: .same(proto: "INVERTED"), - 3: .same(proto: "COLOR") + 3: .same(proto: "COLOR"), ] } @@ -2091,7 +2091,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem 12: .standard(proto: "override_duty_cycle"), 13: .standard(proto: "sx126x_rx_boosted_gain"), 14: .standard(proto: "override_frequency"), - 103: .standard(proto: "ignore_incoming") + 103: .standard(proto: "ignore_incoming"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2207,7 +2207,7 @@ extension Config.LoRaConfig.RegionCode: SwiftProtobuf._ProtoNameProviding { 12: .same(proto: "TH"), 13: .same(proto: "LORA_24"), 14: .same(proto: "UA_433"), - 15: .same(proto: "UA_868") + 15: .same(proto: "UA_868"), ] } @@ -2220,7 +2220,7 @@ extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding { 4: .same(proto: "MEDIUM_FAST"), 5: .same(proto: "SHORT_SLOW"), 6: .same(proto: "SHORT_FAST"), - 7: .same(proto: "LONG_MODERATE") + 7: .same(proto: "LONG_MODERATE"), ] } @@ -2229,7 +2229,7 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "enabled"), 2: .same(proto: "mode"), - 3: .standard(proto: "fixed_pin") + 3: .standard(proto: "fixed_pin"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2272,6 +2272,6 @@ extension Config.BluetoothConfig.PairingMode: SwiftProtobuf._ProtoNameProviding static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "RANDOM_PIN"), 1: .same(proto: "FIXED_PIN"), - 2: .same(proto: "NO_PIN") + 2: .same(proto: "NO_PIN"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/connection_status.pb.swift b/Meshtastic/Protobufs/meshtastic/connection_status.pb.swift index 1b36cd25..b305533f 100644 --- a/Meshtastic/Protobufs/meshtastic/connection_status.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/connection_status.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -73,10 +73,10 @@ struct DeviceConnectionStatus { init() {} - fileprivate var _wifi: WifiConnectionStatus? - fileprivate var _ethernet: EthernetConnectionStatus? - fileprivate var _bluetooth: BluetoothConnectionStatus? - fileprivate var _serial: SerialConnectionStatus? + fileprivate var _wifi: WifiConnectionStatus? = nil + fileprivate var _ethernet: EthernetConnectionStatus? = nil + fileprivate var _bluetooth: BluetoothConnectionStatus? = nil + fileprivate var _serial: SerialConnectionStatus? = nil } /// @@ -98,18 +98,18 @@ struct WifiConnectionStatus { mutating func clearStatus() {self._status = nil} /// - /// WiFi access point ssid + /// WiFi access point SSID var ssid: String = String() /// - /// Rssi of wireless connection + /// RSSI of wireless connection var rssi: Int32 = 0 var unknownFields = SwiftProtobuf.UnknownStorage() init() {} - fileprivate var _status: NetworkConnectionStatus? + fileprivate var _status: NetworkConnectionStatus? = nil } /// @@ -134,7 +134,7 @@ struct EthernetConnectionStatus { init() {} - fileprivate var _status: NetworkConnectionStatus? + fileprivate var _status: NetworkConnectionStatus? = nil } /// @@ -173,11 +173,11 @@ struct BluetoothConnectionStatus { // methods supported on all messages. /// - /// The pairing pin for bluetooth + /// The pairing PIN for bluetooth var pin: UInt32 = 0 /// - /// Rssi of bluetooth connection + /// RSSI of bluetooth connection var rssi: Int32 = 0 /// @@ -197,7 +197,7 @@ struct SerialConnectionStatus { // methods supported on all messages. /// - /// The serial baud rate + /// Serial baud rate var baud: UInt32 = 0 /// @@ -220,7 +220,7 @@ extension SerialConnectionStatus: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension DeviceConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".DeviceConnectionStatus" @@ -228,7 +228,7 @@ extension DeviceConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageI 1: .same(proto: "wifi"), 2: .same(proto: "ethernet"), 3: .same(proto: "bluetooth"), - 4: .same(proto: "serial") + 4: .same(proto: "serial"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -281,7 +281,7 @@ extension WifiConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "status"), 2: .same(proto: "ssid"), - 3: .same(proto: "rssi") + 3: .same(proto: "rssi"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -327,7 +327,7 @@ extension WifiConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp extension EthernetConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".EthernetConnectionStatus" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "status") + 1: .same(proto: "status"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -366,7 +366,7 @@ extension NetworkConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Message 1: .standard(proto: "ip_address"), 2: .standard(proto: "is_connected"), 3: .standard(proto: "is_mqtt_connected"), - 4: .standard(proto: "is_syslog_connected") + 4: .standard(proto: "is_syslog_connected"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -415,7 +415,7 @@ extension BluetoothConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Messa static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "pin"), 2: .same(proto: "rssi"), - 3: .standard(proto: "is_connected") + 3: .standard(proto: "is_connected"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -458,7 +458,7 @@ extension SerialConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageI static let protoMessageName: String = _protobuf_package + ".SerialConnectionStatus" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "baud"), - 2: .standard(proto: "is_connected") + 2: .standard(proto: "is_connected"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift index 1b526036..d0c52075 100644 --- a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -69,7 +69,7 @@ extension ScreenFonts: CaseIterable { static var allCases: [ScreenFonts] = [ .fontSmall, .fontMedium, - .fontLarge + .fontLarge, ] } @@ -153,7 +153,7 @@ struct DeviceState { } /// - /// Some GPSes seem to have bogus settings from the factory, so we always do one factory reset. + /// Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. var didGpsReset: Bool { get {return _storage._didGpsReset} set {_uniqueStorage()._didGpsReset = newValue} @@ -190,7 +190,7 @@ struct ChannelFile { /// /// This can be used for customizing the firmware distribution. If populated, -/// show a secondary bootup screen with cuatom logo and text for 2.5 seconds. +/// show a secondary bootup screen with custom logo and text for 2.5 seconds. struct OEMStore { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -205,7 +205,7 @@ struct OEMStore { var oemIconHeight: UInt32 = 0 /// - /// The Logo in xbm bytechar format + /// The Logo in XBM bytechar format var oemIconBits: Data = Data() /// @@ -246,8 +246,8 @@ struct OEMStore { init() {} - fileprivate var _oemLocalConfig: LocalConfig? - fileprivate var _oemLocalModuleConfig: LocalModuleConfig? + fileprivate var _oemLocalConfig: LocalConfig? = nil + fileprivate var _oemLocalModuleConfig: LocalModuleConfig? = nil } #if swift(>=5.5) && canImport(_Concurrency) @@ -259,13 +259,13 @@ extension OEMStore: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension ScreenFonts: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "FONT_SMALL"), 1: .same(proto: "FONT_MEDIUM"), - 2: .same(proto: "FONT_LARGE") + 2: .same(proto: "FONT_LARGE"), ] } @@ -279,16 +279,16 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 8: .same(proto: "version"), 7: .standard(proto: "rx_text_message"), 9: .standard(proto: "no_save"), - 11: .standard(proto: "did_gps_reset") + 11: .standard(proto: "did_gps_reset"), ] fileprivate class _StorageClass { - var _myNode: MyNodeInfo? - var _owner: User? + var _myNode: MyNodeInfo? = nil + var _owner: User? = nil var _nodeDb: [NodeInfo] = [] var _receiveQueue: [MeshPacket] = [] var _version: UInt32 = 0 - var _rxTextMessage: MeshPacket? + var _rxTextMessage: MeshPacket? = nil var _noSave: Bool = false var _didGpsReset: Bool = false @@ -397,7 +397,7 @@ extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati static let protoMessageName: String = _protobuf_package + ".ChannelFile" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "channels"), - 2: .same(proto: "version") + 2: .same(proto: "version"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -441,7 +441,7 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 5: .standard(proto: "oem_text"), 6: .standard(proto: "oem_aes_key"), 7: .standard(proto: "oem_local_config"), - 8: .standard(proto: "oem_local_module_config") + 8: .standard(proto: "oem_local_module_config"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/localonly.pb.swift b/Meshtastic/Protobufs/meshtastic/localonly.pb.swift index 97fa0b18..7f16ac02 100644 --- a/Meshtastic/Protobufs/meshtastic/localonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/localonly.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -245,7 +245,7 @@ extension LocalModuleConfig: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".LocalConfig" @@ -257,17 +257,17 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 5: .same(proto: "display"), 6: .same(proto: "lora"), 7: .same(proto: "bluetooth"), - 8: .same(proto: "version") + 8: .same(proto: "version"), ] fileprivate class _StorageClass { - var _device: Config.DeviceConfig? - var _position: Config.PositionConfig? - var _power: Config.PowerConfig? - var _network: Config.NetworkConfig? - var _display: Config.DisplayConfig? - var _lora: Config.LoRaConfig? - var _bluetooth: Config.BluetoothConfig? + var _device: Config.DeviceConfig? = nil + var _position: Config.PositionConfig? = nil + var _power: Config.PowerConfig? = nil + var _network: Config.NetworkConfig? = nil + var _display: Config.DisplayConfig? = nil + var _lora: Config.LoRaConfig? = nil + var _bluetooth: Config.BluetoothConfig? = nil var _version: UInt32 = 0 static let defaultInstance = _StorageClass() @@ -383,19 +383,19 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem 7: .standard(proto: "canned_message"), 9: .same(proto: "audio"), 10: .standard(proto: "remote_hardware"), - 8: .same(proto: "version") + 8: .same(proto: "version"), ] fileprivate class _StorageClass { - var _mqtt: ModuleConfig.MQTTConfig? - var _serial: ModuleConfig.SerialConfig? - var _externalNotification: ModuleConfig.ExternalNotificationConfig? - var _storeForward: ModuleConfig.StoreForwardConfig? - var _rangeTest: ModuleConfig.RangeTestConfig? - var _telemetry: ModuleConfig.TelemetryConfig? - var _cannedMessage: ModuleConfig.CannedMessageConfig? - var _audio: ModuleConfig.AudioConfig? - var _remoteHardware: ModuleConfig.RemoteHardwareConfig? + 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 + var _audio: ModuleConfig.AudioConfig? = nil + var _remoteHardware: ModuleConfig.RemoteHardwareConfig? = nil var _version: UInt32 = 0 static let defaultInstance = _StorageClass() diff --git a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift index 1602d428..7fa22878 100644 --- a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -302,7 +302,7 @@ extension HardwareModel: CaseIterable { .heltecWslV3, .betafpv2400Tx, .betafpv900NanoTx, - .privateHw + .privateHw, ] } @@ -353,7 +353,7 @@ extension Constants: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [Constants] = [ .zero, - .dataPayloadLen + .dataPayloadLen, ] } @@ -476,7 +476,7 @@ extension CriticalErrorCode: CaseIterable { .transmitFailed, .brownout, .sx1262Failure, - .radioSpiBug + .radioSpiBug, ] } @@ -781,7 +781,7 @@ extension Position.LocSource: CaseIterable { .locUnset, .locManual, .locInternal, - .locExternal + .locExternal, ] } @@ -792,7 +792,7 @@ extension Position.AltSource: CaseIterable { .altManual, .altInternal, .altExternal, - .altBarometric + .altBarometric, ] } @@ -886,7 +886,7 @@ struct Routing { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var variant: Routing.OneOf_Variant? + var variant: Routing.OneOf_Variant? = nil /// /// A route request going from the requester @@ -1075,7 +1075,7 @@ extension Routing.Error: CaseIterable { .noResponse, .dutyCycleLimit, .badRequest, - .notAuthorized + .notAuthorized, ] } @@ -1503,7 +1503,7 @@ extension MeshPacket.Priority: CaseIterable { .default, .reliable, .ack, - .max + .max, ] } @@ -1512,7 +1512,7 @@ extension MeshPacket.Delayed: CaseIterable { static var allCases: [MeshPacket.Delayed] = [ .noDelay, .broadcast, - .direct + .direct, ] } @@ -1591,9 +1591,9 @@ struct NodeInfo { init() {} - fileprivate var _user: User? - fileprivate var _position: Position? - fileprivate var _deviceMetrics: DeviceMetrics? + fileprivate var _user: User? = nil + fileprivate var _position: Position? = nil + fileprivate var _deviceMetrics: DeviceMetrics? = nil } /// @@ -1796,7 +1796,7 @@ extension LogRecord.Level: CaseIterable { .warning, .info, .debug, - .trace + .trace, ] } @@ -2102,7 +2102,7 @@ struct ToRadio { /// /// Log levels, chosen to match python logging conventions. - var payloadVariant: ToRadio.OneOf_PayloadVariant? + var payloadVariant: ToRadio.OneOf_PayloadVariant? = nil /// /// Send this packet on the mesh @@ -2308,7 +2308,7 @@ extension DeviceMetadata: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension HardwareModel: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -2346,14 +2346,14 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 44: .same(proto: "HELTEC_WSL_V3"), 45: .same(proto: "BETAFPV_2400_TX"), 46: .same(proto: "BETAFPV_900_NANO_TX"), - 255: .same(proto: "PRIVATE_HW") + 255: .same(proto: "PRIVATE_HW"), ] } extension Constants: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "ZERO"), - 237: .same(proto: "DATA_PAYLOAD_LEN") + 237: .same(proto: "DATA_PAYLOAD_LEN"), ] } @@ -2370,7 +2370,7 @@ extension CriticalErrorCode: SwiftProtobuf._ProtoNameProviding { 8: .same(proto: "TRANSMIT_FAILED"), 9: .same(proto: "BROWNOUT"), 10: .same(proto: "SX1262_FAILURE"), - 11: .same(proto: "RADIO_SPI_BUG") + 11: .same(proto: "RADIO_SPI_BUG"), ] } @@ -2398,7 +2398,7 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 19: .standard(proto: "sats_in_view"), 20: .standard(proto: "sensor_id"), 21: .standard(proto: "next_update"), - 22: .standard(proto: "seq_number") + 22: .standard(proto: "seq_number"), ] fileprivate class _StorageClass { @@ -2611,7 +2611,7 @@ extension Position.LocSource: SwiftProtobuf._ProtoNameProviding { 0: .same(proto: "LOC_UNSET"), 1: .same(proto: "LOC_MANUAL"), 2: .same(proto: "LOC_INTERNAL"), - 3: .same(proto: "LOC_EXTERNAL") + 3: .same(proto: "LOC_EXTERNAL"), ] } @@ -2621,7 +2621,7 @@ extension Position.AltSource: SwiftProtobuf._ProtoNameProviding { 1: .same(proto: "ALT_MANUAL"), 2: .same(proto: "ALT_INTERNAL"), 3: .same(proto: "ALT_EXTERNAL"), - 4: .same(proto: "ALT_BAROMETRIC") + 4: .same(proto: "ALT_BAROMETRIC"), ] } @@ -2633,7 +2633,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, 3: .standard(proto: "short_name"), 4: .same(proto: "macaddr"), 5: .standard(proto: "hw_model"), - 6: .standard(proto: "is_licensed") + 6: .standard(proto: "is_licensed"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2690,7 +2690,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, extension RouteDiscovery: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".RouteDiscovery" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "route") + 1: .same(proto: "route"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2724,7 +2724,7 @@ extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .standard(proto: "route_request"), 2: .standard(proto: "route_reply"), - 3: .standard(proto: "error_reason") + 3: .standard(proto: "error_reason"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2815,7 +2815,7 @@ extension Routing.Error: SwiftProtobuf._ProtoNameProviding { 8: .same(proto: "NO_RESPONSE"), 9: .same(proto: "DUTY_CYCLE_LIMIT"), 32: .same(proto: "BAD_REQUEST"), - 33: .same(proto: "NOT_AUTHORIZED") + 33: .same(proto: "NOT_AUTHORIZED"), ] } @@ -2829,7 +2829,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 5: .same(proto: "source"), 6: .standard(proto: "request_id"), 7: .standard(proto: "reply_id"), - 8: .same(proto: "emoji") + 8: .same(proto: "emoji"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2903,7 +2903,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 5: .standard(proto: "locked_to"), 6: .same(proto: "name"), 7: .same(proto: "description"), - 8: .same(proto: "icon") + 8: .same(proto: "icon"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2982,7 +2982,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio 10: .standard(proto: "want_ack"), 11: .same(proto: "priority"), 12: .standard(proto: "rx_rssi"), - 13: .same(proto: "delayed") + 13: .same(proto: "delayed"), ] fileprivate class _StorageClass { @@ -3160,7 +3160,7 @@ extension MeshPacket.Priority: SwiftProtobuf._ProtoNameProviding { 64: .same(proto: "DEFAULT"), 70: .same(proto: "RELIABLE"), 120: .same(proto: "ACK"), - 127: .same(proto: "MAX") + 127: .same(proto: "MAX"), ] } @@ -3168,7 +3168,7 @@ extension MeshPacket.Delayed: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "NO_DELAY"), 1: .same(proto: "DELAYED_BROADCAST"), - 2: .same(proto: "DELAYED_DIRECT") + 2: .same(proto: "DELAYED_DIRECT"), ] } @@ -3180,7 +3180,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 3: .same(proto: "position"), 4: .same(proto: "snr"), 5: .standard(proto: "last_heard"), - 6: .standard(proto: "device_metrics") + 6: .standard(proto: "device_metrics"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -3256,7 +3256,7 @@ extension MyNodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio 13: .standard(proto: "air_period_rx"), 14: .standard(proto: "has_wifi"), 15: .standard(proto: "channel_utilization"), - 16: .standard(proto: "air_util_tx") + 16: .standard(proto: "air_util_tx"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -3366,7 +3366,7 @@ extension LogRecord: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation 1: .same(proto: "message"), 2: .same(proto: "time"), 3: .same(proto: "source"), - 4: .same(proto: "level") + 4: .same(proto: "level"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -3418,7 +3418,7 @@ extension LogRecord.Level: SwiftProtobuf._ProtoNameProviding { 20: .same(proto: "INFO"), 30: .same(proto: "WARNING"), 40: .same(proto: "ERROR"), - 50: .same(proto: "CRITICAL") + 50: .same(proto: "CRITICAL"), ] } @@ -3428,7 +3428,7 @@ extension QueueStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 1: .same(proto: "res"), 2: .same(proto: "free"), 3: .same(proto: "maxlen"), - 4: .standard(proto: "mesh_packet_id") + 4: .standard(proto: "mesh_packet_id"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -3487,7 +3487,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation 10: .same(proto: "channel"), 11: .same(proto: "queueStatus"), 12: .same(proto: "xmodemPacket"), - 13: .same(proto: "metadata") + 13: .same(proto: "metadata"), ] fileprivate class _StorageClass { @@ -3758,7 +3758,7 @@ extension ToRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa 1: .same(proto: "packet"), 3: .standard(proto: "want_config_id"), 4: .same(proto: "disconnect"), - 5: .same(proto: "xmodemPacket") + 5: .same(proto: "xmodemPacket"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -3852,7 +3852,7 @@ extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio static let protoMessageName: String = _protobuf_package + ".Compressed" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "portnum"), - 2: .same(proto: "data") + 2: .same(proto: "data"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -3897,7 +3897,7 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement 6: .same(proto: "hasEthernet"), 7: .same(proto: "role"), 8: .standard(proto: "position_flags"), - 9: .standard(proto: "hw_model") + 9: .standard(proto: "hw_model"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift index 456a18ca..914b5f9f 100644 --- a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -29,7 +29,7 @@ struct ModuleConfig { /// /// TODO: REPLACE - var payloadVariant: ModuleConfig.OneOf_PayloadVariant? + var payloadVariant: ModuleConfig.OneOf_PayloadVariant? = nil /// /// TODO: REPLACE @@ -829,7 +829,7 @@ extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable { .codec21300, .codec21200, .codec2700, - .codec2700B + .codec2700B, ] } @@ -851,7 +851,7 @@ extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable { .baud230400, .baud460800, .baud576000, - .baud921600 + .baud921600, ] } @@ -862,7 +862,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable { .simple, .proto, .textmsg, - .nmea + .nmea, ] } @@ -876,7 +876,7 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable { .right, .select, .back, - .cancel + .cancel, ] } @@ -902,7 +902,7 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable { // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".ModuleConfig" @@ -915,7 +915,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 6: .same(proto: "telemetry"), 7: .standard(proto: "canned_message"), 8: .same(proto: "audio"), - 9: .standard(proto: "remote_hardware") + 9: .standard(proto: "remote_hardware"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1108,7 +1108,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message 3: .same(proto: "username"), 4: .same(proto: "password"), 5: .standard(proto: "encryption_enabled"), - 6: .standard(proto: "json_enabled") + 6: .standard(proto: "json_enabled"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1165,7 +1165,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = ModuleConfig.protoMessageName + ".RemoteHardwareConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled") + 1: .same(proto: "enabled"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1203,7 +1203,7 @@ extension ModuleConfig.AudioConfig: SwiftProtobuf.Message, SwiftProtobuf._Messag 4: .standard(proto: "i2s_ws"), 5: .standard(proto: "i2s_sd"), 6: .standard(proto: "i2s_din"), - 7: .standard(proto: "i2s_sck") + 7: .standard(proto: "i2s_sck"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1272,7 +1272,7 @@ extension ModuleConfig.AudioConfig.Audio_Baud: SwiftProtobuf._ProtoNameProviding 5: .same(proto: "CODEC2_1300"), 6: .same(proto: "CODEC2_1200"), 7: .same(proto: "CODEC2_700"), - 8: .same(proto: "CODEC2_700B") + 8: .same(proto: "CODEC2_700B"), ] } @@ -1285,7 +1285,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa 4: .same(proto: "txd"), 5: .same(proto: "baud"), 6: .same(proto: "timeout"), - 7: .same(proto: "mode") + 7: .same(proto: "mode"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1361,7 +1361,7 @@ extension ModuleConfig.SerialConfig.Serial_Baud: SwiftProtobuf._ProtoNameProvidi 12: .same(proto: "BAUD_230400"), 13: .same(proto: "BAUD_460800"), 14: .same(proto: "BAUD_576000"), - 15: .same(proto: "BAUD_921600") + 15: .same(proto: "BAUD_921600"), ] } @@ -1371,7 +1371,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProvidi 1: .same(proto: "SIMPLE"), 2: .same(proto: "PROTO"), 3: .same(proto: "TEXTMSG"), - 4: .same(proto: "NMEA") + 4: .same(proto: "NMEA"), ] } @@ -1391,7 +1391,7 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP 12: .standard(proto: "alert_bell_vibra"), 13: .standard(proto: "alert_bell_buzzer"), 7: .standard(proto: "use_pwm"), - 14: .standard(proto: "nag_timeout") + 14: .standard(proto: "nag_timeout"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1492,7 +1492,7 @@ extension ModuleConfig.StoreForwardConfig: SwiftProtobuf.Message, SwiftProtobuf. 2: .same(proto: "heartbeat"), 3: .same(proto: "records"), 4: .standard(proto: "history_return_max"), - 5: .standard(proto: "history_return_window") + 5: .standard(proto: "history_return_window"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1546,7 +1546,7 @@ extension ModuleConfig.RangeTestConfig: SwiftProtobuf.Message, SwiftProtobuf._Me static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "enabled"), 2: .same(proto: "sender"), - 3: .same(proto: "save") + 3: .same(proto: "save"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1594,7 +1594,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me 4: .standard(proto: "environment_screen_enabled"), 5: .standard(proto: "environment_display_fahrenheit"), 6: .standard(proto: "air_quality_enabled"), - 7: .standard(proto: "air_quality_interval") + 7: .standard(proto: "air_quality_interval"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1666,7 +1666,7 @@ extension ModuleConfig.CannedMessageConfig: SwiftProtobuf.Message, SwiftProtobuf 8: .standard(proto: "updown1_enabled"), 9: .same(proto: "enabled"), 10: .standard(proto: "allow_input_source"), - 11: .standard(proto: "send_bell") + 11: .standard(proto: "send_bell"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1754,6 +1754,6 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: SwiftProtobuf._ProtoN 19: .same(proto: "LEFT"), 20: .same(proto: "RIGHT"), 24: .same(proto: "CANCEL"), - 27: .same(proto: "BACK") + 27: .same(proto: "BACK"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/mqtt.pb.swift b/Meshtastic/Protobufs/meshtastic/mqtt.pb.swift index de297a69..73fe4c30 100644 --- a/Meshtastic/Protobufs/meshtastic/mqtt.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/mqtt.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -52,7 +52,7 @@ struct ServiceEnvelope { init() {} - fileprivate var _packet: MeshPacket? + fileprivate var _packet: MeshPacket? = nil } #if swift(>=5.5) && canImport(_Concurrency) @@ -61,14 +61,14 @@ extension ServiceEnvelope: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension ServiceEnvelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".ServiceEnvelope" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "packet"), 2: .standard(proto: "channel_id"), - 3: .standard(proto: "gateway_id") + 3: .standard(proto: "gateway_id"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift index 5bd3c705..9b7198a5 100644 --- a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -238,7 +238,7 @@ extension PortNum: CaseIterable { .tracerouteApp, .privateApp, .atakForwarder, - .max + .max, ] } @@ -273,6 +273,6 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding { 70: .same(proto: "TRACEROUTE_APP"), 256: .same(proto: "PRIVATE_APP"), 257: .same(proto: "ATAK_FORWARDER"), - 511: .same(proto: "MAX") + 511: .same(proto: "MAX"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift b/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift index 2b257297..1f24d0db 100644 --- a/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -125,7 +125,7 @@ extension HardwareMessage.TypeEnum: CaseIterable { .watchGpios, .gpiosChanged, .readGpios, - .readGpiosReply + .readGpiosReply, ] } @@ -138,14 +138,14 @@ extension HardwareMessage.TypeEnum: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension HardwareMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".HardwareMessage" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "type"), 2: .standard(proto: "gpio_mask"), - 3: .standard(proto: "gpio_value") + 3: .standard(proto: "gpio_value"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -191,6 +191,6 @@ extension HardwareMessage.TypeEnum: SwiftProtobuf._ProtoNameProviding { 2: .same(proto: "WATCH_GPIOS"), 3: .same(proto: "GPIOS_CHANGED"), 4: .same(proto: "READ_GPIOS"), - 5: .same(proto: "READ_GPIOS_REPLY") + 5: .same(proto: "READ_GPIOS_REPLY"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/rtttl.pb.swift b/Meshtastic/Protobufs/meshtastic/rtttl.pb.swift index 2780c6ad..3adac8a2 100644 --- a/Meshtastic/Protobufs/meshtastic/rtttl.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/rtttl.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -42,12 +42,12 @@ extension RTTTLConfig: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension RTTTLConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".RTTTLConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "ringtone") + 1: .same(proto: "ringtone"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift b/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift index d467b249..925eb558 100644 --- a/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -33,7 +33,7 @@ struct StoreAndForward { /// /// TODO: REPLACE - var variant: StoreAndForward.OneOf_Variant? + var variant: StoreAndForward.OneOf_Variant? = nil /// /// TODO: REPLACE @@ -345,7 +345,7 @@ extension StoreAndForward.RequestResponse: CaseIterable { .clientStats, .clientPing, .clientPong, - .clientAbort + .clientAbort, ] } @@ -362,7 +362,7 @@ extension StoreAndForward.Heartbeat: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension StoreAndForward: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".StoreAndForward" @@ -371,7 +371,7 @@ extension StoreAndForward: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen 2: .same(proto: "stats"), 3: .same(proto: "history"), 4: .same(proto: "heartbeat"), - 5: .same(proto: "empty") + 5: .same(proto: "empty"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -486,7 +486,7 @@ extension StoreAndForward.RequestResponse: SwiftProtobuf._ProtoNameProviding { 66: .same(proto: "CLIENT_STATS"), 67: .same(proto: "CLIENT_PING"), 68: .same(proto: "CLIENT_PONG"), - 106: .same(proto: "CLIENT_ABORT") + 106: .same(proto: "CLIENT_ABORT"), ] } @@ -501,7 +501,7 @@ extension StoreAndForward.Statistics: SwiftProtobuf.Message, SwiftProtobuf._Mess 6: .standard(proto: "requests_history"), 7: .same(proto: "heartbeat"), 8: .standard(proto: "return_max"), - 9: .standard(proto: "return_window") + 9: .standard(proto: "return_window"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -575,7 +575,7 @@ extension StoreAndForward.History: SwiftProtobuf.Message, SwiftProtobuf._Message static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .standard(proto: "history_messages"), 2: .same(proto: "window"), - 3: .standard(proto: "last_request") + 3: .standard(proto: "last_request"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -618,7 +618,7 @@ extension StoreAndForward.Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._Messa static let protoMessageName: String = StoreAndForward.protoMessageName + ".Heartbeat" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "period"), - 2: .same(proto: "secondary") + 2: .same(proto: "secondary"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift b/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift index 01fa9e2e..f41b4b80 100644 --- a/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -146,7 +146,7 @@ extension TelemetrySensorType: CaseIterable { .qmi8658, .qmc5883L, .sht31, - .pmsa003I + .pmsa003I, ] } @@ -160,7 +160,7 @@ struct DeviceMetrics { // methods supported on all messages. /// - /// 1-100 (0 means powered) + /// 0-100 (>100 means powered) var batteryLevel: UInt32 = 0 /// @@ -291,7 +291,7 @@ struct Telemetry { /// seconds since 1970 var time: UInt32 = 0 - var variant: Telemetry.OneOf_Variant? + var variant: Telemetry.OneOf_Variant? = nil /// /// Key native device metrics such as battery level @@ -374,7 +374,7 @@ extension Telemetry.OneOf_Variant: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -391,7 +391,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { 10: .same(proto: "QMI8658"), 11: .same(proto: "QMC5883L"), 12: .same(proto: "SHT31"), - 13: .same(proto: "PMSA003I") + 13: .same(proto: "PMSA003I"), ] } @@ -401,7 +401,7 @@ extension DeviceMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa 1: .standard(proto: "battery_level"), 2: .same(proto: "voltage"), 3: .standard(proto: "channel_utilization"), - 4: .standard(proto: "air_util_tx") + 4: .standard(proto: "air_util_tx"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -453,7 +453,7 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple 3: .standard(proto: "barometric_pressure"), 4: .standard(proto: "gas_resistance"), 5: .same(proto: "voltage"), - 6: .same(proto: "current") + 6: .same(proto: "current"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -521,7 +521,7 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem 9: .standard(proto: "particles_10um"), 10: .standard(proto: "particles_25um"), 11: .standard(proto: "particles_50um"), - 12: .standard(proto: "particles_100um") + 12: .standard(proto: "particles_100um"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -611,7 +611,7 @@ extension Telemetry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation 1: .same(proto: "time"), 2: .standard(proto: "device_metrics"), 3: .standard(proto: "environment_metrics"), - 4: .standard(proto: "air_quality_metrics") + 4: .standard(proto: "air_quality_metrics"), ] mutating func decodeMessage(decoder: inout D) throws { diff --git a/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift b/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift index 15d977b1..a70841f2 100644 --- a/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift @@ -15,7 +15,7 @@ import SwiftProtobuf // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. -private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} typealias Version = _2 } @@ -96,7 +96,7 @@ extension XModem.Control: CaseIterable { .ack, .nak, .can, - .ctrlz + .ctrlz, ] } @@ -109,7 +109,7 @@ extension XModem.Control: @unchecked Sendable {} // MARK: - Code below here is support for the SwiftProtobuf runtime. -private let _protobuf_package = "meshtastic" +fileprivate let _protobuf_package = "meshtastic" extension XModem: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".XModem" @@ -117,7 +117,7 @@ extension XModem: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas 1: .same(proto: "control"), 2: .same(proto: "seq"), 3: .same(proto: "crc16"), - 4: .same(proto: "buffer") + 4: .same(proto: "buffer"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -170,6 +170,6 @@ extension XModem.Control: SwiftProtobuf._ProtoNameProviding { 6: .same(proto: "ACK"), 21: .same(proto: "NAK"), 24: .same(proto: "CAN"), - 26: .same(proto: "CTRLZ") + 26: .same(proto: "CTRLZ"), ] } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 9a2eac76..6fd4844c 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -162,9 +162,16 @@ struct Connect: View { Section(header: Text("available.radios").font(.title)) { ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.name > $1.name })) { peripheral in HStack { - Image(systemName: "circle.fill") - .imageScale(.large).foregroundColor(.gray) - .padding(.trailing) + if userSettings.preferredPeripheralId == peripheral.peripheral.identifier.uuidString { + Image(systemName: "star.fill") + .imageScale(.large).foregroundColor(.yellow) + .padding(.trailing) + } else { + Image(systemName: "circle.fill") + .imageScale(.large).foregroundColor(.gray) + .padding(.trailing) + } + Button(action: { if userSettings.preferredPeripheralId.count > 0 && peripheral.peripheral.identifier.uuidString != userSettings.preferredPeripheralId { presentingSwitchPreferredPeripheral = true @@ -173,7 +180,7 @@ struct Connect: View { self.bleManager.connectTo(peripheral: peripheral.peripheral) } }) { - Text(peripheral.name).font(.title3) + Text(peripheral.name).font(.callout) } Spacer() VStack { @@ -289,13 +296,13 @@ struct Connect: View { liveActivityStarted = true let timerSeconds = 60 - let mostRecent = node?.telemetries?.lastObject as! TelemetryEntity + let mostRecent = node?.telemetries?.lastObject as? TelemetryEntity let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName ?? "unknown") let future = Date(timeIntervalSinceNow: Double(timerSeconds)) - let initialContentState = MeshActivityAttributes.ContentState(timerRange: Date.now...future, connected: true, channelUtilization: mostRecent.channelUtilization, airtime: mostRecent.airUtilTx, batteryLevel: UInt32(mostRecent.batteryLevel)) + let initialContentState = MeshActivityAttributes.ContentState(timerRange: Date.now...future, connected: true, channelUtilization: mostRecent?.channelUtilization ?? 0.0, airtime: mostRecent?.airUtilTx ?? 0.0, batteryLevel: UInt32(mostRecent?.batteryLevel ?? 0)) let activityContent = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .minute, value: 2, to: Date())!) diff --git a/Meshtastic/Views/Helpers/BatteryGauge.swift b/Meshtastic/Views/Helpers/BatteryGauge.swift index c2756be5..a52de95d 100644 --- a/Meshtastic/Views/Helpers/BatteryGauge.swift +++ b/Meshtastic/Views/Helpers/BatteryGauge.swift @@ -10,12 +10,12 @@ import Charts struct BatteryGauge: View { @State var batteryLevel = 0.0 - private let minValue = 1.0 + private let minValue = 0.0 private let maxValue = 100.00 var body: some View { VStack { - if batteryLevel == 0.0 { + if batteryLevel > 100.0 { // Plugged in Image(systemName: "powerplug") .font(.largeTitle) @@ -24,7 +24,7 @@ struct BatteryGauge: View { } else { let gradient = Gradient(colors: [.red, .orange, .green]) Gauge(value: batteryLevel, in: minValue...maxValue) { - if batteryLevel > 1.0 && batteryLevel < 10 { + if batteryLevel >= 0.0 && batteryLevel < 10 { Label("Battery Level %", systemImage: "battery.0") } else if batteryLevel >= 10.0 && batteryLevel < 25.00 { Label("Battery Level %", systemImage: "battery.25") @@ -38,7 +38,11 @@ struct BatteryGauge: View { Label("Battery Level %", systemImage: "battery.100.bolt") } } currentValueLabel: { - Text(Int(batteryLevel), format: .percent) + if batteryLevel == 0.0 { + Text("< 1%") + } else { + Text(Int(batteryLevel), format: .percent) + } } .tint(gradient) .gaugeStyle(.accessoryCircular) @@ -57,6 +61,7 @@ struct BatteryGauge_Previews: PreviewProvider { BatteryGauge(batteryLevel: 74.0) BatteryGauge(batteryLevel: 99.0) BatteryGauge(batteryLevel: 100.0) + BatteryGauge(batteryLevel: 111.0) } } } diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index a71cc4e8..46a6d070 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -199,7 +199,7 @@ struct MapViewSwiftUI: UIViewRepresentable { annotationView.leftCalloutAccessoryView = leftIcon let subtitle = UILabel() subtitle.text = "Long Name: \(positionAnnotation.nodePosition?.user?.longName ?? "Unknown") \n" - subtitle.text! += "Latitude: \(String(format: "%.5f", positionAnnotation.coordinate.latitude)) \n" + subtitle.text? += "Latitude: \(String(format: "%.5f", positionAnnotation.coordinate.latitude)) \n" subtitle.text! += "Longitude: \(String(format: "%.5f", positionAnnotation.coordinate.longitude)) \n" let distanceFormatter = MKDistanceFormatter() subtitle.text! += "Altitude: \(distanceFormatter.string(fromDistance: Double(positionAnnotation.altitude))) \n" diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 87304f23..0abcb0b9 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -185,7 +185,11 @@ struct UserMessageList: View { let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) if currentUser && message.receivedACK { // Ack Received - Text("\(ackErrorVal?.display ?? "Empty Ack Error")").font(.caption2).foregroundColor(message.realACK ? .gray : .orange) + if message.realACK { + Text("\(ackErrorVal?.display ?? "Empty Ack Error")").font(.caption2).foregroundColor(.gray) + } else { + Text("Implicit ACK from Unknown Node").font(.caption2).foregroundColor(.orange) + } } else if currentUser && message.ackError == 0 { // Empty Error Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.orange) diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index 3456447b..98ba306b 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -23,7 +23,7 @@ struct DeviceMetricsLog: View { let data = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0 && time !=nil && time >= %@", oneDayAgo! as CVarArg)) ?? [] if data.count > 0 { GroupBox(label: Label("battery.level.trend", systemImage: "battery.100")) { - Chart(data.array as! [TelemetryEntity], id: \.self) { + Chart(data.array as? [TelemetryEntity] ?? [], id: \.self) { LineMark( x: .value("Hour", $0.time!.formattedDate(format: "ha")), y: .value("Value", $0.batteryLevel) @@ -40,7 +40,7 @@ struct DeviceMetricsLog: View { let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "") if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { // Add a table for mac and ipad - Table(node.telemetries!.reversed() as! [TelemetryEntity]) { + Table(node.telemetries?.reversed() as? [TelemetryEntity] ?? []) { TableColumn("battery.level") { dm in if dm.metricsType == 0 { @@ -102,7 +102,7 @@ struct DeviceMetricsLog: View { .font(.caption) .fontWeight(.bold) } - ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (dm: TelemetryEntity) in + ForEach(node.telemetries?.reversed() as? [TelemetryEntity] ?? [], id: \.self) { (dm: TelemetryEntity) in if dm.metricsType == 0 { GridRow { if dm.batteryLevel == 0 { diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 4758a95f..46a16dd8 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -33,17 +33,17 @@ struct EnvironmentMetricsLog: View { } TableColumn("Humidity") { em in if em.metricsType == 1 { - Text("\(String(format: "%.2f", em.relativeHumidity))") + Text("\(String(format: "%.2f", em.relativeHumidity))%") } } TableColumn("Barometric Pressure") { em in if em.metricsType == 1 { - Text("\(String(format: "%.2f", em.barometricPressure))") + Text("\(String(format: "%.2f", em.barometricPressure)) hPa") } } TableColumn("gas.resistance") { em in if em.metricsType == 1 { - Text("\(String(format: "%.2f", em.gasResistance))") + Text("\(String(format: "%.2f", em.gasResistance)) ohms") } } TableColumn("current") { em in @@ -90,7 +90,7 @@ struct EnvironmentMetricsLog: View { .font(.caption) .fontWeight(.bold) } - ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (em: TelemetryEntity) in + ForEach(node.telemetries?.reversed() as? [TelemetryEntity] ?? [], id: \.self) { (em: TelemetryEntity) in if em.metricsType == 1 { @@ -98,7 +98,7 @@ struct EnvironmentMetricsLog: View { Text(em.temperature.formattedTemperature()) .font(.caption) - Text("\(String(format: "%.2f", em.relativeHumidity))") + Text("\(String(format: "%.2f", em.relativeHumidity))%") .font(.caption) Text("\(String(format: "%.2f", em.barometricPressure))") .font(.caption) diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index d13bd286..d914ec74 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -56,7 +56,7 @@ struct NodeDetail: View { VStack { if node.positions?.count ?? 0 > 0 { ZStack { - let annotations = node.positions?.array as? [PositionEntity] ?? [] + let annotations = node.positions?.array as? [PositionEntity] ?? [] ZStack { MapViewSwiftUI(onLongPress: { coord in waypointCoordinate = coord @@ -104,7 +104,8 @@ struct NodeDetail: View { Text("Today's Weather Forecast") .font(.title) .padding() - NodeWeatherForecastView(location: CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) ) + let nodeLocation = node.positions?.lastObject as? PositionEntity + NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation?.nodeCoordinate!.latitude ?? LocationHelper.currentLocation.latitude, longitude: nodeLocation?.nodeCoordinate!.longitude ?? LocationHelper.currentLocation.longitude) ) .frame(height: 250) } #else @@ -112,8 +113,8 @@ struct NodeDetail: View { Text("Today's Weather Forecast") .font(.title) .padding() - NodeWeatherForecastView(location: CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) ) - .frame(height: 250) + let nodeLocation = node.positions?.lastObject as? PositionEntity + NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation?.nodeCoordinate!.latitude ?? LocationHelper.currentLocation.latitude, longitude: nodeLocation?.nodeCoordinate!.longitude ?? LocationHelper.currentLocation.longitude) ).frame(height: 250) .presentationDetents([.medium]) .presentationDragIndicator(.automatic) } @@ -177,13 +178,13 @@ struct NodeDetail: View { if node.telemetries?.count ?? 0 >= 1 { - let mostRecent = node.telemetries?.lastObject as! TelemetryEntity + let mostRecent = node.telemetries?.lastObject as? TelemetryEntity Divider() VStack(alignment: .center) { - BatteryGauge(batteryLevel: Double(mostRecent.batteryLevel)) - if mostRecent.voltage > 0 { + BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0)) + if mostRecent?.voltage ?? 0 > 0 { - Text(String(format: "%.2f", mostRecent.voltage) + " V") + Text(String(format: "%.2f", mostRecent?.voltage ?? 0.0) + " V") .font(.title) .foregroundColor(.gray) .fixedSize() @@ -286,13 +287,13 @@ struct NodeDetail: View { } if node.telemetries?.count ?? 0 >= 1 { - let mostRecent = node.telemetries?.lastObject as! TelemetryEntity + let mostRecent = node.telemetries?.lastObject as? TelemetryEntity Divider() VStack(alignment: .center) { - BatteryGauge(batteryLevel: Double(mostRecent.batteryLevel)) - if mostRecent.voltage > 0 { + BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0)) + if mostRecent?.voltage ?? 0 > 0 { - Text(String(format: "%.2f", mostRecent.voltage) + " V") + Text(String(format: "%.2f", mostRecent?.voltage ?? 0) + " V") .font(.callout) .foregroundColor(.gray) .fixedSize() @@ -389,12 +390,11 @@ struct NodeDetail: View { HStack { let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context) - if node.metadata?.canShutdown ?? false || hwModelString == "RAK4631" {// node.metadata?.hwModel ?? "UNSET" == "RAK4631" { + if node.metadata?.canShutdown ?? false { Button(action: { showingShutdownConfirm = true }) { - Label("Power Off", systemImage: "power") } .buttonStyle(.bordered) @@ -491,9 +491,9 @@ struct NodeDetail: View { if node.positions?.count ?? 0 > 0 { - let mostRecent = node.positions?.lastObject as! PositionEntity + let mostRecent = node.positions?.lastObject as? PositionEntity - let weather = try await WeatherService.shared.weather(for: mostRecent.nodeLocation!) + let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)) condition = weather.currentWeather.condition temperature = weather.currentWeather.temperature humidity = Int(weather.currentWeather.humidity * 100) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 59e42419..3a9d3de5 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -57,6 +57,10 @@ struct NodeMap: View { ) @State private var overlays: [MapViewSwiftUI.Overlay] = [] +// init() { +// _positions = FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)], predicate: NSPredicate(format: "time >= %@ && nodePosition != nil", Calendar.current.startOfDay(for: Date()) as NSDate), animation: .none) +// } + var body: some View { NavigationStack { diff --git a/Meshtastic/Views/Nodes/PositionLog.swift b/Meshtastic/Views/Nodes/PositionLog.swift index d27997ba..dcb2e262 100644 --- a/Meshtastic/Views/Nodes/PositionLog.swift +++ b/Meshtastic/Views/Nodes/PositionLog.swift @@ -26,7 +26,7 @@ struct PositionLog: View { if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { // Add a table for mac and ipad - Table(node.positions!.reversed() as! [PositionEntity]) { + Table(node.positions?.reversed() as? [PositionEntity] ?? []) { TableColumn("SeqNo") { position in Text(String(position.seqNo)) } diff --git a/Meshtastic/Views/Settings/AdminMessageList.swift b/Meshtastic/Views/Settings/AdminMessageList.swift index 309c703b..dacd952e 100644 --- a/Meshtastic/Views/Settings/AdminMessageList.swift +++ b/Meshtastic/Views/Settings/AdminMessageList.swift @@ -41,14 +41,21 @@ struct AdminMessageList: View { let ackErrorVal = RoutingError(rawValue: Int(am.ackError)) if am.ackTimestamp > 0 { - Text(ackErrorVal?.display ?? "Empty Ack Error") - .foregroundColor(am.receivedACK ? .gray : .red) - .font(.caption2) + if am.realACK { + + Text(ackErrorVal?.display ?? "Empty Ack Error") + .foregroundColor(am.receivedACK ? .gray : .red) + .font(.caption2) + } else { + Text("Implicit ACK from Unknown Node") + .foregroundColor(.orange) + .font(.caption2) + } } - if am.receivedACK && am.ackTimestamp > 0 { + if am.receivedACK && am.ackTimestamp > 0 { Text(" \(Date(timeIntervalSince1970: TimeInterval(am.ackTimestamp)).formattedDate(format: "h:mm:ss a"))") - .foregroundColor(.gray) + .foregroundColor(am.realACK ? .gray : .orange) .font(.caption2) } } diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 6e2eba28..c28b580c 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -40,7 +40,7 @@ struct Channels: View { NavigationStack { List { if node != nil && node?.myInfo != nil { - ForEach(node!.myInfo!.channels?.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in + ForEach(node?.myInfo?.channels?.array as? [ChannelEntity] ?? [], id: \.self) { (channel: ChannelEntity) in Button(action: { channelIndex = channel.index channelRole = Int(channel.role) @@ -123,7 +123,6 @@ struct Channels: View { .disableAutocorrection(true) .keyboardType(.alphabet) .foregroundColor(Color.gray) - .disabled(channelRole == 1 && channelName.count > 0) .onChange(of: channelName, perform: { _ in channelName = channelName.replacing(" ", with: "") let totalBytes = channelName.utf8.count diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index b81cd053..3ec63ef3 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -26,6 +26,29 @@ struct BluetoothConfig: View { }() var body: some View { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.bluetoothConfig == nil { + Text("Bluetooth config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Toggle(isOn: $enabled) { Label("enabled", systemImage: "antenna.radiowaves.left.and.right") @@ -93,7 +116,7 @@ struct BluetoothConfig: View { bc.enabled = enabled bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin bc.fixedPin = UInt32(fixedPin) ?? 123456 - let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 50772f9b..1210cc99 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -31,7 +31,29 @@ struct DeviceConfig: View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.deviceConfig == nil { + Text("Device config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Picker("Device Role", selection: $deviceRole ) { @@ -183,7 +205,7 @@ struct DeviceConfig: View { dc.buttonGpio = UInt32(buttonGPIO) dc.buzzerGpio = UInt32(buzzerGPIO) - let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 8172b5f0..e45fe011 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -29,8 +29,30 @@ struct DisplayConfig: View { var body: some View { Form { - Section(header: Text("Device Screen")) { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.displayConfig == nil { + Text("Display config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } + Section(header: Text("Device Screen")) { Picker("Display Mode", selection: $displayMode ) { ForEach(DisplayModes.allCases) { dm in Text(dm.description) @@ -130,7 +152,7 @@ struct DisplayConfig: View { dc.oled = OledTypes(rawValue: oledType)!.protoEnumValue() dc.displaymode = DisplayModes(rawValue: displayMode)!.protoEnumValue() - let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -163,7 +185,7 @@ struct DisplayConfig: View { // Need to request a LoRaConfig from the remote node before allowing changes if bleManager.connectedPeripheral != nil && node?.displayConfig == nil { print("empty display config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) if node != nil && connectedNode != nil { _ = bleManager.requestDisplayConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) } diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 87b2f54a..751fb1ee 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -40,11 +40,35 @@ struct LoRaConfig: View { @State var bandwidth = 0 @State var spreadFactor = 0 @State var codingRate = 0 + @State var rxBoostedGain = false var body: some View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.loRaConfig == nil { + Text("LoRa config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("Options")) { Picker("Region", selection: $region ) { @@ -142,8 +166,12 @@ struct LoRaConfig: View { .scrollDismissesKeyboard(.immediately) .focused($focusedField, equals: .channelNum) } - Text("A hash of the primary channel's name sets the LoRa channel number, this determines the actual frequency you are transmitting on in the band. To ensure devices with different primary channel names transmit on the same frequency, you must explicitly set the LoRa channel number.") + Text("This determines the actual frequency you are transmitting on in the band.") .font(.caption) + Toggle(isOn: $rxBoostedGain) { + Label("RX Boosted Gain", systemImage: "waveform.badge.plus") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) @@ -166,7 +194,7 @@ struct LoRaConfig: View { let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) if connectedNode != nil { var lc = Config.LoRaConfig() lc.hopLimit = UInt32(hopLimit) @@ -178,7 +206,8 @@ struct LoRaConfig: View { lc.bandwidth = UInt32(bandwidth) lc.codingRate = UInt32(codingRate) lc.spreadFactor = UInt32(spreadFactor) - let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + lc.sx126XRxBoostedGain = rxBoostedGain + let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save @@ -209,8 +238,8 @@ struct LoRaConfig: View { self.bandwidth = Int(node?.loRaConfig?.bandwidth ?? 0) self.codingRate = Int(node?.loRaConfig?.codingRate ?? 0) self.spreadFactor = Int(node?.loRaConfig?.spreadFactor ?? 0) - print("Spreadum: \(self.spreadFactor)") - + self.rxBoostedGain = node?.loRaConfig?.sx126xRxBoostedGain ?? false + print(rxBoostedGain) self.hasChanges = false // Need to request a LoRaConfig from the remote node before allowing changes @@ -262,5 +291,10 @@ struct LoRaConfig: View { if newSpreadFactor != node!.loRaConfig!.spreadFactor { hasChanges = true } } } + .onChange(of: rxBoostedGain) { newRxBoostedGain in + if node != nil && node!.loRaConfig != nil { + if newRxBoostedGain != node!.loRaConfig!.sx126xRxBoostedGain { hasChanges = true } + } + } } } diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 0c0d805d..386683c8 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -39,6 +39,29 @@ struct CannedMessagesConfig: View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.cannedMessageConfig == nil { + Text("Canned messages config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Toggle(isOn: $enabled) { @@ -184,37 +207,40 @@ struct CannedMessagesConfig: View { let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context) if hasChanges { - var cmc = ModuleConfig.CannedMessageConfig() - cmc.enabled = enabled - cmc.sendBell = sendBell - cmc.rotary1Enabled = rotary1Enabled - cmc.updown1Enabled = updown1Enabled - if rotary1Enabled { - /// Input event origin accepted by the canned messages - /// Can be e.g. "rotEnc1", "upDownEnc1", "cardkb", or keyword "_any" - cmc.allowInputSource = "rotEnc1" - } else if updown1Enabled { - cmc.allowInputSource = "upDown1" - } else { - cmc.allowInputSource = "_any" - } - cmc.inputbrokerPinA = UInt32(inputbrokerPinA) - cmc.inputbrokerPinB = UInt32(inputbrokerPinB) - cmc.inputbrokerPinPress = UInt32(inputbrokerPinPress) - cmc.inputbrokerEventCw = InputEventChars(rawValue: inputbrokerEventCw)!.protoEnumValue() - cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue() - cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue() - let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) - if adminMessageId > 0 { - // Should show a saved successfully alert once I know that to be true - // for now just disable the button after a successful save - hasChanges = false - goBack() + if connectedNode != nil { + var cmc = ModuleConfig.CannedMessageConfig() + cmc.enabled = enabled + cmc.sendBell = sendBell + cmc.rotary1Enabled = rotary1Enabled + cmc.updown1Enabled = updown1Enabled + if rotary1Enabled { + /// Input event origin accepted by the canned messages + /// Can be e.g. "rotEnc1", "upDownEnc1", "cardkb", or keyword "_any" + cmc.allowInputSource = "rotEnc1" + } else if updown1Enabled { + cmc.allowInputSource = "upDown1" + } else { + cmc.allowInputSource = "_any" + } + cmc.inputbrokerPinA = UInt32(inputbrokerPinA) + cmc.inputbrokerPinB = UInt32(inputbrokerPinB) + cmc.inputbrokerPinPress = UInt32(inputbrokerPinPress) + cmc.inputbrokerEventCw = InputEventChars(rawValue: inputbrokerEventCw)!.protoEnumValue() + cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue() + cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue() + let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if adminMessageId > 0 { + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + goBack() + } } } if hasMessagesChanges { - let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messages, fromUser: node!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messages, fromUser: node!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index f31b3c74..2074bf0f 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -34,6 +34,29 @@ struct ExternalNotificationConfig: View { var body: some View { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.externalNotificationConfig == nil { + Text("External notification config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Toggle(isOn: $enabled) { Label("enabled", systemImage: "megaphone") diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 96b72da0..f264907e 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -24,6 +24,29 @@ struct MQTTConfig: View { var body: some View { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.mqttConfig == nil { + Text("MQTT config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Toggle(isOn: $enabled) { diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 0460bfc2..9d6ed6de 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -23,6 +23,29 @@ struct RangeTestConfig: View { var body: some View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.rangeTestConfig == nil { + Text("Range test config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Toggle(isOn: $enabled) { diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index 5d0f830c..c5d1e900 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -30,7 +30,29 @@ struct SerialConfig: View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.serialConfig == nil { + Text("Serial config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("options")) { Toggle(isOn: $enabled) { diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index feff320a..3d5910b7 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -26,6 +26,29 @@ struct TelemetryConfig: View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.telemetryConfig == nil { + Text("Telemetry config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("update.interval")) { Picker("Device Metrics", selection: $deviceUpdateInterval ) { ForEach(UpdateIntervals.allCases) { ui in @@ -61,7 +84,7 @@ struct TelemetryConfig: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } - .disabled(self.bleManager.connectedPeripheral == nil || node?.positionConfig == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.telemetryConfig == nil) Button { isPresentingSaveConfirm = true } label: { diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index c7e1f663..4c74afea 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -29,8 +29,30 @@ struct NetworkConfig: View { VStack { Form { - Section(header: Text("WiFi Options (ESP32 Only)")) { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.networkConfig == nil { + Text("Network config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } + Section(header: Text("WiFi Options (ESP32 Only)")) { Toggle(isOn: $wifiEnabled) { Label("enabled", systemImage: "wifi") } @@ -119,7 +141,7 @@ struct NetworkConfig: View { network.ethEnabled = self.ethEnabled // network.addressMode = Config.NetworkConfig.AddressMode.dhcp - let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index a080e1a2..b275f77d 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -72,6 +72,29 @@ struct PositionConfig: View { VStack { Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.positionConfig == nil { + Text("Position config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } Section(header: Text("Device GPS")) { Toggle(isOn: $deviceGpsEnabled) { Label("Device GPS Enabled", systemImage: "location") @@ -230,7 +253,7 @@ struct PositionConfig: View { if includeSpeed { pf.insert(.Speed) } if includeHeading { pf.insert(.Heading) } pc.positionFlags = UInt32(pf.rawValue) - let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift new file mode 100644 index 00000000..fa9965c4 --- /dev/null +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -0,0 +1,291 @@ +// +// Firmware.swift +// Meshtastic +// +// Created by Garth Vander Houwen on 3/10/23. +// + +// +// About.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 10/6/22. +// +import SwiftUI +import StoreKit + +struct Firmware: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + var node: NodeInfoEntity? + + @State var minimumVersion = "2.1.0" + @State var version = "" + + @State private var firmwareReleaseData: FirmwareRelease = FirmwareRelease() + + var body: some View { + // NavigationSplitView { + NavigationStack { + + let hwModel: HardwareModels = HardwareModels.allCases.first(where: { $0.rawValue == node?.user?.hwModel ?? "UNSET" }) ?? HardwareModels.UNSET + + VStack(alignment: .leading) { + Text("Current Version: \(bleManager.connectedVersion)") + .font(.largeTitle) + Text("Your device supports the following firmware: ") + .font(.callout) + HStack { + ForEach(hwModel.firmwareStrings, id: \.self) { fs in + Text(fs).font(.callout) + } + } + .padding(.bottom) + + if hwModel.platform() == HardwarePlatforms.NRF52 { + VStack(alignment: .leading) { + if hwModel == HardwareModels.RAK4631 { + Text("nRF OTA Device Firmware Update App") + .font(.title3) + Text("You can update your Meshtastic device over bluetooth using the Nordic DFU app. This currently works for RAK NRF devices.") + .font(.caption) + Link("Get NRF DFU from the App Store", destination: URL(string: "https://apps.apple.com/us/app/nrf-device-firmware-update/id1624454660")!) + .font(.callout) + } else { + Text("OTA Updates are not supported on the this NRF Device.") + .font(.title3) + Link("Drag & Drop Firmware Update", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/drag-n-drop")!) + .font(.callout) + } + } + } else if hwModel.platform() == HardwarePlatforms.ESP32 { + VStack(alignment: .leading) { + Text("ESP32 Device Firmware Update") + .font(.title3) + Text("Currently the reccomended way to update ESP32 devices is using the web flasher from a chrome based browser. It does not work on mobile devices or over BLE.") + .font(.caption) + Link("Web Flasher", destination: URL(string: "https://flasher.meshtastic.org")!) + .font(.callout) + .padding(.bottom) + Text("ESP 32 OTA update is a work in progress, click the button below to sent your device a reboot into ota admin message.") + .font(.caption) + HStack(alignment: .center) { + Spacer() + Button { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode != nil { + if !bleManager.sendRebootOta(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode!.myInfo!.adminIndex) { + print("Reboot Failed") + } else { + bleManager.disconnectPeripheral(reconnect: false) + } + } + } label: { + Label("Send Reboot OTA", systemImage: "square.and.arrow.down") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.regular) + .padding(5) + Spacer() + } + } + } else { + Text("OTA Updates are not supported on your platform.") + .font(.title3) + } + + }.padding() + VStack(alignment: .leading) { + Text("Firmware Releases") + .font(.title3) + .padding([.leading, .trailing]) + List { + Section(header: Text("Stable")) { + ForEach(firmwareReleaseData.releases?.stable ?? [], id: \.id) { fr in + Link(destination: URL(string: fr.zipUrl ?? "")!) { + HStack { + Text(fr.title ?? "Unknown") + .font(.caption) + Spacer() + Image(systemName: "square.and.arrow.down") + .font(.title3) + } + } + } + } + Section("Alpha") { + ForEach(firmwareReleaseData.releases?.alpha ?? [], id: \.id) { fr in + Link(destination: URL(string: fr.zipUrl ?? "")!) { + HStack { + Text(fr.title ?? "Unknown") + .font(.caption) + Spacer() + Image(systemName: "square.and.arrow.down") + .font(.title3) + } + } + } + } + Section("Pull Requests") { + ForEach(firmwareReleaseData.pullRequests ?? [], id: \.id) { fr in + Link(destination: URL(string: fr.zipUrl ?? "")!) { + HStack { + Text(fr.title ?? "Unknown") + .font(.caption) + Spacer() + Image(systemName: "square.and.arrow.down") + .font(.title3) + } + } + } + } + } + } + .padding(.bottom, 5) + .onAppear(perform: loadData) + .navigationTitle("Firmware Updates") + .navigationBarTitleDisplayMode(.inline) + } + } + + func loadData() { + + guard let url = URL(string: "https://api.meshtastic.org/github/firmware/list") else { + return + } + + let request = URLRequest(url: url) + URLSession.shared.dataTask(with: request) { data, _, _ in + + if let data = data { + if let response_obj = try? JSONDecoder().decode(FirmwareRelease.self, from: data) { + + DispatchQueue.main.async { + self.firmwareReleaseData = response_obj + } + } + } + + }.resume() + } +} + +struct FirmwareRelease: Codable { + + var releases: Releases? = Releases() + var pullRequests: [PullRequests]? = [] + + enum CodingKeys: String, CodingKey { + + case releases = "releases" + case pullRequests = "pullRequests" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + + releases = try values.decodeIfPresent(Releases.self, forKey: .releases ) + pullRequests = try values.decodeIfPresent([PullRequests].self, forKey: .pullRequests ) + } + + init() { + + } +} + +struct Releases: Codable { + + var stable: [Stable]? = [] + var alpha: [Alpha]? = [] + + enum CodingKeys: String, CodingKey { + case stable = "stable" + case alpha = "alpha" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + stable = try values.decodeIfPresent([Stable].self, forKey: .stable ) + alpha = try values.decodeIfPresent([Alpha].self, forKey: .alpha ) + } + + init() {} +} + +struct Alpha: Codable { + + var id: String? + var title: String? + var pageUrl: String? + var zipUrl: String? + + enum CodingKeys: String, CodingKey { + case id = "id" + case title = "title" + case pageUrl = "page_url" + case zipUrl = "zip_url" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + id = try values.decodeIfPresent(String.self, forKey: .id ) + title = try values.decodeIfPresent(String.self, forKey: .title ) + pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) + zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) + } + + init() {} +} + +struct Stable: Codable { + + var id: String? + var title: String? + var pageUrl: String? + var zipUrl: String? + + enum CodingKeys: String, CodingKey { + case id = "id" + case title = "title" + case pageUrl = "page_url" + case zipUrl = "zip_url" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + id = try values.decodeIfPresent(String.self, forKey: .id ) + title = try values.decodeIfPresent(String.self, forKey: .title ) + pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) + zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) + } + + init() {} +} + +struct PullRequests: Codable { + + var id: String? + var title: String? + var pageUrl: String? + var zipUrl: String? + + enum CodingKeys: String, CodingKey { + case id = "id" + case title = "title" + case pageUrl = "page_url" + case zipUrl = "zip_url" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + id = try values.decodeIfPresent(String.self, forKey: .id ) + title = try values.decodeIfPresent(String.self, forKey: .title ) + pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) + zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) + } + + init() {} +} diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index edcba954..6074fb0c 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -45,6 +45,15 @@ struct Settings: View { var body: some View { NavigationSplitView { List { + NavigationLink { + AboutMeshtastic() + } label: { + Image(systemName: "questionmark.app") + .symbolRenderingMode(.hierarchical) + + Text("about.meshtastic") + } + .tag(SettingsSidebar.about) NavigationLink { AppSettings() } label: { @@ -260,16 +269,17 @@ struct Settings: View { } .tag(SettingsSidebar.adminMessageLog) } - Section(header: Text("about")) { + Section(header: Text("Firmware")) { NavigationLink { - AboutMeshtastic() + Firmware(node: nodes.first(where: { $0.num == connectedNodeNum })) } label: { - Image(systemName: "questionmark.app") + Image(systemName: "arrow.up.arrow.down.square") .symbolRenderingMode(.hierarchical) - Text("about.meshtastic") + Text("Firmware Updates") } .tag(SettingsSidebar.about) + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) } } .onAppear { diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 9a9eda8f..44d9cbfc 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -68,7 +68,7 @@ struct ShareChannels: View { .font(.caption) .fontWeight(.bold) } - ForEach(node!.myInfo!.channels?.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in + ForEach(node?.myInfo?.channels?.array as? [ChannelEntity] ?? [], id: \.self) { (channel: ChannelEntity) in GridRow { Spacer() if channel.index == 0 { @@ -287,9 +287,10 @@ struct ShareChannels: View { loRaConfig.txPower = node?.loRaConfig?.txPower ?? 0 loRaConfig.usePreset = node?.loRaConfig?.usePreset ?? true loRaConfig.channelNum = UInt32(node?.loRaConfig?.channelNum ?? 0) + loRaConfig.sx126XRxBoostedGain = node?.loRaConfig?.sx126xRxBoostedGain ?? false channelSet.loraConfig = loRaConfig if node?.myInfo?.channels != nil && node?.myInfo?.channels?.count ?? 0 > 0 { - for ch in node!.myInfo!.channels!.array as! [ChannelEntity] { + for ch in node?.myInfo?.channels?.array as? [ChannelEntity] ?? [] { if ch.role > 0 { if ch.index == 0 && includeChannel0 || ch.index == 1 && includeChannel1 || ch.index == 2 && includeChannel2 || ch.index == 3 && includeChannel3 || diff --git a/Widgets/WidgetsLiveActivity.swift b/Widgets/WidgetsLiveActivity.swift index 3e079b0f..835ecbfa 100644 --- a/Widgets/WidgetsLiveActivity.swift +++ b/Widgets/WidgetsLiveActivity.swift @@ -100,8 +100,12 @@ struct LiveActivityView: View { var body: some View { HStack { Image(colorScheme == .light ? "logo-black" : "logo-white") + .resizable() .clipShape(ContainerRelativeShape()) .opacity(isLuminanceReduced ? 0.5 : 1.0) + .aspectRatio(contentMode: .fit) + .frame(width: 65) + Spacer() NodeInfoView(nodeName: nodeName, timerRange: timerRange, channelUtilization: channelUtilization, airtime: airtime, batteryLevel: batteryLevel) Spacer() TimerView(timerRange: timerRange) @@ -126,42 +130,41 @@ struct NodeInfoView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { Text(nodeName) - .font(.title3) + .font(nodeName.count > 14 ? .callout : .title3) .fontWeight(.semibold) .foregroundStyle(.tint) - .fixedSize() Text("\(String(format: "Ch. Util: %.2f", channelUtilization))%") .font(.subheadline) .fontWeight(.medium) .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.5 : 1.0) + .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() Text("\(String(format: "Airtime: %.2f", airtime))%") .font(.subheadline) .fontWeight(.medium) .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.5 : 1.0) + .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() if batteryLevel < 101 { Text("Battery Level: \(batteryLevel > 0 ? String(batteryLevel) : "< 1")%") .font(.subheadline) .fontWeight(.medium) .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.5 : 1.0) + .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() } else { Text("Plugged In") .font(.subheadline) .fontWeight(.medium) .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.5 : 1.0) + .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() } Text(Date().formatted()) .font(.subheadline) .fontWeight(.medium) .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.5 : 1.0) + .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() } }