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 {