From 70014a7b3fa8bf3e7c1620c6115aca99b700b2b9 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 17 Aug 2023 16:30:48 -0500 Subject: [PATCH] Added detection sensor config view and core data --- Meshtastic/Helpers/BLEManager.swift | 54 +++++++ .../contents | 7 + Meshtastic/Persistence/UpdateCoreData.swift | 61 +++++++ .../Config/Module/DetectionSensorConfig.swift | 152 ++++++++++++++++-- en.lproj/Localizable.strings | 2 + 5 files changed, 260 insertions(+), 16 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 05747bb2..d1298f4e 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1534,6 +1534,32 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate return 0 } + public func saveDetectionSensorModuleConfig(config: ModuleConfig.DetectionSensorConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 { + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.detectionSensor = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { var adminPacket = AdminMessage() @@ -1902,6 +1928,34 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate return false } + public func requestDetectionSensorModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.detectionsensorConfig + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { var adminPacket = AdminMessage() diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV16.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV16.xcdatamodel/contents index f3885386..87e34883 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV16.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV16.xcdatamodel/contents @@ -40,7 +40,14 @@ + + + + + + + diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index eb760bc0..5270461f 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -972,3 +972,64 @@ func upsertTelemetryModuleConfigPacket(config: Meshtastic.ModuleConfig.Telemetry print("💥 Fetching node for core data TelemetryConfigEntity failed: \(nsError)") } } + +func upsertDetectionSensorModuleConfigPacket(config: Meshtastic.ModuleConfig.DetectionSensorConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat("mesh.log.detectionsensor.config %@".localized, String(nodeNum)) + MeshLogger.log("📈 \(logString)") + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) + + do { + + guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else { + return + } + // Found a node, save Detection Sensor Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].detectionSensorConfig == nil { + + let newConfig = DetectionSensorConfigEntity(context: context) + newConfig.enabled = config.enabled + newConfig.sendBell = config.sendBell + newConfig.name = config.name + + newConfig.monitorPin = Int32(config.monitorPin) + newConfig.detectionTriggeredHigh = config.detectionTriggeredHigh + newConfig.usePullup = config.usePullup + newConfig.minimumBroadcastSecs = Int32(config.minimumBroadcastSecs) + newConfig.stateBroadcastSecs = Int32(config.stateBroadcastSecs) + fetchedNode[0].detectionSensorConfig = newConfig + + } else { + fetchedNode[0].detectionSensorConfig?.enabled = config.enabled + fetchedNode[0].detectionSensorConfig?.sendBell = config.sendBell + fetchedNode[0].detectionSensorConfig?.name = config.name + fetchedNode[0].detectionSensorConfig?.monitorPin = Int32(config.monitorPin) + fetchedNode[0].detectionSensorConfig?.usePullup = config.usePullup + fetchedNode[0].detectionSensorConfig?.detectionTriggeredHigh = config.detectionTriggeredHigh + fetchedNode[0].detectionSensorConfig?.minimumBroadcastSecs = Int32(config.minimumBroadcastSecs) + fetchedNode[0].detectionSensorConfig?.stateBroadcastSecs = Int32(config.stateBroadcastSecs) + } + + do { + try context.save() + print("💾 Updated Detection Sensor Module Config for node number: \(String(nodeNum))") + + } catch { + context.rollback() + let nsError = error as NSError + print("💥 Error Updating Core Data DetectionSensorConfigEntity: \(nsError)") + } + + } else { + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Detection Sensor Module Config") + } + + } catch { + let nsError = error as NSError + print("💥 Fetching node for core data DetectionSensorConfigEntity failed: \(nsError)") + } +} diff --git a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift index 18ce6479..97b5c60f 100644 --- a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift @@ -15,6 +15,14 @@ struct DetectionSensorConfig: View { @State private var isPresentingSaveConfirm: Bool = false @State var hasChanges: Bool = false @State var enabled = false + /// DetectionSensorModule will sends a bell character with the messages. + @State var sendBell: Bool = false + @State var name: String = "" + @State var detectionTriggeredHigh: Bool = true + @State var usePullup: Bool = false + @State var minimumBroadcastSecs = UInt32(0) + @State var stateBroadcastSecs = UInt32(0) + @State var monitorPin = UInt32(0) var body: some View { @@ -26,7 +34,7 @@ struct DetectionSensorConfig: View { } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { // Let users know what is going on if they are using remote admin and don't have the config yet - if node?.mqttConfig == nil { + if node?.detectionSensorConfig == nil { Text("Detection Sensor config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") .font(.callout) .foregroundColor(.orange) @@ -49,10 +57,72 @@ struct DetectionSensorConfig: View { Toggle(isOn: $enabled) { Label("enabled", systemImage: "dot.radiowaves.right") } + Toggle(isOn: $sendBell) { + Label("Send Bell", systemImage: "bell") + } + TextField("Friendly name (sent for detection alerts text messages)", text: $name, axis: .vertical) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: name, perform: { _ in + + let totalBytes = name.utf8.count + // Only mess with the value if it is too big + if totalBytes > 20 { + + let firstNBytes = Data(name.utf8.prefix(20)) + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + // Set the shortName back to the last place where it was the right size + name = maxBytesString + } + } + }) + .foregroundColor(.gray) + } + Section(header: Text("Sensor option")) { + Picker("GPIO Pin to monitor", selection: $monitorPin) { + ForEach(0..<46) { + if $0 == 0 { + Text("unset") + } else { + Text("Pin \($0)") + } + } + } + .pickerStyle(DefaultPickerStyle()) + Toggle(isOn: $detectionTriggeredHigh) { + Label("Detection trigger High", systemImage: "dial.high") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $usePullup) { + Label("Uses pullup resistor", systemImage: "arrow.up.to.line") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + } + + Section(header: Text("update.interval")) { + Text("Mininum time between detection broadcasts. Default is 45 seconds.") + .font(.caption) + Picker("Minimum time between detection broadcasts", selection: $minimumBroadcastSecs) { + ForEach(UpdateIntervals.allCases) { ui in + Text(ui.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("How often to send detection sensor state to mesh regardless of detection. Default is Never.") + .font(.caption) + Picker("State Broadcast Interval", selection: $stateBroadcastSecs) { + ForEach(UpdateIntervals.allCases) { ui in + Text(ui.description) + } + } + .pickerStyle(DefaultPickerStyle()) + } } .scrollDismissesKeyboard(.interactively) - .disabled(self.bleManager.connectedPeripheral == nil || node?.mqttConfig == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.detectionSensorConfig == nil) Button { isPresentingSaveConfirm = true @@ -74,22 +144,29 @@ struct DetectionSensorConfig: View { let nodeName = node?.user?.longName ?? "unknown".localized let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName) Button(buttonText) { - var dsc = DetectionSensorConfig() + var dsc = ModuleConfig.DetectionSensorConfig() dsc.enabled = self.enabled -// let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) -// if adminMessageId > 0 { -// // Should show a saved successfully alert once I know that to be true -// // for now just disable the button after a successful save -// hasChanges = false -// goBack() -// } - } + dsc.sendBell = self.sendBell + dsc.name = self.name + dsc.monitorPin = UInt32(self.monitorPin) + dsc.detectionTriggeredHigh = self.detectionTriggeredHigh + dsc.usePullup = self.usePullup + dsc.minimumBroadcastSecs = UInt32(self.minimumBroadcastSecs) + dsc.stateBroadcastSecs = UInt32(self.stateBroadcastSecs) + + let adminMessageId = bleManager.saveDetectionSensorModuleConfig(config: dsc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if adminMessageId > 0 { + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + goBack() + } } } } message: { Text("config.save.confirm") } - .navigationTitle("mqtt.config") + .navigationTitle("detection.sensor.config") .navigationBarItems(trailing: ZStack { ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") @@ -98,12 +175,12 @@ struct DetectionSensorConfig: View { self.bleManager.context = context setDetectionSensorValues() - // Need to request a TelemetryModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.mqttConfig == nil { - print("empty mqtt module config") + // Need to request a Detection Sensor Module Config from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.detectionSensorConfig == nil { + print("empty detection sensor module config") let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) if node != nil && connectedNode != nil { - _ = bleManager.requestMqttModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + _ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) } } } @@ -112,10 +189,53 @@ struct DetectionSensorConfig: View { if newEnabled != node!.detectionSensorConfig!.enabled { hasChanges = true } } } + .onChange(of: sendBell) { newSendBell in + if node != nil && node?.detectionSensorConfig != nil { + if newSendBell != node!.detectionSensorConfig!.sendBell { hasChanges = true } + } + } + .onChange(of: detectionTriggeredHigh) { newDetectionTriggeredHigh in + if node != nil && node?.detectionSensorConfig != nil { + if newDetectionTriggeredHigh != node!.detectionSensorConfig!.detectionTriggeredHigh { hasChanges = true } + } + } + .onChange(of: usePullup) { newUsePullup in + if node != nil && node?.detectionSensorConfig != nil { + if newUsePullup != node!.detectionSensorConfig!.usePullup { hasChanges = true } + } + } + .onChange(of: name) { newName in + if node != nil && node?.detectionSensorConfig != nil { + if newName != node!.detectionSensorConfig!.name { hasChanges = true } + } + } + .onChange(of: monitorPin) { newMonitorPin in + if node != nil && node?.detectionSensorConfig != nil { + if newMonitorPin != node!.detectionSensorConfig!.monitorPin { hasChanges = true } + } + } + .onChange(of: minimumBroadcastSecs) { newMinimumBroadcastSecs in + if node != nil && node?.detectionSensorConfig != nil { + if newMinimumBroadcastSecs != node!.detectionSensorConfig!.minimumBroadcastSecs { hasChanges = true } + } + } + .onChange(of: stateBroadcastSecs) { newStateBroadcastSecs in + if node != nil && node?.detectionSensorConfig != nil { + if newStateBroadcastSecs != node!.detectionSensorConfig!.stateBroadcastSecs { hasChanges = true } + } + } } func setDetectionSensorValues() { self.enabled = (node?.detectionSensorConfig?.enabled ?? false) + self.sendBell = (node?.detectionSensorConfig?.sendBell ?? false) + self.name = (node?.detectionSensorConfig?.name ?? "") + self.monitorPin = UInt32(node?.detectionSensorConfig?.monitorPin ?? 0) + self.usePullup = (node?.detectionSensorConfig?.usePullup ?? false) + self.detectionTriggeredHigh = (node?.detectionSensorConfig?.detectionTriggeredHigh ?? true) + self.minimumBroadcastSecs = UInt32(node?.detectionSensorConfig?.minimumBroadcastSecs ?? 45) + self.stateBroadcastSecs = UInt32(node?.detectionSensorConfig?.stateBroadcastSecs ?? 0) + self.hasChanges = false } } diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index a7b870a6..6ab770c4 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -57,6 +57,8 @@ "current"="Current"; "default"="Default"; "delete"="Delete"; +"detection.sensor"="Detection Sensor"; +"detection.sensor.config"="Detection Sensor Config"; "device"="Device"; "device.config"="Device Config"; "device.metrics.delete"="Delete all device metrics?";