diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 479f6dce..9bcc189e 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -839,7 +839,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.22; + MARKETING_VERSION = 1.3.23; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -871,7 +871,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.22; + MARKETING_VERSION = 1.3.23; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 738dec39..33ef4b6e 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -54,6 +54,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7") let FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5") let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453") + + // Meshtastic DFU details + let DFUSERVICE_UUID = CBUUID(string : "cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30") + let DFUSIZE_UUID = CBUUID(string: "e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e") + let DFUDATA_UUID = CBUUID(string: "e272ebac-d463-4b98-bc84-5cc1a39ee517") + let DFUCRC32_UUID = CBUUID(string: "4826129c-c22a-43a3-b066-ce8f0d5bacc6") + let DFURESULT_UUID = CBUUID(string: "5e134862-7411-4424-ac4a-210937432c77") + let DFUREGION_UUID = CBUUID(string: "5e134862-7411-4424-ac4a-210937432c67") + + var DFUSIZE_characteristic: CBCharacteristic? + var DFUDATA_characteristic: CBCharacteristic? + var DFUCRC32_characteristic: CBCharacteristic? + var DFURESULT_characteristic: CBCharacteristic? + var DFUREGION_characteristic: CBCharacteristic? private var meshLoggingEnabled: Bool = true let meshLog = documentsFolder.appendingPathComponent("meshlog.txt") @@ -217,7 +231,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph connectedPeripheral.peripheral.delegate = self // Discover Services - peripheral.discoverServices([meshtasticServiceCBUUID]) + peripheral.discoverServices([meshtasticServiceCBUUID, DFUSERVICE_UUID]) if meshLoggingEnabled { MeshLogger.log("✅ BLE Connected: \(peripheral.name ?? "Unknown")") } } @@ -296,7 +310,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if meshLoggingEnabled { MeshLogger.log("✅ BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")") } //peripheral.discoverCharacteristics(nil, for: service) peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID], for: service) - } + + } else if (service.uuid == DFUSERVICE_UUID) { + + print("✅ Meshtastic DFU service discovered OK") + if meshLoggingEnabled { MeshLogger.log("✅ BLE Service for Meshtastic DFU discovered by \(peripheral.name ?? "Unknown")") } + peripheral.discoverCharacteristics([DFUDATA_UUID, DFUSIZE_UUID, DFUREGION_UUID, DFURESULT_UUID, DFUCRC32_UUID], for: service) + + } } } @@ -340,6 +361,36 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") } FROMNUM_characteristic = characteristic peripheral.setNotifyValue(true, for: characteristic) + + case DFUSIZE_UUID: + + print("✅ DFU Size characteristic OK") + if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover DFU Size characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")") } + DFUSIZE_characteristic = characteristic + + case DFUDATA_UUID: + + print("✅ DFU Data characteristic OK") + if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover DFU Data characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")") } + DFUDATA_characteristic = characteristic + + case DFUCRC32_UUID: + + print("✅ DFU CRC32 characteristic OK") + if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover DFU CRC32 characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")") } + DFUCRC32_characteristic = characteristic + + case DFURESULT_UUID: + + print("✅ DFU Result characteristic OK") + if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover DFU Result characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")") } + DFURESULT_characteristic = characteristic + + case DFUREGION_UUID: + + print("✅ DFU Region characteristic OK") + if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover DFU Region characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")") } + DFUREGION_characteristic = characteristic default: break @@ -405,60 +456,65 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Handle Any local only packets we get over BLE case .unknownApp: - if decodedInfo.myInfo.myNodeNum != 0 { + var nowKnown = false + + // MyInfo + if decodedInfo.myInfo.isInitialized && decodedInfo.myInfo.myNodeNum > 0 { - let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!) + nowKnown = true + let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!) + + if myInfo != nil { - if myInfo != nil { - - self.connectedPeripheral.bitrate = myInfo!.bitrate - self.connectedPeripheral.num = myInfo!.myNodeNum - lastConnnectionVersion = myInfo?.firmwareVersion ?? myInfo!.firmwareVersion ?? "Unknown" - self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown" - self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown" - - } - - } else if decodedInfo.nodeInfo.num != 0 { + self.connectedPeripheral.bitrate = myInfo!.bitrate + self.connectedPeripheral.num = myInfo!.myNodeNum + lastConnnectionVersion = myInfo?.firmwareVersion ?? myInfo!.firmwareVersion ?? "Unknown" + self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown" + self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown" + self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown" - let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, meshLogging: meshLoggingEnabled, context: context!) + } + } + // NodeInfo + if decodedInfo.nodeInfo.num != 0 { + + nowKnown = true + let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, meshLogging: meshLoggingEnabled, context: context!) + + if nodeInfo != nil { - if nodeInfo != nil { - - self.connectedPeripheral.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization - self.connectedPeripheral.airTime = decodedInfo.nodeInfo.deviceMetrics.airUtilTx + self.connectedPeripheral.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization + self.connectedPeripheral.airTime = decodedInfo.nodeInfo.deviceMetrics.airUtilTx - if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num { + if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num { - if nodeInfo!.user != nil { - - connectedPeripheral.name = nodeInfo!.user!.longName ?? "Unknown" - connectedPeripheral.shortName = nodeInfo!.user!.shortName ?? "?????" - connectedPeripheral.longName = nodeInfo!.user!.longName ?? "Unknown" - } + if nodeInfo!.user != nil { + + connectedPeripheral.shortName = nodeInfo!.user!.shortName ?? "????" + connectedPeripheral.longName = nodeInfo!.user!.longName ?? "Unknown" } } - - } else if decodedInfo.config.isInitialized { - - localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName) - - } else if decodedInfo.moduleConfig.isInitialized { - - moduleConfig(config: decodedInfo.moduleConfig, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName) - - } else { - - if decodedInfo.configCompleteID == 0 { - - if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for App UNHANDLED \(try! decodedInfo.packet.jsonString())") } - } else { - - if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") } - } - - print(decodedInfo.moduleConfig.isInitialized) } + } + // Config + if decodedInfo.config.isInitialized { + + nowKnown = true + localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName) + + } + // Module Config + if decodedInfo.moduleConfig.isInitialized { + + nowKnown = true + moduleConfig(config: decodedInfo.moduleConfig, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName) + } + // Log any other unknownApp calls + if !nowKnown { + + if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") } + } + case .textMessageApp: textMessageAppPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), meshLogging: meshLoggingEnabled, context: context!) case .remoteHardwareApp: @@ -607,6 +663,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph newMessage.direction = "IN" newMessage.toUser = fetchedUsers.first(where: { $0.num == toUserNum }) newMessage.isEmoji = isEmoji + newMessage.admin = false if replyID > 0 { @@ -793,6 +850,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) + return true } @@ -834,7 +892,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } - public func saveUser(config: User, destNum: Int64, wantResponse: Bool) -> Bool { + public func saveUser(config: User, entity: UserEntity, wantResponse: Bool) -> Int64 { + + var newMessageId: Int64 = 0 var adminPacket = AdminMessage() adminPacket.setOwner = config @@ -860,13 +920,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let binaryData: Data = try! toRadio.serializedData() if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + + let newMessage = MessageEntity(context: context!) + newMessage.messageId = Int64(UInt32.random(in: UInt32(UInt8.max).. Bool { @@ -1047,20 +1129,19 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } - public func getChannelSet (destNum: Int64, wantResponse: Bool) -> Bool { + public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 { + + var newMessageId: Int64 = 0 var adminPacket = AdminMessage() - adminPacket.getChannelRequest = 1 - - + adminPacket.setModuleConfig.cannedMessage = config var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(connectedPeripheral.num) - meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.to = UInt32(toUser.num) + meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + public func saveExternalNotificationModuleConfig(config: ModuleConfig.ExternalNotificationConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 { + + var newMessageId: Int64 = 0 + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.externalNotification = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = 0 //UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + + var newMessageId: Int64 = 0 var adminPacket = AdminMessage() adminPacket.setModuleConfig.rangeTest = config var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(connectedPeripheral.num) - meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.to = UInt32(toUser.num) + meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + public func saveSerialModuleConfig(config: ModuleConfig.SerialConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 { + + var newMessageId: Int64 = 0 var adminPacket = AdminMessage() adminPacket.setModuleConfig.serial = config @@ -1145,12 +1332,93 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let binaryData: Data = try! toRadio.serializedData() if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + + let newMessage = MessageEntity(context: context!) + newMessage.messageId = Int64(UInt32.random(in: UInt32(UInt8.max).. Int64 { + + var newMessageId: Int64 = 0 + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.telemetry = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = 0 //UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. = 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) + + if isDefault { + + newCannedMessageConfig.enabled = false + newCannedMessageConfig.sendBell = false + newCannedMessageConfig.rotary1Enabled = false + newCannedMessageConfig.updown1Enabled = false + newCannedMessageConfig.inputbrokerPinA = 0 + newCannedMessageConfig.inputbrokerPinB = 0 + newCannedMessageConfig.inputbrokerPinPress = 0 + newCannedMessageConfig.inputbrokerEventCw = 0 + newCannedMessageConfig.inputbrokerEventCcw = 0 + newCannedMessageConfig.inputbrokerEventPress = 0 + + } else { + + 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 { + + if isDefault { + + fetchedNode[0].cannedMessageConfig?.enabled = false + fetchedNode[0].cannedMessageConfig?.sendBell = false + fetchedNode[0].cannedMessageConfig?.rotary1Enabled = false + fetchedNode[0].cannedMessageConfig?.updown1Enabled = false + fetchedNode[0].cannedMessageConfig?.inputbrokerPinA = 0 + fetchedNode[0].cannedMessageConfig?.inputbrokerPinB = 0 + fetchedNode[0].cannedMessageConfig?.inputbrokerPinPress = 0 + fetchedNode[0].cannedMessageConfig?.inputbrokerEventCw = 0 + fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = 0 + fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = 0 + + } 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() + if meshlogging { MeshLogger.log("💾 Updated Canned Message Module Config for node number: \(String(nodeNum))") } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Updating Core Data CannedMessageConfigEntity: \(nsError)") + } + } + + } catch { + + } + } + + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) { + + var isDefault = false + + if (try! config.externalNotification.jsonString()) == "{}" { + + isDefault = true + print("🚨 Default External Notifiation Module config") + } + + 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) + + if isDefault { + + + newExternalNotificationConfig.enabled = false + newExternalNotificationConfig.alertBell = false + newExternalNotificationConfig.alertMessage = false + newExternalNotificationConfig.active = false + newExternalNotificationConfig.output = 0 + newExternalNotificationConfig.outputMilliseconds = 0 + + + } else { + + 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 { + + if isDefault { + + fetchedNode[0].externalNotificationConfig?.enabled = false + fetchedNode[0].externalNotificationConfig?.alertBell = false + fetchedNode[0].externalNotificationConfig?.alertMessage = false + fetchedNode[0].externalNotificationConfig?.active = false + fetchedNode[0].externalNotificationConfig?.output = 0 + fetchedNode[0].externalNotificationConfig?.outputMilliseconds = 0 + + } 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() + if meshlogging { MeshLogger.log("💾 Updated External Notification Module Config for node number: \(String(nodeNum))") } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Updating Core Data ExternalNotificationConfigEntity: \(nsError)") + } + } + + } catch { + + } + } + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(config.rangeTest) { var isDefault = false @@ -363,7 +551,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj } else { newRangeTestConfig.sender = Int32(config.rangeTest.sender) - newRangeTestConfig.enabled = !config.rangeTest.enabled + newRangeTestConfig.enabled = config.rangeTest.enabled newRangeTestConfig.save = config.rangeTest.save } @@ -378,9 +566,9 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj fetchedNode[0].rangeTestConfig?.save = false } else { - // Client default protobuf value of 0 + fetchedNode[0].rangeTestConfig?.sender = Int32(config.rangeTest.sender) - fetchedNode[0].rangeTestConfig?.enabled = !config.rangeTest.enabled + fetchedNode[0].rangeTestConfig?.enabled = config.rangeTest.enabled fetchedNode[0].rangeTestConfig?.save = config.rangeTest.save } } @@ -403,6 +591,190 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj } } + + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(config.serial) { + + var isDefault = false + + if (try! config.serial.jsonString()) == "{}" { + + isDefault = true + print("🤖 Default Serial Module config") + } + + 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) + + if isDefault { + + newSerialConfig.enabled = false + newSerialConfig.echo = false + newSerialConfig.rxd = 0 + newSerialConfig.txd = 0 + newSerialConfig.baudRate = 0 + newSerialConfig.timeout = 0 + newSerialConfig.mode = 0 + + } else { + + 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 { + + if isDefault { + + fetchedNode[0].serialConfig?.enabled = false + fetchedNode[0].serialConfig?.echo = false + fetchedNode[0].serialConfig?.rxd = 0 + fetchedNode[0].serialConfig?.txd = 0 + fetchedNode[0].serialConfig?.baudRate = 0 + fetchedNode[0].serialConfig?.timeout = 0 + fetchedNode[0].serialConfig?.mode = 0 + + } 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() + if meshlogging { 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)") + } + } + + } catch { + + } + } + + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(config.telemetry) { + + var isDefault = false + + if (try! config.telemetry.jsonString()) == "{}" { + + isDefault = true + print("📈 Default Telemetry Module config") + } + + 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) + + if isDefault { + + newTelemetryConfig.deviceUpdateInterval = 0 + newTelemetryConfig.environmentUpdateInterval = 0 + newTelemetryConfig.environmentMeasurementEnabled = false + newTelemetryConfig.environmentSensorType = 0 + newTelemetryConfig.environmentScreenEnabled = false + newTelemetryConfig.environmentDisplayFahrenheit = false + newTelemetryConfig.environmentRecoveryInterval = 0 + newTelemetryConfig.environmentReadErrorCountThreshold = 0 + + } else { + + newTelemetryConfig.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) + newTelemetryConfig.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) + newTelemetryConfig.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled + newTelemetryConfig.environmentSensorType = Int32(config.telemetry.environmentSensorType.rawValue) + newTelemetryConfig.environmentScreenEnabled = config.telemetry.environmentScreenEnabled + newTelemetryConfig.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit + newTelemetryConfig.environmentRecoveryInterval = Int32(config.telemetry.environmentRecoveryInterval) + newTelemetryConfig.environmentReadErrorCountThreshold = Int32(config.telemetry.environmentReadErrorCountThreshold) + + } + + fetchedNode[0].telemetryConfig = newTelemetryConfig + + } else { + + if isDefault { + + fetchedNode[0].telemetryConfig?.deviceUpdateInterval = 0 + fetchedNode[0].telemetryConfig?.environmentUpdateInterval = 0 + fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = false + fetchedNode[0].telemetryConfig?.environmentSensorType = 0 + fetchedNode[0].telemetryConfig?.environmentScreenEnabled = false + fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = false + fetchedNode[0].telemetryConfig?.environmentRecoveryInterval = 0 + fetchedNode[0].telemetryConfig?.environmentReadErrorCountThreshold = 0 + + + } 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?.environmentSensorType = Int32(config.telemetry.environmentSensorType.rawValue) + fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.telemetry.environmentScreenEnabled + fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit + fetchedNode[0].telemetryConfig?.environmentRecoveryInterval = Int32(config.telemetry.environmentRecoveryInterval) + fetchedNode[0].telemetryConfig?.environmentReadErrorCountThreshold = Int32(config.telemetry.environmentReadErrorCountThreshold) + } + } + + do { + + try context.save() + if meshlogging { 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)") + } + } + + } catch { + + } + } } func myInfoPacket (myInfo: MyNodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> MyInfoEntity? { diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents index 8cb1191b..16c9eca6 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents @@ -1,7 +1,10 @@ - - + + + + + @@ -9,7 +12,7 @@ - + @@ -25,6 +28,16 @@ + + + + + + + + + + @@ -35,6 +48,7 @@ + @@ -78,15 +92,18 @@ - + + + + @@ -119,6 +136,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -144,21 +184,24 @@ - + + - + - + - + + + \ No newline at end of file diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index ee5043cd..d0b56c58 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -68,7 +68,7 @@ struct Connect: View { if bleManager.connectedPeripheral != nil { - Text(bleManager.connectedPeripheral.name).font(.title2) + Text(bleManager.connectedPeripheral.longName).font(.title2) } Text("BLE Name: ").font(.caption)+Text(bleManager.connectedPeripheral.peripheral.name ?? "Unknown") @@ -265,7 +265,7 @@ struct Connect: View { bluetoothOn: self.bleManager.isSwitchedOn, deviceConnected: self.bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.shortName : - "?????") + "????") } ) } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 9b4cc054..9e87f10c 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -464,7 +464,7 @@ struct UserMessageList: View { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") } } } diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 72f38ad2..acfa3103 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -345,7 +345,7 @@ struct NodeDetail: View { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") } ) .onAppear(perform: { diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 061852db..b8d715e4 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -126,7 +126,7 @@ struct NodeMap: View { bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : - "?????") + "????") }) .onAppear(perform: { diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index ba546345..bc410981 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -169,7 +169,7 @@ struct AppSettings: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 0ecf8732..eb29495f 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -166,7 +166,7 @@ struct DeviceConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index ffc883de..d76b31b3 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -220,7 +220,7 @@ struct DisplayConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index be7d8a59..635d0550 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -281,7 +281,7 @@ struct LoRaConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index eaf9981e..cabbc687 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -68,6 +68,28 @@ enum InputEventChars: Int, CaseIterable, Identifiable { } } } + func protoEnumValue() -> ModuleConfig.CannedMessageConfig.InputEventChar { + + switch self { + + case .keyNone: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyNone + case .keyUp: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyUp + case .keyDown: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyDown + case .keyLeft: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyLeft + case .keyRight: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyRight + case .keySelect: + return ModuleConfig.CannedMessageConfig.InputEventChar.keySelect + case .keyBack: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyBack + case .keyCancel: + return ModuleConfig.CannedMessageConfig.InputEventChar.keyCancel + } + } } struct CannedMessagesConfig: View { @@ -282,16 +304,18 @@ struct CannedMessagesConfig: View { cmc.inputbrokerPinA = UInt32(inputbrokerPinA) cmc.inputbrokerPinB = UInt32(inputbrokerPinB) cmc.inputbrokerPinPress = UInt32(inputbrokerPinPress) + cmc.inputbrokerEventCw = InputEventChars(rawValue: inputbrokerEventCw)!.protoEnumValue() + cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue() + cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue() - //if bleManager.saveRangeTestModuleConfig(config: rtc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node.user!, toUser: node.user!, wantResponse: true) + if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save hasChanges = false - - //} else { - - //} + } + } } @@ -300,19 +324,20 @@ struct CannedMessagesConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { if self.initialLoad{ self.bleManager.context = context - self.enabled = node.cannedMessagesConfig?.enabled ?? false - self.rotary1Enabled = node.cannedMessagesConfig?.rotary1Enabled ?? false - self.updown1Enabled = node.cannedMessagesConfig?.updown1Enabled ?? false - self.inputbrokerPinA = Int(node.cannedMessagesConfig?.inputbrokerPinA ?? 0) - self.inputbrokerPinB = Int(node.cannedMessagesConfig?.inputbrokerPinB ?? 0) - self.inputbrokerPinPress = Int(node.cannedMessagesConfig?.inputbrokerPinPress ?? 0) + self.enabled = node.cannedMessageConfig?.enabled ?? false + self.sendBell = node.cannedMessageConfig?.sendBell ?? false + self.rotary1Enabled = node.cannedMessageConfig?.rotary1Enabled ?? false + self.updown1Enabled = node.cannedMessageConfig?.updown1Enabled ?? false + self.inputbrokerPinA = Int(node.cannedMessageConfig?.inputbrokerPinA ?? 0) + self.inputbrokerPinB = Int(node.cannedMessageConfig?.inputbrokerPinB ?? 0) + self.inputbrokerPinPress = Int(node.cannedMessageConfig?.inputbrokerPinPress ?? 0) self.hasChanges = false self.initialLoad = false } @@ -336,17 +361,35 @@ struct CannedMessagesConfig: View { } .onChange(of: enabled) { newEnabled in - //if newEnabled != node.cannedMessagesConfig!.enabled { - - hasChanges = true - //} + if newEnabled != node.cannedMessageConfig!.enabled { hasChanges = true } } .onChange(of: sendBell) { newBell in - //if newBell != node.rangeTestConfig!.save { - - hasChanges = true - //} + if newBell != node.cannedMessageConfig!.sendBell { hasChanges = true } + } + .onChange(of: inputbrokerPinA) { newPinA in + + if newPinA != node.cannedMessageConfig!.inputbrokerPinA { hasChanges = true } + } + .onChange(of: inputbrokerPinB) { newPinB in + + if newPinB != node.cannedMessageConfig!.inputbrokerPinB { hasChanges = true } + } + .onChange(of: inputbrokerPinPress) { newPinPress in + + if newPinPress != node.cannedMessageConfig!.inputbrokerPinPress { hasChanges = true } + } + .onChange(of: inputbrokerEventCw) { newKeyA in + + if newKeyA != node.cannedMessageConfig!.inputbrokerEventCw { hasChanges = true } + } + .onChange(of: inputbrokerEventCcw) { newKeyB in + + if newKeyB != node.cannedMessageConfig!.inputbrokerEventCcw { hasChanges = true } + } + .onChange(of: inputbrokerEventPress) { newKeyPress in + + if newKeyPress != node.cannedMessageConfig!.inputbrokerEventPress { hasChanges = true } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 0340c876..8a52e813 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -63,9 +63,6 @@ struct ExternalNotificationConfig: View { @State var active = false @State var output = 0 @State var outputMilliseconds = 0 - - - var body: some View { @@ -161,15 +158,17 @@ struct ExternalNotificationConfig: View { enc.output = UInt32(output) enc.outputMs = UInt32(outputMilliseconds) - //if bleManager.saveRangeTestModuleConfig(config: rtc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node.user!, toUser: node.user!, wantResponse: true) + + if adminMessageId > 0{ // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save hasChanges = false - //} else { + } else { - //} + } } } @@ -178,11 +177,48 @@ struct ExternalNotificationConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context + + self.enabled = node.externalNotificationConfig?.enabled ?? false + self.alertBell = node.externalNotificationConfig?.alertBell ?? false + self.alertMessage = node.externalNotificationConfig?.alertMessage ?? false + self.active = node.externalNotificationConfig?.active ?? false + self.output = Int(node.externalNotificationConfig?.output ?? 0) + self.outputMilliseconds = Int(node.externalNotificationConfig?.outputMilliseconds ?? 0) + + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: enabled) { newEnabled in + + if newEnabled != node.externalNotificationConfig!.enabled { hasChanges = true } + } + .onChange(of: alertBell) { newAlertBell in + + if newAlertBell != node.externalNotificationConfig!.alertBell { hasChanges = true } + } + .onChange(of: alertMessage) { newAlertMessage in + + if newAlertMessage != node.externalNotificationConfig!.alertMessage { hasChanges = true } + } + .onChange(of: active) { newActuve in + + if newActuve != node.externalNotificationConfig!.active { hasChanges = true } + } + .onChange(of: output) { newOutput in + + if newOutput != node.externalNotificationConfig!.output { hasChanges = true } + } + .onChange(of: outputMilliseconds) { newOutputMs in + + if newOutputMs != node.externalNotificationConfig!.outputMilliseconds { hasChanges = true } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 309c00c4..8c7c672e 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -113,15 +113,17 @@ struct RangeTestConfig: View { rtc.save = save rtc.sender = UInt32(sender) - //if bleManager.saveRangeTestModuleConfig(config: rtc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node.user!, toUser: node.user!, wantResponse: true) + + if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true // for now just disable the button after a successful save hasChanges = false - //} else { + } else { - //} + } } } @@ -130,7 +132,7 @@ struct RangeTestConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { @@ -146,21 +148,24 @@ struct RangeTestConfig: View { } .onChange(of: enabled) { newEnabled in - //if newEnabled != node.rangeTestConfig!.enabled { + if newEnabled != node.rangeTestConfig!.enabled { hasChanges = true - //} + } } .onChange(of: save) { newSave in - //if newSave != node.rangeTestConfig!.save { + if newSave != node.rangeTestConfig!.save { hasChanges = true - //} + } } .onChange(of: sender) { newSender in - hasChanges = true + if newSender != node.rangeTestConfig!.sender { + + hasChanges = true + } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index fc44b6cd..780879c7 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -65,6 +65,45 @@ enum SerialBaudRates: Int, CaseIterable, Identifiable { } } } + + func protoEnumValue() -> ModuleConfig.SerialConfig.Serial_Baud { + + switch self { + + case .baudDefault: + return ModuleConfig.SerialConfig.Serial_Baud.baudDefault + case .baud110: + return ModuleConfig.SerialConfig.Serial_Baud.baud110 + case .baud300: + return ModuleConfig.SerialConfig.Serial_Baud.baud300 + case .baud600: + return ModuleConfig.SerialConfig.Serial_Baud.baud600 + case .baud1200: + return ModuleConfig.SerialConfig.Serial_Baud.baud1200 + case .baud2400: + return ModuleConfig.SerialConfig.Serial_Baud.baud2400 + case .baud4800: + return ModuleConfig.SerialConfig.Serial_Baud.baud4800 + case .baud9600: + return ModuleConfig.SerialConfig.Serial_Baud.baud9600 + case .baud19200: + return ModuleConfig.SerialConfig.Serial_Baud.baud19200 + case .baud38400: + return ModuleConfig.SerialConfig.Serial_Baud.baud38400 + case .baud57600: + return ModuleConfig.SerialConfig.Serial_Baud.baud57600 + case .baud115200: + return ModuleConfig.SerialConfig.Serial_Baud.baud115200 + case .baud230400: + return ModuleConfig.SerialConfig.Serial_Baud.baud230400 + case .baud460800: + return ModuleConfig.SerialConfig.Serial_Baud.baud460800 + case .baud576000: + return ModuleConfig.SerialConfig.Serial_Baud.baud576000 + case .baud921600: + return ModuleConfig.SerialConfig.Serial_Baud.baud921600 + } + } } enum SerialModeTypes: Int, CaseIterable, Identifiable { @@ -86,6 +125,18 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable { } } } + func protoEnumValue() -> ModuleConfig.SerialConfig.Serial_Mode { + + switch self { + + case .modeDefault: + return ModuleConfig.SerialConfig.Serial_Mode.modeDefault + case .modeSimple: + return ModuleConfig.SerialConfig.Serial_Mode.modeSimple + case .modeProto: + return ModuleConfig.SerialConfig.Serial_Mode.modeProto + } + } } enum SerialTimeoutIntervals: Int, CaseIterable, Identifiable { @@ -127,6 +178,8 @@ struct SerialConfig: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + var node: NodeInfoEntity + @State private var isPresentingSaveConfirm: Bool = false @State var initialLoad: Bool = true @State var hasChanges = false @@ -238,14 +291,20 @@ struct SerialConfig: View { "Are you sure?", isPresented: $isPresentingSaveConfirm ) { - Button("Save Range Test Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + Button("Save Serial Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { var sc = ModuleConfig.SerialConfig() sc.enabled = enabled - //sc.save = save - //sc.sender = sender ? 1 : 0 + sc.echo = echo + sc.rxd = UInt32(rxd) + sc.txd = UInt32(txd) + sc.baud = SerialBaudRates(rawValue: baudRate)!.protoEnumValue() + sc.timeout = UInt32(timeout) + sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue() - if bleManager.saveSerialModuleConfig(config: sc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node.user!, toUser: node.user!, wantResponse: true) + + 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 @@ -262,11 +321,53 @@ struct SerialConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context + + self.enabled = node.serialConfig?.enabled ?? false + self.echo = node.serialConfig?.echo ?? false + self.rxd = Int(node.serialConfig?.rxd ?? 0) + self.txd = Int(node.serialConfig?.txd ?? 0) + self.baudRate = Int(node.serialConfig?.baudRate ?? 0) + self.timeout = Int(node.serialConfig?.timeout ?? 0) + self.mode = Int(node.serialConfig?.mode ?? 0) + + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: enabled) { newEnabled in + + if newEnabled != node.serialConfig!.enabled { hasChanges = true } + } + .onChange(of: echo) { newEcho in + + if newEcho != node.serialConfig!.echo { hasChanges = true } + } + .onChange(of: rxd) { newRxd in + + if newRxd != node.serialConfig!.rxd { hasChanges = true } + } + .onChange(of: txd) { newTxd in + + if newTxd != node.serialConfig!.txd { hasChanges = true } + } + .onChange(of: baudRate) { newBaud in + + if newBaud != node.serialConfig!.baudRate { hasChanges = true } + } + .onChange(of: timeout) { newTimeout in + + if newTimeout != node.serialConfig!.timeout { hasChanges = true } + } + .onChange(of: mode) { newMode in + + if newMode != node.serialConfig!.mode { hasChanges = true } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 9071a498..991bd0ff 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -187,7 +187,6 @@ struct TelemetryConfig: View { @State var deviceUpdateInterval = 0 @State var environmentUpdateInterval = 0 - @State var environmentMeasurementEnabled = false @State var environmentSensorType = 0 @State var environmentScreenEnabled = false @@ -303,6 +302,8 @@ struct TelemetryConfig: View { Button("Save Telemetry Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { var tc = ModuleConfig.TelemetryConfig() + tc.deviceUpdateInterval = UInt32(deviceUpdateInterval) + tc.environmentUpdateInterval = UInt32(environmentUpdateInterval) tc.environmentMeasurementEnabled = environmentMeasurementEnabled tc.environmentSensorType = SensorTypes(rawValue: environmentSensorType)!.protoEnumValue() tc.environmentScreenEnabled = environmentScreenEnabled @@ -327,11 +328,58 @@ struct TelemetryConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context + + self.deviceUpdateInterval = Int(node.telemetryConfig?.deviceUpdateInterval ?? 0) + self.environmentUpdateInterval = Int(node.telemetryConfig?.environmentUpdateInterval ?? 0) + self.environmentMeasurementEnabled = node.telemetryConfig?.environmentMeasurementEnabled ?? false + self.environmentSensorType = Int(node.telemetryConfig?.environmentSensorType ?? 0) + self.environmentScreenEnabled = node.telemetryConfig?.environmentScreenEnabled ?? false + self.environmentDisplayFahrenheit = node.telemetryConfig?.environmentDisplayFahrenheit ?? false + self.environmentRecoveryInterval = Int(node.telemetryConfig?.environmentRecoveryInterval ?? 0) + self.environmentReadErrorCountThreshold = Int(node.telemetryConfig?.environmentReadErrorCountThreshold ?? 0) + + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: deviceUpdateInterval) { newDeviceInterval in + + if newDeviceInterval != node.telemetryConfig!.deviceUpdateInterval { hasChanges = true } + } + .onChange(of: environmentUpdateInterval) { newEnvInterval in + + if newEnvInterval != node.telemetryConfig!.environmentUpdateInterval { hasChanges = true } + } + .onChange(of: environmentMeasurementEnabled) { newEnvEnabled in + + if newEnvEnabled != node.telemetryConfig!.environmentMeasurementEnabled { hasChanges = true } + } + .onChange(of: environmentSensorType) { newEnvSensorType in + + if newEnvSensorType != node.telemetryConfig!.environmentSensorType { hasChanges = true } + } + .onChange(of: environmentScreenEnabled) { newEnvScreenEnabled in + + if newEnvScreenEnabled != node.telemetryConfig!.environmentScreenEnabled { hasChanges = true } + } + .onChange(of: environmentDisplayFahrenheit) { newEnvDisplayF in + + if newEnvDisplayF != node.telemetryConfig!.environmentDisplayFahrenheit { hasChanges = true } + } + .onChange(of: environmentRecoveryInterval) { newEnvRecoveryInterval in + + if newEnvRecoveryInterval != node.telemetryConfig!.environmentRecoveryInterval { hasChanges = true } + } + .onChange(of: environmentReadErrorCountThreshold) { newEnvReadErrorCountThreshold in + + if newEnvReadErrorCountThreshold != node.telemetryConfig!.environmentReadErrorCountThreshold { hasChanges = true } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index ae44be5e..02934b0c 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -299,7 +299,7 @@ struct PositionConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index a89df691..359a76d0 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -151,7 +151,7 @@ struct Settings: View { // nodes.first(where: { $0.num == connectedNodeNum })!.rangeTestConfig != nil) NavigationLink { - SerialConfig() + SerialConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) } label: { Image(systemName: "terminal") diff --git a/Meshtastic/Views/Settings/ShareChannel.swift b/Meshtastic/Views/Settings/ShareChannel.swift index 4e428f15..e53f979a 100644 --- a/Meshtastic/Views/Settings/ShareChannel.swift +++ b/Meshtastic/Views/Settings/ShareChannel.swift @@ -81,7 +81,7 @@ struct ShareChannel: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear { diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index f9844e43..d791740c 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -112,14 +112,11 @@ struct UserConfig: View { u.shortName = shortName u.longName = longName - if bleManager.saveUser(config: u, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + let adminMessageId = bleManager.saveUser(config: u, entity: node.user!, wantResponse: true) + + if adminMessageId > 0 { - // Should show a saved successfully alert once I know that to be true - // for now just disable the button after a successful save hasChanges = false - - } else { - } } } @@ -132,7 +129,7 @@ struct UserConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) .onAppear {