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