From a11d41c1372660f33e3ca25eaa251728858bea18 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 1 Aug 2023 22:28:02 -0700 Subject: [PATCH 01/21] Setup MQTT client proxy connection --- Meshtastic.xcodeproj/project.pbxproj | 12 ++ Meshtastic/Helpers/BLEManager.swift | 18 ++ .../Helpers/Mqtt/MqttClientProxyManager.swift | 190 ++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 89b0f090..40a144f0 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -121,6 +121,7 @@ DDCDC6CB29481FCC004C1DDA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDCDC6CD29481FCC004C1DDA /* Localizable.strings */; }; DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; }; DDD3BBD5292D763200D609B3 /* MeshtasticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */; }; + DDD43FE32A78C8900083A3E9 /* MqttClientProxyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD43FE22A78C8900083A3E9 /* MqttClientProxyManager.swift */; }; DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD6EEAE29BC024700383354 /* Firmware.swift */; }; DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; }; DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; }; @@ -323,6 +324,7 @@ DDCDC6CE294821AD004C1DDA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserConfig.swift; sourceTree = ""; }; DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeshtasticTests.swift; sourceTree = ""; }; + DDD43FE22A78C8900083A3E9 /* MqttClientProxyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MqttClientProxyManager.swift; sourceTree = ""; }; DDD6EEAE29BC024700383354 /* Firmware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firmware.swift; sourceTree = ""; }; DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = ""; }; DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = ""; }; @@ -710,6 +712,7 @@ DDC2E1A526CEB32B0042C5E4 /* Helpers */ = { isa = PBXGroup; children = ( + DDD43FE12A78C86B0083A3E9 /* Mqtt */, DDB75A122A0593CD006ED576 /* Map */, DDAF8C5226EB1DF10058C060 /* BLEManager.swift */, DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */, @@ -736,6 +739,14 @@ path = Persistence; sourceTree = ""; }; + DDD43FE12A78C86B0083A3E9 /* Mqtt */ = { + isa = PBXGroup; + children = ( + DDD43FE22A78C8900083A3E9 /* MqttClientProxyManager.swift */, + ); + path = Mqtt; + sourceTree = ""; + }; DDDB443E29F79A9400EE2349 /* Extensions */ = { isa = PBXGroup; children = ( @@ -1032,6 +1043,7 @@ DD3CC6C228EB9D4900FA9159 /* UpdateCoreData.swift in Sources */, DDE0F7C5295F77B700B8AAB3 /* AppSettingsEnums.swift in Sources */, DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */, + DDD43FE32A78C8900083A3E9 /* MqttClientProxyManager.swift in Sources */, DDDB444629F8A96500EE2349 /* Character.swift in Sources */, DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */, diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 8830ce33..3c9f86d4 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -3,6 +3,7 @@ import CoreData import CoreBluetooth import SwiftUI import MapKit +import CocoaMQTT // --------------------------------------------------------------------------------------- // Meshtastic BLE Device Manager @@ -37,6 +38,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { var positionTimer: Timer? var lastPosition: CLLocationCoordinate2D? let emptyNodeNum: UInt32 = 4294967295 + let mqttManager = MqttClientProxyManager.shared /* Meshtastic Service Details */ var TORADIO_characteristic: CBCharacteristic! var FROMRADIO_characteristic: CBCharacteristic! @@ -573,6 +575,22 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { RunLoop.current.add(positionTimer!, forMode: .common) } } + /// MQTT Client Proxy + if true { + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(connectedPeripheral.num)) + do { + let fetchedNodeInfo = try context?.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] ?? [] + if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].mqttConfig != nil { + //Subscribe to Mqtt Client Proxy if enabled + mqttManager.connectFromConfigSettings(config: fetchedNodeInfo[0].mqttConfig!) + + } + } catch { + print("Failed to find a node info for the connected node") + } + } return } diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift new file mode 100644 index 00000000..d9422e60 --- /dev/null +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -0,0 +1,190 @@ +// +// MQTTManager.swift +// Meshtastic +// +// Created by Garth Vander Houwen on 7/31/23. +// + +import Foundation +import CocoaMQTT + +class MqttClientProxyManager { + + enum ConnectionStatus { + case connecting + case connected + case disconnecting + case disconnected + case error + case none + } + + enum MqttQos: Int { + case atMostOnce = 0 + case atLeastOnce = 1 + case exactlyOnce = 2 + } + + static let shared = MqttClientProxyManager() + private static let defaultKeepAliveInterval: Int32 = 60 + weak var delegate: CocoaMQTTDelegate? + var status = ConnectionStatus.none + var mqttClient: CocoaMQTT? + + private init() { + + } + + func connectFromConfigSettings(config: MQTTConfigEntity) { + + let defaultServerAddress = "mqtt.meshtastic.org" + let defaultServerPort = 1883 + //let + var host = config.address + if host == nil || host!.isEmpty { + host = defaultServerAddress + } + + if let host = host { + let port = defaultServerPort + let username = config.username + let password = config.password + connect(host: host, port: port, username: username, password: password, cleanSession: true) + } + } + + func connect(host: String, port: Int, username: String?, password: String?, cleanSession: Bool) { + + let clientId = "MeshtasticAppleMqttProxy-" + String(ProcessInfo().processIdentifier) + status = .connecting + mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) + + if let mqttClient = mqttClient { + + mqttClient.username = username + mqttClient.password = password + mqttClient.keepAlive = 60 + mqttClient.cleanSession = cleanSession + mqttClient.logLevel = .debug + mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout") + mqttClient.autoReconnect = true + + mqttClient.delegate = self + let success = mqttClient.connect() + if !success { + status = .error + } + + } else { + status = .error + } + } + + func subscribe(topic: String, qos: MqttQos) { + let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! + mqttClient?.subscribe(topic, qos: qos) + } + + func unsubscribe(topic: String) { + mqttClient?.unsubscribe(topic) + } + + func publish(message: String, topic: String, qos: MqttQos) { + let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! + mqttClient?.publish(topic, withString: message, qos: qos) + } + + func disconnect() { + //MqttSettings.shared.isConnected = false + + if let client = mqttClient { + status = .disconnecting + client.disconnect() + } else { + status = .disconnected + } + } +} + +extension MqttClientProxyManager: CocoaMQTTDelegate { + + func mqtt(_ mqtt: CocoaMQTT, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void) { + completionHandler(true) + } + + func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { + + print("didConnectAck: \(ack)") + if ack == .accept { + //delegate?.onMqttConnected() + + // if let topic = mqttSettings.subscribeTopic, mqttSettings.isSubscribeEnabled { + // self.subscribe(topic: topic, qos: mqttSettings.subscribeQos) + // } + } else { + // Connection error + var errorDescription = "Unknown Error" + switch ack { + case .accept: + errorDescription = "No Error" + case .unacceptableProtocolVersion: + errorDescription = "Proto ver" + case .identifierRejected: + errorDescription = "Invalid Id" + case .serverUnavailable: + errorDescription = "Invalid Server" + case .badUsernameOrPassword: + errorDescription = "Invalid Credentials" + case .notAuthorized: + errorDescription = "Authorization Error" + default: + errorDescription = "Unknown Error" + } + print(errorDescription) + //delegate?.onMqttError(message: errorDescription) + + //self.disconnect() // Stop reconnecting + //mqttSettings.isConnected = false // Disable automatic connect on start + } + + self.status = ack == .accept ? ConnectionStatus.connected : ConnectionStatus.error // Set AFTER sending onMqttError (so the delegate can detect that was an error while stablishing connection) + + + } + + func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) { + print("didPublishMessage") + } + + func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) { + print("didPublishAck") + } + + func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { + if let string = message.string { + print("didReceiveMessage: \(string) from topic: \(message.topic)") + } else { + print("didReceiveMessage but message is not defined") + } + } + + func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String]) { + print("didSubscribeTopics: \(success.allKeys.count) topics. failed: \(failed.count) topics") + } + + func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopics topics: [String]) { + print("didUnsubscribeTopics: \(topics.joined(separator: ", "))") + } + + func mqttDidPing(_ mqtt: CocoaMQTT) { + print("mqttDidPing") + } + + func mqttDidReceivePong(_ mqtt: CocoaMQTT) { + print("mqttDidReceivePong") + } + + func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) { + print("mqttDidDisconnect") + } +} From 1356ebea1421f51681092df4c6ee3e603e6618a0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 17:41:46 -0700 Subject: [PATCH 02/21] More MQTT updates --- Meshtastic.xcodeproj/project.pbxproj | 4 +- Meshtastic/Enums/HardwareModels.swift | 63 ++++ .../Helpers/Mqtt/MqttClientProxyManager.swift | 2 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 332 ++++++++++++++++++ Meshtastic/Persistence/UpdateCoreData.swift | 2 + Meshtastic/Views/Nodes/DeviceMetricsLog.swift | 5 +- .../Settings/Config/Module/MQTTConfig.swift | 13 + 8 files changed, 418 insertions(+), 5 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV15.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 40a144f0..175c5369 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -197,6 +197,7 @@ C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = ""; }; DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = ""; }; DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = ""; }; + DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV15.xcdatamodel; sourceTree = ""; }; DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfigEnums.swift; sourceTree = ""; }; DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = ""; }; DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; @@ -1617,6 +1618,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */, DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */, DDB75A1F2A10766D006ED576 /* MeshtasticDataModelV13.xcdatamodel */, DDB759E12A04B264006ED576 /* MeshtasticDataModelV12.xcdatamodel */, @@ -1632,7 +1634,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */; + currentVersion = DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Enums/HardwareModels.swift b/Meshtastic/Enums/HardwareModels.swift index 04dc9328..36575bed 100644 --- a/Meshtastic/Enums/HardwareModels.swift +++ b/Meshtastic/Enums/HardwareModels.swift @@ -32,6 +32,13 @@ enum HardwareModels: String, CaseIterable, Identifiable { case M5STACK case HELTECV3 case HELTECWSLV3 + case NANOG2ULTRA + case RAK11310 + case RPIPICO + case HELTECWIRELESSTRACKER + case HELTECWIRELESSPAPER + case TDECK + case TWATCHS3 var id: String { self.rawValue } var description: String { @@ -83,6 +90,20 @@ enum HardwareModels: String, CaseIterable, Identifiable { return "Heltec V3" case .HELTECWSLV3: return "Heltec wireless stick lite V3" + case .NANOG2ULTRA: + return "Nano G2 Ultra" + case .RAK11310: + return "RAK 11310 Pi Pico" + case .RPIPICO: + return "Pi Pico" + case .HELTECWIRELESSTRACKER: + return "Heltec Wireless Tracker" + case .HELTECWIRELESSPAPER: + return "Heltec Wireless Paper" + case .TDECK: + return "T-Deck" + case .TWATCHS3: + return "T-Watch S3" } } @@ -135,6 +156,20 @@ enum HardwareModels: String, CaseIterable, Identifiable { return ["firmware-heltec-v3-"] case .HELTECWSLV3: return ["firmware-heltec-wsl-v3-"] + case .NANOG2ULTRA: + return ["firmware-nano-g2-ultra-"] + case .RAK11310: + return ["firmware-rak11310-"] + case .RPIPICO: + return ["firmware-pico-"] + case .HELTECWIRELESSTRACKER: + return ["firmware-heltec-wireless-tracker-"] + case .HELTECWIRELESSPAPER: + return ["firmware-heltec-wireless-paper-"] + case .TDECK: + return ["firmware-t-echo-"] + case .TWATCHS3: + return ["firmware-t-watch-s3-"] } } @@ -188,6 +223,20 @@ enum HardwareModels: String, CaseIterable, Identifiable { return HardwarePlatforms.esp32 case .HELTECWSLV3: return HardwarePlatforms.esp32 + case .NANOG2ULTRA: + return HardwarePlatforms.nrf52 + case .RAK11310: + return HardwarePlatforms.piPico + case .RPIPICO: + return HardwarePlatforms.piPico + case .HELTECWIRELESSTRACKER: + return HardwarePlatforms.esp32 + case .HELTECWIRELESSPAPER: + return HardwarePlatforms.esp32 + case .TDECK: + return HardwarePlatforms.esp32 + case .TWATCHS3: + return HardwarePlatforms.esp32 } } func protoEnumValue() -> HardwareModel { @@ -240,6 +289,20 @@ enum HardwareModels: String, CaseIterable, Identifiable { return HardwareModel.heltecV3 case .HELTECWSLV3: return HardwareModel.heltecWslV3 + case .NANOG2ULTRA: + return HardwareModel.nanoG2Ultra + case .RAK11310: + return HardwareModel.rak11310 + case .RPIPICO: + return HardwareModel.rpiPico + case .HELTECWIRELESSTRACKER: + return HardwareModel.heltecWirelessTracker + case .HELTECWIRELESSPAPER: + return HardwareModel.heltecWirelessPaper + case .TDECK: + return HardwareModel.tDeck + case .TWATCHS3: + return HardwareModel.tWatchS3 } } } diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index d9422e60..28ab2861 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -37,7 +37,7 @@ class MqttClientProxyManager { func connectFromConfigSettings(config: MQTTConfigEntity) { - let defaultServerAddress = "mqtt.meshtastic.org" + let defaultServerAddress = "devmqtt.meshtastic.org" let defaultServerPort = 1883 //let var host = config.address diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 9235346e..fdb1b5c8 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV14.xcdatamodel + MeshtasticDataModelV15.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV15.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV15.xcdatamodel/contents new file mode 100644 index 00000000..bb210b96 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV15.xcdatamodel/contents @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 2430d577..eb760bc0 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -776,6 +776,7 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no if fetchedNode[0].mqttConfig == nil { let newMQTTConfig = MQTTConfigEntity(context: context) newMQTTConfig.enabled = config.enabled + newMQTTConfig.proxyToClientEnabled = config.proxyToClientEnabled newMQTTConfig.address = config.address newMQTTConfig.username = config.username newMQTTConfig.password = config.password @@ -786,6 +787,7 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no fetchedNode[0].mqttConfig = newMQTTConfig } else { fetchedNode[0].mqttConfig?.enabled = config.enabled + fetchedNode[0].mqttConfig?.proxyToClientEnabled = config.proxyToClientEnabled fetchedNode[0].mqttConfig?.address = config.address fetchedNode[0].mqttConfig?.username = config.username fetchedNode[0].mqttConfig?.password = config.password diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index c34862f2..86e3bd33 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -23,10 +23,10 @@ struct DeviceMetricsLog: View { var body: some View { - let oneDayAgo = Calendar.current.date(byAdding: .day, value: -1, to: Date()) + let oneWeekAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date()) let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")).reversed() as? [TelemetryEntity] ?? [] let chartData = deviceMetrics - .filter { $0.time != nil && $0.time! >= oneDayAgo! } + .filter { $0.time != nil && $0.time! >= oneWeekAgo! } .sorted { $0.time! < $1.time! } NavigationStack { @@ -48,6 +48,7 @@ struct DeviceMetricsLog: View { .accessibilityValue("X: \(point.time!), Y: \(point.batteryLevel)") .foregroundStyle(batteryChartColor) .interpolationMethod(.cardinal) + //.interpolationMethod(.catmullRom(alpha: 1.0)) Plot { PointMark( diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 22a44edf..8aa3f92e 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -15,6 +15,7 @@ struct MQTTConfig: View { @State private var isPresentingSaveConfirm: Bool = false @State var hasChanges: Bool = false @State var enabled = false + @State var proxyToClientEnabled = false @State var address = "" @State var username = "" @State var password = "" @@ -59,6 +60,11 @@ struct MQTTConfig: View { Label("enabled", systemImage: "dot.radiowaves.right") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Toggle(isOn: $proxyToClientEnabled) { + + Label("proxyToClientEnabled", systemImage: "dot.radiowaves.right") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Toggle(isOn: $encryptionEnabled) { @@ -209,6 +215,7 @@ struct MQTTConfig: View { Button(buttonText) { var mqtt = ModuleConfig.MQTTConfig() mqtt.enabled = self.enabled + mqtt.proxyToClientEnabled = self.proxyToClientEnabled mqtt.address = self.address mqtt.username = self.username mqtt.password = self.password @@ -272,6 +279,11 @@ struct MQTTConfig: View { if newEnabled != node!.mqttConfig!.enabled { hasChanges = true } } } + .onChange(of: proxyToClientEnabled) { newProxyToClientEnabled in + if node != nil && node?.mqttConfig != nil { + if newProxyToClientEnabled != node!.mqttConfig!.proxyToClientEnabled { hasChanges = true } + } + } .onChange(of: encryptionEnabled) { newEncryptionEnabled in if node != nil && node?.mqttConfig != nil { if newEncryptionEnabled != node!.mqttConfig!.encryptionEnabled { hasChanges = true } @@ -291,6 +303,7 @@ struct MQTTConfig: View { func setMqttValues() { 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 ?? "" From 3ad5f8aac3cd520b6ece1adfb9d1a929113e61f8 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 17:42:36 -0700 Subject: [PATCH 03/21] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 175c5369..43765a5e 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1324,7 +1324,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.16; + MARKETING_VERSION = 2.1.17; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1358,7 +1358,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.16; + MARKETING_VERSION = 2.1.17; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From 9963a7b1edae830ba66db2a3565ab2606c9f8da8 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 17:50:01 -0700 Subject: [PATCH 04/21] Bump to right version for client proxy feature --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 43765a5e..972413c5 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1324,7 +1324,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.17; + MARKETING_VERSION = 2.1.19; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1358,7 +1358,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.17; + MARKETING_VERSION = 2.1.19; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 8aa3f92e..0146af9d 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -62,7 +62,7 @@ struct MQTTConfig: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Toggle(isOn: $proxyToClientEnabled) { - Label("proxyToClientEnabled", systemImage: "dot.radiowaves.right") + Label("proxyToClientEnabled", systemImage: "iphone.radiowaves.left.and.right") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) From 71f114f203d07eff091cfe8f9bf4801fadd74da9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 18:05:05 -0700 Subject: [PATCH 05/21] Fix default server --- Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 28ab2861..d9422e60 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -37,7 +37,7 @@ class MqttClientProxyManager { func connectFromConfigSettings(config: MQTTConfigEntity) { - let defaultServerAddress = "devmqtt.meshtastic.org" + let defaultServerAddress = "mqtt.meshtastic.org" let defaultServerPort = 1883 //let var host = config.address From e3e83f130fcf1489161e7b75e21b7144855f86e9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 18:09:29 -0700 Subject: [PATCH 06/21] Bump widgets version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 972413c5..e1094464 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1477,7 +1477,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.1.16; + MARKETING_VERSION = 2.1.19; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1508,7 +1508,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.1.16; + MARKETING_VERSION = 2.1.19; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; From af94d05f84b7161a626621b9b683712303f7a985 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 20:59:39 -0700 Subject: [PATCH 07/21] Subscribe to mqtt --- Meshtastic/Helpers/BLEManager.swift | 8 +++++--- .../Helpers/Mqtt/MqttClientProxyManager.swift | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 3c9f86d4..e64d79fa 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -576,16 +576,18 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } } /// MQTT Client Proxy - if true { + if connectedPeripheral.num > 0 { let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(connectedPeripheral.num)) do { let fetchedNodeInfo = try context?.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] ?? [] if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].mqttConfig != nil { + //Subscribe to Mqtt Client Proxy if enabled - mqttManager.connectFromConfigSettings(config: fetchedNodeInfo[0].mqttConfig!) - + if fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false { + mqttManager.connectFromConfigSettings(config: fetchedNodeInfo[0].mqttConfig!, metadata: fetchedNodeInfo[0].metadata!) + } } } catch { print("Failed to find a node info for the connected node") diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index d9422e60..45029540 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -35,7 +35,7 @@ class MqttClientProxyManager { } - func connectFromConfigSettings(config: MQTTConfigEntity) { + func connectFromConfigSettings(config: MQTTConfigEntity, metadata: DeviceMetadataEntity) { let defaultServerAddress = "mqtt.meshtastic.org" let defaultServerPort = 1883 @@ -49,11 +49,18 @@ class MqttClientProxyManager { let port = defaultServerPort let username = config.username let password = config.password - connect(host: host, port: port, username: username, password: password, cleanSession: true) + + var root = config.root?.count ?? 0 > 0 ? config.root : "msh" + var prefix = root! + "/" + metadata.firmwareVersion! + var topic = prefix + "/#" + let qos = CocoaMQTTQoS(rawValue :UInt8(2))! + connect(host: host, port: port, username: username, password: password, topic: topic, qos: qos, cleanSession: true) + + } } - func connect(host: String, port: Int, username: String?, password: String?, cleanSession: Bool) { + func connect(host: String, port: Int, username: String?, password: String?, topic: String?, qos: CocoaMQTTQoS, cleanSession: Bool) { let clientId = "MeshtasticAppleMqttProxy-" + String(ProcessInfo().processIdentifier) status = .connecting @@ -73,8 +80,9 @@ class MqttClientProxyManager { let success = mqttClient.connect() if !success { status = .error + } else { + mqttClient.subscribe(topic!, qos: qos) } - } else { status = .error } From 81f824c79c4c854ca665abc49c8437c4f2749fab Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 21:11:58 -0700 Subject: [PATCH 08/21] Subscribe to client proxy topic --- Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 45029540..ae020d41 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -51,12 +51,10 @@ class MqttClientProxyManager { let password = config.password var root = config.root?.count ?? 0 > 0 ? config.root : "msh" - var prefix = root! + "/" + metadata.firmwareVersion! + var prefix = root! + "/2" //+ metadata.firmwareVersion! var topic = prefix + "/#" let qos = CocoaMQTTQoS(rawValue :UInt8(2))! connect(host: host, port: port, username: username, password: password, topic: topic, qos: qos, cleanSession: true) - - } } @@ -75,13 +73,13 @@ class MqttClientProxyManager { mqttClient.logLevel = .debug mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout") mqttClient.autoReconnect = true - mqttClient.delegate = self let success = mqttClient.connect() if !success { status = .error } else { - mqttClient.subscribe(topic!, qos: qos) + //mqttClient.subscribe(topic!, qos: qos) + subscribe(topic: topic!, qos: MqttQos.atLeastOnce) } } else { status = .error @@ -91,6 +89,7 @@ class MqttClientProxyManager { func subscribe(topic: String, qos: MqttQos) { let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! mqttClient?.subscribe(topic, qos: qos) + print("MQTT Client Proxy subscribed to: " + topic) } func unsubscribe(topic: String) { @@ -156,8 +155,6 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { } self.status = ack == .accept ? ConnectionStatus.connected : ConnectionStatus.error // Set AFTER sending onMqttError (so the delegate can detect that was an error while stablishing connection) - - } func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) { From 8afc76ea2ae71af7bc9fd22acbde4f0def6ed352 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 6 Aug 2023 22:31:19 -0700 Subject: [PATCH 09/21] Connect to mqtt on the right topic --- Meshtastic/Enums/LoraConfigEnums.swift | 20 +++++++++++++++++ Meshtastic/Helpers/BLEManager.swift | 2 +- .../Helpers/Mqtt/MqttClientProxyManager.swift | 22 ++++++++++++------- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift index 805a4288..85ed35ce 100644 --- a/Meshtastic/Enums/LoraConfigEnums.swift +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -135,6 +135,26 @@ enum ModemPresets: Int, CaseIterable, Identifiable { return "Short Range - Fast" } } + var name: String { + switch self { + case .longFast: + return "LongFast" + case .longSlow: + return "LongSlow" + case .longModerate: + return "LongModerate" + case .vLongSlow: + return "VLongFast" + case .medSlow: + return "MediumSlow" + case .medFast: + return "MediumFast" + case .shortSlow: + return "ShortSlow" + case .shortFast: + return "ShortFast" + } + } func snrLimit() -> Float { switch self { case .longFast: diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index e64d79fa..910d0f51 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -586,7 +586,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { //Subscribe to Mqtt Client Proxy if enabled if fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false { - mqttManager.connectFromConfigSettings(config: fetchedNodeInfo[0].mqttConfig!, metadata: fetchedNodeInfo[0].metadata!) + mqttManager.connectFromConfigSettings(node: fetchedNodeInfo[0]) } } } catch { diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index ae020d41..95e104f0 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -35,23 +35,24 @@ class MqttClientProxyManager { } - func connectFromConfigSettings(config: MQTTConfigEntity, metadata: DeviceMetadataEntity) { + func connectFromConfigSettings(node: NodeInfoEntity) { let defaultServerAddress = "mqtt.meshtastic.org" let defaultServerPort = 1883 //let - var host = config.address + var host = node.mqttConfig?.address if host == nil || host!.isEmpty { host = defaultServerAddress } if let host = host { let port = defaultServerPort - let username = config.username - let password = config.password + let username = node.mqttConfig?.username + let password = node.mqttConfig?.password - var root = config.root?.count ?? 0 > 0 ? config.root : "msh" - var prefix = root! + "/2" //+ metadata.firmwareVersion! + var root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" + let preset = ModemPresets(rawValue: Int(node.loRaConfig?.modemPreset ?? 0)) + var prefix = root! + "/c/\(preset?.name ?? "LongFast")" var topic = prefix + "/#" let qos = CocoaMQTTQoS(rawValue :UInt8(2))! connect(host: host, port: port, username: username, password: password, topic: topic, qos: qos, cleanSession: true) @@ -70,7 +71,9 @@ class MqttClientProxyManager { mqttClient.password = password mqttClient.keepAlive = 60 mqttClient.cleanSession = cleanSession +#if DEBUG mqttClient.logLevel = .debug +#endif mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout") mqttClient.autoReconnect = true mqttClient.delegate = self @@ -89,16 +92,18 @@ class MqttClientProxyManager { func subscribe(topic: String, qos: MqttQos) { let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! mqttClient?.subscribe(topic, qos: qos) - print("MQTT Client Proxy subscribed to: " + topic) + print("๐Ÿ“ฒ MQTT Client Proxy subscribed to: " + topic) } func unsubscribe(topic: String) { mqttClient?.unsubscribe(topic) + print("๐Ÿ“ฒ MQTT Client Proxy unsubscribe for: " + topic) } func publish(message: String, topic: String, qos: MqttQos) { let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! mqttClient?.publish(topic, withString: message, qos: qos) + print("๐Ÿ“ฒ MQTT Client Proxy publish for: " + topic) } func disconnect() { @@ -107,6 +112,7 @@ class MqttClientProxyManager { if let client = mqttClient { status = .disconnecting client.disconnect() + print("๐Ÿ“ฒ MQTT Client Proxy Disconnected") } else { status = .disconnected } @@ -121,7 +127,7 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { - print("didConnectAck: \(ack)") + print("๐Ÿ“ฒ MQTT Client Proxy didConnectAck: \(ack)") if ack == .accept { //delegate?.onMqttConnected() From 7929a55ae41968ac5d7726baa92bcbb2d6dcc161 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 7 Aug 2023 13:26:02 -0700 Subject: [PATCH 10/21] Subscribe after connect --- .../Helpers/Mqtt/MqttClientProxyManager.swift | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 95e104f0..6852e7f2 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -30,6 +30,7 @@ class MqttClientProxyManager { weak var delegate: CocoaMQTTDelegate? var status = ConnectionStatus.none var mqttClient: CocoaMQTT? + var topic = "msh/2/c" private init() { @@ -39,7 +40,6 @@ class MqttClientProxyManager { let defaultServerAddress = "mqtt.meshtastic.org" let defaultServerPort = 1883 - //let var host = node.mqttConfig?.address if host == nil || host!.isEmpty { host = defaultServerAddress @@ -50,11 +50,11 @@ class MqttClientProxyManager { let username = node.mqttConfig?.username let password = node.mqttConfig?.password - var root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" - let preset = ModemPresets(rawValue: Int(node.loRaConfig?.modemPreset ?? 0)) - var prefix = root! + "/c/\(preset?.name ?? "LongFast")" - var topic = prefix + "/#" - let qos = CocoaMQTTQoS(rawValue :UInt8(2))! + let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" + let preset = ModemPresets(rawValue: Int(node.loRaConfig?.modemPreset ?? 0)) ?? ModemPresets.longFast + let prefix = root! + "/2/c" + topic = prefix + "/#" + let qos = CocoaMQTTQoS(rawValue :UInt8(1))! connect(host: host, port: port, username: username, password: password, topic: topic, qos: qos, cleanSession: true) } } @@ -80,9 +80,6 @@ class MqttClientProxyManager { let success = mqttClient.connect() if !success { status = .error - } else { - //mqttClient.subscribe(topic!, qos: qos) - subscribe(topic: topic!, qos: MqttQos.atLeastOnce) } } else { status = .error @@ -129,11 +126,10 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { print("๐Ÿ“ฒ MQTT Client Proxy didConnectAck: \(ack)") if ack == .accept { - //delegate?.onMqttConnected() + let qos = CocoaMQTTQoS.qos1 + mqttClient!.subscribe(topic, qos: qos) + print("๐Ÿ“ฒ MQTT Client Proxy subscribed to: " + topic) - // if let topic = mqttSettings.subscribeTopic, mqttSettings.isSubscribeEnabled { - // self.subscribe(topic: topic, qos: mqttSettings.subscribeQos) - // } } else { // Connection error var errorDescription = "Unknown Error" @@ -154,7 +150,7 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { errorDescription = "Unknown Error" } print(errorDescription) - //delegate?.onMqttError(message: errorDescription) + // mqttClient!.delegate.onMqttError(message: errorDescription) //self.disconnect() // Stop reconnecting //mqttSettings.isConnected = false // Disable automatic connect on start @@ -164,14 +160,30 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { } func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) { - print("didPublishMessage") + print("๐Ÿ“ฒ MQTT Client Proxy didPublishMessage: \(message)") } func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) { + print("๐Ÿ“ฒ MQTT Client Proxy didPublishAck: \(id)") print("didPublishAck") } func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { + + print("๐Ÿ“ฒ MQTT Client Proxy message received on topic: \(message.topic)") + + if message.topic.contains("/stat/") { + return + } + +// // Get bytes from utf8 string +// var toRadio = new ToRadioMessageFactory() +// .CreateMqttClientProxyMessage(e.ApplicationMessage.Topic, e.ApplicationMessage.PayloadSegment.ToArray(), e.ApplicationMessage.Retain); +// Logger.LogDebug(toRadio.ToString()); +// await Connection.WriteToRadio(toRadio); + + + if let string = message.string { print("didReceiveMessage: \(string) from topic: \(message.topic)") } else { From b346ce2a379449d1c0335b56383878e1994fa381 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 7 Aug 2023 16:25:01 -0700 Subject: [PATCH 11/21] Clean up client proxy, set up publish of fromRadio packets --- Meshtastic/Helpers/BLEManager.swift | 69 +++++++++++-------- .../Helpers/Mqtt/MqttClientProxyManager.swift | 19 ----- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 910d0f51..d756a0d4 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -393,6 +393,31 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { print(characteristic.value!) } + // Send a packet from mqtt to the radio + if decodedInfo.payloadVariant == FromRadio.OneOf_PayloadVariant.mqttClientProxyMessage(decodedInfo.mqttClientProxyMessage) { + var message = CocoaMQTTMessage ( + topic: decodedInfo.mqttClientProxyMessage.topic, + payload: [UInt8](decodedInfo.mqttClientProxyMessage.data), + retained: decodedInfo.mqttClientProxyMessage.retained + ) + print("๐Ÿ“ฒ Publish Mqtt client proxy message received on FromRadio \(message)") + mqttManager.mqttClient?.publish(message) + } + + //To Radio +// if decodedInfo.mqttClientProxyMessage.topic.contains("/stat/") { +// return +// } +// +// var toRadio: ToRadio! +// toRadio = ToRadio() +// toRadio.mqttClientProxyMessage = decodedInfo.mqttClientProxyMessage +// let binaryData: Data = try! toRadio.serializedData() +// if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { +// connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) +// print("๐Ÿ“ฒ Sent Mqtt client proxy message to the connected device.") +// } + switch decodedInfo.packet.decoded.portnum { // Handle Any local only packets we get over BLE @@ -540,22 +565,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { print("MAX PORT NUM OF 511") } - // MARK: Check for an All / Broadcast User and delete it as a transition to multi channel - let fetchBCUserRequest: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") - fetchBCUserRequest.predicate = NSPredicate(format: "num == %lld", Int64(emptyNodeNum)) - - do { - guard let fetchedUser = try context?.fetch(fetchBCUserRequest) as? [UserEntity] else { - return - } - if fetchedUser.count > 0 { - context?.delete(fetchedUser[0]) - print("๐Ÿ—‘๏ธ Deleted the All - Broadcast User") - } - } catch { - print("๐Ÿ’ฅ Error Deleting the All - Broadcast User") - } - if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce { invalidVersion = false lastConnectionError = "" @@ -563,18 +572,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { print("๐Ÿคœ Want Config Complete. ID:\(decodedInfo.configCompleteID)") peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected }) // Config conplete returns so we don't read the characteristic again - // MARK: Share Location Position Update Timer - // Use context to pass the radio name with the timer - // Use a RunLoop to prevent the timer from running on the main UI thread - if UserDefaults.provideLocation { - if positionTimer != nil { - positionTimer!.invalidate() - } - positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval((UserDefaults.provideLocationInterval )), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true) - if positionTimer != nil { - RunLoop.current.add(positionTimer!, forMode: .common) - } - } + /// MQTT Client Proxy if connectedPeripheral.num > 0 { @@ -593,8 +591,23 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { print("Failed to find a node info for the connected node") } } + + // MARK: Share Location Position Update Timer + // Use context to pass the radio name with the timer + // Use a RunLoop to prevent the timer from running on the main UI thread + if UserDefaults.provideLocation { + if positionTimer != nil { + positionTimer!.invalidate() + } + positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval((UserDefaults.provideLocationInterval )), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true) + if positionTimer != nil { + RunLoop.current.add(positionTimer!, forMode: .common) + } + } + return } + case FROMNUM_UUID: print("๐Ÿ—ž๏ธ BLE (Notify) characteristic, value will be read next") diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 6852e7f2..d261c4a1 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -169,26 +169,7 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { } func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { - print("๐Ÿ“ฒ MQTT Client Proxy message received on topic: \(message.topic)") - - if message.topic.contains("/stat/") { - return - } - -// // Get bytes from utf8 string -// var toRadio = new ToRadioMessageFactory() -// .CreateMqttClientProxyMessage(e.ApplicationMessage.Topic, e.ApplicationMessage.PayloadSegment.ToArray(), e.ApplicationMessage.Retain); -// Logger.LogDebug(toRadio.ToString()); -// await Connection.WriteToRadio(toRadio); - - - - if let string = message.string { - print("didReceiveMessage: \(string) from topic: \(message.topic)") - } else { - print("didReceiveMessage but message is not defined") - } } func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String]) { From 59292681711ebcc1ba1c4bd888eeec9f4f5cdbc5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 8 Aug 2023 18:10:50 -0700 Subject: [PATCH 12/21] Might work --- Meshtastic.xcodeproj/project.pbxproj | 4 ++ Meshtastic/Helpers/BLEManager.swift | 62 +++++++++++----- .../Helpers/Mqtt/MqttClientProxyManager.swift | 70 ++++++++++++------- Meshtastic/Views/Nodes/RemoteHardware.swift | 8 +++ 4 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 Meshtastic/Views/Nodes/RemoteHardware.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index e1094464..a7798509 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; }; DD0D3D222A55CEB10066DB71 /* CocoaMQTT in Frameworks */ = {isa = PBXBuildFile; productRef = DD0D3D212A55CEB10066DB71 /* CocoaMQTT */; }; DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; }; + DD14E72E2A82A614006E39BC /* RemoteHardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD14E72D2A82A614006E39BC /* RemoteHardware.swift */; }; DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */; }; DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1925B828CDA93900720036 /* SerialConfigEnums.swift */; }; DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; }; @@ -198,6 +199,7 @@ DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = ""; }; DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = ""; }; DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV15.xcdatamodel; sourceTree = ""; }; + DD14E72D2A82A614006E39BC /* RemoteHardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteHardware.swift; sourceTree = ""; }; DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfigEnums.swift; sourceTree = ""; }; DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = ""; }; DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; @@ -425,6 +427,7 @@ DD47E3CD26F103C600029299 /* NodeList.swift */, DD90860D26F69BAE00DC5189 /* NodeMap.swift */, DD73FD1028750779000852D6 /* PositionLog.swift */, + DD14E72D2A82A614006E39BC /* RemoteHardware.swift */, ); path = Nodes; sourceTree = ""; @@ -1111,6 +1114,7 @@ DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */, DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */, DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */, + DD14E72E2A82A614006E39BC /* RemoteHardware.swift in Sources */, DDDB444429F8A8DD00EE2349 /* Float.swift in Sources */, DD5E5211298EE33B00D21B61 /* remote_hardware.pb.swift in Sources */, DD5E5204298EE33B00D21B61 /* xmodem.pb.swift in Sources */, diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index d756a0d4..ebaafbde 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -8,8 +8,47 @@ import CocoaMQTT // --------------------------------------------------------------------------------------- // Meshtastic BLE Device Manager // --------------------------------------------------------------------------------------- -class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { +class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate, ObservableObject { + // MqttClientProxyManagerDelegate + func onMqttConnected() { + mqttManager.status = .connected + print("MQTT Connected") + } + + func onMqttDisconnected() { + mqttManager.status = .disconnected + print("MQTT Disconnected") + } + + func onMqttMessageReceived(message: CocoaMQTTMessage) { + + print("onMqttMessageReceived") + if message.topic.contains("/stat/") { + // return + } + var proxyMessage = MqttClientProxyMessage() + proxyMessage.topic = message.topic + proxyMessage.data = Data(message.payload) + proxyMessage.retained = message.retained + + + var toRadio: ToRadio! + toRadio = ToRadio() + toRadio.mqttClientProxyMessage = proxyMessage + let binaryData: Data = try! toRadio.serializedData() + if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) + print("๐Ÿ“ฒ Sent Mqtt client proxy message to the connected device.") + } + + } + + func onMqttError(message: String) { + print("MQTT Error") + } + + private static var documentsFolder: URL { do { return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) @@ -57,6 +96,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { self.connectedVersion = "0.0.0" super.init() centralManager = CBCentralManager(delegate: self, queue: nil) + mqttManager.delegate = self // centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: restoreKey]) } @@ -393,31 +433,17 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { print(characteristic.value!) } - // Send a packet from mqtt to the radio + // Publish mqttClientProxyMessages received on the from radio if decodedInfo.payloadVariant == FromRadio.OneOf_PayloadVariant.mqttClientProxyMessage(decodedInfo.mqttClientProxyMessage) { var message = CocoaMQTTMessage ( topic: decodedInfo.mqttClientProxyMessage.topic, payload: [UInt8](decodedInfo.mqttClientProxyMessage.data), retained: decodedInfo.mqttClientProxyMessage.retained ) - print("๐Ÿ“ฒ Publish Mqtt client proxy message received on FromRadio \(message)") + print("๐Ÿ“ฒ Publish Mqtt client proxy message received on FromRadio to the Mqtt server \(message)") mqttManager.mqttClient?.publish(message) } - //To Radio -// if decodedInfo.mqttClientProxyMessage.topic.contains("/stat/") { -// return -// } -// -// var toRadio: ToRadio! -// toRadio = ToRadio() -// toRadio.mqttClientProxyMessage = decodedInfo.mqttClientProxyMessage -// let binaryData: Data = try! toRadio.serializedData() -// if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { -// connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) -// print("๐Ÿ“ฒ Sent Mqtt client proxy message to the connected device.") -// } - switch decodedInfo.packet.decoded.portnum { // Handle Any local only packets we get over BLE @@ -620,6 +646,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } } + + public func sendMessage(message: String, toUserNum: Int64, channel: Int32, isEmoji: Bool, replyID: Int64) -> Bool { var success = false diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index d261c4a1..ebf385a2 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -8,8 +8,14 @@ import Foundation import CocoaMQTT +protocol MqttClientProxyManagerDelegate: AnyObject { + func onMqttConnected() + func onMqttDisconnected() + func onMqttMessageReceived(message: CocoaMQTTMessage) + func onMqttError(message: String) +} + class MqttClientProxyManager { - enum ConnectionStatus { case connecting case connected @@ -25,11 +31,16 @@ class MqttClientProxyManager { case exactlyOnce = 2 } + // Singleton Instance static let shared = MqttClientProxyManager() + private static let defaultKeepAliveInterval: Int32 = 60 - weak var delegate: CocoaMQTTDelegate? + + weak var delegate: MqttClientProxyManagerDelegate? var status = ConnectionStatus.none + var mqttClient: CocoaMQTT? + var topic = "msh/2/c" private init() { @@ -51,7 +62,6 @@ class MqttClientProxyManager { let password = node.mqttConfig?.password let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" - let preset = ModemPresets(rawValue: Int(node.loRaConfig?.modemPreset ?? 0)) ?? ModemPresets.longFast let prefix = root! + "/2/c" topic = prefix + "/#" let qos = CocoaMQTTQoS(rawValue :UInt8(1))! @@ -61,10 +71,16 @@ class MqttClientProxyManager { func connect(host: String, port: Int, username: String?, password: String?, topic: String?, qos: CocoaMQTTQoS, cleanSession: Bool) { - let clientId = "MeshtasticAppleMqttProxy-" + String(ProcessInfo().processIdentifier) - status = .connecting - mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) + guard !host.isEmpty else { + delegate?.onMqttDisconnected() + return + } + status = .connecting + + let clientId = "MeshtasticAppleMqttProxy-" + String(ProcessInfo().processIdentifier) + + mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) if let mqttClient = mqttClient { mqttClient.username = username @@ -79,9 +95,11 @@ class MqttClientProxyManager { mqttClient.delegate = self let success = mqttClient.connect() if !success { + delegate?.onMqttError(message: "Mqtt connect error") status = .error } } else { + delegate?.onMqttError(message: "Mqtt initialization error") status = .error } } @@ -118,16 +136,11 @@ class MqttClientProxyManager { extension MqttClientProxyManager: CocoaMQTTDelegate { - func mqtt(_ mqtt: CocoaMQTT, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void) { - completionHandler(true) - } - func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { print("๐Ÿ“ฒ MQTT Client Proxy didConnectAck: \(ack)") if ack == .accept { - let qos = CocoaMQTTQoS.qos1 - mqttClient!.subscribe(topic, qos: qos) + delegate?.onMqttConnected() print("๐Ÿ“ฒ MQTT Client Proxy subscribed to: " + topic) } else { @@ -150,25 +163,38 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { errorDescription = "Unknown Error" } print(errorDescription) - // mqttClient!.delegate.onMqttError(message: errorDescription) + delegate?.onMqttError(message: errorDescription) - //self.disconnect() // Stop reconnecting + self.disconnect() // Stop reconnecting //mqttSettings.isConnected = false // Disable automatic connect on start } - self.status = ack == .accept ? ConnectionStatus.connected : ConnectionStatus.error // Set AFTER sending onMqttError (so the delegate can detect that was an error while stablishing connection) + self.status = ack == .accept ? ConnectionStatus.connected : ConnectionStatus.error // Set AFTER sending onMqttError (so the delegate can detect that was an error while establishing connection) + } + + func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) { + print("mqttDidDisconnect: \(err?.localizedDescription ?? "")") + + if let error = err, status == .connecting { + delegate?.onMqttError(message: error.localizedDescription) + } + + status = err == nil ? .disconnected : .error + delegate?.onMqttDisconnected() } func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) { - print("๐Ÿ“ฒ MQTT Client Proxy didPublishMessage: \(message)") + print("๐Ÿ“ฒ MQTT Client Proxy didPublishMessage from MqttClientProxyManager: \(message)") } func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) { - print("๐Ÿ“ฒ MQTT Client Proxy didPublishAck: \(id)") - print("didPublishAck") + print("๐Ÿ“ฒ MQTT Client Proxy didPublishAck from MqttClientProxyManager: \(id)") } + + func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { + delegate?.onMqttMessageReceived(message: message) print("๐Ÿ“ฒ MQTT Client Proxy message received on topic: \(message.topic)") } @@ -181,14 +207,10 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { } func mqttDidPing(_ mqtt: CocoaMQTT) { - print("mqttDidPing") + //print("mqttDidPing") } func mqttDidReceivePong(_ mqtt: CocoaMQTT) { - print("mqttDidReceivePong") - } - - func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) { - print("mqttDidDisconnect") + //print("mqttDidReceivePong") } } diff --git a/Meshtastic/Views/Nodes/RemoteHardware.swift b/Meshtastic/Views/Nodes/RemoteHardware.swift new file mode 100644 index 00000000..42ddc4d4 --- /dev/null +++ b/Meshtastic/Views/Nodes/RemoteHardware.swift @@ -0,0 +1,8 @@ +// +// RemoteHardware.swift +// Meshtastic +// +// Created by Garth Vander Houwen on 8/8/23. +// + +import Foundation From a08b49b72afdbd2746edf0685247d8a35f9ab89f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 9 Aug 2023 22:51:26 -0700 Subject: [PATCH 13/21] Working mqtt client proxy --- Meshtastic/Helpers/BLEManager.swift | 10 ++++---- .../Helpers/Mqtt/MqttClientProxyManager.swift | 24 ++++++++----------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index ebaafbde..5ab07067 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -13,7 +13,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate // MqttClientProxyManagerDelegate func onMqttConnected() { mqttManager.status = .connected - print("MQTT Connected") + print("๐Ÿ“ฒ Mqtt Client Proxy onMqttConnected now subscribing to \(mqttManager.topic).") + mqttManager.mqttClientProxy?.subscribe(mqttManager.topic) } func onMqttDisconnected() { @@ -25,14 +26,13 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate print("onMqttMessageReceived") if message.topic.contains("/stat/") { - // return + return } var proxyMessage = MqttClientProxyMessage() proxyMessage.topic = message.topic proxyMessage.data = Data(message.payload) proxyMessage.retained = message.retained - var toRadio: ToRadio! toRadio = ToRadio() toRadio.mqttClientProxyMessage = proxyMessage @@ -435,13 +435,13 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate // Publish mqttClientProxyMessages received on the from radio if decodedInfo.payloadVariant == FromRadio.OneOf_PayloadVariant.mqttClientProxyMessage(decodedInfo.mqttClientProxyMessage) { - var message = CocoaMQTTMessage ( + let message = CocoaMQTTMessage ( topic: decodedInfo.mqttClientProxyMessage.topic, payload: [UInt8](decodedInfo.mqttClientProxyMessage.data), retained: decodedInfo.mqttClientProxyMessage.retained ) print("๐Ÿ“ฒ Publish Mqtt client proxy message received on FromRadio to the Mqtt server \(message)") - mqttManager.mqttClient?.publish(message) + mqttManager.mqttClientProxy?.publish(message) } switch decodedInfo.packet.decoded.portnum { diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index ebf385a2..291936dd 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -39,7 +39,7 @@ class MqttClientProxyManager { weak var delegate: MqttClientProxyManagerDelegate? var status = ConnectionStatus.none - var mqttClient: CocoaMQTT? + var mqttClientProxy: CocoaMQTT? var topic = "msh/2/c" @@ -80,8 +80,8 @@ class MqttClientProxyManager { let clientId = "MeshtasticAppleMqttProxy-" + String(ProcessInfo().processIdentifier) - mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) - if let mqttClient = mqttClient { + mqttClientProxy = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) + if let mqttClient = mqttClientProxy { mqttClient.username = username mqttClient.password = password @@ -105,26 +105,26 @@ class MqttClientProxyManager { } func subscribe(topic: String, qos: MqttQos) { - let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! - mqttClient?.subscribe(topic, qos: qos) print("๐Ÿ“ฒ MQTT Client Proxy subscribed to: " + topic) + let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! + mqttClientProxy?.subscribe(topic, qos: qos) } func unsubscribe(topic: String) { - mqttClient?.unsubscribe(topic) + mqttClientProxy?.unsubscribe(topic) print("๐Ÿ“ฒ MQTT Client Proxy unsubscribe for: " + topic) } func publish(message: String, topic: String, qos: MqttQos) { let qos = CocoaMQTTQoS(rawValue :UInt8(qos.rawValue))! - mqttClient?.publish(topic, withString: message, qos: qos) + mqttClientProxy?.publish(topic, withString: message, qos: qos) print("๐Ÿ“ฒ MQTT Client Proxy publish for: " + topic) } func disconnect() { //MqttSettings.shared.isConnected = false - if let client = mqttClient { + if let client = mqttClientProxy { status = .disconnecting client.disconnect() print("๐Ÿ“ฒ MQTT Client Proxy Disconnected") @@ -141,8 +141,6 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { print("๐Ÿ“ฒ MQTT Client Proxy didConnectAck: \(ack)") if ack == .accept { delegate?.onMqttConnected() - print("๐Ÿ“ฒ MQTT Client Proxy subscribed to: " + topic) - } else { // Connection error var errorDescription = "Unknown Error" @@ -165,7 +163,7 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { print(errorDescription) delegate?.onMqttError(message: errorDescription) - self.disconnect() // Stop reconnecting + //self.disconnect() // Stop reconnecting //mqttSettings.isConnected = false // Disable automatic connect on start } @@ -190,10 +188,8 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) { print("๐Ÿ“ฒ MQTT Client Proxy didPublishAck from MqttClientProxyManager: \(id)") } - - - func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { + public func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { delegate?.onMqttMessageReceived(message: message) print("๐Ÿ“ฒ MQTT Client Proxy message received on topic: \(message.topic)") } From ae605cca536db3d5db57a4e79689670c3e215882 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 9 Aug 2023 23:02:18 -0700 Subject: [PATCH 14/21] Update logging --- Meshtastic/Helpers/BLEManager.swift | 2 +- Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 5ab07067..95a40d45 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -24,7 +24,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate func onMqttMessageReceived(message: CocoaMQTTMessage) { - print("onMqttMessageReceived") + print("๐Ÿ“ฒ Mqtt Client Proxy onMqttMessageReceived for topic: \(message.topic)") if message.topic.contains("/stat/") { return } diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 291936dd..284f379d 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -195,7 +195,7 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { } func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String]) { - print("didSubscribeTopics: \(success.allKeys.count) topics. failed: \(failed.count) topics") + print("๐Ÿ“ฒ MQTT Client Proxy didSubscribeTopics: \(success.allKeys.count) topics. failed: \(failed.count) topics") } func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopics topics: [String]) { @@ -203,10 +203,10 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { } func mqttDidPing(_ mqtt: CocoaMQTT) { - //print("mqttDidPing") + print("๐Ÿ“ฒ MQTT Client Proxy mqttDidPing") } func mqttDidReceivePong(_ mqtt: CocoaMQTT) { - //print("mqttDidReceivePong") + print("๐Ÿ“ฒ MQTT Client Proxy mqttDidReceivePong") } } From 367e45f382a3a5b8956e8ccf2f6e37fdfe0a386d Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 9 Aug 2023 23:36:00 -0700 Subject: [PATCH 15/21] How about we crash less --- Meshtastic/Helpers/BLEManager.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 95a40d45..0c758bb9 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -37,7 +37,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio = ToRadio() toRadio.mqttClientProxyMessage = proxyMessage let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) print("๐Ÿ“ฒ Sent Mqtt client proxy message to the connected device.") } @@ -314,7 +314,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate func requestDeviceMetadata(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32, context: NSManagedObjectContext) -> Int64 { - guard connectedPeripheral!.peripheral.state == CBPeripheralState.connected else { return 0 } + guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return 0 } var adminPacket = AdminMessage() adminPacket.getDeviceMetadataRequest = true @@ -340,7 +340,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate func sendTraceRouteRequest(destNum: Int64, wantResponse: Bool) -> Bool { var success = false - guard connectedPeripheral!.peripheral.state == CBPeripheralState.connected else { return success } + guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return success } let fromNodeNum = connectedPeripheral.num let routePacket = RouteDiscovery() @@ -359,7 +359,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) success = true @@ -370,7 +370,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } func sendWantConfig() { - guard connectedPeripheral!.peripheral.state == CBPeripheralState.connected else { return } + guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return } if FROMRADIO_characteristic == nil { MeshLogger.log("๐Ÿšจ \("firmware.version.unsupported".localized)") @@ -737,7 +737,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio = ToRadio() toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) let logString = String.localizedStringWithFormat("mesh.log.textmessage.sent %@ %@ %@".localized, String(newMessage.messageId), String(fromUserNum), String(toUserNum)) @@ -782,7 +782,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate let binaryData: Data = try! toRadio.serializedData() let logString = String.localizedStringWithFormat("mesh.log.waypoint.sent %@".localized, String(fromNodeNum)) MeshLogger.log("๐Ÿ“ \(logString)") - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) success = true let wayPointEntity = getWaypoint(id: Int64(waypoint.id), context: context!) @@ -864,7 +864,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio = ToRadio() toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) success = true let logString = String.localizedStringWithFormat("mesh.log.sharelocation %@".localized, String(fromNodeNum)) @@ -1087,7 +1087,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio = ToRadio() toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse) let logString = String.localizedStringWithFormat("mesh.log.channel.sent %@ %d".localized, String(connectedPeripheral.num), chan.index) MeshLogger.log("๐ŸŽ›๏ธ \(logString)") @@ -1111,7 +1111,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio = ToRadio() toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse) let logString = String.localizedStringWithFormat("mesh.log.lora.config.sent %@".localized, String(connectedPeripheral.num)) MeshLogger.log("๐Ÿ“ป \(logString)") @@ -1588,7 +1588,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) let logString = String.localizedStringWithFormat("mesh.log.cannedmessages.messages.get %@".localized, String(connectedPeripheral.num)) MeshLogger.log("๐Ÿฅซ \(logString)") @@ -1963,7 +1963,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected{ let newMessage = MessageEntity(context: context!) newMessage.messageId = Int64(meshPacket.id) newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970) From 0810ab8a7cebe3f0e5451a6df0f88d416a80e2c0 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 10 Aug 2023 10:46:30 -0500 Subject: [PATCH 16/21] Added ssl support and address:port parsing logic --- .../Helpers/Mqtt/MqttClientProxyManager.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 284f379d..af22fcd1 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -50,11 +50,16 @@ class MqttClientProxyManager { func connectFromConfigSettings(node: NodeInfoEntity) { let defaultServerAddress = "mqtt.meshtastic.org" - let defaultServerPort = 1883 + let useSsl = node.mqttConfig?.tlsEnabled == true + var defaultServerPort = useSsl ? 8883 : 1883 var host = node.mqttConfig?.address if host == nil || host!.isEmpty { host = defaultServerAddress } + else if host != nil && host!.contains(":") { + host = host!.components(separatedBy: ":")[0] + defaultServerPort = Int(host!.components(separatedBy: ":")[1])! + } if let host = host { let port = defaultServerPort @@ -65,11 +70,11 @@ class MqttClientProxyManager { let prefix = root! + "/2/c" topic = prefix + "/#" let qos = CocoaMQTTQoS(rawValue :UInt8(1))! - connect(host: host, port: port, username: username, password: password, topic: topic, qos: qos, cleanSession: true) + connect(host: host, port: port, useSsl: useSsl, username: username, password: password, topic: topic, qos: qos, cleanSession: true) } } - func connect(host: String, port: Int, username: String?, password: String?, topic: String?, qos: CocoaMQTTQoS, cleanSession: Bool) { + func connect(host: String, port: Int, useSsl: Bool, username: String?, password: String?, topic: String?, qos: CocoaMQTTQoS, cleanSession: Bool) { guard !host.isEmpty else { delegate?.onMqttDisconnected() @@ -83,6 +88,8 @@ class MqttClientProxyManager { mqttClientProxy = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) if let mqttClient = mqttClientProxy { + mqttClient.enableSSL = useSsl + mqttClient.allowUntrustCACertificate = true mqttClient.username = username mqttClient.password = password mqttClient.keepAlive = 60 From f69aae97dd2635b0aef9fc438815a69622e66095 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 10 Aug 2023 08:50:54 -0700 Subject: [PATCH 17/21] Add a node count to the node list view --- Meshtastic/Views/Nodes/NodeList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 251962e0..413720ed 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -102,7 +102,7 @@ struct NodeList: View { .padding([.top, .bottom]) } } - .navigationTitle("nodes") + .navigationTitle("Nodes (\(nodes.count))") .navigationBarItems(leading: MeshtasticLogo() ) From 8f3d3998ea2b5cf4efb78c48cf7aaa4205a36386 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 10 Aug 2023 10:27:54 -0700 Subject: [PATCH 18/21] localize --- Meshtastic/Views/Nodes/NodeList.swift | 2 +- Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift | 2 +- de.lproj/Localizable.strings | 3 ++- en.lproj/Localizable.strings | 3 ++- zh-Hans.lproj/Localizable.strings | 3 ++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 413720ed..759cd6ca 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -102,7 +102,7 @@ struct NodeList: View { .padding([.top, .bottom]) } } - .navigationTitle("Nodes (\(nodes.count))") + .navigationTitle(String.localizedStringWithFormat("nodes %@".localized, String(nodes.count))) .navigationBarItems(leading: MeshtasticLogo() ) diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 0146af9d..e462f9a5 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -62,7 +62,7 @@ struct MQTTConfig: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Toggle(isOn: $proxyToClientEnabled) { - Label("proxyToClientEnabled", systemImage: "iphone.radiowaves.left.and.right") + Label("mqtt.clientproxy", systemImage: "iphone.radiowaves.left.and.right") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) diff --git a/de.lproj/Localizable.strings b/de.lproj/Localizable.strings index 720cccf5..ea8b3b90 100644 --- a/de.lproj/Localizable.strings +++ b/de.lproj/Localizable.strings @@ -189,11 +189,12 @@ "module.configuration"="Modul Konfiguration"; "mqtt"="MQTT"; "mqtt.config"="MQTT Config"; +"mqtt.clientproxy"="MQTT Client Proxy"; "mqtt.username"="Benutzername"; "name"="Name"; "network"="Netzwerk"; "network.config"="Netzwerkeinstellungen"; -"nodes"="Nodes"; +"nodes %@"="Nodes (%@)"; "no.nodes"="Keine Meshtastic Nodes gefunden"; "not.connected"="Kein Gerรคt verbunden"; "numbers.punctuation"="Ziffern und Interpunktion"; diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 040aa124..a7b870a6 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -189,11 +189,12 @@ "module.configuration"="Module Configuration"; "mqtt"="MQTT"; "mqtt.config"="MQTT Config"; +"mqtt.clientproxy"="MQTT Client Proxy"; "mqtt.username"="Username"; "name"="Name"; "network"="Network"; "network.config"="Network Config"; -"nodes"="Nodes"; +"nodes %@"="Nodes (%@)"; "no.nodes"="No Meshtastic Nodes Found"; "not.connected"="No device connected"; "numbers.punctuation"="Numbers and Punctuation"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 949d7d8a..dac0df52 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -189,11 +189,12 @@ "module.configuration"="ๆจกๅ—้…็ฝฎ"; "mqtt"="MQTT"; "mqtt.config"="MQTT ้…็ฝฎ"; +"mqtt.clientproxy"="MQTT Client Proxy"; "mqtt.username"="็”จๆˆทๅ็งฐ"; "name"="ๅ็งฐ"; "network"="็ฝ‘็ปœ"; "network.config"="็ฝ‘็ปœ้…็ฝฎ"; -"nodes"="่Š‚็‚น"; +"nodes %@"="่Š‚็‚น (%@)"; "no.nodes"="ๆœชๆ‰พๅˆฐ Meshtastic ่Š‚็‚น"; "not.connected"="ๆœช่ฟžๆŽฅๅˆฐ็”ตๅฐ"; "numbers.punctuation"="ๆ•ฐๅญ—ๅ’Œๆ ‡็‚น็ฌฆๅท"; From 68854dee50dd408cad9c8d46a5985e042ad71947 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 10 Aug 2023 12:04:16 -0700 Subject: [PATCH 19/21] Handle nil better --- Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 0e5f13fe..26e24e21 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -206,7 +206,7 @@ struct EnvironmentMetricsLog: View { isPresented: $isExporting, document: CsvDocument(emptyCsv: exportString), contentType: .commaSeparatedText, - defaultFilename: String("\(node.user!.longName ?? "Node") Environment Metrics Log"), + defaultFilename: String("\(node.user?.longName ?? "Node") Environment Metrics Log"), onCompletion: { result in if case .success = result { print("Environment metrics log download succeeded.") From 24e69733f3a9072200791e54037ab5339ef9f7d4 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 10 Aug 2023 12:14:25 -0700 Subject: [PATCH 20/21] save gps pins --- Meshtastic/Views/Settings/Config/PositionConfig.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 7078f17d..d99043c4 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -302,6 +302,8 @@ struct PositionConfig: View { pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds) pc.broadcastSmartMinimumIntervalSecs = UInt32(broadcastSmartMinimumIntervalSecs) pc.broadcastSmartMinimumDistance = UInt32(broadcastSmartMinimumDistance) + pc.rxGpio = UInt32(rxGpio) + pc.txGpio = UInt32(txGpio) var pf: PositionFlags = [] if includeAltitude { pf.insert(.Altitude) } if includeAltitudeMsl { pf.insert(.AltitudeMsl) } From b589406aed2f315f86e200a4e2d26a021adc6282 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 10 Aug 2023 14:53:55 -0700 Subject: [PATCH 21/21] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index a7798509..b53e62c0 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1328,7 +1328,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.19; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1362,7 +1362,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.19; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1481,7 +1481,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.1.19; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1512,7 +1512,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.1.19; + MARKETING_VERSION = 2.2.0; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES;