mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Added detection sensor config view and core data
This commit is contained in:
parent
9a5579a2cf
commit
70014a7b3f
5 changed files with 260 additions and 16 deletions
|
|
@ -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)..<UInt32.max)
|
||||
meshPacket.to = UInt32(toUser.num)
|
||||
meshPacket.from = UInt32(fromUser.num)
|
||||
meshPacket.channel = UInt32(adminIndex)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
upsertDetectionSensorModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
public func getChannel(channelIndex: UInt32, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> 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)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.channel = UInt32(adminIndex)
|
||||
meshPacket.wantAck = true
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
dataMessage.wantResponse = true
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Detection Sensor Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
public func requestSerialModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
|
|
|
|||
|
|
@ -40,7 +40,14 @@
|
|||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="detectionTriggeredHigh" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
|
|
|
|||
|
|
@ -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<NSFetchRequestResult> = 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)")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue