// // MeshPackets.swift // Meshtastic Apple // // Created by Garth Vander Houwen on 5/27/22. // import Foundation import CoreData import SwiftUI func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { // We don't care about any of the Power settings, config is available for everyting else if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { MeshLogger.log("πŸ–₯️ Bluetooth config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Device Config if !fetchedNode.isEmpty { if fetchedNode[0].bluetoothConfig == nil { let newBluetoothConfig = BluetoothConfigEntity(context: context) newBluetoothConfig.enabled = config.bluetooth.enabled newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue) newBluetoothConfig.fixedPin = Int32(config.bluetooth.fixedPin) fetchedNode[0].bluetoothConfig = newBluetoothConfig } else { fetchedNode[0].bluetoothConfig?.enabled = config.bluetooth.enabled fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue) fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.bluetooth.fixedPin) } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Bluetooth Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data BluetoothConfigEntity: \(nsError)") } } else { MeshLogger.log("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Bluetooth Config") } } catch { let nsError = error as NSError MeshLogger.log("πŸ’₯ Fetching node for core data BluetoothConfigEntity failed: \(nsError)") } } if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { MeshLogger.log("πŸ“Ÿ Device config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Device Config if !fetchedNode.isEmpty { if fetchedNode[0].deviceConfig == nil { let newDeviceConfig = DeviceConfigEntity(context: context) newDeviceConfig.role = Int32(config.device.role.rawValue) newDeviceConfig.serialEnabled = config.device.serialEnabled newDeviceConfig.debugLogEnabled = config.device.debugLogEnabled fetchedNode[0].deviceConfig = newDeviceConfig } else { fetchedNode[0].deviceConfig?.role = Int32(config.device.role.rawValue) fetchedNode[0].deviceConfig?.serialEnabled = config.device.serialEnabled fetchedNode[0].deviceConfig?.debugLogEnabled = config.device.debugLogEnabled } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Device Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data DeviceConfigEntity: \(nsError)") } } } catch { let nsError = error as NSError MeshLogger.log("πŸ’₯ Fetching node for core data DeviceConfigEntity failed: \(nsError)") } } if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) { MeshLogger.log("πŸ–₯️ Display config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Device Config if !fetchedNode.isEmpty { if fetchedNode[0].displayConfig == nil { let newDisplayConfig = DisplayConfigEntity(context: context) newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue) newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs) newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) newDisplayConfig.compassNorthTop = config.display.compassNorthTop newDisplayConfig.flipScreen = config.display.flipScreen fetchedNode[0].displayConfig = newDisplayConfig } else { fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue) fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs) fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop fetchedNode[0].displayConfig?.flipScreen = config.display.flipScreen } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Display Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Updating Core Data DisplayConfigEntity: \(nsError)") } } else { print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Display Config") } } catch { let nsError = error as NSError print("πŸ’₯ Fetching node for core data DisplayConfigEntity failed: \(nsError)") } } if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { MeshLogger.log("πŸ“» LoRa config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save LoRa Config if !fetchedNode.isEmpty { if fetchedNode[0].loRaConfig == nil { let newLoRaConfig = LoRaConfigEntity(context: context) newLoRaConfig.regionCode = Int32(config.lora.region.rawValue) newLoRaConfig.usePreset = config.lora.usePreset newLoRaConfig.modemPreset = Int32(config.lora.modemPreset.rawValue) newLoRaConfig.bandwidth = Int32(config.lora.bandwidth) newLoRaConfig.spreadFactor = Int32(config.lora.spreadFactor) newLoRaConfig.codingRate = Int32(config.lora.codingRate) newLoRaConfig.frequencyOffset = config.lora.frequencyOffset newLoRaConfig.hopLimit = Int32(config.lora.hopLimit) newLoRaConfig.txPower = Int32(config.lora.txPower) newLoRaConfig.txEnabled = config.lora.txEnabled newLoRaConfig.channelNum = Int32(config.lora.channelNum) fetchedNode[0].loRaConfig = newLoRaConfig } else { fetchedNode[0].loRaConfig?.regionCode = Int32(config.lora.region.rawValue) fetchedNode[0].loRaConfig?.usePreset = config.lora.usePreset fetchedNode[0].loRaConfig?.modemPreset = Int32(config.lora.modemPreset.rawValue) fetchedNode[0].loRaConfig?.bandwidth = Int32(config.lora.bandwidth) fetchedNode[0].loRaConfig?.spreadFactor = Int32(config.lora.spreadFactor) fetchedNode[0].loRaConfig?.codingRate = Int32(config.lora.codingRate) fetchedNode[0].loRaConfig?.frequencyOffset = config.lora.frequencyOffset fetchedNode[0].loRaConfig?.hopLimit = Int32(config.lora.hopLimit) fetchedNode[0].loRaConfig?.txPower = Int32(config.lora.txPower) fetchedNode[0].loRaConfig?.txEnabled = config.lora.txEnabled fetchedNode[0].loRaConfig?.channelNum = Int32(config.lora.channelNum) } do { try context.save() MeshLogger.log("πŸ’Ύ Updated LoRa Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Updating Core Data LoRaConfigEntity: \(nsError)") } } else { print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Lora Config") } } catch { let nsError = error as NSError print("πŸ’₯ Fetching node for core data LoRaConfigEntity failed: \(nsError)") } } if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { MeshLogger.log("πŸ“Ά Network config received \(String(nodeNum))") print(try! config.network.jsonString()) let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save WiFi Config if !fetchedNode.isEmpty { if fetchedNode[0].networkConfig == nil { let newNetworkConfig = NetworkConfigEntity(context: context) newNetworkConfig.wifiSsid = config.network.wifiSsid newNetworkConfig.wifiPsk = config.network.wifiPsk fetchedNode[0].networkConfig = newNetworkConfig } else { fetchedNode[0].networkConfig?.wifiSsid = config.network.wifiSsid fetchedNode[0].networkConfig?.wifiPsk = config.network.wifiPsk } do { try context.save() MeshLogger.log("πŸ’Ύ Updated WiFi Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Updating Core Data WiFiConfigEntity: \(nsError)") } } else { print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save WiFi Config") } } catch { let nsError = error as NSError print("πŸ’₯ Fetching node for core data WiFiConfigEntity failed: \(nsError)") } } if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { MeshLogger.log("πŸ—ΊοΈ Position config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save LoRa Config if !fetchedNode.isEmpty { if fetchedNode[0].positionConfig == nil { let newPositionConfig = PositionConfigEntity(context: context) newPositionConfig.smartPositionEnabled = config.position.positionBroadcastSmartEnabled newPositionConfig.deviceGpsEnabled = config.position.gpsEnabled newPositionConfig.fixedPosition = config.position.fixedPosition newPositionConfig.gpsUpdateInterval = Int32(config.position.gpsUpdateInterval) newPositionConfig.gpsAttemptTime = Int32(config.position.gpsAttemptTime) newPositionConfig.positionBroadcastSeconds = Int32(config.position.positionBroadcastSecs) newPositionConfig.positionFlags = Int32(config.position.positionFlags) fetchedNode[0].positionConfig = newPositionConfig } else { fetchedNode[0].positionConfig?.smartPositionEnabled = config.position.positionBroadcastSmartEnabled fetchedNode[0].positionConfig?.deviceGpsEnabled = config.position.gpsEnabled fetchedNode[0].positionConfig?.fixedPosition = config.position.fixedPosition fetchedNode[0].positionConfig?.gpsUpdateInterval = Int32(config.position.gpsUpdateInterval) fetchedNode[0].positionConfig?.gpsAttemptTime = Int32(config.position.gpsAttemptTime) fetchedNode[0].positionConfig?.positionBroadcastSeconds = Int32(config.position.positionBroadcastSecs) fetchedNode[0].positionConfig?.positionFlags = Int32(config.position.positionFlags) } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Position Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data PositionConfigEntity: \(nsError)") } } else { MeshLogger.log("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Position Config") } } catch { let nsError = error as NSError MeshLogger.log("πŸ’₯ Fetching node for core data PositionConfigEntity failed: \(nsError)") } } } func moduleConfig (config: ModuleConfig, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) { MeshLogger.log("πŸ₯« Canned Message module config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Canned Message Config if !fetchedNode.isEmpty { if fetchedNode[0].cannedMessageConfig == nil { let newCannedMessageConfig = CannedMessageConfigEntity(context: context) newCannedMessageConfig.enabled = config.cannedMessage.enabled newCannedMessageConfig.sendBell = config.cannedMessage.sendBell newCannedMessageConfig.rotary1Enabled = config.cannedMessage.rotary1Enabled newCannedMessageConfig.updown1Enabled = config.cannedMessage.updown1Enabled newCannedMessageConfig.inputbrokerPinA = Int32(config.cannedMessage.inputbrokerPinA) newCannedMessageConfig.inputbrokerPinB = Int32(config.cannedMessage.inputbrokerPinB) newCannedMessageConfig.inputbrokerPinPress = Int32(config.cannedMessage.inputbrokerPinPress) newCannedMessageConfig.inputbrokerEventCw = Int32(config.cannedMessage.inputbrokerEventCw.rawValue) newCannedMessageConfig.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) newCannedMessageConfig.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) fetchedNode[0].cannedMessageConfig = newCannedMessageConfig } else { fetchedNode[0].cannedMessageConfig?.enabled = config.cannedMessage.enabled fetchedNode[0].cannedMessageConfig?.sendBell = config.cannedMessage.sendBell fetchedNode[0].cannedMessageConfig?.rotary1Enabled = config.cannedMessage.rotary1Enabled fetchedNode[0].cannedMessageConfig?.updown1Enabled = config.cannedMessage.updown1Enabled fetchedNode[0].cannedMessageConfig?.inputbrokerPinA = Int32(config.cannedMessage.inputbrokerPinA) fetchedNode[0].cannedMessageConfig?.inputbrokerPinB = Int32(config.cannedMessage.inputbrokerPinB) fetchedNode[0].cannedMessageConfig?.inputbrokerPinPress = Int32(config.cannedMessage.inputbrokerPinPress) fetchedNode[0].cannedMessageConfig?.inputbrokerEventCw = Int32(config.cannedMessage.inputbrokerEventCw.rawValue) fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Canned Message Module Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data CannedMessageConfigEntity: \(nsError)") } } else { MeshLogger.log("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Canned Message Module Config") } } catch { let nsError = error as NSError MeshLogger.log("πŸ’₯ Fetching node for core data CannedMessageConfigEntity failed: \(nsError)") } } if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) { MeshLogger.log("🚨 External Notifiation module config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save External Notificaitone Config if !fetchedNode.isEmpty { if fetchedNode[0].externalNotificationConfig == nil { let newExternalNotificationConfig = ExternalNotificationConfigEntity(context: context) newExternalNotificationConfig.enabled = config.externalNotification.enabled newExternalNotificationConfig.alertBell = config.externalNotification.alertBell newExternalNotificationConfig.alertMessage = config.externalNotification.alertMessage newExternalNotificationConfig.active = config.externalNotification.active newExternalNotificationConfig.output = Int32(config.externalNotification.output) newExternalNotificationConfig.outputMilliseconds = Int32(config.externalNotification.outputMs) fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig } else { fetchedNode[0].externalNotificationConfig?.enabled = config.externalNotification.enabled fetchedNode[0].externalNotificationConfig?.alertBell = config.externalNotification.alertBell fetchedNode[0].externalNotificationConfig?.alertMessage = config.externalNotification.alertMessage fetchedNode[0].externalNotificationConfig?.active = config.externalNotification.active fetchedNode[0].externalNotificationConfig?.output = Int32(config.externalNotification.output) fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.externalNotification.outputMs) } do { try context.save() MeshLogger.log("πŸ’Ύ Updated External Notification Module Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data ExternalNotificationConfigEntity: \(nsError)") } } else { MeshLogger.log("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save External Notifiation Module Config") } } catch { let nsError = error as NSError print("πŸ’₯ Fetching node for core data ExternalNotificationConfigEntity failed: \(nsError)") } } if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(config.mqtt) { MeshLogger.log("🌐 MQTT module config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save MQTT Config if !fetchedNode.isEmpty { if fetchedNode[0].mqttConfig == nil { let newMQTTConfig = MQTTConfigEntity(context: context) newMQTTConfig.enabled = config.mqtt.enabled newMQTTConfig.address = config.mqtt.address newMQTTConfig.address = config.mqtt.username newMQTTConfig.password = config.mqtt.password newMQTTConfig.encryptionEnabled = config.mqtt.encryptionEnabled newMQTTConfig.jsonEnabled = config.mqtt.jsonEnabled fetchedNode[0].mqttConfig = newMQTTConfig } else { fetchedNode[0].mqttConfig?.enabled = config.mqtt.enabled fetchedNode[0].mqttConfig?.address = config.mqtt.address fetchedNode[0].mqttConfig?.address = config.mqtt.username fetchedNode[0].mqttConfig?.password = config.mqtt.password fetchedNode[0].mqttConfig?.encryptionEnabled = config.mqtt.encryptionEnabled fetchedNode[0].mqttConfig?.jsonEnabled = config.mqtt.jsonEnabled } do { try context.save() MeshLogger.log("πŸ’Ύ Updated MQTT Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data MQTTConfigEntity: \(nsError)") } } else { MeshLogger.log("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save MQTT Module Config") } } catch { let nsError = error as NSError MeshLogger.log("πŸ’₯ Fetching node for core data MQTTConfigEntity failed: \(nsError)") } } if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(config.rangeTest) { MeshLogger.log("⛰️ Range Test module config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Device Config if !fetchedNode.isEmpty { if fetchedNode[0].rangeTestConfig == nil { let newRangeTestConfig = RangeTestConfigEntity(context: context) newRangeTestConfig.sender = Int32(config.rangeTest.sender) newRangeTestConfig.enabled = config.rangeTest.enabled newRangeTestConfig.save = config.rangeTest.save fetchedNode[0].rangeTestConfig = newRangeTestConfig } else { fetchedNode[0].rangeTestConfig?.sender = Int32(config.rangeTest.sender) fetchedNode[0].rangeTestConfig?.enabled = config.rangeTest.enabled fetchedNode[0].rangeTestConfig?.save = config.rangeTest.save } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Range Test Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Updating Core Data RangeTestConfigEntity: \(nsError)") } } else { MeshLogger.log("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Range Test Module Config") } } catch { let nsError = error as NSError MeshLogger.log("πŸ’₯ Fetching node for core data RangeTestConfigEntity failed: \(nsError)") } } if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(config.serial) { MeshLogger.log("πŸ€– Serial module config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Device Config if !fetchedNode.isEmpty { if fetchedNode[0].serialConfig == nil { let newSerialConfig = SerialConfigEntity(context: context) newSerialConfig.enabled = config.serial.enabled newSerialConfig.echo = config.serial.echo newSerialConfig.rxd = Int32(config.serial.rxd) newSerialConfig.txd = Int32(config.serial.txd) newSerialConfig.baudRate = Int32(config.serial.baud.rawValue) newSerialConfig.timeout = Int32(config.serial.timeout) newSerialConfig.mode = Int32(config.serial.mode.rawValue) fetchedNode[0].serialConfig = newSerialConfig } else { fetchedNode[0].serialConfig?.enabled = config.serial.enabled fetchedNode[0].serialConfig?.echo = config.serial.echo fetchedNode[0].serialConfig?.rxd = Int32(config.serial.rxd) fetchedNode[0].serialConfig?.txd = Int32(config.serial.txd) fetchedNode[0].serialConfig?.baudRate = Int32(config.serial.baud.rawValue) fetchedNode[0].serialConfig?.timeout = Int32(config.serial.timeout) fetchedNode[0].serialConfig?.mode = Int32(config.serial.mode.rawValue) } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Serial Module Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Updating Core Data SerialConfigEntity: \(nsError)") } } else { print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Serial Module Config") } } catch { let nsError = error as NSError print("πŸ’₯ Fetching node for core data SerialConfigEntity failed: \(nsError)") } } if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(config.telemetry) { MeshLogger.log("πŸ“ˆ Telemetry module config received: \(String(nodeNum))") let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // Found a node, save Telemetry Config if !fetchedNode.isEmpty { if fetchedNode[0].telemetryConfig == nil { let newTelemetryConfig = TelemetryConfigEntity(context: context) newTelemetryConfig.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) newTelemetryConfig.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) newTelemetryConfig.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled newTelemetryConfig.environmentScreenEnabled = config.telemetry.environmentScreenEnabled newTelemetryConfig.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit fetchedNode[0].telemetryConfig = newTelemetryConfig } else { fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.telemetry.environmentScreenEnabled fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit } do { try context.save() MeshLogger.log("πŸ’Ύ Updated Telemetry Module Config for node number: \(String(nodeNum))") } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Updating Core Data TelemetryConfigEntity: \(nsError)") } } else { print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Telemetry Module Config") } } catch { let nsError = error as NSError print("πŸ’₯ Fetching node for core data TelemetryConfigEntity failed: \(nsError)") } } } func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedObjectContext) -> MyInfoEntity? { let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(myInfo.myNodeNum)) do { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] // Not Found Insert if fetchedMyInfo.isEmpty { let myInfoEntity = MyInfoEntity(context: context) myInfoEntity.peripheralId = peripheralId myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum) myInfoEntity.hasGps = myInfo.hasGps_p myInfoEntity.hasWifi = myInfo.hasWifi_p myInfoEntity.bitrate = myInfo.bitrate // Swift does strings weird, this does work to get the version without the github hash let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".") var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))] version = version.dropLast() myInfoEntity.firmwareVersion = String(version) myInfoEntity.messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) myInfoEntity.minAppVersion = Int32(bitPattern: myInfo.minAppVersion) myInfoEntity.maxChannels = Int32(bitPattern: myInfo.maxChannels) do { try context.save() MeshLogger.log("πŸ’Ύ Saved a new myInfo for node number: \(String(myInfo.myNodeNum))") return myInfoEntity } catch { context.rollback() let nsError = error as NSError 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 fetchedMyInfo[0].bitrate = myInfo.bitrate let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1) var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset:6, in: myInfo.firmwareVersion))] version = version.dropLast() fetchedMyInfo[0].firmwareVersion = String(version) 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() MeshLogger.log("πŸ’Ύ Updated myInfo for node number: \(String(myInfo.myNodeNum))") return fetchedMyInfo[0] } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Updating Core Data MyInfoEntity: \(nsError)") } } } catch { print("πŸ’₯ Fetch MyInfo Error") } return nil } func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectContext) { if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled { let fetchedMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum) do { let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as! [MyInfoEntity] if fetchedMyInfo.count == 1 { let newChannel = ChannelEntity(context: context) newChannel.index = Int32(channel.index) newChannel.uplinkEnabled = channel.settings.uplinkEnabled newChannel.downlinkEnabled = channel.settings.downlinkEnabled newChannel.name = channel.settings.name newChannel.role = Int32(channel.role.rawValue) newChannel.psk = channel.settings.psk let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet if mutableChannels.contains(newChannel) { mutableChannels.replaceObject(at: Int(newChannel.index), with: newChannel) } else { mutableChannels.add(newChannel) } fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet do { try context.save() } catch { print("Failed to save channel") } MeshLogger.log("πŸ’Ύ Updated MyInfo channel \(channel.index) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)") } else if channel.role.rawValue > 0 { print("πŸ’₯ Trying to save a channel to a MyInfo that does not exist: \(fromNum)") } } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Saving MyInfo Channel from ADMIN_APP \(nsError)") } } } func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? { let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeInfo.num)) do { let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] // 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) telemetry.voltage = nodeInfo.deviceMetrics.voltage telemetry.channelUtilization = nodeInfo.deviceMetrics.channelUtilization telemetry.airUtilTx = nodeInfo.deviceMetrics.airUtilTx var newTelemetries = [TelemetryEntity]() newTelemetries.append(telemetry) newNode.telemetries? = NSOrderedSet(array: newTelemetries) } newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) newNode.snr = nodeInfo.snr if nodeInfo.hasUser { let newUser = UserEntity(context: context) newUser.userId = nodeInfo.user.id newUser.num = Int64(nodeInfo.num) newUser.longName = nodeInfo.user.longName newUser.shortName = nodeInfo.user.shortName newUser.macaddr = nodeInfo.user.macaddr newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() newNode.user = newUser } if nodeInfo.position.latitudeI > 0 || nodeInfo.position.longitudeI > 0 { let position = PositionEntity(context: context) position.seqNo = Int32(nodeInfo.position.seqNumber) position.latitudeI = nodeInfo.position.latitudeI position.longitudeI = nodeInfo.position.longitudeI position.altitude = nodeInfo.position.altitude position.satsInView = Int32(nodeInfo.position.satsInView) position.speed = Int32(nodeInfo.position.groundSpeed) position.heading = Int32(nodeInfo.position.groundTrack) position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time))) var newPostions = [PositionEntity]() 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 { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] if fetchedMyInfo.count > 0 { newNode.myInfo = fetchedMyInfo[0] } do { try context.save() if nodeInfo.hasUser { MeshLogger.log("πŸ’Ύ BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.user.longName)") } else { MeshLogger.log("πŸ’Ύ BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.num)") } return newNode } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Saving Core Data NodeInfoEntity: \(nsError)") } } catch { 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 fetchedNode[0].user!.shortName = nodeInfo.user.shortName 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 newTelemetry.channelUtilization = nodeInfo.deviceMetrics.channelUtilization newTelemetry.airUtilTx = nodeInfo.deviceMetrics.airUtilTx let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet } if nodeInfo.hasPosition { let position = PositionEntity(context: context) position.latitudeI = nodeInfo.position.latitudeI position.longitudeI = nodeInfo.position.longitudeI 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 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 { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] if fetchedMyInfo.count > 0 { fetchedNode[0].myInfo = fetchedMyInfo[0] } do { try context.save() if nodeInfo.hasUser { MeshLogger.log("πŸ’Ύ BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.user.longName)") } else { MeshLogger.log("πŸ’Ύ BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.num)") } return fetchedNode[0] } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Saving Core Data NodeInfoEntity: \(nsError)") } } catch { print("πŸ’₯ Fetch MyInfo Error") } } } catch { print("πŸ’₯ Fetch NodeInfoEntity Error") } return nil } func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { let fetchNodeInfoAppRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) do { let fetchedNode = try context.fetch(fetchNodeInfoAppRequest) as! [NodeInfoEntity] if fetchedNode.count == 1 { fetchedNode[0].id = Int64(packet.from) fetchedNode[0].num = Int64(packet.from) fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime))) fetchedNode[0].snr = packet.rxSnr fetchedNode[0].channel = Int32(packet.channel) if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) { if nodeInfoMessage.hasDeviceMetrics { let telemetry = TelemetryEntity(context: context) telemetry.batteryLevel = Int32(nodeInfoMessage.deviceMetrics.batteryLevel) telemetry.voltage = nodeInfoMessage.deviceMetrics.voltage telemetry.channelUtilization = nodeInfoMessage.deviceMetrics.channelUtilization telemetry.airUtilTx = nodeInfoMessage.deviceMetrics.airUtilTx var newTelemetries = [TelemetryEntity]() newTelemetries.append(telemetry) fetchedNode[0].telemetries? = NSOrderedSet(array: newTelemetries) } if nodeInfoMessage.hasUser { fetchedNode[0].user!.userId = nodeInfoMessage.user.id fetchedNode[0].user!.num = Int64(nodeInfoMessage.num) fetchedNode[0].user!.longName = nodeInfoMessage.user.longName fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName fetchedNode[0].user!.macaddr = nodeInfoMessage.user.macaddr fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased() } } } else { // New node info not from device but potentially from another network } do { try context.save() MeshLogger.log("πŸ’Ύ Updated NodeInfo from Node Info App Packet For: \(fetchedNode[0].num)") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)") } } catch { MeshLogger.log("πŸ’₯ Error Fetching NodeInfoEntity for NODEINFO_APP") } } func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { print(try! packet.decoded.jsonString()) } func positionPacket (packet: MeshPacket, context: NSManagedObjectContext) { let fetchNodePositionRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) do { if let positionMessage = try? Position(serializedData: packet.decoded.payload) { // Don't save empty position packets if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 { let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity] if fetchedNode.count == 1 { let position = PositionEntity(context: context) position.snr = packet.rxSnr position.seqNo = Int32(positionMessage.seqNumber) position.latitudeI = positionMessage.latitudeI position.longitudeI = positionMessage.longitudeI position.altitude = positionMessage.altitude position.satsInView = Int32(positionMessage.satsInView) position.speed = Int32(positionMessage.groundSpeed) position.heading = Int32(positionMessage.groundTrack) if positionMessage.timestamp != 0 { position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.timestamp))) } else { position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time))) } let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet mutablePositions.add(position) fetchedNode[0].id = Int64(packet.from) fetchedNode[0].num = Int64(packet.from) fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time))) fetchedNode[0].snr = packet.rxSnr fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet do { try context.save() MeshLogger.log("πŸ’Ύ Updated Node Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)") } catch { context.rollback() let nsError = error as NSError print("πŸ’₯ Error Saving NodeInfoEntity from POSITION_APP \(nsError)") } } } else { print("πŸ’₯ Empty POSITION_APP Packet") print(try! packet.jsonString()) if let dataMessage = try? DataMessage(serializedData: packet.decoded.payload) { print(dataMessage) } } } } catch { print("πŸ’₯ Error Fetching NodeInfoEntity for POSITION_APP") } } func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSManagedObjectContext) { print("Routing packet", packet) if let routingMessage = try? Routing(serializedData: packet.decoded.payload) { let error = routingMessage.errorReason var errorExplanation = "Unknown Routing Error" switch error { case Routing.Error.none: errorExplanation = "This message is not a failure" case Routing.Error.noRoute: errorExplanation = "Our node doesn't have a route to the requested destination anymore." case Routing.Error.gotNak: errorExplanation = "We received a nak while trying to forward on your behalf" case Routing.Error.timeout: errorExplanation = "Timeout" case Routing.Error.noInterface: errorExplanation = "No suitable interface could be found for delivering this packet" case Routing.Error.maxRetransmit: errorExplanation = "We reached the max retransmission count (typically for naive flood routing)" case Routing.Error.noChannel: errorExplanation = "No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)" case Routing.Error.tooLarge: errorExplanation = "The packet was too big for sending (exceeds interface MTU after encoding)" case Routing.Error.noResponse: errorExplanation = "The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions)" case Routing.Error.badRequest: errorExplanation = "The application layer service on the remote node received your request, but considered your request somehow invalid" case Routing.Error.notAuthorized: errorExplanation = "The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel)" fallthrough default: () } MeshLogger.log("πŸ•ΈοΈ ROUTING PACKET received for RequestID: \(packet.decoded.requestID) Error: \(errorExplanation)") 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 { 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 { let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", connectedNodeNum) 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 } try context.save() MeshLogger.log("πŸ’Ύ ACK Received and saved for MessageID \(packet.decoded.requestID)") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)") } } } func telemetryPacket(packet: MeshPacket, context: NSManagedObjectContext) { if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) { let telemetry = TelemetryEntity(context: context) let fetchNodeTelemetryRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) do { let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) as! [NodeInfoEntity] if fetchedNode.count == 1 { if telemetryMessage.variant == Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) { // Device Metrics telemetry.airUtilTx = telemetryMessage.deviceMetrics.airUtilTx telemetry.channelUtilization = telemetryMessage.deviceMetrics.channelUtilization telemetry.batteryLevel = Int32(telemetryMessage.deviceMetrics.batteryLevel) telemetry.voltage = telemetryMessage.deviceMetrics.voltage telemetry.metricsType = 0 } else if telemetryMessage.variant == Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) { // Environment Metrics telemetry.barometricPressure = telemetryMessage.environmentMetrics.barometricPressure telemetry.current = telemetryMessage.environmentMetrics.current telemetry.gasResistance = telemetryMessage.environmentMetrics.gasResistance telemetry.relativeHumidity = telemetryMessage.environmentMetrics.relativeHumidity telemetry.temperature = telemetryMessage.environmentMetrics.temperature telemetry.current = telemetryMessage.environmentMetrics.current telemetry.voltage = telemetryMessage.environmentMetrics.voltage telemetry.metricsType = 1 } telemetry.time = Date(timeIntervalSince1970: TimeInterval(Int64(telemetryMessage.time))) let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet mutableTelemetries.add(telemetry) fetchedNode[0].lastHeard = telemetry.time fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet } try context.save() MeshLogger.log("πŸ’Ύ Telemetry Saved for Node: \(packet.from)") } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Error Saving Telemetry for Node \(packet.from) Error: \(nsError)") } } else { MeshLogger.log("πŸ’₯ Error Fetching NodeInfoEntity for Node \(packet.from)") } } func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) { MeshLogger.log("πŸ’¬ Message received for text message app") let messageUsers: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from]) do { let fetchedUsers = try context.fetch(messageUsers) as! [UserEntity] let newMessage = MessageEntity(context: context) newMessage.messageId = Int64(packet.id) newMessage.messageTimestamp = Int32(bitPattern: packet.rxTime) newMessage.receivedACK = false newMessage.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 }) } if fetchedUsers.first(where: { $0.num == packet.from }) != nil { newMessage.fromUser = fetchedUsers.first(where: { $0.num == packet.from }) } newMessage.messagePayload = messageText newMessage.fromUser?.objectWillChange.send() newMessage.toUser?.objectWillChange.send() var messageSaved = false do { try context.save() MeshLogger.log("πŸ’Ύ Saved a new message for \(newMessage.messageId)") messageSaved = true if messageSaved { if newMessage.fromUser != nil { // Create an iOS Notification for the received message and schedule it immediately let manager = LocalNotificationManager() manager.notifications = [ Notification( id: ("notification.id.\(newMessage.messageId)"), title: "\(newMessage.fromUser?.longName ?? "Unknown")", subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")", content: messageText) ] manager.schedule() MeshLogger.log("πŸ’¬ iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown")") } } } catch { context.rollback() let nsError = error as NSError MeshLogger.log("πŸ’₯ Failed to save new MessageEntity \(nsError)") } } catch { MeshLogger.log("πŸ’₯ Fetch Message To and From Users Error") } } }