Meshtastic-Apple/Meshtastic/Helpers/BLEManager.swift

2093 lines
84 KiB
Swift
Raw Normal View History

2021-09-10 07:41:26 -07:00
import Foundation
2021-09-12 17:51:10 -07:00
import CoreData
2021-09-10 07:41:26 -07:00
import CoreBluetooth
import SwiftUI
2022-01-04 22:57:33 -08:00
import MapKit
2023-08-01 22:28:02 -07:00
import CocoaMQTT
2021-09-10 07:41:26 -07:00
2021-11-29 15:59:06 -08:00
// ---------------------------------------------------------------------------------------
2021-09-10 21:50:54 -07:00
// Meshtastic BLE Device Manager
2021-11-29 15:59:06 -08:00
// ---------------------------------------------------------------------------------------
2023-08-08 18:10:50 -07:00
class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate, ObservableObject {
2023-08-08 18:10:50 -07:00
// MqttClientProxyManagerDelegate
func onMqttConnected() {
mqttManager.status = .connected
2023-08-09 22:51:26 -07:00
print("📲 Mqtt Client Proxy onMqttConnected now subscribing to \(mqttManager.topic).")
mqttManager.mqttClientProxy?.subscribe(mqttManager.topic)
2023-08-08 18:10:50 -07:00
}
func onMqttDisconnected() {
mqttManager.status = .disconnected
print("MQTT Disconnected")
}
func onMqttMessageReceived(message: CocoaMQTTMessage) {
2023-08-09 23:02:18 -07:00
print("📲 Mqtt Client Proxy onMqttMessageReceived for topic: \(message.topic)")
2023-08-08 18:10:50 -07:00
if message.topic.contains("/stat/") {
2023-08-09 22:51:26 -07:00
return
2023-08-08 18:10:50 -07:00
}
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()
2023-08-09 23:36:00 -07:00
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
2023-08-08 18:10:50 -07:00
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 {
2023-03-06 10:33:18 -08:00
return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
} catch {
fatalError("Can't find documents directory.")
}
}
2021-12-12 17:17:46 -08:00
var context: NSManagedObjectContext?
//var userSettings: UserSettings?
2021-12-12 17:17:46 -08:00
private var centralManager: CBCentralManager!
private let restoreKey = "Meshtastic.BLE.Manager"
@Published var peripherals: [Peripheral] = []
2022-09-28 15:50:35 -07:00
@Published var connectedPeripheral: Peripheral!
@Published var lastConnectionError: String
2022-12-04 06:56:55 -08:00
@Published var invalidVersion = false
@Published var isSwitchedOn: Bool = false
@Published var automaticallyReconnect: Bool = true
public var minimumVersion = "2.0.0"
public var connectedVersion: String
public var isConnecting: Bool = false
public var isConnected: Bool = false
public var isSubscribed: Bool = false
private var configNonce: UInt32 = 1
var timeoutTimer: Timer?
var timeoutTimerCount = 0
2022-02-22 09:08:06 -10:00
var positionTimer: Timer?
var lastPosition: CLLocationCoordinate2D?
let emptyNodeNum: UInt32 = 4294967295
2023-08-01 22:28:02 -07:00
let mqttManager = MqttClientProxyManager.shared
2022-09-28 15:50:35 -07:00
/* Meshtastic Service Details */
var TORADIO_characteristic: CBCharacteristic!
var FROMRADIO_characteristic: CBCharacteristic!
var FROMNUM_characteristic: CBCharacteristic!
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
2022-10-13 14:08:36 -07:00
let FROMRADIO_UUID = CBUUID(string: "0x2C55E69E-4993-11ED-B878-0242AC120002")
2022-10-11 14:25:59 -07:00
let EOL_FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
2022-09-28 15:50:35 -07:00
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
let meshLog = documentsFolder.appendingPathComponent("meshlog.txt")
2022-09-28 15:50:35 -07:00
// MARK: init BLEManager
override init() {
self.lastConnectionError = ""
self.connectedVersion = "0.0.0"
2022-09-28 15:50:35 -07:00
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
2023-08-08 18:10:50 -07:00
mqttManager.delegate = self
2023-03-06 10:33:18 -08:00
// centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: restoreKey])
2022-09-28 15:50:35 -07:00
}
2021-12-12 17:17:46 -08:00
// MARK: Scanning for BLE Devices
2022-09-28 15:50:35 -07:00
// Scan for nearby BLE devices using the Meshtastic BLE service ID
func startScanning() {
if isSwitchedOn {
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
2022-09-28 15:50:35 -07:00
print("✅ Scanning Started")
}
}
2021-12-12 17:17:46 -08:00
// Stop Scanning For BLE Devices
2022-09-28 15:50:35 -07:00
func stopScanning() {
if centralManager.isScanning {
centralManager.stopScan()
print("🛑 Stopped Scanning")
}
}
2021-12-12 17:17:46 -08:00
// MARK: BLE Connect functions
/// The action after the timeout-timer has fired
///
/// - Parameters:
/// - timer: The time that fired the event
///
2021-11-29 15:59:06 -08:00
@objc func timeoutTimerFired(timer: Timer) {
2021-12-15 23:53:45 -08:00
guard let timerContext = timer.userInfo as? [String: String] else { return }
let name: String = timerContext["name", default: "Unknown"]
self.timeoutTimerCount += 1
2022-10-12 15:26:25 -07:00
self.lastConnectionError = ""
if timeoutTimerCount == 10 {
2021-11-04 08:36:55 -07:00
if connectedPeripheral != nil {
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
}
2021-11-04 08:36:55 -07:00
connectedPeripheral = nil
2022-08-11 23:34:09 -07:00
if self.timeoutTimer != nil {
2022-08-11 23:34:09 -07:00
self.timeoutTimer!.invalidate()
}
2022-10-12 15:26:25 -07:00
self.isConnected = false
self.isConnecting = false
self.lastConnectionError = "🚨 " + String.localizedStringWithFormat("ble.connection.timeout %d %@".localized, timeoutTimerCount, name)
2022-10-15 10:14:08 -07:00
MeshLogger.log(lastConnectionError)
2022-10-12 15:26:25 -07:00
self.timeoutTimerCount = 0
self.startScanning()
2021-11-29 15:59:06 -08:00
} else {
print("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)")
}
}
2022-09-28 15:50:35 -07:00
// Connect to a specific peripheral
func connectTo(peripheral: CBPeripheral) {
stopScanning()
DispatchQueue.main.async {
self.isConnecting = true
self.lastConnectionError = ""
self.automaticallyReconnect = true
}
2022-10-12 16:09:55 -07:00
if connectedPeripheral != nil {
print(" BLE Disconnecting from: \(connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")")
2022-10-12 16:09:55 -07:00
disconnectPeripheral()
2022-09-28 15:50:35 -07:00
}
2022-10-12 16:09:55 -07:00
centralManager?.connect(peripheral)
// Invalidate any existing timer
2022-10-12 16:09:55 -07:00
if timeoutTimer != nil {
timeoutTimer!.invalidate()
2022-08-11 23:34:09 -07:00
}
// Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent
// the timer from running on the main UI thread
2022-10-12 15:46:53 -07:00
let context = ["name": "\(peripheral.name ?? "Unknown")"]
2022-10-12 16:09:55 -07:00
timeoutTimer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true)
RunLoop.current.add(timeoutTimer!, forMode: .common)
print(" BLE Connecting: \(peripheral.name ?? "Unknown")")
2022-09-28 15:50:35 -07:00
}
2022-09-28 15:50:35 -07:00
// Disconnect Connected Peripheral
func disconnectPeripheral(reconnect: Bool = true) {
2021-11-21 13:48:28 -08:00
guard let connectedPeripheral = connectedPeripheral else { return }
automaticallyReconnect = reconnect
2022-09-28 15:50:35 -07:00
centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
2022-10-11 14:25:59 -07:00
FROMRADIO_characteristic = nil
2022-09-28 15:50:35 -07:00
isConnected = false
isSubscribed = false
2022-09-28 15:50:35 -07:00
invalidVersion = false
connectedVersion = "0.0.0"
2022-10-12 16:09:55 -07:00
startScanning()
2022-09-28 15:50:35 -07:00
}
2022-09-28 15:50:35 -07:00
// Called each time a peripheral is discovered
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
2022-10-22 07:41:40 -07:00
isConnecting = false
isConnected = true
if UserDefaults.preferredPeripheralId.count < 1 {
UserDefaults.preferredPeripheralId = peripheral.identifier.uuidString
2022-10-01 09:37:10 -07:00
}
// Invalidate and reset connection timer count
2022-10-22 07:49:39 -07:00
timeoutTimerCount = 0
if timeoutTimer != nil {
timeoutTimer!.invalidate()
2022-08-11 23:34:09 -07:00
}
// remove any connection errors
self.lastConnectionError = ""
2022-09-09 08:25:52 -07:00
// Map the peripheral to the connectedPeripheral ObservedObjects
2022-09-28 15:50:35 -07:00
connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first
2022-09-09 08:25:52 -07:00
if connectedPeripheral != nil {
connectedPeripheral.peripheral.delegate = self
2023-03-06 10:33:18 -08:00
} else {
2022-09-09 08:25:52 -07:00
// we are null just disconnect and start over
2022-10-22 07:41:40 -07:00
lastConnectionError = "Bluetooth connection error, please try again."
disconnectPeripheral()
2022-09-09 08:25:52 -07:00
return
}
// Discover Services
2022-12-05 19:32:58 -08:00
peripheral.discoverServices([meshtasticServiceCBUUID])
print("✅ BLE Connected: \(peripheral.name ?? "Unknown")")
2022-09-28 15:50:35 -07:00
}
2021-12-12 17:17:46 -08:00
// Called when a Peripheral fails to connect
2021-11-21 13:48:28 -08:00
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
disconnectPeripheral()
print("🚫 BLE Failed to Connect: \(peripheral.name ?? "Unknown")")
2021-11-21 13:48:28 -08:00
}
2022-09-28 15:50:35 -07:00
// Disconnect Peripheral Event
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
self.connectedPeripheral = nil
2022-10-12 15:26:25 -07:00
self.isConnecting = false
self.isConnected = false
self.isSubscribed = false
2022-09-28 15:50:35 -07:00
if let e = error {
// https://developer.apple.com/documentation/corebluetooth/cberror/code
2022-09-28 15:50:35 -07:00
let errorCode = (e as NSError).code
if errorCode == 6 { // CBError.Code.connectionTimeout The connection has timed out unexpectedly.
// Happens when device is manually reset / powered off
lastConnectionError = "🚨" + String.localizedStringWithFormat("ble.errorcode.6 %@".localized, e.localizedDescription)
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
2022-09-28 15:50:35 -07:00
} else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us.
// Seems to be what is received when a tbeam sleeps, immediately recconnecting does not work.
lastConnectionError = "🚨 \(e.localizedDescription)"
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
2022-09-28 15:50:35 -07:00
} else if errorCode == 14 { // Peer removed pairing information
// Forgetting and reconnecting seems to be necessary so we need to show the user an error telling them to do that
lastConnectionError = "🚨 " + String.localizedStringWithFormat("ble.errorcode.14 %@".localized, e.localizedDescription)
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(lastConnectionError)")
2022-09-28 15:50:35 -07:00
} else {
lastConnectionError = "🚨 \(e.localizedDescription)"
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
}
2022-09-28 15:50:35 -07:00
} else {
// Disconnected without error which indicates user intent to disconnect
// Happens when swiping to disconnect
print(" BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect")
2022-09-28 15:50:35 -07:00
}
// Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake
self.startScanning()
2022-09-28 15:50:35 -07:00
}
2022-09-28 15:50:35 -07:00
// MARK: Peripheral Services functions
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let e = error {
print("🚫 Discover Services error \(e)")
}
guard let services = peripheral.services else { return }
for service in services {
if service.uuid == meshtasticServiceCBUUID {
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID], for: service)
print("✅ BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")")
2022-12-05 19:32:58 -08:00
}
2022-09-28 15:50:35 -07:00
}
}
2022-09-28 15:50:35 -07:00
// MARK: Discover Characteristics Event
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
2022-09-28 15:50:35 -07:00
if let e = error {
print("🚫 BLE Discover Characteristics error for \(peripheral.name ?? "Unknown") \(e) disconnecting device")
// Try and stop crashes when this error occurs
disconnectPeripheral()
return
2022-09-28 15:50:35 -07:00
}
2022-09-28 15:50:35 -07:00
guard let characteristics = service.characteristics else { return }
2022-09-28 15:50:35 -07:00
for characteristic in characteristics {
2021-11-29 16:51:59 -08:00
switch characteristic.uuid {
2021-11-29 16:51:59 -08:00
case TORADIO_UUID:
print("✅ BLE did discover TORADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")")
2021-11-29 16:51:59 -08:00
TORADIO_characteristic = characteristic
2021-11-29 16:51:59 -08:00
case FROMRADIO_UUID:
print("✅ BLE did discover FROMRADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")")
2021-11-29 16:51:59 -08:00
FROMRADIO_characteristic = characteristic
peripheral.readValue(for: FROMRADIO_characteristic)
2021-11-29 16:51:59 -08:00
case FROMNUM_UUID:
print("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")")
2021-11-29 16:51:59 -08:00
FROMNUM_characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
2021-11-29 16:51:59 -08:00
default:
break
}
}
2023-03-06 10:33:18 -08:00
if ![FROMNUM_characteristic, TORADIO_characteristic].contains(nil) {
2022-08-30 07:46:46 -05:00
sendWantConfig()
}
2022-09-28 15:50:35 -07:00
}
func requestDeviceMetadata(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32, context: NSManagedObjectContext) -> Int64 {
2023-08-09 23:36:00 -07:00
guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return 0 }
2022-09-27 06:33:00 -07:00
var adminPacket = AdminMessage()
adminPacket.getDeviceMetadataRequest = true
2022-09-27 06:33:00 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
2022-09-27 06:33:00 -07:00
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2022-09-27 06:33:00 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛎️ Requested Device Metadata for node \(toUser.longName ?? "unknown".localized) by \(fromUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
2022-09-27 06:33:00 -07:00
}
2023-03-06 10:33:18 -08:00
func sendTraceRouteRequest(destNum: Int64, wantResponse: Bool) -> Bool {
var success = false
2023-08-09 23:36:00 -07:00
guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return success }
2023-02-02 23:17:23 -08:00
let fromNodeNum = connectedPeripheral.num
let routePacket = RouteDiscovery()
var meshPacket = MeshPacket()
meshPacket.to = UInt32(destNum)
2023-02-02 23:17:23 -08:00
meshPacket.from = UInt32(fromNodeNum)
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! routePacket.serializedData()
dataMessage.portnum = PortNum.tracerouteApp
dataMessage.wantResponse = wantResponse
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
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.traceroute.sent %@".localized, String(destNum))
MeshLogger.log("🪧 \(logString)")
}
return success
}
2022-08-30 07:57:15 -05:00
func sendWantConfig() {
2023-08-09 23:36:00 -07:00
guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return }
2022-10-11 14:25:59 -07:00
if FROMRADIO_characteristic == nil {
MeshLogger.log("🚨 \("firmware.version.unsupported".localized)")
2022-10-11 14:25:59 -07:00
invalidVersion = true
return
} else {
let nodeName = connectedPeripheral?.peripheral.name ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.wantconfig %@".localized, nodeName)
MeshLogger.log("🛎️ \(logString)")
// BLE Characteristics discovered, issue wantConfig
var toRadio: ToRadio = ToRadio()
configNonce += 1
toRadio.wantConfigID = configNonce
let binaryData: Data = try! toRadio.serializedData()
connectedPeripheral!.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
2022-10-11 14:25:59 -07:00
// Either Read the config complete value or from num notify value
connectedPeripheral!.peripheral.readValue(for: FROMRADIO_characteristic)
}
2022-08-30 07:46:46 -05:00
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
2021-11-29 15:59:06 -08:00
if let errorText = error?.localizedDescription {
print("🚫 didUpdateNotificationStateFor error: \(errorText)")
2021-12-12 17:17:46 -08:00
}
}
2022-09-28 15:50:35 -07:00
// MARK: Data Read / Update Characteristic Event
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("🚫 didUpdateValueFor Characteristic error \(e)")
let errorCode = (e as NSError).code
if errorCode == 5 || errorCode == 15 {
// BLE PIN connection errors
// 5 CBATTErrorDomain Code=5 "Authentication is insufficient."
// 15 CBATTErrorDomain Code=15 "Encryption is insufficient."
lastConnectionError = "🚨" + String.localizedStringWithFormat("ble.errorcode.pin %@".localized, e.localizedDescription)
print("🚨 \(e.localizedDescription) Please try connecting again and check the PIN carefully.")
self.disconnectPeripheral(reconnect: false)
}
2022-09-28 15:50:35 -07:00
}
2022-09-28 15:50:35 -07:00
switch characteristic.uuid {
2021-11-29 17:09:27 -08:00
case FROMRADIO_UUID:
2021-11-29 17:09:27 -08:00
if characteristic.value == nil || characteristic.value!.isEmpty {
return
}
var decodedInfo = FromRadio()
2022-06-24 07:48:25 -07:00
do {
decodedInfo = try FromRadio(serializedData: characteristic.value!)
2022-06-24 07:48:25 -07:00
} catch {
print(characteristic.value!)
}
2023-08-08 18:10:50 -07:00
// Publish mqttClientProxyMessages received on the from radio
if decodedInfo.payloadVariant == FromRadio.OneOf_PayloadVariant.mqttClientProxyMessage(decodedInfo.mqttClientProxyMessage) {
2023-08-09 22:51:26 -07:00
let message = CocoaMQTTMessage (
topic: decodedInfo.mqttClientProxyMessage.topic,
payload: [UInt8](decodedInfo.mqttClientProxyMessage.data),
retained: decodedInfo.mqttClientProxyMessage.retained
)
2023-08-08 18:10:50 -07:00
print("📲 Publish Mqtt client proxy message received on FromRadio to the Mqtt server \(message)")
2023-08-09 22:51:26 -07:00
mqttManager.mqttClientProxy?.publish(message)
}
switch decodedInfo.packet.decoded.portnum {
// Handle Any local only packets we get over BLE
2023-03-06 13:26:04 -08:00
case .unknownApp:
var nowKnown = false
// MyInfo from initial connection
if decodedInfo.myInfo.isInitialized && decodedInfo.myInfo.myNodeNum > 0 {
let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, peripheralId: self.connectedPeripheral.id, context: context!)
if myInfo != nil {
connectedPeripheral.num = myInfo!.myNodeNum
connectedPeripheral.name = myInfo?.bleName ?? "unknown".localized
connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized
}
2023-03-09 14:23:15 -06:00
tryClearExistingChannels()
}
// NodeInfo
2023-02-06 10:26:04 -08:00
if decodedInfo.nodeInfo.num > 0 && !invalidVersion {
nowKnown = true
2022-10-30 19:27:15 -07:00
let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, channel: decodedInfo.packet.channel, context: context!)
if nodeInfo != nil {
if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num {
if nodeInfo!.user != nil {
2022-11-11 19:21:52 -08:00
connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "????"
connectedPeripheral.longName = nodeInfo?.user?.longName ?? "unknown".localized
}
}
}
}
// Channels
2023-05-30 23:07:34 -07:00
if decodedInfo.channel.isInitialized && connectedPeripheral != nil {
nowKnown = true
2022-10-15 10:14:08 -07:00
channelPacket(channel: decodedInfo.channel, fromNum: connectedPeripheral.num, context: context!)
}
// Config
2023-05-30 23:07:34 -07:00
if decodedInfo.config.isInitialized && !invalidVersion && connectedPeripheral != nil {
nowKnown = true
2022-10-15 10:14:08 -07:00
localConfig(config: decodedInfo.config, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
}
// Module Config
2022-09-27 06:33:00 -07:00
if decodedInfo.moduleConfig.isInitialized && !invalidVersion {
nowKnown = true
2022-10-15 10:14:08 -07:00
moduleConfig(config: decodedInfo.moduleConfig, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
if decodedInfo.moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(decodedInfo.moduleConfig.cannedMessage) {
if decodedInfo.moduleConfig.cannedMessage.enabled {
2022-12-12 21:19:22 -08:00
_ = self.getCannedMessageModuleMessages(destNum: self.connectedPeripheral.num, wantResponse: true)
}
}
}
// Device Metadata
if decodedInfo.metadata.firmwareVersion.count > 0 && !invalidVersion {
nowKnown = true
deviceMetadataPacket(metadata: decodedInfo.metadata, fromNum: connectedPeripheral.num, context: context!)
connectedPeripheral.firmwareVersion = decodedInfo.metadata.firmwareVersion ?? "unknown".localized
let lastDotIndex = decodedInfo.metadata.firmwareVersion.lastIndex(of: ".")
if lastDotIndex == nil {
invalidVersion = true
connectedVersion = "0.0.0"
} else {
let version = decodedInfo.metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.metadata.firmwareVersion))]
nowKnown = true
connectedVersion = String(version.dropLast())
}
let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame
if !supportedVersion {
invalidVersion = true
lastConnectionError = "🚨" + "update.firmware".localized
return
}
}
// Log any other unknownApp calls
2023-03-06 13:26:04 -08:00
if !nowKnown { MeshLogger.log("🕸️ MESH PACKET received for Unknown App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") }
2023-03-06 13:26:04 -08:00
case .textMessageApp:
textMessageAppPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), context: context!)
case .remoteHardwareApp:
MeshLogger.log("🕸️ MESH PACKET received for Remote Hardware App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .positionApp:
upsertPositionPacket(packet: decodedInfo.packet, context: context!)
case .waypointApp:
waypointPacket(packet: decodedInfo.packet, context: context!)
case .nodeinfoApp:
if !invalidVersion { upsertNodeInfoPacket(packet: decodedInfo.packet, context: context!) }
2023-03-06 13:26:04 -08:00
case .routingApp:
if !invalidVersion { routingPacket(packet: decodedInfo.packet, connectedNodeNum: self.connectedPeripheral.num, context: context!) }
case .adminApp:
adminAppPacket(packet: decodedInfo.packet, context: context!)
case .replyApp:
MeshLogger.log("🕸️ MESH PACKET received for Reply App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .ipTunnelApp:
MeshLogger.log("🕸️ MESH PACKET received for IP Tunnel App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .serialApp:
MeshLogger.log("🕸️ MESH PACKET received for Serial App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .storeForwardApp:
MeshLogger.log("🕸️ MESH PACKET received for Store Forward App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .rangeTestApp:
MeshLogger.log("🕸️ MESH PACKET received for Range Test App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .telemetryApp:
if !invalidVersion { telemetryPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), context: context!) }
2023-03-06 13:26:04 -08:00
case .textMessageCompressedApp:
MeshLogger.log("🕸️ MESH PACKET received for Text Message Compressed App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .zpsApp:
MeshLogger.log("🕸️ MESH PACKET received for ZPS App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .privateApp:
MeshLogger.log("🕸️ MESH PACKET received for Private App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .atakForwarder:
MeshLogger.log("🕸️ MESH PACKET received for ATAK Forwarder App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .simulatorApp:
MeshLogger.log("🕸️ MESH PACKET received for Simulator App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .audioApp:
MeshLogger.log("🕸️ MESH PACKET received for Audio App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .tracerouteApp:
if let routingMessage = try? RouteDiscovery(serializedData: decodedInfo.packet.decoded.payload) {
if routingMessage.route.count == 0 {
let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.direct %@".localized, String(decodedInfo.packet.from))
MeshLogger.log("🪧 \(logString)")
} else {
var routeString = "\(decodedInfo.packet.to) --> "
for node in routingMessage.route {
routeString += "\(node) --> "
}
routeString += "\(decodedInfo.packet.from)"
let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString)
MeshLogger.log("🪧 \(logString)")
}
}
2023-05-04 20:11:42 -07:00
case .neighborinfoApp:
MeshLogger.log("🕸️ MESH PACKET received for Neighbor Info App App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
2023-03-06 13:26:04 -08:00
case .UNRECOGNIZED:
MeshLogger.log("🕸️ MESH PACKET received for Other App UNHANDLED \(try! decodedInfo.packet.jsonString())")
case .max:
print("MAX PORT NUM OF 511")
}
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce {
2022-10-22 07:45:07 -07:00
invalidVersion = false
lastConnectionError = ""
isSubscribed = true
2022-12-30 09:35:05 -08:00
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
2023-08-01 22:28:02 -07:00
/// MQTT Client Proxy
2023-08-06 20:59:39 -07:00
if connectedPeripheral.num > 0 {
2023-08-01 22:28:02 -07:00
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = 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 {
2023-08-06 20:59:39 -07:00
2023-08-01 22:28:02 -07:00
//Subscribe to Mqtt Client Proxy if enabled
2023-08-06 20:59:39 -07:00
if fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false {
2023-08-06 22:31:19 -07:00
mqttManager.connectFromConfigSettings(node: fetchedNodeInfo[0])
2023-08-06 20:59:39 -07:00
}
2023-08-01 22:28:02 -07:00
}
} catch {
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
2021-11-29 17:09:27 -08:00
}
2023-03-06 10:33:18 -08:00
case FROMNUM_UUID:
2022-10-12 22:15:15 -07:00
print("🗞️ BLE (Notify) characteristic, value will be read next")
2021-11-29 17:09:27 -08:00
default:
print("🚨 Unhandled Characteristic UUID: \(characteristic.uuid)")
2022-09-28 15:50:35 -07:00
}
if FROMRADIO_characteristic != nil {
// Either Read the config complete value or from num notify value
peripheral.readValue(for: FROMRADIO_characteristic)
}
}
2023-08-08 18:10:50 -07:00
2022-11-07 18:51:17 -08:00
public func sendMessage(message: String, toUserNum: Int64, channel: Int32, isEmoji: Bool, replyID: Int64) -> Bool {
var success = false
// Return false if we are not properly connected to a device, handle retry logic in the view for now
if connectedPeripheral == nil || connectedPeripheral!.peripheral.state != CBPeripheralState.connected {
2021-11-21 13:48:28 -08:00
self.disconnectPeripheral()
self.startScanning()
// Try and connect to the preferredPeripherial first
let preferredPeripheral = peripherals.filter({ $0.peripheral.identifier.uuidString == UserDefaults.preferredPeripheralId as? String ?? "" }).first
if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil {
connectTo(peripheral: preferredPeripheral!.peripheral)
}
let nodeName = connectedPeripheral?.peripheral.name ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.textmessage.send.failed %@".localized, nodeName)
MeshLogger.log("🚫 \(logString)")
success = false
2021-11-29 15:59:06 -08:00
} else if message.count < 1 {
// Don't send an empty message
print("🚫 Don't Send an Empty Message")
success = false
2021-11-29 15:59:06 -08:00
} else {
2021-12-25 23:48:12 -08:00
let fromUserNum: Int64 = self.connectedPeripheral.num
2021-12-25 23:48:12 -08:00
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
messageUsers.predicate = NSPredicate(format: "num IN %@", [fromUserNum, Int64(toUserNum)])
2021-12-18 20:49:50 -08:00
do {
2023-03-06 15:30:10 -08:00
guard let fetchedUsers = try context?.fetch(messageUsers) as? [UserEntity] else {
return false
}
2021-12-19 19:40:16 -08:00
if fetchedUsers.isEmpty {
print("🚫 Message Users Not Found, Fail")
2021-12-18 20:49:50 -08:00
success = false
2021-12-25 23:48:12 -08:00
} else if fetchedUsers.count >= 1 {
2021-12-18 20:49:50 -08:00
let newMessage = MessageEntity(context: context!)
2022-02-22 18:06:50 -10:00
newMessage.messageId = Int64(UInt32.random(in: UInt32(UInt8.max)..<UInt32.max))
2021-12-18 20:49:50 -08:00
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
newMessage.receivedACK = false
2022-11-08 11:30:08 -08:00
if toUserNum > 0 {
newMessage.toUser = fetchedUsers.first(where: { $0.num == toUserNum })
}
2022-11-07 18:51:17 -08:00
newMessage.fromUser = fetchedUsers.first(where: { $0.num == fromUserNum })
2022-03-29 21:16:15 -07:00
newMessage.isEmoji = isEmoji
newMessage.admin = false
2022-11-07 18:51:17 -08:00
newMessage.channel = channel
2022-01-04 22:57:33 -08:00
if replyID > 0 {
newMessage.replyID = replyID
2022-01-01 22:55:25 -08:00
}
2021-12-18 20:49:50 -08:00
newMessage.messagePayload = message
newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: message)
2021-12-18 20:49:50 -08:00
let dataType = PortNum.textMessageApp
2023-02-11 07:47:49 -08:00
var messageQuotesReplaced = message.replacingOccurrences(of: "", with: "'")
messageQuotesReplaced = message.replacingOccurrences(of: "", with: "\"")
let payloadData: Data = messageQuotesReplaced.data(using: String.Encoding.utf8)!
2021-12-18 20:49:50 -08:00
var dataMessage = DataMessage()
dataMessage.payload = payloadData
dataMessage.portnum = dataType
2021-12-18 20:49:50 -08:00
var meshPacket = MeshPacket()
2022-02-22 18:06:50 -10:00
meshPacket.id = UInt32(newMessage.messageId)
2022-11-08 11:30:08 -08:00
if toUserNum > 0 {
meshPacket.to = UInt32(toUserNum)
} else {
meshPacket.to = emptyNodeNum
2022-11-08 11:30:08 -08:00
}
2022-11-08 12:48:16 -08:00
meshPacket.channel = UInt32(channel)
2021-12-19 19:40:16 -08:00
meshPacket.from = UInt32(fromUserNum)
meshPacket.decoded = dataMessage
2022-03-29 21:16:15 -07:00
meshPacket.decoded.emoji = isEmoji ? 1 : 0
2022-01-04 22:57:33 -08:00
if replyID > 0 {
2022-02-04 02:26:58 -08:00
meshPacket.decoded.replyID = UInt32(replyID)
2022-01-01 22:55:25 -08:00
}
2021-12-18 20:49:50 -08:00
meshPacket.wantAck = true
2021-12-18 20:49:50 -08:00
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
2021-12-18 20:49:50 -08:00
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
2023-05-05 17:42:24 -07:00
let logString = String.localizedStringWithFormat("mesh.log.textmessage.sent %@ %@ %@".localized, String(newMessage.messageId), String(fromUserNum), String(toUserNum))
MeshLogger.log("💬 \(logString)")
2021-12-18 20:49:50 -08:00
do {
try context!.save()
2022-12-30 21:28:51 -08:00
print("💾 Saved a new sent message from \(connectedPeripheral.num) to \(toUserNum)")
2021-12-18 20:49:50 -08:00
success = true
2021-12-18 20:49:50 -08:00
} catch {
context!.rollback()
let nsError = error as NSError
2022-12-30 20:04:43 -08:00
print("💥 Unresolved Core Data error in Send Message Function your database is corrupted running a node db reset should clean up the data. Error: \(nsError)")
2021-12-18 20:49:50 -08:00
}
}
2021-12-15 23:53:45 -08:00
}
2021-12-18 20:49:50 -08:00
} catch {
}
}
return success
}
2023-01-14 09:42:09 -08:00
public func sendWaypoint(waypoint: Waypoint) -> Bool {
2023-01-21 07:28:50 -08:00
if waypoint.latitudeI == 373346000 && waypoint.longitudeI == -1220090000 {
return false
}
var success = false
2023-01-14 09:42:09 -08:00
let fromNodeNum = UInt32(connectedPeripheral.num)
var meshPacket = MeshPacket()
2023-01-14 09:42:09 -08:00
meshPacket.to = emptyNodeNum
meshPacket.from = fromNodeNum
2023-01-14 09:58:28 -08:00
meshPacket.wantAck = true
var dataMessage = DataMessage()
2023-01-29 08:28:44 -08:00
dataMessage.payload = try! waypoint.serializedData()
2022-11-13 09:25:00 -08:00
dataMessage.portnum = PortNum.waypointApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
let logString = String.localizedStringWithFormat("mesh.log.waypoint.sent %@".localized, String(fromNodeNum))
MeshLogger.log("📍 \(logString)")
2023-08-09 23:36:00 -07:00
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
2023-01-16 17:40:28 -08:00
let wayPointEntity = getWaypoint(id: Int64(waypoint.id), context: context!)
2023-01-16 23:16:57 -08:00
wayPointEntity.id = Int64(waypoint.id)
2023-01-29 08:28:44 -08:00
wayPointEntity.name = waypoint.name.count >= 1 ? waypoint.name : "Dropped Pin"
2023-01-16 17:40:28 -08:00
wayPointEntity.longDescription = waypoint.description_p
wayPointEntity.icon = Int64(waypoint.icon)
wayPointEntity.latitudeI = waypoint.latitudeI
wayPointEntity.longitudeI = waypoint.longitudeI
if waypoint.expire > 1 {
2023-02-03 19:20:23 -08:00
wayPointEntity.expire = Date.init(timeIntervalSince1970: Double(waypoint.expire))
} else {
wayPointEntity.expire = nil
}
if waypoint.lockedTo > 0 {
wayPointEntity.locked = Int64(waypoint.lockedTo)
} else {
wayPointEntity.locked = 0
}
if wayPointEntity.created == nil {
wayPointEntity.created = Date()
} else {
wayPointEntity.lastUpdated = Date()
}
2023-01-14 20:27:05 -08:00
do {
try context!.save()
print("💾 Updated Waypoint from Waypoint App Packet From: \(fromNodeNum)")
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from WAYPOINT_APP \(nsError)")
}
}
2022-08-11 23:34:09 -07:00
return success
}
public func sendPosition(destNum: Int64, wantResponse: Bool, smartPosition: Bool) -> Bool {
var success = false
let fromNodeNum = connectedPeripheral.num
2023-01-24 20:33:48 -08:00
if fromNodeNum <= 0 || LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) == 0.0 {
return false
}
if smartPosition {
if lastPosition != nil {
let connectedNode = getNodeInfo(id: connectedPeripheral?.num ?? 0, context: context!)
if connectedNode?.positionConfig?.smartPositionEnabled ?? false {
if lastPosition!.distance(from: LocationHelper.currentLocation) < Double(connectedNode?.positionConfig?.broadcastSmartMinimumDistance ?? 50) {
return false
}
}
}
}
lastPosition = LocationHelper.currentLocation
var positionPacket = Position()
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
positionPacket.timestamp = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
positionPacket.satsInView = UInt32(LocationHelper.satsInView)
if LocationHelper.currentSpeed > 0 && (!LocationHelper.currentSpeed.isNaN || !LocationHelper.currentSpeed.isInfinite) {
positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed * 3.6)
2023-02-05 18:36:35 -08:00
}
if LocationHelper.currentHeading > 0 && (!LocationHelper.currentHeading.isNaN || !LocationHelper.currentHeading.isInfinite) {
positionPacket.groundTrack = UInt32(LocationHelper.currentHeading)
}
2023-05-13 14:23:12 -07:00
var meshPacket = MeshPacket()
meshPacket.to = UInt32(destNum)
2023-02-05 18:36:35 -08:00
meshPacket.from = UInt32(fromNodeNum)
var dataMessage = DataMessage()
dataMessage.payload = try! positionPacket.serializedData()
dataMessage.portnum = PortNum.positionApp
dataMessage.wantResponse = wantResponse
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
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))
MeshLogger.log("📍 \(logString)")
}
return success
}
2022-02-22 09:08:06 -10:00
@objc func positionTimerFired(timer: Timer) {
// Check for connected node
if connectedPeripheral != nil {
// Send a position out to the mesh if "share location with the mesh" is enabled in settings
if UserDefaults.provideLocation {
let _ = sendPosition(destNum: connectedPeripheral.num, wantResponse: false, smartPosition: true)
2022-02-22 09:08:06 -10:00
}
}
}
2023-02-02 22:03:27 -08:00
public func sendShutdown(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
var adminPacket = AdminMessage()
2023-02-02 22:03:27 -08:00
adminPacket.shutdownSeconds = 5
var meshPacket: MeshPacket = MeshPacket()
2022-12-30 19:21:22 -08:00
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
2022-05-29 22:02:25 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2023-02-02 22:03:27 -08:00
meshPacket.channel = UInt32(adminIndex)
2022-05-29 22:02:25 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Shutdown Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
2022-12-30 19:21:22 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
2022-05-29 22:02:25 -07:00
}
return false
}
2023-02-02 22:03:27 -08:00
public func sendReboot(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2022-05-29 22:02:25 -07:00
var adminPacket = AdminMessage()
2023-02-02 22:03:27 -08:00
adminPacket.rebootSeconds = 5
2022-05-29 22:02:25 -07:00
var meshPacket: MeshPacket = MeshPacket()
2023-01-29 00:16:17 -08:00
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2023-02-02 22:03:27 -08:00
meshPacket.channel = UInt32(adminIndex)
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Reboot Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
public func sendRebootOta(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
var adminPacket = AdminMessage()
adminPacket.rebootOtaSeconds = 5
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
meshPacket.channel = UInt32(adminIndex)
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Reboot OTA Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
public func sendFactoryReset(fromUser: UserEntity, toUser: UserEntity) -> Bool {
var adminPacket = AdminMessage()
adminPacket.factoryReset = 5
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Factory Reset Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2022-10-09 07:38:19 -07:00
return true
}
return false
}
public func sendNodeDBReset(fromUser: UserEntity, toUser: UserEntity) -> Bool {
2022-10-02 09:19:03 -07:00
var adminPacket = AdminMessage()
adminPacket.nodedbReset = 5
2022-10-02 09:19:03 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = 0 // UInt32(fromUser.num)
2022-10-02 09:19:03 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
2022-10-02 09:19:03 -07:00
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent NodeDB Reset Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2022-10-15 10:14:08 -07:00
return true
2022-10-02 09:19:03 -07:00
}
return false
}
2022-10-18 19:50:42 -07:00
public func connectToPreferredPeripheral() -> Bool {
var success = false
// Return false if we are not properly connected to a device, handle retry logic in the view for now
if connectedPeripheral == nil || connectedPeripheral!.peripheral.state != CBPeripheralState.connected {
self.disconnectPeripheral()
self.startScanning()
// Try and connect to the preferredPeripherial first
let preferredPeripheral = peripherals.filter({ $0.peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" }).first
if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil {
connectTo(peripheral: preferredPeripheral!.peripheral)
success = true
}
} else if connectedPeripheral != nil && isSubscribed {
success = true
}
return success
}
public func getChannel(channel: Channel, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.getChannelRequest = UInt32(channel.index + 1)
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
2023-03-06 10:33:18 -08:00
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🎛️ Requested Channel \(channel.index) for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
}
2022-12-21 11:47:02 -08:00
public func saveChannel(channel: Channel, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
2022-12-21 11:47:02 -08:00
var adminPacket = AdminMessage()
adminPacket.setChannel = channel
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
2023-03-06 10:33:18 -08:00
meshPacket.from = UInt32(fromUser.num)
2022-12-21 11:47:02 -08:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2022-12-21 11:47:02 -08:00
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Channel \(channel.index) for \(toUser.longName ?? "unknown".localized)"
2022-12-21 11:47:02 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
}
public func saveChannelSet(base64UrlString: String) -> Bool {
2022-10-18 19:50:42 -07:00
if isConnected {
2023-03-06 10:33:18 -08:00
// Before we get started delete the existing channels from the myNodeInfo
2022-10-28 09:02:37 -07:00
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num))
2023-03-09 14:23:15 -06:00
tryClearExistingChannels()
2022-10-28 09:02:37 -07:00
let decodedString = base64UrlString.base64urlToBase64()
2022-10-18 19:50:42 -07:00
if let decodedData = Data(base64Encoded: decodedString) {
do {
2022-10-29 07:31:50 -07:00
let channelSet: ChannelSet = try ChannelSet(serializedData: decodedData)
2023-03-06 10:33:18 -08:00
var i: Int32 = 0
2022-10-19 16:58:49 -07:00
for cs in channelSet.settings {
var chan = Channel()
2022-10-29 07:31:50 -07:00
if i == 0 {
2022-10-28 18:53:10 -07:00
chan.role = Channel.Role.primary
2023-03-06 10:33:18 -08:00
} else {
2022-10-28 18:53:10 -07:00
chan.role = Channel.Role.secondary
}
2022-10-29 07:31:50 -07:00
chan.settings = cs
chan.index = i
i += 1
2022-10-19 16:58:49 -07:00
var adminPacket = AdminMessage()
adminPacket.setChannel = chan
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = UInt32(connectedPeripheral.num)
2022-10-19 16:58:49 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
2022-10-29 07:31:50 -07:00
meshPacket.wantAck = true
meshPacket.channel = 0
2022-10-19 16:58:49 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
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)")
2022-10-19 16:58:49 -07:00
}
}
// Save the LoRa Config and the device will reboot
var adminPacket = AdminMessage()
adminPacket.setConfig.lora = channelSet.loraConfig
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
2023-03-06 10:33:18 -08:00
meshPacket.from = UInt32(connectedPeripheral.num)
2022-10-19 16:58:49 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
2022-10-28 18:53:10 -07:00
meshPacket.wantAck = true
2022-10-29 07:44:43 -07:00
meshPacket.channel = 0
2022-10-19 16:58:49 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
2022-10-28 09:02:37 -07:00
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)")
2022-10-19 16:58:49 -07:00
}
return true
2022-10-18 19:50:42 -07:00
} catch {
2022-10-19 16:58:49 -07:00
return false
2022-10-18 19:50:42 -07:00
}
}
}
return false
}
public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setOwner = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved User Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
2022-07-11 15:43:25 -07:00
return 0
}
2023-02-09 22:59:39 -08:00
public func saveLicensedUser(ham: HamParameters, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setHamMode = ham
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Ham Parameters for \(toUser.longName ?? "unknown".localized)"
2023-02-09 22:59:39 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
}
public func saveBluetoothConfig(config: Config.BluetoothConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2022-08-20 12:31:52 -07:00
var adminPacket = AdminMessage()
adminPacket.setConfig.bluetooth = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
2022-08-20 12:31:52 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Bluetooth Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertBluetoothConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-07-11 15:43:25 -07:00
return Int64(meshPacket.id)
}
2022-07-11 15:43:25 -07:00
return 0
}
public func saveDeviceConfig(config: Config.DeviceConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2022-06-21 02:43:37 -07:00
var adminPacket = AdminMessage()
adminPacket.setConfig.device = config
2022-06-21 02:43:37 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
2022-06-21 02:43:37 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2022-06-21 02:43:37 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Device Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertDeviceConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-07-11 15:43:25 -07:00
return Int64(meshPacket.id)
2022-06-21 02:43:37 -07:00
}
2022-07-11 15:43:25 -07:00
return 0
2022-06-21 02:43:37 -07:00
}
public func saveDisplayConfig(config: Config.DisplayConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2022-06-21 10:02:05 -07:00
var adminPacket = AdminMessage()
adminPacket.setConfig.display = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
if adminIndex > 0 {
meshPacket.channel = UInt32(adminIndex)
}
2022-06-21 10:02:05 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2022-06-21 10:02:05 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Display Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertDisplayConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-07-11 15:43:25 -07:00
return Int64(meshPacket.id)
2022-06-21 10:02:05 -07:00
}
2022-07-11 15:43:25 -07:00
return 0
2022-06-21 10:02:05 -07:00
}
2023-01-20 19:14:49 -08:00
public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.lora = config
var meshPacket: MeshPacket = MeshPacket()
2023-01-20 19:14:49 -08:00
meshPacket.to = UInt32(toUser.num)
2023-03-14 19:16:04 -07:00
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved LoRa Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertLoRaConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-07-11 15:43:25 -07:00
return Int64(meshPacket.id)
}
2022-07-11 15:43:25 -07:00
return 0
}
public func savePositionConfig(config: Config.PositionConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2022-06-21 10:15:47 -07:00
var adminPacket = AdminMessage()
adminPacket.setConfig.position = config
2022-06-21 10:15:47 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
2022-06-21 10:15:47 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2022-06-21 10:15:47 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
2022-06-21 10:15:47 -07:00
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Position Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertPositionConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-07-11 15:43:25 -07:00
return Int64(meshPacket.id)
2022-06-21 10:15:47 -07:00
}
2022-07-11 15:43:25 -07:00
return 0
2022-06-21 10:15:47 -07:00
}
public func saveNetworkConfig(config: Config.NetworkConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2022-08-02 09:44:59 -07:00
var adminPacket = AdminMessage()
2022-09-07 22:33:44 -07:00
adminPacket.setConfig.network = config
2022-08-02 09:44:59 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
2022-08-02 09:44:59 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2022-08-02 09:44:59 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
2022-08-02 09:44:59 -07:00
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Network Config for \(toUser.longName ?? "unknown".localized)"
2022-08-02 09:44:59 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertNetworkConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-08-02 09:44:59 -07:00
return Int64(meshPacket.id)
}
2022-08-02 09:44:59 -07:00
return 0
}
public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2022-07-02 12:18:20 -07:00
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.cannedMessage = config
2022-07-02 12:18:20 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
2022-07-02 12:18:20 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
2022-07-02 12:18:20 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Canned Message Module Config for \(toUser.longName ?? "unknown".localized)"
2022-07-11 15:43:25 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
2022-07-11 15:43:25 -07:00
return Int64(meshPacket.id)
2022-07-02 12:34:45 -07:00
}
2022-07-11 15:43:25 -07:00
return 0
2022-07-02 12:34:45 -07:00
}
public func saveCannedMessageModuleMessages(messages: String, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
2022-08-15 19:40:46 -07:00
adminPacket.setCannedMessageModuleMessages = messages
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Canned Message Module Messages for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
}
public func saveExternalNotificationModuleConfig(config: ModuleConfig.ExternalNotificationConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.externalNotification = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved External Notification Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
}
return 0
}
2023-03-25 14:30:18 -07:00
2023-03-25 22:14:39 -07:00
public func saveRtttlConfig(ringtone: String, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
2023-03-25 14:30:18 -07:00
var adminPacket = AdminMessage()
2023-03-25 22:14:39 -07:00
adminPacket.setRingtoneMessage = ringtone
2023-03-25 14:30:18 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved RTTTL Ringtone Config for \(toUser.longName ?? "unknown".localized)"
2023-03-25 14:30:18 -07:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-25 22:14:39 -07:00
upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: toUser.num, context: context!)
2023-03-25 14:30:18 -07:00
return Int64(meshPacket.id)
}
2023-03-25 14:30:18 -07:00
return 0
}
public func saveMQTTConfig(config: ModuleConfig.MQTTConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.mqtt = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved WiFi Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertMqttModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
}
return 0
}
public func saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.rangeTest = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Range Test Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertRangeTestModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
}
return 0
}
public func saveSerialModuleConfig(config: ModuleConfig.SerialConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.serial = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Serial Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertSerialModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
}
return 0
}
public func saveTelemetryModuleConfig(config: ModuleConfig.TelemetryConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.telemetry = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.channel = UInt32(adminIndex)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-03-15 09:54:20 -07:00
upsertTelemetryModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
}
return 0
}
public func getChannel(channelIndex: UInt32, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Bool {
var adminPacket = AdminMessage()
adminPacket.getChannelRequest = channelIndex
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
2023-02-02 23:17:23 -08:00
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
2022-07-31 19:53:44 -04:00
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛎️ Sent a Get Channel \(channelIndex) Request Admin Message for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-03-06 10:33:18 -08:00
public func getCannedMessageModuleMessages(destNum: Int64, wantResponse: Bool) -> Bool {
2022-07-26 07:35:16 -07:00
var adminPacket = AdminMessage()
2022-08-15 19:40:46 -07:00
adminPacket.getCannedMessageModuleMessagesRequest = true
2022-07-26 07:35:16 -07:00
var meshPacket: MeshPacket = MeshPacket()
2023-02-02 23:17:23 -08:00
meshPacket.to = UInt32(destNum)
meshPacket.from = UInt32(connectedPeripheral.num)
2022-07-26 07:35:16 -07:00
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
2022-08-11 23:34:09 -07:00
meshPacket.wantAck = true
2022-08-02 09:44:59 -07:00
meshPacket.decoded.wantResponse = wantResponse
2022-07-26 07:35:16 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = wantResponse
2022-07-26 07:35:16 -07:00
meshPacket.decoded = dataMessage
2022-07-26 07:35:16 -07:00
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
2022-07-26 07:35:16 -07:00
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
2022-10-15 10:14:08 -07:00
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)")
2022-10-15 10:14:08 -07:00
return true
2022-07-26 07:35:16 -07:00
}
2022-07-26 07:35:16 -07:00
return false
}
2023-01-23 17:56:04 -08:00
public func requestBluetoothConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-01-23 17:56:04 -08:00
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.bluetoothConfig
2023-01-23 17:56:04 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-01-23 17:56:04 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-01-23 17:56:04 -08:00
meshPacket.decoded = dataMessage
2023-01-23 17:56:04 -08:00
let messageDescription = "🛎️ Requested Bluetooth Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
2023-01-23 17:56:04 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
2023-01-31 22:08:03 -08:00
return false
}
2023-01-31 22:08:03 -08:00
public func requestDeviceConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-01-31 22:08:03 -08:00
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.deviceConfig
2023-01-31 22:08:03 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-01-31 22:08:03 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-01-31 22:08:03 -08:00
meshPacket.decoded = dataMessage
2023-01-31 22:08:03 -08:00
let messageDescription = "🛎️ Requested Device Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
2023-01-31 22:08:03 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-01-31 22:08:03 -08:00
public func requestDisplayConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-01-31 22:08:03 -08:00
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.displayConfig
2023-01-31 22:08:03 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-01-31 22:08:03 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-01-31 22:08:03 -08:00
meshPacket.decoded = dataMessage
2023-01-31 22:08:03 -08:00
let messageDescription = "🛎️ Requested Display Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
2023-01-31 22:08:03 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
2023-01-23 17:56:04 -08:00
return false
}
2023-01-23 17:56:04 -08:00
public func requestLoRaConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-01-20 19:14:49 -08:00
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.loraConfig
2023-01-20 19:14:49 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-01-20 19:14:49 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-01-20 19:14:49 -08:00
meshPacket.decoded = dataMessage
2023-01-23 17:56:04 -08:00
let messageDescription = "🛎️ Requested LoRa Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
2023-01-20 19:14:49 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
2023-01-20 19:14:49 -08:00
return true
}
2023-01-20 19:14:49 -08:00
return false
}
2023-01-31 22:08:03 -08:00
public func requestNetworkConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-01-31 22:08:03 -08:00
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.networkConfig
2023-01-31 22:08:03 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-01-31 22:08:03 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
2023-01-31 22:08:03 -08:00
let messageDescription = "🛎️ Requested Network Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
2023-01-31 22:08:03 -08:00
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-01-31 22:08:03 -08:00
public func requestPositionConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-01-31 22:08:03 -08:00
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.positionConfig
2023-01-31 22:08:03 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-01-31 22:08:03 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-01-31 22:08:03 -08:00
meshPacket.decoded = dataMessage
2023-01-31 22:08:03 -08:00
let messageDescription = "🛎️ Requested Position Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-02-01 09:19:45 -08:00
public func requestCannedMessagesModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-02-01 09:19:45 -08:00
var adminPacket = AdminMessage()
adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.cannedmsgConfig
2023-02-01 09:19:45 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-02-01 09:19:45 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-02-01 09:19:45 -08:00
meshPacket.decoded = dataMessage
2023-02-01 09:19:45 -08:00
let messageDescription = "🛎️ Requested Canned Messages Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-02-01 09:19:45 -08:00
public func requestExternalNotificationModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-02-01 09:19:45 -08:00
var adminPacket = AdminMessage()
adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.extnotifConfig
2023-02-01 09:19:45 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-02-01 09:19:45 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-02-01 09:19:45 -08:00
meshPacket.decoded = dataMessage
2023-02-01 09:19:45 -08:00
let messageDescription = "🛎️ Requested External Notificaiton Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-03-25 14:30:18 -07:00
public func requestRtttlConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-03-25 14:30:18 -07:00
var adminPacket = AdminMessage()
adminPacket.getRingtoneRequest = true
2023-03-25 14:30:18 -07:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
meshPacket.wantAck = true
2023-03-25 14:30:18 -07:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-03-25 14:30:18 -07:00
meshPacket.decoded = dataMessage
2023-03-25 14:30:18 -07:00
let messageDescription = "🛎️ Requested RTTTL Ringtone Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
public func requestRangeTestModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
var adminPacket = AdminMessage()
adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.rangetestConfig
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛎️ Requested Range Test Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-02-01 09:19:45 -08:00
public func requestMqttModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-02-01 09:19:45 -08:00
var adminPacket = AdminMessage()
adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.mqttConfig
2023-02-01 09:19:45 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-02-01 09:19:45 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-02-01 09:19:45 -08:00
meshPacket.decoded = dataMessage
2023-02-01 09:19:45 -08:00
let messageDescription = "🛎️ Requested MQTT Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
2023-02-01 09:19:45 -08:00
public func requestSerialModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
2023-02-01 09:19:45 -08:00
var adminPacket = AdminMessage()
adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.serialConfig
2023-02-01 09:19:45 -08:00
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
2023-02-01 09:19:45 -08:00
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
2023-02-01 09:19:45 -08:00
meshPacket.decoded = dataMessage
2023-02-01 09:19:45 -08:00
let messageDescription = "🛎️ Requested Serial Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
public func requestTelemetryModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
var adminPacket = AdminMessage()
adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.telemetryConfig
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.channel = UInt32(adminIndex)
2023-02-02 23:17:23 -08:00
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛎️ Requested Telemetry Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return true
}
return false
}
// Send an admin message to a radio, save a message to core data for logging
private func sendAdminMessageToRadio(meshPacket: MeshPacket, adminDescription: String, fromUser: UserEntity, toUser: UserEntity) -> Bool {
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2023-08-09 23:36:00 -07:00
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected{
let newMessage = MessageEntity(context: context!)
newMessage.messageId = Int64(meshPacket.id)
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
newMessage.receivedACK = false
newMessage.admin = true
newMessage.adminDescription = adminDescription
newMessage.fromUser = fromUser
newMessage.toUser = toUser
do {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
try context!.save()
2023-01-23 17:56:04 -08:00
print(adminDescription)
return true
} catch {
context!.rollback()
let nsError = error as NSError
2022-12-30 20:04:43 -08:00
print("💥 Error inserting new core data MessageEntity: \(nsError)")
}
}
return false
}
2023-03-09 14:23:15 -06:00
public func tryClearExistingChannels() {
2023-03-14 12:44:10 -07:00
// Before we get started delete the existing channels from the myNodeInfo
2023-03-09 14:23:15 -06:00
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num))
2023-03-09 14:23:15 -06:00
do {
2023-03-14 13:01:35 -07:00
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as? [MyInfoEntity] ?? []
2023-03-09 14:23:15 -06:00
if fetchedMyInfo.count == 1 {
2023-03-14 13:01:35 -07:00
let mutableChannels = fetchedMyInfo[0].channels?.mutableCopy() as? NSMutableOrderedSet
mutableChannels?.removeAllObjects()
2023-03-09 14:23:15 -06:00
fetchedMyInfo[0].channels = mutableChannels
do {
try context!.save()
} catch {
print("Failed to clear existing channels from local app database")
}
}
} catch {
print("Failed to find a node MyInfo to save these channels to")
}
}
2021-09-10 07:41:26 -07:00
}
// MARK: - CB Central Manager implmentation
extension BLEManager: CBCentralManagerDelegate {
// MARK: Bluetooth enabled/disabled
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == CBManagerState.poweredOn {
print("BLE powered on")
isSwitchedOn = true
startScanning()
2023-03-06 10:33:18 -08:00
} else {
isSwitchedOn = false
}
var status = ""
switch central.state {
2023-03-06 13:26:04 -08:00
case .poweredOff:
status = "BLE is powered off"
case .poweredOn:
status = "BLE is poweredOn"
case .resetting:
status = "BLE is resetting"
case .unauthorized:
status = "BLE is unauthorized"
case .unknown:
status = "BLE is unknown"
case .unsupported:
status = "BLE is unsupported"
default:
status = "default"
}
print("BLEManager status: \(status)")
}
// Called each time a peripheral is discovered
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
2023-05-04 20:11:42 -07:00
if self.automaticallyReconnect && peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
self.connectTo(peripheral: peripheral)
2022-12-30 02:10:14 -08:00
print(" BLE Reconnecting to prefered peripheral: \(peripheral.name ?? "Unknown")")
}
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "????", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral)
let index = peripherals.map { $0.peripheral }.firstIndex(of: peripheral)
if let peripheralIndex = index {
peripherals[peripheralIndex] = device
} else {
peripherals.append(device)
}
let today = Date()
let visibleDuration = Calendar.current.date(byAdding: .second, value: -5, to: today)!
self.peripherals.removeAll(where: { $0.lastUpdate < visibleDuration})
}
// func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
//
// guard let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] else {
// return
// }
//
// if peripherals.count > 0 {
//
// for peripheral in peripherals {
// print(peripheral)
// switch peripheral.state {
// case .connecting: // I've only seen this happen when
// // re-launching attached to Xcode.
// print("Xcode Restore")
//
// case .connected:
// connectTo(peripheral: peripheral)
// print("Restore BLE State")
// default: break
// }
// }
// }
// print("willRestoreState Hit!")
// }
}