From 5cd2b3342b330aae8f9ad71b8331a0390befaa61 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 26 Mar 2024 13:26:23 -0700 Subject: [PATCH] Mqtt config updates --- .../contents | 2 + Meshtastic/Persistence/UpdateCoreData.swift | 6 + .../Settings/Config/Module/MQTTConfig.swift | 200 ++++++++++++------ 3 files changed, 147 insertions(+), 61 deletions(-) diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 31.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 31.xcdatamodel/contents index 9852b36b..16c3d91a 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 31.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 31.xcdatamodel/contents @@ -189,6 +189,8 @@ + + diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 41920890..f2933922 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -1119,6 +1119,9 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no newMQTTConfig.encryptionEnabled = config.encryptionEnabled newMQTTConfig.jsonEnabled = config.jsonEnabled newMQTTConfig.tlsEnabled = config.tlsEnabled + newMQTTConfig.mapReportingEnabled = config.mapReportingEnabled + newMQTTConfig.mapPositionPrecision = Int32(config.mapReportSettings.positionPrecision) + newMQTTConfig.mapPublishIntervalSecs = Int32(config.mapReportSettings.publishIntervalSecs) fetchedNode[0].mqttConfig = newMQTTConfig } else { fetchedNode[0].mqttConfig?.enabled = config.enabled @@ -1130,6 +1133,9 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no fetchedNode[0].mqttConfig?.encryptionEnabled = config.encryptionEnabled fetchedNode[0].mqttConfig?.jsonEnabled = config.jsonEnabled fetchedNode[0].mqttConfig?.tlsEnabled = config.tlsEnabled + fetchedNode[0].mqttConfig?.mapReportingEnabled = config.mapReportingEnabled + fetchedNode[0].mqttConfig?.mapPositionPrecision = Int32(config.mapReportSettings.positionPrecision) + fetchedNode[0].mqttConfig?.mapPublishIntervalSecs = Int32(config.mapReportSettings.publishIntervalSecs) } do { try context.save() diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 8191aec9..10563eb3 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -28,6 +28,12 @@ struct MQTTConfig: View { @State var mqttConnected: Bool = false @State var defaultTopic = "msh/US" @State var nearbyTopics = [String]() + @State var mapReportingEnabled = false + @State var mapPublishIntervalSecs = 3600 + @State var preciseLocation: Bool = false + @State var mapPositionPrecision: Double = 13.0 + + let locale = Locale.current var body: some View { @@ -58,7 +64,7 @@ struct MQTTConfig: View { } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - if enabled && proxyToClientEnabled { + if enabled && proxyToClientEnabled && node!.mqttConfig!.proxyToClientEnabled == true { Toggle(isOn: $mqttConnected) { Label(mqttConnected ? "mqtt.disconnect".localized : "mqtt.connect".localized, systemImage: "server.rack") } @@ -75,14 +81,95 @@ struct MQTTConfig: View { Text("JSON mode is a limited, unencrypted MQTT output for locally integrating with home assistant") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + } + + Section(header: Text("Map Report")) { - Toggle(isOn: $tlsEnabled) { - Label("TLS Enabled", systemImage: "checkmark.shield.fill") - Text("Your MQTT Server must support TLS.") + Toggle(isOn: $mapReportingEnabled) { + Label("enabled", systemImage: "map") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - + if mapReportingEnabled { + Picker("Map Publish Interval", selection: $mapPublishIntervalSecs ) { + ForEach(UpdateIntervals.allCases) { ui in + if ui.rawValue >= 3600 { + Text(ui.description) + } + } + } + .pickerStyle(DefaultPickerStyle()) + + VStack(alignment: .leading) { + Toggle(isOn: $preciseLocation) { + Label("Precise Location", systemImage: "scope") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .listRowSeparator(.visible) + .onChange(of: preciseLocation) { pl in + if pl == false { + mapPositionPrecision = 12 + } else { + mapPositionPrecision = 32 + } + } + } + + if !preciseLocation { + VStack(alignment: .leading) { + Label("Approximate Location", systemImage: "location.slash.circle.fill") + Slider(value: $mapPositionPrecision, in: 11...16, step: 1) { + } minimumValueLabel: { + Image(systemName: "minus") + } maximumValueLabel: { + Image(systemName: "plus") + } + Text(PositionPrecision(rawValue: Int(mapPositionPrecision))?.description ?? "") + .foregroundColor(.gray) + .font(.callout) + } + } + } } + Section(header: Text("Root Topic")) { + HStack { + Label("Root Topic", systemImage: "tree") + TextField("Root Topic", text: $root) + .foregroundColor(.gray) + .onChange(of: root, perform: { _ in + let totalBytes = root.utf8.count + // Only mess with the value if it is too big + if totalBytes > 30 { + let firstNBytes = Data(root.utf8.prefix(30)) + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + // Set the shortName back to the last place where it was the right size + root = maxBytesString + } + } + }) + .foregroundColor(.gray) + } + .keyboardType(.asciiCapable) + .scrollDismissesKeyboard(.interactively) + .disableAutocorrection(true) + .listRowSeparator(.hidden) + Text("The root topic to use for MQTT.") + .foregroundColor(.gray) + .font(.callout) + + if nearbyTopics.count > 0 { + Picker("Nearby Topics", selection: $selectedTopic ) { + ForEach(nearbyTopics, id: \.self) { nt in + Text(nt) + } + } + .pickerStyle(InlinePickerStyle()) + .listRowSeparator(.hidden) + Text("If the default region topic is too busy you can choose a more local topic.") + .foregroundColor(.gray) + .font(.callout) + } + } + Section(header: Text("Server")) { HStack { Label("Address", systemImage: "server.rack") @@ -161,45 +248,13 @@ struct MQTTConfig: View { .keyboardType(.default) .scrollDismissesKeyboard(.interactively) .listRowSeparator(/*@START_MENU_TOKEN@*/.visible/*@END_MENU_TOKEN@*/) - HStack { - Label("Root Topic", systemImage: "tree") - TextField("Root Topic", text: $root) - .foregroundColor(.gray) - .onChange(of: root, perform: { _ in - let totalBytes = root.utf8.count - // Only mess with the value if it is too big - if totalBytes > 30 { - let firstNBytes = Data(root.utf8.prefix(30)) - if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { - // Set the shortName back to the last place where it was the right size - root = maxBytesString - } - } - }) - .foregroundColor(.gray) - } - .keyboardType(.asciiCapable) - .scrollDismissesKeyboard(.interactively) - .disableAutocorrection(true) - .listRowSeparator(.hidden) - Text("The root topic to use for MQTT.") - .foregroundColor(.gray) - .font(.callout) - - if nearbyTopics.count > 0 { - Picker("Nearby Topics", selection: $selectedTopic ) { - ForEach(nearbyTopics, id: \.self) { nt in - Text(nt) - } - } - .pickerStyle(InlinePickerStyle()) - .listRowSeparator(.hidden) - Text("If the default region topic is too busy you can choose a more local topic.") - .foregroundColor(.gray) - .font(.callout) + Toggle(isOn: $tlsEnabled) { + Label("TLS Enabled", systemImage: "checkmark.shield.fill") + Text("Your MQTT Server must support TLS.") } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } - Text("You can set uplink and downlink for each channel.") + Text("For all Mqtt functionality other than the map report you must also set uplink and downlink for each channel you want to brige over Mqtt.") .font(.callout) } .scrollDismissesKeyboard(.interactively) @@ -219,6 +274,9 @@ struct MQTTConfig: View { mqtt.encryptionEnabled = self.encryptionEnabled mqtt.jsonEnabled = self.jsonEnabled mqtt.tlsEnabled = self.tlsEnabled + mqtt.mapReportingEnabled = self.mapReportingEnabled + mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision) + mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs) 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 @@ -233,20 +291,6 @@ struct MQTTConfig: View { ZStack { ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyConnected: bleManager.mqttProxyConnected) }) - .onAppear { - if self.bleManager.context == nil { - self.bleManager.context = context - } - setMqttValues() - // Need to request a TelemetryModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.mqttConfig == nil { - print("empty mqtt 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) - } - } - } .onChange(of: address) { newAddress in if node != nil && node?.mqttConfig != nil { if newAddress != node!.mqttConfig!.address { hasChanges = true } @@ -315,6 +359,33 @@ struct MQTTConfig: View { } } } + .onChange(of: mapReportingEnabled) { newMapReportingEnabled in + if node != nil && node?.mqttConfig != nil { + if newMapReportingEnabled != node!.mqttConfig!.mapReportingEnabled { hasChanges = true } + } + } + .onChange(of: preciseLocation) { _ in + hasChanges = true + } + .onChange(of: mapPublishIntervalSecs) { newMapPublishIntervalSecs in + if node != nil && node?.mqttConfig != nil { + if newMapPublishIntervalSecs != node!.mqttConfig!.mapPublishIntervalSecs { hasChanges = true } + } + } + .onAppear { + if self.bleManager.context == nil { + self.bleManager.context = context + } + setMqttValues() + // Need to request a TelemetryModuleConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.mqttConfig == nil { + print("empty mqtt 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) + } + } + } } func setMqttValues() { @@ -367,16 +438,23 @@ struct MQTTConfig: View { }) } } - self.enabled = (node?.mqttConfig?.enabled ?? false) - self.proxyToClientEnabled = (node?.mqttConfig?.proxyToClientEnabled ?? false) + self.enabled = node?.mqttConfig?.enabled ?? false + self.proxyToClientEnabled = node?.mqttConfig?.proxyToClientEnabled ?? false self.address = node?.mqttConfig?.address ?? "" self.username = node?.mqttConfig?.username ?? "" self.password = node?.mqttConfig?.password ?? "" self.root = node?.mqttConfig?.root ?? "msh" - self.encryptionEnabled = (node?.mqttConfig?.encryptionEnabled ?? false) - self.jsonEnabled = (node?.mqttConfig?.jsonEnabled ?? false) - self.tlsEnabled = (node?.mqttConfig?.tlsEnabled ?? false) + self.encryptionEnabled = node?.mqttConfig?.encryptionEnabled ?? false + self.jsonEnabled = node?.mqttConfig?.jsonEnabled ?? false + self.tlsEnabled = node?.mqttConfig?.tlsEnabled ?? false self.mqttConnected = bleManager.mqttProxyConnected + self.mapReportingEnabled = node?.mqttConfig?.mapReportingEnabled ?? false + self.mapPublishIntervalSecs = Int(node?.mqttConfig?.mapPublishIntervalSecs ?? 3600) + self.mapPositionPrecision = Double(node?.mqttConfig?.mapPositionPrecision ?? 12) + if mapPositionPrecision == 0.0 { + self.mapPositionPrecision = 12 + } + self.preciseLocation = mapPositionPrecision == 32 self.hasChanges = false } }