Meshtastic-Apple/MeshtasticClient/Helpers/BLEManager.swift

944 lines
36 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
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
// ---------------------------------------------------------------------------------------
2021-09-10 21:50:54 -07:00
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
static let shared = BLEManager()
2021-12-25 23:48:12 -08:00
private static var documentsFolder: URL {
do {
2021-12-12 17:17:46 -08:00
return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
} catch {
fatalError("Can't find documents directory.")
}
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
var context: NSManagedObjectContext?
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
private var centralManager: CBCentralManager!
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
@Published var peripherals = [Peripheral]()
2021-11-29 15:59:06 -08:00
@Published var connectedPeripheral: Peripheral!
//@Published var lastConnectedPeripheral: String
@Published var lastConnectionError: String
2021-11-29 15:59:06 -08:00
2021-11-21 13:48:28 -08:00
@Published var isSwitchedOn: Bool = false
@Published var isScanning: Bool = false
@Published var isConnected: Bool = false
2021-11-29 15:59:06 -08:00
var timeoutTimer: Timer?
var timeoutTimerCount = 0
2021-11-29 15:59:06 -08:00
2021-12-26 21:38:14 -08:00
let broadcastNodeNum: UInt32 = 4294967295
2021-10-12 17:54:10 -07:00
/* Meshtastic Service Details */
2021-09-10 21:50:54 -07:00
var TORADIO_characteristic: CBCharacteristic!
var FROMRADIO_characteristic: CBCharacteristic!
var FROMNUM_characteristic: CBCharacteristic!
2021-11-29 15:59:06 -08:00
2021-09-10 21:50:54 -07:00
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
2021-09-10 07:41:26 -07:00
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
let FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
2021-12-25 23:48:12 -08:00
private var meshLoggingEnabled: Bool = true
let meshLog = documentsFolder.appendingPathComponent("meshlog.txt")
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
// MARK: init BLEManager
2021-09-10 07:41:26 -07:00
override init() {
2021-11-29 15:59:06 -08:00
self.meshLoggingEnabled = true // UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? true
//self.lastConnectedPeripheral = ""
self.lastConnectionError = ""
2021-09-10 07:41:26 -07:00
super.init()
2021-12-25 23:48:12 -08:00
// let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager")
2021-10-14 21:08:03 -07:00
centralManager = CBCentralManager(delegate: self, queue: nil)
2021-09-10 07:41:26 -07:00
}
2021-12-12 17:17:46 -08:00
// MARK: Bluetooth enabled/disabled for the app
2021-09-10 07:41:26 -07:00
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
2021-11-29 15:59:06 -08:00
2021-09-10 07:41:26 -07:00
isSwitchedOn = true
2021-11-21 13:48:28 -08:00
startScanning()
2021-11-29 15:59:06 -08:00
} else {
2021-09-10 07:41:26 -07:00
isSwitchedOn = false
}
}
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
// MARK: Scanning for BLE Devices
2021-10-05 09:33:10 -07:00
// Scan for nearby BLE devices using the Meshtastic BLE service ID
2021-09-10 21:50:54 -07:00
func startScanning() {
2021-11-29 15:59:06 -08:00
2021-10-05 09:33:10 -07:00
if isSwitchedOn {
2021-12-25 23:48:12 -08:00
2021-10-05 09:33:10 -07:00
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: nil)
self.isScanning = self.centralManager.isScanning
2021-12-25 23:48:12 -08:00
print("✅ Scanning Started")
2021-10-05 09:33:10 -07:00
}
2021-09-10 21:50:54 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
// Stop Scanning For BLE Devices
2021-09-10 21:50:54 -07:00
func stopScanning() {
2021-11-29 15:59:06 -08:00
2021-10-05 09:33:10 -07:00
if centralManager.isScanning {
2021-11-29 15:59:06 -08:00
2021-10-05 09:33:10 -07:00
self.centralManager.stopScan()
self.isScanning = self.centralManager.isScanning
2021-12-25 23:48:12 -08:00
print("🛑 Stopped Scanning")
2021-10-05 09:33:10 -07:00
}
}
2021-11-29 15:59:06 -08:00
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"]
2021-11-29 15:59:06 -08:00
self.timeoutTimerCount += 1
2021-12-26 21:38:14 -08:00
if timeoutTimerCount == 5 {
2021-11-29 15:59:06 -08:00
2021-11-04 08:36:55 -07:00
if connectedPeripheral != nil {
2021-11-29 15:59:06 -08:00
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
}
2021-11-04 08:36:55 -07:00
connectedPeripheral = nil
2021-11-29 15:59:06 -08:00
self.lastConnectionError = "🚫 BLE Connecting Timeout after making \(timeoutTimerCount) attempts to connect to \(name)."
print("🚫 BLE Connecting Timeout after making \(timeoutTimerCount) attempts to connect to \(name).")
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Connecting Timeout after making \(timeoutTimerCount) attempts to connect to \(String(name)).") }
2021-11-29 15:59:06 -08:00
self.timeoutTimerCount = 0
2021-11-04 08:36:55 -07:00
self.timeoutTimer?.invalidate()
2021-11-29 15:59:06 -08:00
} else {
print("🚫 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)")
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)") }
}
}
2021-11-29 15:59:06 -08:00
2021-10-12 17:54:10 -07:00
// Connect to a specific peripheral
func connectTo(peripheral: CBPeripheral) {
2021-11-29 15:59:06 -08:00
if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") }
print("✅ BLE Connecting: \(peripheral.name ?? "Unknown")")
2021-11-29 15:59:06 -08:00
2021-10-12 17:54:10 -07:00
stopScanning()
2021-11-29 15:59:06 -08:00
if self.connectedPeripheral != nil {
2021-11-21 13:48:28 -08:00
self.disconnectPeripheral()
2021-10-12 17:54:10 -07:00
}
2021-11-29 15:59:06 -08:00
self.centralManager?.connect(peripheral)
2021-11-29 15:59:06 -08: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
let context = ["name": "@\(peripheral.name ?? "Unknown")"]
self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true)
RunLoop.current.add(self.timeoutTimer!, forMode: .common)
2021-10-12 17:54:10 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
// Disconnect Connected Peripheral
2021-11-29 15:59:06 -08:00
func disconnectPeripheral() {
2021-11-21 13:48:28 -08:00
guard let connectedPeripheral = connectedPeripheral else { return }
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
2021-09-10 21:50:54 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-25 23:48:12 -08:00
// Called each time a peripheral is discovered
2021-11-29 15:59:06 -08:00
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
2021-10-05 09:33:10 -07:00
var peripheralName: String = peripheral.name ?? "Unknown"
2021-11-29 15:59:06 -08:00
2021-10-05 09:33:10 -07:00
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
2021-10-02 23:15:12 -07:00
}
2021-11-29 15:59:06 -08:00
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, subscribed: false, peripheral: peripheral)
let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id })
2021-11-20 11:02:15 -08:00
if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected {
peripherals[peripheralIndex!] = newPeripheral
2021-11-20 11:02:15 -08:00
peripherals.remove(at: peripheralIndex!)
peripherals.append(newPeripheral)
print(" Updating peripheral: \(peripheralName)")
2021-12-25 23:48:12 -08:00
2021-11-29 15:59:06 -08:00
} else {
2021-11-21 13:48:28 -08:00
if newPeripheral.peripheral.state != CBPeripheralState.connected {
2021-11-29 15:59:06 -08:00
2021-11-21 13:48:28 -08:00
peripherals.append(newPeripheral)
print(" Adding peripheral: \(peripheralName)")
2021-11-21 13:48:28 -08:00
}
}
2021-10-05 09:33:10 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
// Called when a peripheral is connected
2021-10-05 09:33:10 -07:00
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
2021-10-14 20:36:06 -07:00
2021-11-21 13:48:28 -08:00
self.isConnected = true
// Invalidate and reset connection timer count, remove any connection errors
2021-11-21 13:48:28 -08:00
self.lastConnectionError = ""
self.timeoutTimer!.invalidate()
self.timeoutTimerCount = 0
2021-11-29 15:59:06 -08:00
// Map the peripheral to the connectedNode and connectedPeripheral ObservedObjects
2021-10-12 17:54:10 -07:00
connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first
2021-11-17 07:34:37 -08:00
connectedPeripheral.peripheral.delegate = self
2021-12-25 23:48:12 -08:00
let fetchConnectedPeripheralRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
2021-12-16 14:13:54 -08:00
fetchConnectedPeripheralRequest.predicate = NSPredicate(format: "bleName MATCHES %@", String(peripheral.name ?? "???"))
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
do {
2021-12-16 14:13:54 -08:00
let fetchedNode = try context?.fetch(fetchConnectedPeripheralRequest) as! [NodeInfoEntity]
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
if fetchedNode.count == 1 {
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
connectedPeripheral.num = fetchedNode[0].user!.num
2021-12-16 14:13:54 -08:00
connectedPeripheral.shortName = fetchedNode[0].user!.shortName!
connectedPeripheral.longName = fetchedNode[0].user!.longName!
2021-12-15 23:53:45 -08:00
connectedPeripheral.firmwareVersion = (fetchedNode[0].myInfo?.firmwareVersion ?? "Unknown")
}
} catch {
print("💥 Fetch NodeInfo Failed")
if meshLoggingEnabled { MeshLogger.log("💥 Fetch NodeInfo Failed") }
2021-12-15 23:53:45 -08:00
}
2021-12-25 23:48:12 -08:00
//lastConnectedPeripheral = peripheral.identifier.uuidString
2021-11-29 15:59:06 -08:00
// Discover Services
peripheral.discoverServices([meshtasticServiceCBUUID])
if meshLoggingEnabled { MeshLogger.log("✅ BLE Connected: \(peripheral.name ?? "Unknown")") }
print("✅ BLE Connected: \(peripheral.name ?? "Unknown")")
2021-11-02 21:47:41 -07:00
}
2021-11-29 15:59:06 -08: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?) {
2021-11-29 15:59:06 -08:00
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Failed to Connect: \(peripheral.name ?? "Unknown")") }
print("🚫 BLE Failed to Connect: \(peripheral.name ?? "Unknown")")
2021-11-21 13:48:28 -08:00
disconnectPeripheral()
}
2021-09-10 21:50:54 -07:00
2021-10-05 09:33:10 -07:00
// Disconnect Peripheral Event
2021-11-29 15:59:06 -08:00
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake
2021-10-06 17:51:52 -07:00
self.startScanning()
self.connectedPeripheral = nil
2021-11-29 15:59:06 -08:00
2021-09-28 00:00:09 -07:00
if let e = error {
2021-11-29 15:59:06 -08:00
// https://developer.apple.com/documentation/corebluetooth/cberror/code
2021-10-06 17:51:52 -07:00
let errorCode = (e as NSError).code
// unknown = 0,
2021-11-29 15:59:06 -08:00
if errorCode == 6 { // CBError.Code.connectionTimeout The connection has timed out unexpectedly.
2021-11-29 15:59:06 -08:00
// Happens when device is manually reset / powered off
// We will try and re-connect to this device
lastConnectionError = "🚫 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds."
if peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
if meshLoggingEnabled { MeshLogger.log(" BLE Reconnecting: \(peripheral.name ?? "Unknown")") }
print(" BLE Reconnecting: \(peripheral.name ?? "Unknown")")
self.connectTo(peripheral: peripheral)
}
2021-11-29 15:59:06 -08:00
} else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us.
2021-10-12 17:54:10 -07:00
// Seems to be what is received when a tbeam sleeps, immediately recconnecting does not work.
lastConnectionError = e.localizedDescription
2021-12-25 23:48:12 -08:00
print("🚫 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") }
2021-11-29 15:59:06 -08:00
} else if errorCode == 14 { // Peer removed pairing information
2021-10-12 17:54:10 -07:00
// Forgetting and reconnecting seems to be necessary so we need to show the user an error telling them to do that
lastConnectionError = "🚫 \(e.localizedDescription) This error usually cannot be fixed without forgetting the device unders Settings > Bluetooth and re-connecting to the radio."
2021-12-25 23:48:12 -08:00
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(lastConnectionError)") }
2021-11-29 15:59:06 -08:00
} else {
lastConnectionError = e.localizedDescription
2021-12-25 23:48:12 -08:00
print("🚫 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
if meshLoggingEnabled { MeshLogger.log("🚫 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") }
}
2021-09-28 00:00:09 -07:00
} else {
2021-11-29 15:59:06 -08:00
2021-10-12 17:54:10 -07:00
// Disconnected without error which indicates user intent to disconnect
// Happens when swiping to disconnect
if meshLoggingEnabled { MeshLogger.log(" BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect") }
print(" BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect")
2021-09-10 21:50:54 -07:00
}
2021-09-10 07:41:26 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-25 23:48:12 -08:00
// MARK: Peripheral Services functions
2021-09-10 21:50:54 -07:00
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
2021-11-29 15:59:06 -08:00
2021-10-06 17:51:52 -07:00
if let e = error {
2021-11-29 15:59:06 -08:00
print("🚫 Discover Services error \(e)")
2021-10-06 17:51:52 -07:00
}
2021-11-29 15:59:06 -08:00
2021-09-10 21:50:54 -07:00
guard let services = peripheral.services else { return }
2021-11-29 15:59:06 -08:00
for service in services {
if service.uuid == meshtasticServiceCBUUID {
print("✅ Meshtastic service discovered OK")
if meshLoggingEnabled { MeshLogger.log("✅ BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")") }
2021-11-21 13:48:28 -08:00
peripheral.discoverCharacteristics(nil, for: service)
// peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID], for: service)
2021-09-10 07:41:26 -07:00
}
2021-09-10 21:50:54 -07:00
}
2021-09-10 07:41:26 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-25 23:48:12 -08:00
// MARK: Discover Characteristics Event
2021-11-29 15:59:06 -08:00
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
2021-10-06 17:51:52 -07:00
if let e = error {
2021-11-29 15:59:06 -08:00
print("🚫 Discover Characteristics error \(e)")
if meshLoggingEnabled { MeshLogger.log("🚫 BLE didDiscoverCharacteristicsFor error by \(peripheral.name ?? "Unknown") \(e)") }
2021-10-06 17:51:52 -07:00
}
2021-11-29 15:59:06 -08:00
2021-10-06 17:51:52 -07:00
guard let characteristics = service.characteristics else { return }
2021-09-10 21:50:54 -07:00
2021-10-06 17:51:52 -07:00
for characteristic in characteristics {
2021-11-29 15:59:06 -08:00
2021-11-29 16:51:59 -08:00
switch characteristic.uuid {
case TORADIO_UUID:
print("✅ TORADIO characteristic OK")
if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover TORADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
2021-11-29 16:51:59 -08:00
TORADIO_characteristic = characteristic
var toRadio: ToRadio = ToRadio()
toRadio.wantConfigID = 32168
let binaryData: Data = try! toRadio.serializedData()
peripheral.writeValue(binaryData, for: characteristic, type: .withResponse)
case FROMRADIO_UUID:
print("✅ FROMRADIO characteristic OK")
if meshLoggingEnabled { MeshLogger.log("✅ 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 15:59:06 -08:00
2021-11-29 16:51:59 -08:00
case FROMNUM_UUID:
print("✅ FROMNUM (Notify) characteristic OK")
if meshLoggingEnabled { MeshLogger.log("✅ 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 15:59:06 -08:00
2021-11-29 16:51:59 -08:00
default:
break
}
2021-11-29 15:59:06 -08:00
2021-09-10 21:50:54 -07:00
}
}
2021-11-29 15:59:06 -08:00
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
2021-11-29 15:59:06 -08:00
print(" didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)")
if meshLoggingEnabled { MeshLogger.log(" didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)") }
2021-12-25 23:48:12 -08:00
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
}
}
2021-11-29 15:59:06 -08:00
2021-12-25 23:48:12 -08:00
// MARK: Data Read / Update Characteristic Event
// TODO: Convert to CoreData
// FIXME: Remove broken JSON file data layer implementation
2021-11-29 15:59:06 -08:00
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 { // CBATTErrorDomain Code=5 "Authentication is insufficient."
// BLE Pin connection error
// We will try and re-connect to this device
lastConnectionError = "🚫 BLE \(e.localizedDescription) Please try connecting again and check the PIN carefully."
if meshLoggingEnabled { MeshLogger.log("🚫 BLE \(e.localizedDescription) Please try connecting again and check the PIN carefully.") }
self.centralManager?.cancelPeripheralConnection(peripheral)
2021-11-29 15:59:06 -08:00
}
2021-10-06 17:51:52 -07:00
}
2021-11-29 15:59:06 -08:00
switch characteristic.uuid {
2021-11-29 17:09:27 -08:00
case FROMNUM_UUID:
peripheral.readValue(for: FROMNUM_characteristic)
2021-12-12 17:17:46 -08:00
let characteristicValue: [UInt8] = [UInt8](characteristic.value!)
let bigEndianUInt32 = characteristicValue.withUnsafeBytes { $0.load(as: UInt32.self) }
let returnValue = CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderLittleEndian.rawValue)
? UInt32(bigEndian: bigEndianUInt32) : bigEndianUInt32
2021-12-25 23:48:12 -08:00
// print(returnValue)
2021-11-29 17:09:27 -08:00
case FROMRADIO_UUID:
if characteristic.value == nil || characteristic.value!.isEmpty {
return
}
// print(characteristic.value ?? "no value")
// print(characteristic.value?.hexDescription ?? "no value")
var decodedInfo = FromRadio()
decodedInfo = try! FromRadio(serializedData: characteristic.value!)
2021-12-25 23:48:12 -08:00
// print("Print DecodedInfo")
// print(decodedInfo)
2021-11-29 17:09:27 -08:00
2021-12-12 17:17:46 -08:00
// MyInfo Data
2021-11-29 17:09:27 -08:00
if decodedInfo.myInfo.myNodeNum != 0 {
2021-12-25 23:48:12 -08:00
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
2021-12-16 14:13:54 -08:00
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.myInfo.myNodeNum))
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
do {
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
// Not Found Insert
if fetchedMyInfo.isEmpty {
let myInfo = MyInfoEntity(context: context!)
myInfo.myNodeNum = Int64(decodedInfo.myInfo.myNodeNum)
myInfo.hasGps = decodedInfo.myInfo.hasGps_p
myInfo.numBands = Int32(bitPattern: decodedInfo.myInfo.numBands)
myInfo.firmwareVersion = decodedInfo.myInfo.firmwareVersion
myInfo.messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec)
myInfo.minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion)
myInfo.maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels)
connectedPeripheral.num = myInfo.myNodeNum
connectedPeripheral.firmwareVersion = myInfo.firmwareVersion ?? "Unknown"
2021-12-25 23:48:12 -08:00
} else {
2021-12-15 23:53:45 -08:00
fetchedMyInfo[0].myNodeNum = Int64(decodedInfo.myInfo.myNodeNum)
fetchedMyInfo[0].hasGps = decodedInfo.myInfo.hasGps_p
fetchedMyInfo[0].numBands = Int32(bitPattern: decodedInfo.myInfo.numBands)
fetchedMyInfo[0].firmwareVersion = decodedInfo.myInfo.firmwareVersion
fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec)
fetchedMyInfo[0].minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion)
fetchedMyInfo[0].maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels)
2021-11-29 17:09:27 -08:00
}
2021-12-15 23:53:45 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
try context!.save()
print("💾 Saved a myInfo for \(decodedInfo.myInfo.myNodeNum)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a myInfo for \(peripheral.name ?? String(decodedInfo.myInfo.myNodeNum))") }
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
} catch {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
context!.rollback()
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
let nsError = error as NSError
print("💥 Error Saving CoreData MyInfoEntity: \(nsError)")
2021-12-15 23:53:45 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} catch {
2021-12-25 23:48:12 -08:00
print("💥 Fetch MyInfo Error")
2021-11-29 17:09:27 -08:00
}
}
2021-12-12 17:17:46 -08:00
// NodeInfo Data
2021-11-29 17:09:27 -08:00
if decodedInfo.nodeInfo.num != 0 {
2021-12-25 23:48:12 -08:00
let fetchNodeRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
2021-12-15 23:53:45 -08:00
fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.nodeInfo.num))
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let fetchedNode = try context?.fetch(fetchNodeRequest) as! [NodeInfoEntity]
// Not Found Insert
2021-12-25 23:48:12 -08:00
if fetchedNode.isEmpty && decodedInfo.nodeInfo.hasUser {
2021-12-12 17:17:46 -08:00
let newNode = NodeInfoEntity(context: context!)
newNode.id = Int64(decodedInfo.nodeInfo.num)
newNode.num = Int64(decodedInfo.nodeInfo.num)
2021-12-26 21:38:14 -08:00
if decodedInfo.nodeInfo.lastHeard != nil && decodedInfo.nodeInfo.lastHeard > 0 {
newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.lastHeard)))
}
2021-12-26 21:38:14 -08:00
else {
newNode.lastHeard = Date()
}
2021-12-12 17:17:46 -08:00
newNode.snr = decodedInfo.nodeInfo.snr
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
if self.connectedPeripheral != nil && self.connectedPeripheral.num == newNode.id {
2021-12-25 23:48:12 -08:00
newNode.bleName = self.connectedPeripheral.peripheral.name
if decodedInfo.nodeInfo.hasUser {
2021-12-25 23:48:12 -08:00
connectedPeripheral.name = decodedInfo.nodeInfo.user.longName
connectedPeripheral.longName = decodedInfo.nodeInfo.user.longName
connectedPeripheral.shortName = decodedInfo.nodeInfo.user.shortName
connectedPeripheral.num = Int64(decodedInfo.nodeInfo.num)
}
2021-12-19 19:40:16 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
if decodedInfo.nodeInfo.hasUser {
let newUser = UserEntity(context: context!)
newUser.userId = decodedInfo.nodeInfo.user.id
newUser.num = Int64(decodedInfo.nodeInfo.num)
newUser.longName = decodedInfo.nodeInfo.user.longName
newUser.shortName = decodedInfo.nodeInfo.user.shortName
newUser.macaddr = decodedInfo.nodeInfo.user.macaddr
newUser.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased()
newUser.team = (String(describing: decodedInfo.nodeInfo.user.team))
2021-12-12 17:17:46 -08:00
newNode.user = newUser
}
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
let position = PositionEntity(context: context!)
position.latitudeI = decodedInfo.nodeInfo.position.latitudeI
position.longitudeI = decodedInfo.nodeInfo.position.longitudeI
position.altitude = decodedInfo.nodeInfo.position.altitude
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
position.batteryLevel = decodedInfo.nodeInfo.position.batteryLevel
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.position.time)))
var newPostions = [PositionEntity]()
newPostions.append(position)
2021-12-25 23:48:12 -08:00
newNode.positions? = NSOrderedSet(array: newPostions)
2021-12-12 17:17:46 -08:00
// Look for a MyInfo
2021-12-25 23:48:12 -08:00
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
2021-12-16 14:13:54 -08:00
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.nodeInfo.num))
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
if fetchedMyInfo.count > 0 {
newNode.myInfo = fetchedMyInfo[0]
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} catch {
print("💥 Fetch MyInfo Error")
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
} else if decodedInfo.nodeInfo.hasUser {
2021-12-15 23:53:45 -08:00
fetchedNode[0].id = Int64(decodedInfo.nodeInfo.num)
fetchedNode[0].num = Int64(decodedInfo.nodeInfo.num)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.lastHeard)))
2021-12-15 23:53:45 -08:00
fetchedNode[0].snr = decodedInfo.nodeInfo.snr
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
if decodedInfo.nodeInfo.hasUser {
2021-12-15 23:53:45 -08:00
fetchedNode[0].user!.userId = decodedInfo.nodeInfo.user.id
fetchedNode[0].user!.longName = decodedInfo.nodeInfo.user.longName
fetchedNode[0].user!.shortName = decodedInfo.nodeInfo.user.shortName
fetchedNode[0].user!.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased()
fetchedNode[0].user!.team = (String(describing: decodedInfo.nodeInfo.user.team))
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
let position = PositionEntity(context: context!)
position.latitudeI = decodedInfo.nodeInfo.position.latitudeI
position.longitudeI = decodedInfo.nodeInfo.position.longitudeI
position.altitude = decodedInfo.nodeInfo.position.altitude
position.batteryLevel = decodedInfo.nodeInfo.position.batteryLevel
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.position.time)))
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
mutablePositions.add(position)
2021-12-25 23:48:12 -08:00
2021-12-20 22:29:28 -08:00
if position.coordinate == nil {
var newPostions = [PositionEntity]()
newPostions.append(position)
2021-12-25 23:48:12 -08:00
fetchedNode[0].positions? = NSOrderedSet(array: newPostions)
2021-12-20 22:29:28 -08:00
} else {
2021-12-25 23:48:12 -08:00
2021-12-20 22:29:28 -08:00
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
// Look for a MyInfo
2021-12-25 23:48:12 -08:00
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
2021-12-15 23:53:45 -08:00
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.nodeInfo.num))
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
if fetchedMyInfo.count > 0 {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
fetchedNode[0].myInfo = fetchedMyInfo[0]
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} catch {
print("💥 Fetch MyInfo Error")
2021-12-12 17:17:46 -08:00
}
2021-11-29 17:09:27 -08:00
}
2021-12-12 17:17:46 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
try context!.save()
print("💾 Saved a nodeInfo for \(decodedInfo.nodeInfo.num)")
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} catch {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
context!.rollback()
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let nsError = error as NSError
print("💥 Error Saving CoreData NodeInfoEntity: \(nsError)")
2021-11-29 17:09:27 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} catch {
2021-12-25 23:48:12 -08:00
print("💥 Fetch NodeInfoEntity Error")
2021-11-29 17:09:27 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
if decodedInfo.nodeInfo.hasUser {
2021-12-25 23:48:12 -08:00
print("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)")
if meshLoggingEnabled { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") }
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} else {
2021-12-25 23:48:12 -08:00
print("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)")
if meshLoggingEnabled { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)") }
2021-12-12 17:17:46 -08:00
}
2021-11-29 17:09:27 -08:00
}
// Handle assorted app packets
if decodedInfo.packet.id != 0 {
2021-11-29 15:59:06 -08:00
2021-11-29 17:09:27 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-26 21:38:14 -08:00
// Text Message App - Primary Broadcast User
2021-11-29 17:09:27 -08:00
if decodedInfo.packet.decoded.portnum == PortNum.textMessageApp {
2021-11-29 15:59:06 -08:00
2021-11-29 17:09:27 -08:00
if let messageText = String(bytes: decodedInfo.packet.decoded.payload, encoding: .utf8) {
print("💬 BLE FROMRADIO received for text message app \(messageText)")
if meshLoggingEnabled { MeshLogger.log("💬 BLE FROMRADIO received for text message app \(messageText)") }
2021-12-25 23:48:12 -08:00
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
messageUsers.predicate = NSPredicate(format: "num IN %@", [decodedInfo.packet.to, decodedInfo.packet.from])
2021-12-12 17:17:46 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let fetchedUsers = try context?.fetch(messageUsers) as! [UserEntity]
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let newMessage = MessageEntity(context: context!)
2021-12-15 23:53:45 -08:00
newMessage.messageId = Int64(decodedInfo.packet.id)
2021-12-12 17:17:46 -08:00
newMessage.messageTimestamp = Int32(bitPattern: decodedInfo.packet.rxTime)
newMessage.receivedACK = false
newMessage.direction = "IN"
2021-12-25 23:48:12 -08:00
2021-12-19 17:57:04 -08:00
if decodedInfo.packet.to == broadcastNodeNum && fetchedUsers.count == 1 {
2021-12-25 23:48:12 -08:00
// Save the broadcast user if it does not exist
2021-12-19 19:40:16 -08:00
let bcu: UserEntity = UserEntity(context: context!)
bcu.shortName = "ALL"
2021-12-28 09:11:24 -08:00
bcu.longName = "All - Broadcast"
2021-12-19 19:40:16 -08:00
bcu.hwModel = "UNSET"
bcu.num = Int64(broadcastNodeNum)
bcu.userId = "BROADCASTNODE"
newMessage.toUser = bcu
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} else {
2021-12-19 19:40:16 -08:00
newMessage.toUser = fetchedUsers.first(where: { $0.num == decodedInfo.packet.to })
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
newMessage.fromUser = fetchedUsers.first(where: { $0.num == decodedInfo.packet.from })
newMessage.messagePayload = messageText
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
try context!.save()
print("💾 Saved a new message for \(decodedInfo.packet.id)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new message for \(decodedInfo.packet.id)") }
2021-12-26 21:38:14 -08:00
if newMessage.toUser!.num == self.broadcastNodeNum || self.connectedPeripheral != nil && self.connectedPeripheral.num == newMessage.toUser!.num {
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(decodedInfo.packet.id)"),
title: "\(newMessage.fromUser?.longName ?? "Unknown")",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
if meshLoggingEnabled { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
}
2021-12-12 17:17:46 -08:00
} catch {
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
context!.rollback()
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
let nsError = error as NSError
print("💥 Failed to save new MessageEntity \(nsError)")
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
} catch {
2021-12-25 23:48:12 -08:00
print("💥 Fetch Message To and From Users Error")
2021-11-29 17:09:27 -08:00
}
}
2021-12-12 17:17:46 -08:00
} else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
2021-12-25 23:48:12 -08:00
let fetchNodeInfoAppRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
2021-12-16 14:13:54 -08:00
fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from))
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-16 14:13:54 -08:00
let fetchedNode = try context?.fetch(fetchNodeInfoAppRequest) as! [NodeInfoEntity]
2021-12-15 23:53:45 -08:00
if fetchedNode.count == 1 {
fetchedNode[0].id = Int64(decodedInfo.packet.from)
fetchedNode[0].num = Int64(decodedInfo.packet.from)
2021-12-26 21:38:14 -08:00
if decodedInfo.packet.rxTime != nil && decodedInfo.packet.rxTime > 0 {
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime)))
}
else {
fetchedNode[0].lastHeard = Date()
}
2021-12-15 23:53:45 -08:00
fetchedNode[0].snr = decodedInfo.packet.rxSnr
2021-12-25 23:48:12 -08:00
} else {
2021-12-15 23:53:45 -08:00
return
}
do {
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
try context!.save()
2021-12-25 23:48:12 -08:00
if meshLoggingEnabled { MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(Int64(decodedInfo.nodeInfo.num))")}
print("💾 Updated NodeInfo SNR and Time from Packet For: \(fetchedNode[0].num)")
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
} catch {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
context!.rollback()
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
}
} catch {
2021-12-25 23:48:12 -08:00
print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP")
2021-11-29 17:09:27 -08:00
}
2021-12-25 23:48:12 -08:00
2021-11-29 17:09:27 -08:00
} else if decodedInfo.packet.decoded.portnum == PortNum.positionApp {
2021-12-25 23:48:12 -08:00
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
2021-12-15 23:53:45 -08:00
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from))
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
do {
2021-12-25 23:48:12 -08:00
2021-12-20 22:29:28 -08:00
let fetchedNode = try context?.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
if fetchedNode.count == 1 {
fetchedNode[0].id = Int64(decodedInfo.packet.from)
fetchedNode[0].num = Int64(decodedInfo.packet.from)
2021-12-25 23:48:12 -08:00
if decodedInfo.packet.rxTime == 0 {
2021-12-19 19:40:16 -08:00
fetchedNode[0].lastHeard = Date()
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
} else {
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime)))
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
}
2021-12-15 23:53:45 -08:00
fetchedNode[0].snr = decodedInfo.packet.rxSnr
2021-12-25 23:48:12 -08:00
} else {
2021-12-26 21:38:14 -08:00
2021-12-15 23:53:45 -08:00
return
}
do {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
try context!.save()
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
if meshLoggingEnabled {
MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(fetchedNode[0].num)")
2021-12-15 23:53:45 -08:00
}
print("💾 Updated NodeInfo SNR and Time from Position Packet For: \(fetchedNode[0].num)")
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
} catch {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
context!.rollback()
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
let nsError = error as NSError
print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
2021-12-15 23:53:45 -08:00
}
} catch {
2021-12-25 23:48:12 -08:00
print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP")
2021-11-29 17:09:27 -08:00
}
2021-12-25 23:48:12 -08:00
2021-11-29 17:09:27 -08:00
} else if decodedInfo.packet.decoded.portnum == PortNum.adminApp {
2021-11-29 15:59:06 -08:00
if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print("🚨 MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())")
2021-12-25 23:48:12 -08:00
2021-11-29 15:59:06 -08:00
} else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print("🚨 MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())")
2021-11-29 15:59:06 -08:00
} else {
2021-12-25 23:48:12 -08:00
if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print("🚨 MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())")
}
2021-11-29 15:59:06 -08:00
2021-11-29 17:09:27 -08:00
} catch {
if meshLoggingEnabled { MeshLogger.log("⚰️ Fatal Error: Failed to decode json") }
print("⚰️ Fatal Error: Failed to decode json")
2021-11-29 17:09:27 -08:00
}
}
2021-11-29 15:59:06 -08:00
2021-11-29 17:09:27 -08:00
if decodedInfo.configCompleteID != 0 {
2021-12-25 23:48:12 -08:00
if meshLoggingEnabled { MeshLogger.log("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)") }
print("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)")
2021-11-29 17:09:27 -08:00
self.connectedPeripheral.subscribed = true
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
2021-11-29 17:09:27 -08:00
}
2021-11-29 15:59:06 -08:00
2021-11-29 17:09:27 -08:00
default:
if meshLoggingEnabled { MeshLogger.log("🚨 Unhandled Characteristic UUID: \(characteristic.uuid)") }
print("🚨 Unhandled Characteristic UUID: \(characteristic.uuid)")
}
peripheral.readValue(for: FROMRADIO_characteristic)
2021-09-10 21:50:54 -07:00
}
2021-11-29 15:59:06 -08:00
// Send Broadcast Message
2021-12-19 17:57:04 -08:00
public func sendMessage(message: String, toUserNum: Int64) -> Bool {
2021-12-26 21:38:14 -08:00
var success = false
2021-11-29 15:59:06 -08:00
// 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-29 15:59:06 -08:00
2021-11-21 13:48:28 -08:00
self.disconnectPeripheral()
self.startScanning()
2021-11-29 15:59:06 -08:00
// 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)
}
// else {
//
// // Try and connect to the last connected device
// let lastConnectedPeripheral = peripherals.filter({ $0.peripheral.identifier.uuidString == self.lastConnectedPeripheral }).first
// if lastConnectedPeripheral != nil && lastConnectedPeripheral?.peripheral != nil {
// connectTo(peripheral: lastConnectedPeripheral!.peripheral)
// }
// }
//print("🚫 Message Send Failed, not properly connected to \(lastConnectedPeripheral)")
//if meshLoggingEnabled { MeshLogger.log("🚫 Message Send Failed, not properly connected to \(lastConnectedPeripheral)") }
2021-12-25 23:48:12 -08:00
success = false
2021-11-29 15:59:06 -08:00
} else if message.count < 1 {
2021-12-25 23:48:12 -08:00
// Don't send an empty message
print("🚫 Don't Send an Empty Message")
success = false
2021-12-25 23:48:12 -08:00
2021-11-29 15:59:06 -08:00
} else {
2021-12-25 23:48:12 -08:00
let fromUserNum: Int64 = self.connectedPeripheral.num
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 {
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
let fetchedUsers = try context?.fetch(messageUsers) as! [UserEntity]
2021-12-25 23:48:12 -08:00
2021-12-19 19:40:16 -08:00
if fetchedUsers.isEmpty {
2021-12-25 23:48:12 -08:00
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!)
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
newMessage.direction = "IN"
2021-12-19 19:40:16 -08:00
newMessage.toUser = fetchedUsers.first(where: { $0.num == toUserNum })
if newMessage.toUser == nil {
2021-12-25 23:48:12 -08:00
let bcu: UserEntity = UserEntity(context: context!)
bcu.shortName = "ALL"
2021-12-28 09:11:24 -08:00
bcu.longName = "All - Broadcast"
bcu.hwModel = "UNSET"
bcu.num = Int64(broadcastNodeNum)
bcu.userId = "BROADCASTNODE"
newMessage.toUser = bcu
}
2021-12-19 19:40:16 -08:00
newMessage.fromUser = fetchedUsers.first(where: { $0.num == fromUserNum })
2021-12-18 20:49:50 -08:00
newMessage.messagePayload = message
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
let dataType = PortNum.textMessageApp
let payloadData: Data = message.data(using: String.Encoding.utf8)!
var dataMessage = DataMessage()
dataMessage.payload = payloadData
dataMessage.portnum = dataType
var meshPacket = MeshPacket()
2021-12-19 19:40:16 -08:00
meshPacket.to = UInt32(toUserNum)
meshPacket.from = UInt32(fromUserNum)
2021-12-18 20:49:50 -08:00
meshPacket.decoded = dataMessage
meshPacket.wantAck = true
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
2021-12-25 23:48:12 -08:00
if meshLoggingEnabled { MeshLogger.log("📲 New message sent to \(newMessage.toUser?.longName! ?? "Unknown")") }
print("📲 New message sent to \(newMessage.toUser?.longName! ?? "Unknown")")
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
do {
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
try context!.save()
2021-12-26 21:38:14 -08:00
print("💾 Saved a new sent message to \(toUserNum)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new sent message from \(connectedPeripheral.num) to \(toUserNum)") }
2021-12-18 20:49:50 -08:00
success = true
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
} catch {
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
context!.rollback()
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
let nsError = error as NSError
print("🚫 Unresolved error \(nsError)")
2021-12-18 20:49:50 -08:00
}
}
2021-12-15 23:53:45 -08:00
}
2021-12-25 23:48:12 -08:00
2021-12-18 20:49:50 -08:00
} catch {
2021-12-25 23:48:12 -08:00
}
}
return success
}
2021-09-10 07:41:26 -07:00
}