diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 7eeb0ddf..27b04646 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1188,17 +1188,19 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { return 0 } - public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { + public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.lora = config var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(connectedPeripheral.num) - meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. 0 { + meshPacket.channel = UInt32(adminIndex) + } var dataMessage = DataMessage() dataMessage.payload = try! adminPacket.serializedData() dataMessage.portnum = PortNum.adminApp @@ -1392,6 +1394,35 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { return false } + public func getLoRaConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getConfigRequest = AdminMessage.ConfigType.loraConfig + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 9d874f35..d95c2126 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -184,59 +184,7 @@ func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64 if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.lora.config %@", comment: "LoRa config received: %@"), String(nodeNum)) - MeshLogger.log("📻 \(logString)") - - 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() - print("💾 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)") - } + upsertLoraConfigPacket(config: config, nodeNum: nodeNum, context: context) } if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { @@ -911,7 +859,6 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].snr = nodeInfo.snr fetchedNode[0].channel = Int32(channel) - if nodeInfo.hasUser { fetchedNode[0].user!.userId = nodeInfo.user.id @@ -1074,10 +1021,14 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { 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) { - print(try! adminMessage.getDeviceMetadataResponse.jsonString()) - - + if let config = try? Config(serializedData: packet.decoded.payload) { + + if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { + upsertLoraConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) + } + } } } } diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 40bc81b9..a97c70ce 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -100,3 +100,61 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext) { } } } + +func upsertLoraConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.lora.config %@", comment: "LoRa config received: %@"), String(nodeNum)) + MeshLogger.log("📻 \(logString)") + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", 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() + context.refreshAllObjects() + print("💾 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)") + } +} diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index d9e29e1b..fded4ec7 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -111,7 +111,7 @@ struct Connect: View { if isUnsetRegion { HStack { NavigationLink { - LoRaConfig(node: node) + LoRaConfig(node: node, connectedNode: node) } label: { Label("set.region", systemImage: "globe.americas.fill") .foregroundColor(.red) diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index dc4ea18e..ac4d277a 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -14,6 +14,7 @@ struct LoRaConfig: View { @Environment(\.dismiss) private var goBack var node: NodeInfoEntity? + var connectedNode: NodeInfoEntity? @State var isPresentingSaveConfirm = false @State var hasChanges = false @@ -75,7 +76,7 @@ struct LoRaConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { var lc = Config.LoRaConfig() @@ -84,7 +85,7 @@ struct LoRaConfig: View { lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue() lc.usePreset = true lc.txEnabled = true - let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save @@ -102,14 +103,22 @@ struct LoRaConfig: View { ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { + self.bleManager.context = context - self.hopLimit = Int(node?.loRaConfig?.hopLimit ?? 0) + self.hopLimit = Int(node?.loRaConfig?.hopLimit ?? 3) self.region = Int(node?.loRaConfig?.regionCode ?? 0) self.usePreset = node?.loRaConfig?.usePreset ?? true self.modemPreset = Int(node?.loRaConfig?.modemPreset ?? 0) self.txEnabled = node?.loRaConfig?.txEnabled ?? true self.txPower = Int(node?.loRaConfig?.txPower ?? 0) self.hasChanges = false + + // Need to request a LoRaConfig from the remote node before allowing changes + if node?.loRaConfig == nil { + + let adminMessageId = bleManager.getLoRaConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + } + } .onChange(of: region) { newRegion in if node != nil && node!.loRaConfig != nil { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index e3b953ca..647242dc 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -86,7 +86,7 @@ struct Settings: View { .disabled(selectedNode == 0) NavigationLink() { - LoRaConfig(node: nodes.first(where: { $0.num == selectedNode })) + LoRaConfig(node: nodes.first(where: { $0.num == selectedNode }), connectedNode: nodes.first(where: { $0.num == connectedNodeNum })) } label: { Image(systemName: "dot.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical)