From bff65194503057a0815b4499ac90480b75e3bc88 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 8 Mar 2023 16:57:55 -0800 Subject: [PATCH 01/21] Widget Fixes --- Widgets/WidgetsLiveActivity.swift | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) 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() } } From 377381cf169b3546029a47c9152bebe35dbc7ad1 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 8 Mar 2023 21:14:46 -0800 Subject: [PATCH 02/21] Rx boosted gain is the truth --- .../Views/Settings/Config/LoRaConfig.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 87b2f54a..1963eca1 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -40,6 +40,7 @@ struct LoRaConfig: View { @State var bandwidth = 0 @State var spreadFactor = 0 @State var codingRate = 0 + @State var rxBoostedGain = false var body: some View { @@ -142,8 +143,13 @@ 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) @@ -178,6 +184,7 @@ struct LoRaConfig: View { lc.bandwidth = UInt32(bandwidth) lc.codingRate = UInt32(codingRate) lc.spreadFactor = UInt32(spreadFactor) + lc.sx126XRxBoostedGain = rxBoostedGain let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.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 @@ -209,8 +216,7 @@ 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 self.hasChanges = false // Need to request a LoRaConfig from the remote node before allowing changes @@ -262,5 +268,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 } + } + } } } From 39fa4be49c9e1f07fde12d73182ec37bc522a2a0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 8 Mar 2023 21:15:11 -0800 Subject: [PATCH 03/21] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 58b7e8c2..0e9838e2 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1180,7 +1180,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 +1214,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; From ac7b04c344afd7b7da54dccf4402f7cce06386a7 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 8 Mar 2023 21:32:21 -0800 Subject: [PATCH 04/21] Fix a couple of things found by SwiftLint --- Meshtastic/Helpers/MeshPackets.swift | 12 +++++++++--- Meshtastic/Views/Settings/Config/LoRaConfig.swift | 1 - 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index c5c6a377..2775e0ab 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -342,7 +342,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 +358,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 } @@ -685,7 +689,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 diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 1963eca1..4fb11c4d 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -145,7 +145,6 @@ struct LoRaConfig: View { } 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") } From 2646f9a436b6d7ada3096c6074af48a91a36bf0f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 9 Mar 2023 14:23:15 -0600 Subject: [PATCH 05/21] Try clear channels --- Meshtastic/Helpers/BLEManager.swift | 43 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 3375e012..aa1b5dad 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -446,6 +446,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { connectedPeripheral.longName = myInfo?.bleName ?? NSLocalizedString("unknown", comment: "Unknown") } } + tryClearExistingChannels() } // NodeInfo if decodedInfo.nodeInfo.num > 0 && !invalidVersion { @@ -982,25 +983,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 { @@ -1891,6 +1874,28 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } return false } + + public func tryClearExistingChannels() { + //Before we get started delete the existing channels from the myNodeInfo + let fetchMyInfoRequest: NSFetchRequest = 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 From aef8e612e32161343e11cf9b77ab2b3635a3e155 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 10 Mar 2023 08:46:53 -0800 Subject: [PATCH 06/21] Update node list if a new node is discovered after connection Update battery level display --- Meshtastic/Enums/IntervalEnums.swift | 3 + Meshtastic/Helpers/MeshPackets.swift | 269 ++++++++++-------- Meshtastic/Views/Helpers/BatteryGauge.swift | 11 +- .../Views/Map/Custom/MapViewSwiftUI.swift | 3 +- Meshtastic/Views/Nodes/NodeMap.swift | 6 + 5 files changed, 168 insertions(+), 124 deletions(-) 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/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 2775e0ab..26124884 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -13,13 +13,13 @@ 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 +40,7 @@ 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) @@ -58,7 +58,7 @@ func localConfig (config: Config, context: NSManagedObjectContext, nodeNum: Int6 } func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { - + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) { upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum, context: context) } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) { @@ -75,20 +75,20 @@ func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNu } func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedObjectContext) -> MyInfoEntity? { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.myinfo %@", comment: "MyInfo received: %@"), String(myInfo.myNodeNum)) MeshLogger.log("ℹ️ \(logString)") - + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(myInfo.myNodeNum)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return nil } // Not Found Insert if fetchedMyInfo.isEmpty { - + let myInfoEntity = MyInfoEntity(context: context) myInfoEntity.peripheralId = peripheralId myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum) @@ -113,7 +113,7 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO print("πŸ’₯ Error Inserting New Core Data MyInfoEntity: \(nsError)") } } else { - + fetchedMyInfo[0].peripheralId = peripheralId fetchedMyInfo[0].myNodeNum = Int64(myInfo.myNodeNum) fetchedMyInfo[0].hasGps = myInfo.hasGps_p @@ -125,7 +125,7 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) fetchedMyInfo[0].minAppVersion = Int32(bitPattern: myInfo.minAppVersion) fetchedMyInfo[0].maxChannels = Int32(bitPattern: myInfo.maxChannels) - + do { try context.save() print("πŸ’Ύ Updated myInfo for node number: \(String(myInfo.myNodeNum))") @@ -143,15 +143,15 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO } func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectContext) { - + if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.channel.received %d %@", comment: "Channel %d received from: %@"), channel.index, String(fromNum)) MeshLogger.log("πŸŽ›οΈ \(logString)") - + let fetchedMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum) - + do { guard let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as? [MyInfoEntity] else { return @@ -195,14 +195,14 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo } func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NSManagedObjectContext) { - + if metadata.isInitialized { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.metadata.received %@", comment: "Device Metadata admin message received from: %@"), String(fromNum)) MeshLogger.log("🏷️ \(logString)") - + let fetchedNodeRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchedNodeRequest.predicate = NSPredicate(format: "num == %lld", fromNum) - + do { guard let fetchedNode = try context.fetch(fetchedNodeRequest) as? [NodeInfoEntity] else { return @@ -218,7 +218,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS newMetadata.role = Int32(metadata.role.rawValue) newMetadata.positionFlags = Int32(metadata.positionFlags) fetchedNode[0].metadata = newMetadata - + do { try context.save() } catch { @@ -235,27 +235,27 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS } func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(nodeInfo.num)) MeshLogger.log("πŸ“Ÿ \(logString)") - + guard nodeInfo.num > 0 else { return nil } - + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeInfo.num)) - + do { guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else { return nil } // Not Found Insert if fetchedNode.isEmpty && nodeInfo.hasUser { - + let newNode = NodeInfoEntity(context: context) newNode.id = Int64(nodeInfo.num) newNode.num = Int64(nodeInfo.num) newNode.channel = Int32(channel) - + if nodeInfo.hasDeviceMetrics { let telemetry = TelemetryEntity(context: context) telemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel) @@ -266,7 +266,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newTelemetries.append(telemetry) newNode.telemetries? = NSOrderedSet(array: newTelemetries) } - + newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) newNode.snr = nodeInfo.snr if nodeInfo.hasUser { @@ -279,7 +279,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() newNode.user = newUser } - + if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) { let position = PositionEntity(context: context) position.seqNo = Int32(nodeInfo.position.seqNumber) @@ -294,11 +294,11 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newPostions.append(position) newNode.positions? = NSOrderedSet(array: newPostions) } - + // Look for a MyInfo let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return nil @@ -318,15 +318,15 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje print("πŸ’₯ Fetch MyInfo Error") } } else if nodeInfo.hasUser && nodeInfo.num > 0 { - + fetchedNode[0].id = Int64(nodeInfo.num) fetchedNode[0].num = Int64(nodeInfo.num) fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) fetchedNode[0].snr = nodeInfo.snr fetchedNode[0].channel = Int32(channel) - + if nodeInfo.hasUser { - + fetchedNode[0].user!.userId = nodeInfo.user.id fetchedNode[0].user!.num = Int64(nodeInfo.num) fetchedNode[0].user!.longName = nodeInfo.user.longName @@ -334,9 +334,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].user!.macaddr = nodeInfo.user.macaddr fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() } - + if nodeInfo.hasDeviceMetrics { - + let newTelemetry = TelemetryEntity(context: context) newTelemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel) newTelemetry.voltage = nodeInfo.deviceMetrics.voltage @@ -347,11 +347,11 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet } - + if nodeInfo.hasPosition { - + if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) { - + let position = PositionEntity(context: context) position.latitudeI = nodeInfo.position.latitudeI position.longitudeI = nodeInfo.position.longitudeI @@ -363,13 +363,13 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet } - + } - + // Look for a MyInfo let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return nil @@ -397,26 +397,57 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } 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 { + + // Not Found Insert + if fetchedNode.count == 0 { + + 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 + } + + if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) { + let telemetry = TelemetryEntity(context: context) + telemetry.batteryLevel = Int32(telemetryMessage.deviceMetrics.batteryLevel) + telemetry.voltage = telemetryMessage.deviceMetrics.voltage + telemetry.channelUtilization = telemetryMessage.deviceMetrics.channelUtilization + telemetry.airUtilTx = telemetryMessage.deviceMetrics.airUtilTx + var newTelemetries = [TelemetryEntity]() + newTelemetries.append(telemetry) + newNode.telemetries? = NSOrderedSet(array: newTelemetries) + } + } else { 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) @@ -445,8 +476,6 @@ func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { 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") @@ -454,21 +483,21 @@ func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { - + if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) { - + if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getCannedMessageModuleMessagesResponse(adminMessage.getCannedMessageModuleMessagesResponse) { - + if let cmmc = try? CannedMessageModuleConfig(serializedData: packet.decoded.payload) { - + if !cmmc.messages.isEmpty { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessages.messages.received %@", comment: "Canned Messages Messages Received For: %@"), String(packet.from)) MeshLogger.log("πŸ₯« \(logString)") - + let fetchNodeRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) - + do { guard let fetchedNode = try context.fetch(fetchNodeRequest) as? [NodeInfoEntity] else { return @@ -495,65 +524,65 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getChannelResponse(adminMessage.getChannelResponse) { channelPacket(channel: adminMessage.getChannelResponse, fromNum: Int64(packet.from), context: context) - + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getDeviceMetadataResponse(adminMessage.getDeviceMetadataResponse) { deviceMetadataPacket(metadata: adminMessage.getDeviceMetadataResponse, fromNum: Int64(packet.from), context: context) - + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getConfigResponse(adminMessage.getConfigResponse) { - + let config = adminMessage.getConfigResponse - + if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), context: context) - + } } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getModuleConfigResponse(adminMessage.getModuleConfigResponse) { - + let moduleConfig = adminMessage.getModuleConfigResponse - + if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfig.cannedMessage) { upsertCannedMessagesModuleConfigPacket(config: moduleConfig.cannedMessage, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(moduleConfig.externalNotification) { upsertExternalNotificationModuleConfigPacket(config: moduleConfig.externalNotification, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(moduleConfig.mqtt) { upsertMqttModuleConfigPacket(config: moduleConfig.mqtt, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(moduleConfig.rangeTest) { upsertRangeTestModuleConfigPacket(config: moduleConfig.rangeTest, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(moduleConfig.serial) { upsertSerialModuleConfigPacket(config: moduleConfig.serial, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(moduleConfig.telemetry) { upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context) - + } - + } else { MeshLogger.log("πŸ•ΈοΈ MESH PACKET received for Admin App \(try! packet.decoded.jsonString())") } - + // Save an ack for the admin message log for each admin message response received as we stopped sending acks if there is also a response to reduce airtime. adminResponseAck(packet: packet, context: context) } } func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) { - + let fetchedAdminMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") fetchedAdminMessageRequest.predicate = NSPredicate(format: "messageId == %lld", packet.decoded.requestID) do { @@ -581,22 +610,22 @@ func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) { } func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSManagedObjectContext) { - + if let routingMessage = try? Routing(serializedData: packet.decoded.payload) { - + let routingError = RoutingError(rawValue: routingMessage.errorReason.rawValue) - + let routingErrorString = routingError?.display ?? NSLocalizedString("unknown", comment: "") let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.routing.message %@ %@", comment: "Routing received for RequestID: %@ Ack Status: %@"), String(packet.decoded.requestID), routingErrorString) MeshLogger.log("πŸ•ΈοΈ \(logString)") - + let fetchMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID)) - + do { let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity] if fetchedMessage?.count ?? 0 > 0 { - + if fetchedMessage![0].toUser != nil { // Real ACK from DM Recipient if packet.to != packet.from { @@ -604,14 +633,14 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana } } fetchedMessage![0].ackError = Int32(routingMessage.errorReason.rawValue) - + if routingMessage.errorReason == Routing.Error.none { - + fetchedMessage![0].receivedACK = true } fetchedMessage![0].ackSNR = packet.rxSnr fetchedMessage![0].ackTimestamp = Int32(packet.rxTime) - + if fetchedMessage![0].toUser != nil { fetchedMessage![0].toUser?.objectWillChange.send() } else { @@ -620,19 +649,19 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana do { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] if fetchedMyInfo?.count ?? 0 > 0 { - + for ch in fetchedMyInfo![0].channels!.array as! [ChannelEntity] { - + if ch.index == packet.channel { ch.objectWillChange.send() } } } } catch { - + } } - + } else { return } @@ -647,25 +676,25 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana } func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { - + if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) { - + // Only log telemetry from the mesh not the connected device if connectedNode != Int64(packet.from) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.received %@", comment: "Telemetry received for: %@"), String(packet.from)) MeshLogger.log("πŸ“ˆ \(logString)") } else { // If it is the connected node - + } - + let telemetry = TelemetryEntity(context: context) - + let fetchNodeTelemetryRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) - + do { - + guard let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) as? [NodeInfoEntity] else { return } @@ -722,15 +751,15 @@ 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())! let date = Date.now...oneMinuteLater let updatedMeshStatus = MeshActivityAttributes.MeshActivityStatus(timerRange: date, connected: true, channelUtilization: telemetry.channelUtilization, airtime: telemetry.airUtilTx, batteryLevel: UInt32(telemetry.batteryLevel)) let alertConfiguration = AlertConfiguration(title: "Mesh activity update", body: "Updated Device Metrics Data.", sound: .default) let updatedContent = ActivityContent(state: updatedMeshStatus, staleDate: nil) - + let meshActivity = Activity.activities.first(where: { $0.attributes.nodeNum == connectedNode }) if meshActivity != nil { Task { @@ -740,7 +769,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage } } } - #endif +#endif } } catch { context.rollback() @@ -753,11 +782,11 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage } func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { - + if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) { - + MeshLogger.log("πŸ’¬ \(NSLocalizedString("mesh.log.textmessage.received", comment: "Message received from the text message app"))") - + let messageUsers: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from]) do { @@ -771,11 +800,11 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM newMessage.snr = packet.rxSnr newMessage.isEmoji = packet.decoded.emoji == 1 newMessage.channel = Int32(packet.channel) - + if packet.decoded.replyID > 0 { newMessage.replyID = Int64(packet.decoded.replyID) } - + if fetchedUsers.first(where: { $0.num == packet.to }) != nil && packet.to != 4294967295 { newMessage.toUser = fetchedUsers.first(where: { $0.num == packet.to }) } @@ -784,20 +813,20 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } newMessage.messagePayload = messageText newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: messageText) - + newMessage.fromUser?.objectWillChange.send() newMessage.toUser?.objectWillChange.send() - + var messageSaved = false - + do { - + try context.save() print("πŸ’Ύ Saved a new message for \(newMessage.messageId)") messageSaved = true - + if messageSaved { - + if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) { // Create an iOS Notification for the received DM message and schedule it immediately let manager = LocalNotificationManager() @@ -811,10 +840,10 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM manager.schedule() print("πŸ’¬ iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))") } else if newMessage.fromUser != nil && newMessage.toUser == nil { - + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return @@ -823,7 +852,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM if channel.index == newMessage.channel { context.refresh(channel, mergeChanges: true) } - + if channel.index == newMessage.channel && !channel.mute { // Create an iOS Notification for the received private channel message and schedule it immediately let manager = LocalNotificationManager() @@ -839,7 +868,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } } } catch { - + } } } @@ -855,22 +884,22 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.received %@", comment: "Waypoint Packet received from node: %@"), String(packet.from)) MeshLogger.log("πŸ“ \(logString)") - + let fetchWaypointRequest: NSFetchRequest = NSFetchRequest.init(entityName: "WaypointEntity") fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id)) - + do { - + if let waypointMessage = try? Waypoint(serializedData: packet.decoded.payload) { guard let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as? [WaypointEntity] else { return } if fetchedWaypoint.isEmpty { let waypoint = WaypointEntity(context: context) - + waypoint.id = Int64(packet.id) waypoint.name = waypointMessage.name waypoint.longDescription = waypointMessage.description_p diff --git a/Meshtastic/Views/Helpers/BatteryGauge.swift b/Meshtastic/Views/Helpers/BatteryGauge.swift index c2756be5..83e04e5f 100644 --- a/Meshtastic/Views/Helpers/BatteryGauge.swift +++ b/Meshtastic/Views/Helpers/BatteryGauge.swift @@ -15,7 +15,7 @@ struct BatteryGauge: View { 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..89ea0c16 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -82,6 +82,7 @@ struct MapViewSwiftUI: UIViewRepresentable { #else #if os(iOS) + mapView.showsPointsOfInterest = false // Hide the default compass that only appears when you are not going north and instead always show the compass in the bottom right corner of the map mapView.showsCompass = false let compassButton = MKCompassButton(mapView: mapView) // Make a new compass @@ -199,7 +200,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/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 59e42419..8505b0fd 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -57,7 +57,13 @@ 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 { ZStack { From 7d478f71fd98b22416ef91dff8a8ad7f1f04d070 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 10 Mar 2023 19:41:26 -0800 Subject: [PATCH 07/21] * Use node location for weather not user * Firmware update view * Update battery gauge to handle new numbers from device --- Meshtastic.xcodeproj/project.pbxproj | 4 + Meshtastic/Helpers/BLEManager.swift | 23 +- Meshtastic/Helpers/MeshPackets.swift | 86 ------ Meshtastic/Persistence/UpdateCoreData.swift | 73 +++++ Meshtastic/Views/Helpers/BatteryGauge.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 8 +- Meshtastic/Views/Settings/Firmware.swift | 282 ++++++++++++++++++++ Meshtastic/Views/Settings/Settings.swift | 18 +- 8 files changed, 402 insertions(+), 94 deletions(-) create mode 100644 Meshtastic/Views/Settings/Firmware.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 0e9838e2..0a73ceda 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -110,6 +110,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 */; }; @@ -284,6 +285,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 +387,7 @@ DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */, DD4A911D2708C65400501B7E /* AppSettings.swift */, DDA0B6B1294CDC55001356EC /* Channels.swift */, + DDD6EEAE29BC024700383354 /* Firmware.swift */, DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */, DD3501882852FC3B000FC853 /* Settings.swift */, @@ -891,6 +894,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 */, diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index aa1b5dad..7180a887 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -503,7 +503,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 +873,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() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 26124884..60a2ac4b 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -396,92 +396,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] ?? [] - - // Not Found Insert - if fetchedNode.count == 0 { - - 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 - } - - if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) { - let telemetry = TelemetryEntity(context: context) - telemetry.batteryLevel = Int32(telemetryMessage.deviceMetrics.batteryLevel) - telemetry.voltage = telemetryMessage.deviceMetrics.voltage - telemetry.channelUtilization = telemetryMessage.deviceMetrics.channelUtilization - telemetry.airUtilTx = telemetryMessage.deviceMetrics.airUtilTx - var newTelemetries = [TelemetryEntity]() - newTelemetries.append(telemetry) - newNode.telemetries? = NSOrderedSet(array: newTelemetries) - } - } else { - 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 adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) { diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 6ba49d8e..4207560a 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)) diff --git a/Meshtastic/Views/Helpers/BatteryGauge.swift b/Meshtastic/Views/Helpers/BatteryGauge.swift index 83e04e5f..a52de95d 100644 --- a/Meshtastic/Views/Helpers/BatteryGauge.swift +++ b/Meshtastic/Views/Helpers/BatteryGauge.swift @@ -10,7 +10,7 @@ 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 { diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index d13bd286..c095fd4d 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -104,7 +104,9 @@ 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, longitude: nodeLocation.nodeCoordinate!.longitude) ) .frame(height: 250) } #else @@ -112,7 +114,9 @@ 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, longitude: nodeLocation.nodeCoordinate!.longitude) ) .frame(height: 250) .presentationDetents([.medium]) .presentationDragIndicator(.automatic) diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift new file mode 100644 index 00000000..ffbd1bf2 --- /dev/null +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -0,0 +1,282 @@ +// +// 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 private var firmwareReleaseData: FirmwareRelease = FirmwareRelease() + + var body: some View { + //NavigationSplitView { + NavigationStack { + VStack (alignment: .leading) { + Text("nRF 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) + } + .padding([.leading, .trailing, .bottom]) + 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() + } + } + .padding([.leading, .trailing]) + .padding(.bottom, 5) + VStack (alignment: .leading) { + Text("Firmware Releases") + .font(.title3) + .padding([.leading, .trailing]) + List { + Section(header: Text("Stable")) { + ForEach(firmwareReleaseData.releases?.stable ?? [], id: \.id) { fr in + HStack() { + Link(fr.title ?? "Unknown", destination: URL(string: fr.pageUrl ?? "")!) + .font(.caption) + Spacer() + Link(destination: URL(string: fr.zipUrl ?? "")!) { + VStack { + Image(systemName: "square.and.arrow.down") + .font(.title3) + } + } + } + } + } + Section("Alpha") { + ForEach(firmwareReleaseData.releases?.alpha ?? [], id: \.id) { fr in + HStack() { + Link(fr.title ?? "Unknown", destination: URL(string: fr.pageUrl ?? "")!) + .font(.caption) + Spacer() + Link(destination: URL(string: fr.zipUrl ?? "")!) { + VStack { + Image(systemName: "square.and.arrow.down") + .font(.title3) + } + } + } + } + } + Section("Pull Requests") { + ForEach(firmwareReleaseData.pullRequests ?? [], id: \.id) { fr in + HStack() { + Link(fr.title ?? "Unknown", destination: URL(string: fr.pageUrl ?? "")!) + .font(.caption) + Spacer() + Link(destination: URL(string: fr.zipUrl ?? "")!) { + VStack { + Image(systemName: "square.and.arrow.down") + .font(.title3) + } + } + } + } + } + } + } + .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, response, error 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? = nil + var title : String? = nil + var pageUrl : String? = nil + var zipUrl : String? = nil + + 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? = nil + var title : String? = nil + var pageUrl : String? = nil + var zipUrl : String? = nil + + 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? = nil + var title : String? = nil + var pageUrl : String? = nil + var zipUrl : String? = nil + + 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 { From 65dde905f79d8299f992f31b2ef4e7d499bba7be Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 11 Mar 2023 09:26:52 -0800 Subject: [PATCH 08/21] Add descriptive text for LoRa Config depending on state Fix rx boosted gain toggle Make a big hardware model enum --- Meshtastic.xcodeproj/project.pbxproj | 4 + Meshtastic/Enums/HardwareModels.swift | 186 ++++++++++++++++++ Meshtastic/Persistence/UpdateCoreData.swift | 2 + .../Views/Settings/Config/LoRaConfig.swift | 19 ++ Meshtastic/Views/Settings/Firmware.swift | 50 +++-- Meshtastic/Views/Settings/ShareChannels.swift | 1 + 6 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 Meshtastic/Enums/HardwareModels.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 0a73ceda..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 */; }; @@ -178,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 = ""; }; @@ -486,6 +488,7 @@ DD1925B828CDA93900720036 /* SerialConfigEnums.swift */, DD994B68295F88B60013760A /* IntervalEnums.swift */, DD5E5239298EFA5300D21B61 /* TelemetryWeather.swift */, + DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */, ); path = Enums; sourceTree = ""; @@ -930,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 */, diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift new file mode 100644 index 00000000..b9ddcf77 --- /dev/null +++ b/Meshtastic/Enums/HardwareModels.swift @@ -0,0 +1,186 @@ +// +// HardwareModels.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 3/11/23. +// + +import Foundation +// Default of 0 is Client +enum HardwareModels: Int, CaseIterable, Identifiable { + + case UNSET = 0 + case TLORA_V2 = 1 + case TLORA_V1 = 2 + case TLORA_V2_1_1P6 = 3 + case TBEAM = 4 + case HELTEC_V2_0 = 5 + case TBEAM_V0P7 = 6 + case T_ECHO = 7 + case TLORA_V1_1P3 = 8 + case RAK4631 = 9 + case HELTEC_V2_1 = 10 + case HELTEC_V1 = 11 + case LILYGO_TBEAM_S3_CORE = 12 + case RAK11200 = 13 + case NANO_G1 = 14 + case TLORA_V2_1_1P8 = 15 + case TLORA_T3_S3 = 16 + case NANO_G1_EXPLORER = 17 + case STATION_G1 = 25 + case M5STACK = 42 + case HELTEC_V3 = 43 + case HELTEC_WSL_V3 = 45 + + var id: Int { 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 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 + } + } +} diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 4207560a..4233a2e0 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -428,6 +428,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) @@ -443,6 +444,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/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 4fb11c4d..51af0fb1 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -46,6 +46,24 @@ struct LoRaConfig: View { VStack { Form { + if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral.num { + // Let users know what is going on if they are using remote admin and don't have the lora config yet + if node?.loRaConfig == nil { + Text("LoRa config data has been requested but has not yet 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 { + 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 ) { @@ -216,6 +234,7 @@ struct LoRaConfig: View { self.codingRate = Int(node?.loRaConfig?.codingRate ?? 0) self.spreadFactor = Int(node?.loRaConfig?.spreadFactor ?? 0) self.rxBoostedGain = node?.loRaConfig?.sx126xRxBoostedGain ?? false + print(rxBoostedGain) self.hasChanges = false // Need to request a LoRaConfig from the remote node before allowing changes diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index ffbd1bf2..55440604 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -66,7 +66,7 @@ struct Firmware: View { Spacer() } } - .padding([.leading, .trailing]) + .padding([.leading, .trailing, .bottom]) .padding(.bottom, 5) VStack (alignment: .leading) { Text("Firmware Releases") @@ -75,45 +75,39 @@ struct Firmware: View { List { Section(header: Text("Stable")) { ForEach(firmwareReleaseData.releases?.stable ?? [], id: \.id) { fr in - HStack() { - Link(fr.title ?? "Unknown", destination: URL(string: fr.pageUrl ?? "")!) - .font(.caption) - Spacer() - Link(destination: URL(string: fr.zipUrl ?? "")!) { - VStack { - Image(systemName: "square.and.arrow.down") - .font(.title3) - } + 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 - HStack() { - Link(fr.title ?? "Unknown", destination: URL(string: fr.pageUrl ?? "")!) - .font(.caption) - Spacer() - Link(destination: URL(string: fr.zipUrl ?? "")!) { - VStack { - Image(systemName: "square.and.arrow.down") - .font(.title3) - } + 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 - HStack() { - Link(fr.title ?? "Unknown", destination: URL(string: fr.pageUrl ?? "")!) - .font(.caption) - Spacer() - Link(destination: URL(string: fr.zipUrl ?? "")!) { - VStack { - Image(systemName: "square.and.arrow.down") - .font(.title3) - } + Link(destination: URL(string: fr.zipUrl ?? "")!) { + HStack() { + Text(fr.title ?? "Unknown") + .font(.caption) + Spacer() + Image(systemName: "square.and.arrow.down") + .font(.title3) } } } diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 9a9eda8f..4a61fff7 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -287,6 +287,7 @@ 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] { From b73d7d0c66df1a45f5913f9228859d1315cdfe74 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 13 Mar 2023 09:50:24 -0700 Subject: [PATCH 09/21] Enum for hardware --- Meshtastic/Enums/HardwareModels.swift | 71 ++++++++++++------- .../Views/Settings/Config/LoRaConfig.swift | 7 +- Meshtastic/Views/Settings/Firmware.swift | 28 +++----- 3 files changed, 61 insertions(+), 45 deletions(-) diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift index b9ddcf77..9911e465 100644 --- a/Meshtastic/Enums/HardwareModels.swift +++ b/Meshtastic/Enums/HardwareModels.swift @@ -7,32 +7,55 @@ import Foundation // Default of 0 is Client -enum HardwareModels: Int, CaseIterable, Identifiable { +enum HardwareModels: String, CaseIterable, Identifiable { - case UNSET = 0 - case TLORA_V2 = 1 - case TLORA_V1 = 2 - case TLORA_V2_1_1P6 = 3 - case TBEAM = 4 - case HELTEC_V2_0 = 5 - case TBEAM_V0P7 = 6 - case T_ECHO = 7 - case TLORA_V1_1P3 = 8 - case RAK4631 = 9 - case HELTEC_V2_1 = 10 - case HELTEC_V1 = 11 - case LILYGO_TBEAM_S3_CORE = 12 - case RAK11200 = 13 - case NANO_G1 = 14 - case TLORA_V2_1_1P8 = 15 - case TLORA_T3_S3 = 16 - case NANO_G1_EXPLORER = 17 - case STATION_G1 = 25 - case M5STACK = 42 - case HELTEC_V3 = 43 - case HELTEC_WSL_V3 = 45 + 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: Int { self.rawValue } +// case UNSET = 0 +// case TLORA_V2 = 1 +// case TLORA_V1 = 2 +// case TLORA_V2_1_1P6 = 3 +// case TBEAM = 4 +// case HELTEC_V2_0 = 5 +// case TBEAM_V0P7 = 6 +// case T_ECHO = 7 +// case TLORA_V1_1P3 = 8 +// case RAK4631 = 9 +// case HELTEC_V2_1 = 10 +// case HELTEC_V1 = 11 +// case LILYGO_TBEAM_S3_CORE = 12 +// case RAK11200 = 13 +// case NANO_G1 = 14 +// case TLORA_V2_1_1P8 = 15 +// case TLORA_T3_S3 = 16 +// case NANO_G1_EXPLORER = 17 +// case STATION_G1 = 25 +// case M5STACK = 42 +// case HELTEC_V3 = 43 +// case HELTEC_WSL_V3 = 45 + + var id: String { self.rawValue } var description: String { switch self { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 51af0fb1..cb34e10a 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -46,7 +46,12 @@ struct LoRaConfig: View { VStack { Form { - if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral.num { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral.num { + 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 { // Let users know what is going on if they are using remote admin and don't have the lora config yet if node?.loRaConfig == nil { Text("LoRa config data has been requested but has not yet returned from the remote node. You can check the status of admin message requests in the admin message log.") diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 55440604..8b1c1d76 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -26,6 +26,10 @@ struct Firmware: View { var body: some View { //NavigationSplitView { NavigationStack { + + let hwModel: HardwareModels = HardwareModels.allCases.first(where: { $0.rawValue == node?.user?.hwModel ?? "UNSET" } ) ?? HardwareModels.UNSET + Text(hwModel.firmwareStrings[0] + (node?.metadata?.firmwareVersion ?? "Unknown") ) + .font(.title3) VStack (alignment: .leading) { Text("nRF Device Firmware Update App") .font(.title3) @@ -171,21 +175,17 @@ struct Releases: Codable { 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() { - - } + init() {} } struct Alpha: Codable { @@ -196,7 +196,6 @@ struct Alpha: Codable { var zipUrl : String? = nil enum CodingKeys: String, CodingKey { - case id = "id" case title = "title" case pageUrl = "page_url" @@ -205,16 +204,13 @@ struct Alpha: Codable { 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() { - - } + init() {} } struct Stable: Codable { @@ -225,7 +221,6 @@ struct Stable: Codable { var zipUrl : String? = nil enum CodingKeys: String, CodingKey { - case id = "id" case title = "title" case pageUrl = "page_url" @@ -234,16 +229,13 @@ struct Stable: Codable { 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() { - - } + init() {} } struct PullRequests: Codable { @@ -254,7 +246,6 @@ struct PullRequests: Codable { var zipUrl : String? = nil enum CodingKeys: String, CodingKey { - case id = "id" case title = "title" case pageUrl = "page_url" @@ -263,14 +254,11 @@ struct PullRequests: Codable { 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() { - - } + init() {} } From b336307a22c6eae8ea7f25969abc6d6e44593736 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 13 Mar 2023 19:17:43 -0700 Subject: [PATCH 10/21] ACK! Admin message status content if you have an admin channel --- Meshtastic/Enums/HardwareModels.swift | 23 ------------------ .../Views/Messages/UserMessageList.swift | 6 ++++- .../Views/Settings/AdminMessageList.swift | 17 +++++++++---- .../Settings/Config/BluetoothConfig.swift | 23 ++++++++++++++++++ .../Views/Settings/Config/DeviceConfig.swift | 24 ++++++++++++++++++- .../Views/Settings/Config/DisplayConfig.swift | 24 ++++++++++++++++++- .../Views/Settings/Config/LoRaConfig.swift | 10 ++++---- .../Config/Module/CannedMessagesConfig.swift | 23 ++++++++++++++++++ .../Module/ExternalNotificationConfig.swift | 23 ++++++++++++++++++ .../Settings/Config/Module/MQTTConfig.swift | 23 ++++++++++++++++++ .../Config/Module/RangeTestConfig.swift | 23 ++++++++++++++++++ .../Settings/Config/Module/SerialConfig.swift | 24 ++++++++++++++++++- .../Config/Module/TelemetryConfig.swift | 23 ++++++++++++++++++ .../Views/Settings/Config/NetworkConfig.swift | 24 ++++++++++++++++++- .../Settings/Config/PositionConfig.swift | 23 ++++++++++++++++++ 15 files changed, 275 insertions(+), 38 deletions(-) diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift index 9911e465..afff4559 100644 --- a/Meshtastic/Enums/HardwareModels.swift +++ b/Meshtastic/Enums/HardwareModels.swift @@ -32,29 +32,6 @@ enum HardwareModels: String, CaseIterable, Identifiable { case HELTEC_V3 case HELTEC_WSL_V3 -// case UNSET = 0 -// case TLORA_V2 = 1 -// case TLORA_V1 = 2 -// case TLORA_V2_1_1P6 = 3 -// case TBEAM = 4 -// case HELTEC_V2_0 = 5 -// case TBEAM_V0P7 = 6 -// case T_ECHO = 7 -// case TLORA_V1_1P3 = 8 -// case RAK4631 = 9 -// case HELTEC_V2_1 = 10 -// case HELTEC_V1 = 11 -// case LILYGO_TBEAM_S3_CORE = 12 -// case RAK11200 = 13 -// case NANO_G1 = 14 -// case TLORA_V2_1_1P8 = 15 -// case TLORA_T3_S3 = 16 -// case NANO_G1_EXPLORER = 17 -// case STATION_G1 = 25 -// case M5STACK = 42 -// case HELTEC_V3 = 43 -// case HELTEC_WSL_V3 = 45 - var id: String { self.rawValue } var description: String { switch self { 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/Settings/AdminMessageList.swift b/Meshtastic/Views/Settings/AdminMessageList.swift index 309c703b..3bb86237 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/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index b81cd053..59af10c5 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") diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 50772f9b..f456c119 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 ) { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 8172b5f0..c0f52b51 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 { + 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) diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index cb34e10a..8a5b69f2 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -46,22 +46,22 @@ struct LoRaConfig: View { VStack { Form { - if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral.num { + 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 { - // Let users know what is going on if they are using remote admin and don't have the lora config yet + } 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 has been requested but has not yet returned from the remote node. You can check the status of admin message requests in the admin message log.") + 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 { + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 0c0d805d..7dcf323a 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) { diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index f31b3c74..3057be3e 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..8528c0ee 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..6490076c 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..616784ba 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..389f37de 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 diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index c7e1f663..7012ed06 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -29,8 +29,30 @@ struct NetworkConfig: 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?.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") } diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index a080e1a2..57c0710a 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") From e0f1e243a62de2972a6f27c8fc7e0dc952d8384e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 13 Mar 2023 19:27:36 -0700 Subject: [PATCH 11/21] fix dumb disabled logic on telemetry config --- Meshtastic/Views/Nodes/NodeDetail.swift | 3 +-- Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index c095fd4d..d5495f6a 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -393,12 +393,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) diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 389f37de..757400d1 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -84,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: { From 93006b258de4ad887f85c2a1c340f527f0fcc246 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 14 Mar 2023 12:44:10 -0700 Subject: [PATCH 12/21] Swift Lint --- Meshtastic/Enums/HardwareModels.swift | 6 +- Meshtastic/Helpers/BLEManager.swift | 4 +- Meshtastic/Helpers/MeshPackets.swift | 213 +++++++++--------- Meshtastic/Persistence/UpdateCoreData.swift | 23 +- Meshtastic/Views/Nodes/NodeDetail.swift | 6 +- Meshtastic/Views/Nodes/NodeMap.swift | 4 +- .../Views/Settings/AdminMessageList.swift | 2 +- Meshtastic/Views/Settings/Channels.swift | 1 - .../Settings/Config/BluetoothConfig.swift | 6 +- .../Views/Settings/Config/DeviceConfig.swift | 6 +- .../Views/Settings/Config/DisplayConfig.swift | 6 +- .../Views/Settings/Config/LoRaConfig.swift | 6 +- .../Config/Module/CannedMessagesConfig.swift | 6 +- .../Module/ExternalNotificationConfig.swift | 6 +- .../Settings/Config/Module/MQTTConfig.swift | 6 +- .../Config/Module/RangeTestConfig.swift | 6 +- .../Settings/Config/Module/SerialConfig.swift | 6 +- .../Config/Module/TelemetryConfig.swift | 6 +- .../Views/Settings/Config/NetworkConfig.swift | 6 +- .../Settings/Config/PositionConfig.swift | 6 +- Meshtastic/Views/Settings/Firmware.swift | 152 ++++++------- Meshtastic/Views/Settings/ShareChannels.swift | 2 +- 22 files changed, 242 insertions(+), 243 deletions(-) diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift index afff4559..c7470424 100644 --- a/Meshtastic/Enums/HardwareModels.swift +++ b/Meshtastic/Enums/HardwareModels.swift @@ -31,7 +31,7 @@ enum HardwareModels: String, CaseIterable, Identifiable { case M5STACK case HELTEC_V3 case HELTEC_WSL_V3 - + var id: String { self.rawValue } var description: String { switch self { @@ -81,7 +81,7 @@ enum HardwareModels: String, CaseIterable, Identifiable { case .HELTEC_WSL_V3: return "Heltec wireless stick lite V3" } - + } var firmwareStrings: [String] { switch self { @@ -131,7 +131,7 @@ enum HardwareModels: String, CaseIterable, Identifiable { case .HELTEC_WSL_V3: return ["firmware-heltec-wsl-v3-"] } - + } func protoEnumValue() -> HardwareModel { diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 7180a887..a9e9cea6 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -873,7 +873,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } return false } - + public func sendRebootOta(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { var adminPacket = AdminMessage() adminPacket.rebootOtaSeconds = 5 @@ -1897,7 +1897,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } public func tryClearExistingChannels() { - //Before we get started delete the existing channels from the myNodeInfo + // Before we get started delete the existing channels from the myNodeInfo let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num)) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 60a2ac4b..097f0516 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) @@ -58,7 +55,7 @@ func localConfig (config: Config, context: NSManagedObjectContext, nodeNum: Int6 } func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { - + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) { upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum, context: context) } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) { @@ -75,20 +72,20 @@ func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNu } func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedObjectContext) -> MyInfoEntity? { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.myinfo %@", comment: "MyInfo received: %@"), String(myInfo.myNodeNum)) MeshLogger.log("ℹ️ \(logString)") - + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(myInfo.myNodeNum)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return nil } // Not Found Insert if fetchedMyInfo.isEmpty { - + let myInfoEntity = MyInfoEntity(context: context) myInfoEntity.peripheralId = peripheralId myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum) @@ -113,7 +110,7 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO print("πŸ’₯ Error Inserting New Core Data MyInfoEntity: \(nsError)") } } else { - + fetchedMyInfo[0].peripheralId = peripheralId fetchedMyInfo[0].myNodeNum = Int64(myInfo.myNodeNum) fetchedMyInfo[0].hasGps = myInfo.hasGps_p @@ -125,7 +122,7 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) fetchedMyInfo[0].minAppVersion = Int32(bitPattern: myInfo.minAppVersion) fetchedMyInfo[0].maxChannels = Int32(bitPattern: myInfo.maxChannels) - + do { try context.save() print("πŸ’Ύ Updated myInfo for node number: \(String(myInfo.myNodeNum))") @@ -143,15 +140,15 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO } func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectContext) { - + if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.channel.received %d %@", comment: "Channel %d received from: %@"), channel.index, String(fromNum)) MeshLogger.log("πŸŽ›οΈ \(logString)") - + let fetchedMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum) - + do { guard let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as? [MyInfoEntity] else { return @@ -195,14 +192,14 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo } func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NSManagedObjectContext) { - + if metadata.isInitialized { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.metadata.received %@", comment: "Device Metadata admin message received from: %@"), String(fromNum)) MeshLogger.log("🏷️ \(logString)") - + let fetchedNodeRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchedNodeRequest.predicate = NSPredicate(format: "num == %lld", fromNum) - + do { guard let fetchedNode = try context.fetch(fetchedNodeRequest) as? [NodeInfoEntity] else { return @@ -218,7 +215,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS newMetadata.role = Int32(metadata.role.rawValue) newMetadata.positionFlags = Int32(metadata.positionFlags) fetchedNode[0].metadata = newMetadata - + do { try context.save() } catch { @@ -235,27 +232,27 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS } func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(nodeInfo.num)) MeshLogger.log("πŸ“Ÿ \(logString)") - + guard nodeInfo.num > 0 else { return nil } - + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeInfo.num)) - + do { guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else { return nil } // Not Found Insert if fetchedNode.isEmpty && nodeInfo.hasUser { - + let newNode = NodeInfoEntity(context: context) newNode.id = Int64(nodeInfo.num) newNode.num = Int64(nodeInfo.num) newNode.channel = Int32(channel) - + if nodeInfo.hasDeviceMetrics { let telemetry = TelemetryEntity(context: context) telemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel) @@ -266,7 +263,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newTelemetries.append(telemetry) newNode.telemetries? = NSOrderedSet(array: newTelemetries) } - + newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) newNode.snr = nodeInfo.snr if nodeInfo.hasUser { @@ -279,7 +276,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() newNode.user = newUser } - + if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) { let position = PositionEntity(context: context) position.seqNo = Int32(nodeInfo.position.seqNumber) @@ -294,11 +291,11 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newPostions.append(position) newNode.positions? = NSOrderedSet(array: newPostions) } - + // Look for a MyInfo let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return nil @@ -318,15 +315,15 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje print("πŸ’₯ Fetch MyInfo Error") } } else if nodeInfo.hasUser && nodeInfo.num > 0 { - + fetchedNode[0].id = Int64(nodeInfo.num) fetchedNode[0].num = Int64(nodeInfo.num) fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) fetchedNode[0].snr = nodeInfo.snr fetchedNode[0].channel = Int32(channel) - + if nodeInfo.hasUser { - + fetchedNode[0].user!.userId = nodeInfo.user.id fetchedNode[0].user!.num = Int64(nodeInfo.num) fetchedNode[0].user!.longName = nodeInfo.user.longName @@ -334,9 +331,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].user!.macaddr = nodeInfo.user.macaddr fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() } - + if nodeInfo.hasDeviceMetrics { - + let newTelemetry = TelemetryEntity(context: context) newTelemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel) newTelemetry.voltage = nodeInfo.deviceMetrics.voltage @@ -347,11 +344,11 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet } - + if nodeInfo.hasPosition { - + if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) { - + let position = PositionEntity(context: context) position.latitudeI = nodeInfo.position.latitudeI position.longitudeI = nodeInfo.position.longitudeI @@ -363,13 +360,13 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet } - + } - + // Look for a MyInfo let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return nil @@ -397,21 +394,21 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { - + if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) { - + if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getCannedMessageModuleMessagesResponse(adminMessage.getCannedMessageModuleMessagesResponse) { - + if let cmmc = try? CannedMessageModuleConfig(serializedData: packet.decoded.payload) { - + if !cmmc.messages.isEmpty { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessages.messages.received %@", comment: "Canned Messages Messages Received For: %@"), String(packet.from)) MeshLogger.log("πŸ₯« \(logString)") - + let fetchNodeRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) - + do { guard let fetchedNode = try context.fetch(fetchNodeRequest) as? [NodeInfoEntity] else { return @@ -438,65 +435,65 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getChannelResponse(adminMessage.getChannelResponse) { channelPacket(channel: adminMessage.getChannelResponse, fromNum: Int64(packet.from), context: context) - + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getDeviceMetadataResponse(adminMessage.getDeviceMetadataResponse) { deviceMetadataPacket(metadata: adminMessage.getDeviceMetadataResponse, fromNum: Int64(packet.from), context: context) - + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getConfigResponse(adminMessage.getConfigResponse) { - + let config = adminMessage.getConfigResponse - + if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), context: context) - + } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), context: context) - + } } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getModuleConfigResponse(adminMessage.getModuleConfigResponse) { - + let moduleConfig = adminMessage.getModuleConfigResponse - + if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfig.cannedMessage) { upsertCannedMessagesModuleConfigPacket(config: moduleConfig.cannedMessage, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(moduleConfig.externalNotification) { upsertExternalNotificationModuleConfigPacket(config: moduleConfig.externalNotification, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(moduleConfig.mqtt) { upsertMqttModuleConfigPacket(config: moduleConfig.mqtt, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(moduleConfig.rangeTest) { upsertRangeTestModuleConfigPacket(config: moduleConfig.rangeTest, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(moduleConfig.serial) { upsertSerialModuleConfigPacket(config: moduleConfig.serial, nodeNum: Int64(packet.from), context: context) - + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(moduleConfig.telemetry) { upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context) - + } - + } else { MeshLogger.log("πŸ•ΈοΈ MESH PACKET received for Admin App \(try! packet.decoded.jsonString())") } - + // Save an ack for the admin message log for each admin message response received as we stopped sending acks if there is also a response to reduce airtime. adminResponseAck(packet: packet, context: context) } } func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) { - + let fetchedAdminMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") fetchedAdminMessageRequest.predicate = NSPredicate(format: "messageId == %lld", packet.decoded.requestID) do { @@ -524,22 +521,22 @@ func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) { } func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSManagedObjectContext) { - + if let routingMessage = try? Routing(serializedData: packet.decoded.payload) { - + let routingError = RoutingError(rawValue: routingMessage.errorReason.rawValue) - + let routingErrorString = routingError?.display ?? NSLocalizedString("unknown", comment: "") let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.routing.message %@ %@", comment: "Routing received for RequestID: %@ Ack Status: %@"), String(packet.decoded.requestID), routingErrorString) MeshLogger.log("πŸ•ΈοΈ \(logString)") - + let fetchMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID)) - + do { let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity] if fetchedMessage?.count ?? 0 > 0 { - + if fetchedMessage![0].toUser != nil { // Real ACK from DM Recipient if packet.to != packet.from { @@ -547,14 +544,14 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana } } fetchedMessage![0].ackError = Int32(routingMessage.errorReason.rawValue) - + if routingMessage.errorReason == Routing.Error.none { - + fetchedMessage![0].receivedACK = true } fetchedMessage![0].ackSNR = packet.rxSnr fetchedMessage![0].ackTimestamp = Int32(packet.rxTime) - + if fetchedMessage![0].toUser != nil { fetchedMessage![0].toUser?.objectWillChange.send() } else { @@ -563,19 +560,19 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana do { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] if fetchedMyInfo?.count ?? 0 > 0 { - + for ch in fetchedMyInfo![0].channels!.array as! [ChannelEntity] { - + if ch.index == packet.channel { ch.objectWillChange.send() } } } } catch { - + } } - + } else { return } @@ -590,25 +587,25 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana } func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { - + if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) { - + // Only log telemetry from the mesh not the connected device if connectedNode != Int64(packet.from) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.received %@", comment: "Telemetry received for: %@"), String(packet.from)) MeshLogger.log("πŸ“ˆ \(logString)") } else { // If it is the connected node - + } - + let telemetry = TelemetryEntity(context: context) - + let fetchNodeTelemetryRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) - + do { - + guard let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) as? [NodeInfoEntity] else { return } @@ -667,13 +664,13 @@ 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 #available(iOS 16.2, *) { - + let oneMinuteLater = Calendar.current.date(byAdding: .minute, value: (Int(1) ), to: Date())! let date = Date.now...oneMinuteLater let updatedMeshStatus = MeshActivityAttributes.MeshActivityStatus(timerRange: date, connected: true, channelUtilization: telemetry.channelUtilization, airtime: telemetry.airUtilTx, batteryLevel: UInt32(telemetry.batteryLevel)) let alertConfiguration = AlertConfiguration(title: "Mesh activity update", body: "Updated Device Metrics Data.", sound: .default) let updatedContent = ActivityContent(state: updatedMeshStatus, staleDate: nil) - + let meshActivity = Activity.activities.first(where: { $0.attributes.nodeNum == connectedNode }) if meshActivity != nil { Task { @@ -696,11 +693,11 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage } func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { - + if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) { - + MeshLogger.log("πŸ’¬ \(NSLocalizedString("mesh.log.textmessage.received", comment: "Message received from the text message app"))") - + let messageUsers: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from]) do { @@ -714,11 +711,11 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM newMessage.snr = packet.rxSnr newMessage.isEmoji = packet.decoded.emoji == 1 newMessage.channel = Int32(packet.channel) - + if packet.decoded.replyID > 0 { newMessage.replyID = Int64(packet.decoded.replyID) } - + if fetchedUsers.first(where: { $0.num == packet.to }) != nil && packet.to != 4294967295 { newMessage.toUser = fetchedUsers.first(where: { $0.num == packet.to }) } @@ -727,20 +724,20 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } newMessage.messagePayload = messageText newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: messageText) - + newMessage.fromUser?.objectWillChange.send() newMessage.toUser?.objectWillChange.send() - + var messageSaved = false - + do { - + try context.save() print("πŸ’Ύ Saved a new message for \(newMessage.messageId)") messageSaved = true - + if messageSaved { - + if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) { // Create an iOS Notification for the received DM message and schedule it immediately let manager = LocalNotificationManager() @@ -754,10 +751,10 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM manager.schedule() print("πŸ’¬ iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))") } else if newMessage.fromUser != nil && newMessage.toUser == nil { - + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode)) - + do { guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else { return @@ -766,7 +763,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM if channel.index == newMessage.channel { context.refresh(channel, mergeChanges: true) } - + if channel.index == newMessage.channel && !channel.mute { // Create an iOS Notification for the received private channel message and schedule it immediately let manager = LocalNotificationManager() @@ -782,7 +779,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } } } catch { - + } } } @@ -798,22 +795,22 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) { - + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.received %@", comment: "Waypoint Packet received from node: %@"), String(packet.from)) MeshLogger.log("πŸ“ \(logString)") - + let fetchWaypointRequest: NSFetchRequest = NSFetchRequest.init(entityName: "WaypointEntity") fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id)) - + do { - + if let waypointMessage = try? Waypoint(serializedData: packet.decoded.payload) { guard let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as? [WaypointEntity] else { return } if fetchedWaypoint.isEmpty { let waypoint = WaypointEntity(context: context) - + waypoint.id = Int64(packet.id) waypoint.name = waypointMessage.name waypoint.longDescription = waypointMessage.description_p diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 4233a2e0..9763b0e7 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -98,17 +98,17 @@ 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 @@ -135,7 +135,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) 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) @@ -192,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 } @@ -237,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 { diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index d5495f6a..cadf57e4 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 @@ -105,7 +105,7 @@ struct NodeDetail: View { .font(.title) .padding() let nodeLocation = node.positions?.lastObject as! PositionEntity - + NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation.nodeCoordinate!.latitude, longitude: nodeLocation.nodeCoordinate!.longitude) ) .frame(height: 250) } @@ -114,7 +114,7 @@ struct NodeDetail: View { Text("Today's Weather Forecast") .font(.title) .padding() - + let nodeLocation = node.positions?.lastObject as! PositionEntity NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation.nodeCoordinate!.latitude, longitude: nodeLocation.nodeCoordinate!.longitude) ) .frame(height: 250) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 8505b0fd..3a9d3de5 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -60,10 +60,8 @@ struct NodeMap: View { // 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 { ZStack { diff --git a/Meshtastic/Views/Settings/AdminMessageList.swift b/Meshtastic/Views/Settings/AdminMessageList.swift index 3bb86237..dacd952e 100644 --- a/Meshtastic/Views/Settings/AdminMessageList.swift +++ b/Meshtastic/Views/Settings/AdminMessageList.swift @@ -42,7 +42,7 @@ struct AdminMessageList: View { if am.ackTimestamp > 0 { if am.realACK { - + Text(ackErrorVal?.display ?? "Empty Ack Error") .foregroundColor(am.receivedACK ? .gray : .red) .font(.caption2) diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 6e2eba28..00317066 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -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 59af10c5..0152cb14 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -30,10 +30,10 @@ struct BluetoothConfig: View { 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 { + 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) @@ -41,7 +41,7 @@ struct BluetoothConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index f456c119..7f60ddc4 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -35,10 +35,10 @@ struct DeviceConfig: View { 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 { + 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) @@ -46,7 +46,7 @@ struct DeviceConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index c0f52b51..5c5d189b 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -33,10 +33,10 @@ struct DisplayConfig: View { 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 { + 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) @@ -44,7 +44,7 @@ struct DisplayConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 8a5b69f2..f9686fee 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -50,10 +50,10 @@ struct LoRaConfig: View { 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 { + 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) @@ -61,7 +61,7 @@ struct LoRaConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 7dcf323a..7796c959 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -43,10 +43,10 @@ struct CannedMessagesConfig: View { 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 { + 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) @@ -54,7 +54,7 @@ struct CannedMessagesConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 3057be3e..2074bf0f 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -38,10 +38,10 @@ struct ExternalNotificationConfig: View { 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 { + 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) @@ -49,7 +49,7 @@ struct ExternalNotificationConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 8528c0ee..f264907e 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -28,10 +28,10 @@ struct MQTTConfig: View { 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 { + 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) @@ -39,7 +39,7 @@ struct MQTTConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 6490076c..9d6ed6de 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -27,10 +27,10 @@ struct RangeTestConfig: View { 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 { + 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) @@ -38,7 +38,7 @@ struct RangeTestConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index 616784ba..c5d1e900 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -34,10 +34,10 @@ struct SerialConfig: View { 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 { + 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) @@ -45,7 +45,7 @@ struct SerialConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 757400d1..3d5910b7 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -30,10 +30,10 @@ struct TelemetryConfig: View { 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 { + 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) @@ -41,7 +41,7 @@ struct TelemetryConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 7012ed06..1c5244d4 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -33,10 +33,10 @@ struct NetworkConfig: View { 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 { + 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) @@ -44,7 +44,7 @@ struct NetworkConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 57c0710a..6f430d37 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -76,10 +76,10 @@ struct PositionConfig: View { 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 { + 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) @@ -87,7 +87,7 @@ struct PositionConfig: View { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } - } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0{ + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") .font(.title3) } else { diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 8b1c1d76..c11398b4 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -15,22 +15,22 @@ import SwiftUI import StoreKit struct Firmware: View { - + @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager - + var node: NodeInfoEntity? - + @State private var firmwareReleaseData: FirmwareRelease = FirmwareRelease() - + var body: some View { - //NavigationSplitView { + // NavigationSplitView { NavigationStack { - - let hwModel: HardwareModels = HardwareModels.allCases.first(where: { $0.rawValue == node?.user?.hwModel ?? "UNSET" } ) ?? HardwareModels.UNSET + + let hwModel: HardwareModels = HardwareModels.allCases.first(where: { $0.rawValue == node?.user?.hwModel ?? "UNSET" }) ?? HardwareModels.UNSET Text(hwModel.firmwareStrings[0] + (node?.metadata?.firmwareVersion ?? "Unknown") ) .font(.title3) - VStack (alignment: .leading) { + VStack(alignment: .leading) { Text("nRF 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.") @@ -39,7 +39,7 @@ struct Firmware: View { .font(.callout) } .padding([.leading, .trailing, .bottom]) - VStack (alignment: .leading) { + 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.") @@ -49,7 +49,7 @@ struct Firmware: View { .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){ + HStack(alignment: .center) { Spacer() Button { let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) @@ -72,7 +72,7 @@ struct Firmware: View { } .padding([.leading, .trailing, .bottom]) .padding(.bottom, 5) - VStack (alignment: .leading) { + VStack(alignment: .leading) { Text("Firmware Releases") .font(.title3) .padding([.leading, .trailing]) @@ -80,7 +80,7 @@ struct Firmware: View { Section(header: Text("Stable")) { ForEach(firmwareReleaseData.releases?.stable ?? [], id: \.id) { fr in Link(destination: URL(string: fr.zipUrl ?? "")!) { - HStack() { + HStack { Text(fr.title ?? "Unknown") .font(.caption) Spacer() @@ -93,7 +93,7 @@ struct Firmware: View { Section("Alpha") { ForEach(firmwareReleaseData.releases?.alpha ?? [], id: \.id) { fr in Link(destination: URL(string: fr.zipUrl ?? "")!) { - HStack() { + HStack { Text(fr.title ?? "Unknown") .font(.caption) Spacer() @@ -106,7 +106,7 @@ struct Firmware: View { Section("Pull Requests") { ForEach(firmwareReleaseData.pullRequests ?? [], id: \.id) { fr in Link(destination: URL(string: fr.zipUrl ?? "")!) { - HStack() { + HStack { Text(fr.title ?? "Unknown") .font(.caption) Spacer() @@ -123,142 +123,142 @@ struct Firmware: View { .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, response, error in - + 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]? = [] - + + 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 ) + + 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]? = [] - + + 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 ) + stable = try values.decodeIfPresent([Stable].self, forKey: .stable ) + alpha = try values.decodeIfPresent([Alpha].self, forKey: .alpha ) } - + init() {} } struct Alpha: Codable { - - var id : String? = nil - var title : String? = nil - var pageUrl : String? = nil - var zipUrl : String? = nil - + + 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 ) + 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? = nil - var title : String? = nil - var pageUrl : String? = nil - var zipUrl : String? = nil - + + 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 ) + 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? = nil - var title : String? = nil - var pageUrl : String? = nil - var zipUrl : String? = nil - + + 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 ) + 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/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 4a61fff7..97cb1b98 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 { From 59d05a533b7f63b7e17bf11129523773e89a9c7f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 14 Mar 2023 13:01:35 -0700 Subject: [PATCH 13/21] Lint fixes --- Meshtastic/Helpers/BLEManager.swift | 6 +++--- Meshtastic/Helpers/MeshPackets.swift | 2 +- Meshtastic/Views/Bluetooth/Connect.swift | 4 ++-- Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift | 1 - Meshtastic/Views/Settings/Channels.swift | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index a9e9cea6..3ca3936d 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1902,10 +1902,10 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num)) do { - let fetchedMyInfo = try context!.fetch(fetchMyInfoRequest) as! [MyInfoEntity] + let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as? [MyInfoEntity] ?? [] if fetchedMyInfo.count == 1 { - let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet - mutableChannels.removeAllObjects() + let mutableChannels = fetchedMyInfo[0].channels?.mutableCopy() as? NSMutableOrderedSet + mutableChannels?.removeAllObjects() fetchedMyInfo[0].channels = mutableChannels do { try context!.save() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 097f0516..81f0caa4 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -561,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() diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 9a2eac76..d5826289 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -289,13 +289,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/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 89ea0c16..46a6d070 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -82,7 +82,6 @@ struct MapViewSwiftUI: UIViewRepresentable { #else #if os(iOS) - mapView.showsPointsOfInterest = false // Hide the default compass that only appears when you are not going north and instead always show the compass in the bottom right corner of the map mapView.showsCompass = false let compassButton = MKCompassButton(mapView: mapView) // Make a new compass diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 00317066..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) From 373aa9c5116503892979338bb171d1fa1852d5c0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 14 Mar 2023 14:33:27 -0700 Subject: [PATCH 14/21] Fix lint errors --- Meshtastic/Views/Nodes/DeviceMetricsLog.swift | 6 ++-- .../Views/Nodes/EnvironmentMetricsLog.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 31 +++++++++---------- Meshtastic/Views/Nodes/PositionLog.swift | 2 +- Meshtastic/Views/Settings/ShareChannels.swift | 2 +- 5 files changed, 20 insertions(+), 23 deletions(-) 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..13033dc9 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -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 { diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index cadf57e4..d914ec74 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -104,9 +104,8 @@ struct NodeDetail: View { Text("Today's Weather Forecast") .font(.title) .padding() - let nodeLocation = node.positions?.lastObject as! PositionEntity - - NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation.nodeCoordinate!.latitude, longitude: nodeLocation.nodeCoordinate!.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 @@ -114,10 +113,8 @@ struct NodeDetail: View { Text("Today's Weather Forecast") .font(.title) .padding() - - let nodeLocation = node.positions?.lastObject as! PositionEntity - NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation.nodeCoordinate!.latitude, longitude: nodeLocation.nodeCoordinate!.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) } @@ -181,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() @@ -290,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() @@ -494,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/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/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 97cb1b98..44d9cbfc 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -290,7 +290,7 @@ struct ShareChannels: View { 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 || From 94723c07c25249c5cff436ec4e4d05b877cf92ce Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 14 Mar 2023 19:16:04 -0700 Subject: [PATCH 15/21] Update protos --- Meshtastic/Helpers/BLEManager.swift | 17 +--- .../Protobufs/meshtastic/admin.pb.swift | 22 ++--- .../Protobufs/meshtastic/apponly.pb.swift | 8 +- .../meshtastic/cannedmessages.pb.swift | 6 +- .../Protobufs/meshtastic/channel.pb.swift | 14 +-- .../Protobufs/meshtastic/config.pb.swift | 88 +++++++++---------- .../meshtastic/connection_status.pb.swift | 38 ++++---- .../Protobufs/meshtastic/deviceonly.pb.swift | 30 +++---- .../Protobufs/meshtastic/localonly.pb.swift | 40 ++++----- Meshtastic/Protobufs/meshtastic/mesh.pb.swift | 80 ++++++++--------- .../meshtastic/module_config.pb.swift | 42 ++++----- Meshtastic/Protobufs/meshtastic/mqtt.pb.swift | 8 +- .../Protobufs/meshtastic/portnums.pb.swift | 6 +- .../meshtastic/remote_hardware.pb.swift | 10 +-- .../Protobufs/meshtastic/rtttl.pb.swift | 6 +- .../meshtastic/storeforward.pb.swift | 18 ++-- .../Protobufs/meshtastic/telemetry.pb.swift | 20 ++--- .../Protobufs/meshtastic/xmodem.pb.swift | 10 +-- .../Views/Settings/Config/DisplayConfig.swift | 2 +- .../Views/Settings/Config/LoRaConfig.swift | 2 +- 20 files changed, 227 insertions(+), 240 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 3ca3936d..32940e50 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1176,7 +1176,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..(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/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 5c5d189b..71feade9 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -185,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 f9686fee..716a0d0e 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -194,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) From c5d6790bc671b69d0f55f2603dd98713c15be47c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 14 Mar 2023 20:45:09 -0700 Subject: [PATCH 16/21] Star for preferred peripheral --- Meshtastic/Helpers/BLEManager.swift | 6 ------ Meshtastic/Views/Bluetooth/Connect.swift | 15 +++++++++++---- .../Views/Nodes/EnvironmentMetricsLog.swift | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 32940e50..18d9e67e 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 diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index d5826289..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 { diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 13033dc9..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 @@ -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) From 416960388f40a02c724db931ac83f9f1d4b67405 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 15 Mar 2023 09:27:33 -0700 Subject: [PATCH 17/21] Fix admin channel bug --- Meshtastic/Views/Settings/Config/BluetoothConfig.swift | 2 +- Meshtastic/Views/Settings/Config/DeviceConfig.swift | 2 +- Meshtastic/Views/Settings/Config/DisplayConfig.swift | 2 +- Meshtastic/Views/Settings/Config/LoRaConfig.swift | 2 +- .../Views/Settings/Config/Module/CannedMessagesConfig.swift | 2 +- Meshtastic/Views/Settings/Config/NetworkConfig.swift | 2 +- Meshtastic/Views/Settings/Config/PositionConfig.swift | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 0152cb14..3ec63ef3 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -116,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 7f60ddc4..1210cc99 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -205,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 71feade9..e45fe011 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -152,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 diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 716a0d0e..751fb1ee 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -207,7 +207,7 @@ struct LoRaConfig: View { lc.codingRate = UInt32(codingRate) lc.spreadFactor = UInt32(spreadFactor) lc.sx126XRxBoostedGain = rxBoostedGain - let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + 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 diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 7796c959..e8f5fc73 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -228,7 +228,7 @@ struct CannedMessagesConfig: View { 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) + 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 diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 1c5244d4..4c74afea 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -141,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 6f430d37..b275f77d 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -253,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 From 7da242d70f93503218eeb85746f0f9dc1531f007 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 15 Mar 2023 09:30:00 -0700 Subject: [PATCH 18/21] Fix canned messages --- .../Config/Module/CannedMessagesConfig.swift | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index e8f5fc73..386683c8 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -207,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: 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 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 From 7c7da5527deda68e1cf49ad686b67f683519d6c5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 15 Mar 2023 09:54:20 -0700 Subject: [PATCH 19/21] Save data to the right node --- Meshtastic/Helpers/BLEManager.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 18d9e67e..26a061bc 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1127,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) } @@ -1152,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 @@ -1176,7 +1176,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { meshPacket.decoded = dataMessage let messageDescription = "πŸ›Ÿ Saved Display Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertDisplayConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertDisplayConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } return 0 @@ -1200,7 +1200,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "πŸ›Ÿ Saved LoRa Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertLoRaConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertLoRaConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } @@ -1228,7 +1228,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "πŸ›Ÿ Saved Position Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertPositionConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertPositionConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } @@ -1256,7 +1256,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "πŸ›Ÿ Saved Network Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertNetworkConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertNetworkConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } @@ -1283,7 +1283,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "πŸ›Ÿ Saved Canned Message Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } @@ -1338,7 +1338,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "Saved External Notification Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } return 0 @@ -1365,7 +1365,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "Saved WiFi Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertMqttModuleConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertMqttModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } return 0 @@ -1391,7 +1391,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "Saved Range Test Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertRangeTestModuleConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertRangeTestModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } @@ -1418,7 +1418,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "Saved Serial Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertSerialModuleConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertSerialModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } return 0 @@ -1444,7 +1444,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - upsertTelemetryModuleConfigPacket(config: config, nodeNum: fromUser.num, context: context!) + upsertTelemetryModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!) return Int64(meshPacket.id) } return 0 From d9e72fe3590c2640316dcb00c86b7f0211aed048 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 15 Mar 2023 16:18:43 -0700 Subject: [PATCH 20/21] update firmware page layout --- Meshtastic/Enums/HardwareModels.swift | 25 +++- Meshtastic/Views/Settings/Firmware.swift | 169 +++++++++++++---------- 2 files changed, 120 insertions(+), 74 deletions(-) diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift index c7470424..7bdefd25 100644 --- a/Meshtastic/Enums/HardwareModels.swift +++ b/Meshtastic/Enums/HardwareModels.swift @@ -6,7 +6,7 @@ // import Foundation -// Default of 0 is Client + enum HardwareModels: String, CaseIterable, Identifiable { case UNSET @@ -184,3 +184,26 @@ enum HardwareModels: String, CaseIterable, Identifiable { } } } + + +enum HardwarePlatforms: String, CaseIterable, Identifiable { + + case ESP32 + case NRF52 + case STM32 + case PIPICO + var id: String { self.rawValue } + var description: String { + switch self { + + 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/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index c11398b4..b824b407 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -15,63 +15,86 @@ 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 - Text(hwModel.firmwareStrings[0] + (node?.metadata?.firmwareVersion ?? "Unknown") ) - .font(.title3) + VStack(alignment: .leading) { - Text("nRF Device Firmware Update App") + Text("Firmware Version: \(node?.metadata?.firmwareVersion ?? "Unknown")") .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")!) + Text("Your device supports the following firmware: ") .font(.callout) - } - .padding([.leading, .trailing, .bottom]) - 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") + HStack { + ForEach(hwModel.firmwareStrings, id: \.self) { fs in + Text(fs).font(.callout) } - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.regular) - .padding(5) - Spacer() } - } - .padding([.leading, .trailing, .bottom]) - .padding(.bottom, 5) + .padding(.bottom) + + if hwModel == HardwareModels.RAK4631 { + VStack(alignment: .leading) { + Text("nRF 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 if hwModel == HardwareModels.T_ECHO { + Text("OTA Updates are not supported on the this NRF Device.") + .font(.title3) + } else { + 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() + } + } + } + }.padding() + + + + + VStack(alignment: .leading) { Text("Firmware Releases") .font(.title3) @@ -123,85 +146,85 @@ struct Firmware: View { .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 ) @@ -209,24 +232,24 @@ struct Alpha: Codable { 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 ) @@ -234,24 +257,24 @@ struct Stable: Codable { 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 ) @@ -259,6 +282,6 @@ struct PullRequests: Codable { pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) } - + init() {} } From 4fd351d340f80757f3b3a69c46729dd31c7d7c0f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 15 Mar 2023 16:54:06 -0700 Subject: [PATCH 21/21] Firmware update updates --- Meshtastic/Enums/HardwareModels.swift | 53 ++++++++++++++++++++++++ Meshtastic/Helpers/BLEManager.swift | 2 +- Meshtastic/Views/Settings/Firmware.swift | 40 ++++++++++-------- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift index 7bdefd25..de6e9331 100644 --- a/Meshtastic/Enums/HardwareModels.swift +++ b/Meshtastic/Enums/HardwareModels.swift @@ -133,6 +133,56 @@ enum HardwareModels: String, CaseIterable, Identifiable { } } + 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 { @@ -188,6 +238,7 @@ enum HardwareModels: String, CaseIterable, Identifiable { enum HardwarePlatforms: String, CaseIterable, Identifiable { + case NONE case ESP32 case NRF52 case STM32 @@ -196,6 +247,8 @@ enum HardwarePlatforms: String, CaseIterable, Identifiable { var description: String { switch self { + case .NONE: + return "None" case .ESP32: return "Expressif ESP 32" case .NRF52: diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 26a061bc..c5471674 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -420,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 diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index b824b407..fa9965c4 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -33,8 +33,8 @@ struct Firmware: View { let hwModel: HardwareModels = HardwareModels.allCases.first(where: { $0.rawValue == node?.user?.hwModel ?? "UNSET" }) ?? HardwareModels.UNSET VStack(alignment: .leading) { - Text("Firmware Version: \(node?.metadata?.firmwareVersion ?? "Unknown")") - .font(.title3) + Text("Current Version: \(bleManager.connectedVersion)") + .font(.largeTitle) Text("Your device supports the following firmware: ") .font(.callout) HStack { @@ -44,19 +44,23 @@ struct Firmware: View { } .padding(.bottom) - if hwModel == HardwareModels.RAK4631 { + if hwModel.platform() == HardwarePlatforms.NRF52 { VStack(alignment: .leading) { - Text("nRF 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) + 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 == HardwareModels.T_ECHO { - Text("OTA Updates are not supported on the this NRF Device.") - .font(.title3) - } else { + } else if hwModel.platform() == HardwarePlatforms.ESP32 { VStack(alignment: .leading) { Text("ESP32 Device Firmware Update") .font(.title3) @@ -88,13 +92,12 @@ struct Firmware: View { Spacer() } } + } else { + Text("OTA Updates are not supported on your platform.") + .font(.title3) } + }.padding() - - - - - VStack(alignment: .leading) { Text("Firmware Releases") .font(.title3) @@ -141,6 +144,7 @@ struct Firmware: View { } } } + .padding(.bottom, 5) .onAppear(perform: loadData) .navigationTitle("Firmware Updates") .navigationBarTitleDisplayMode(.inline)