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
2021-09-14 21:38:12 -07:00
import SwiftUI
2022-01-04 22:57:33 -08:00
import MapKit
2021-09-10 07:41:26 -07:00
2021-11-29 15:59:06 -08:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2021-09-10 21:50:54 -07:00
// M e s h t a s t i c B L E D e v i c e M a n a g e r
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
2021-10-20 00:31:22 -07: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 )
2021-10-20 00:31:22 -07:00
} 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 ?
2022-02-22 09:08:06 -10:00
var userSettings : UserSettings ?
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
2021-10-03 20:47:04 -07:00
@ Published var connectedPeripheral : Peripheral !
2021-10-11 07:28:28 -07:00
@ Published var lastConnectionError : String
2022-01-05 06:37:29 -08:00
@ Published var lastConnnectionVersion : 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
2021-10-22 10:03:50 -07:00
var timeoutTimer : Timer ?
2021-10-27 20:31:20 -07:00
var timeoutTimerCount = 0
2022-02-22 09:08:06 -10:00
var positionTimer : Timer ?
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
2021-10-03 20:47:04 -07:00
/* M e s h t a s t i c S e r v i c e D e t a i l s */
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 " )
2021-09-20 22:29:10 -07:00
let FROMNUM_UUID = CBUUID ( string : " 0xED9DA18C-A800-4F66-A670-AA7547E34453 " )
2021-12-25 23:48:12 -08:00
2022-03-15 09:11:31 -07:00
private var meshLoggingEnabled : Bool = false
2021-10-20 00:31:22 -07:00
let meshLog = documentsFolder . appendingPathComponent ( " meshlog.txt " )
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
// MARK: i n i t B L E M a n a g e r
2021-09-10 07:41:26 -07:00
override init ( ) {
2021-11-29 15:59:06 -08:00
2022-03-15 09:11:31 -07:00
self . meshLoggingEnabled = UserDefaults . standard . object ( forKey : " meshActivityLog " ) as ? Bool ? ? false
2021-10-11 07:28:28 -07:00
self . lastConnectionError = " "
2022-01-05 06:37:29 -08:00
self . lastConnnectionVersion = " 0.0.0 "
2021-09-10 07:41:26 -07:00
super . init ( )
2021-12-25 23:48:12 -08:00
// l e t b l e Q u e u e : D i s p a t c h Q u e u e = D i s p a t c h Q u e u e ( l a b e l : " C e n t r a l M a n a g e r " )
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: B l u e t o o t h e n a b l e d / d i s a b l e d f o r t h e a p p
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: S c a n n i n g f o r B L E D e v i c e s
2021-10-05 09:33:10 -07:00
// S c a n f o r n e a r b y B L E d e v i c e s u s i n g t h e M e s h t a s t i c B L E s e r v i c e I D
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 )
2021-10-28 00:15:47 -07:00
self . isScanning = self . centralManager . isScanning
2021-12-25 23:48:12 -08:00
2021-12-21 00:33:22 -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
// S t o p S c a n n i n g F o r B L E D e v i c e s
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 ( )
2021-10-28 00:15:47 -07:00
self . isScanning = self . centralManager . isScanning
2021-12-21 00:33:22 -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: B L E C o n n e c t f u n c t i o n s
2021-10-22 10:03:50 -07:00
// / T h e a c t i o n a f t e r t h e t i m e o u t - t i m e r h a s f i r e d
// /
// / - P a r a m e t e r s :
// / - t i m e r : T h e t i m e t h a t f i r e d t h e e v e n t
// /
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
2021-10-28 00:15:47 -07:00
self . timeoutTimerCount += 1
2021-10-22 20:58:20 -07:00
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
2021-10-22 20:58:20 -07:00
self . centralManager ? . cancelPeripheralConnection ( connectedPeripheral . peripheral )
}
2021-11-04 08:36:55 -07:00
connectedPeripheral = nil
2022-02-13 20:48:05 -08:00
self . isConnected = false
2021-11-29 15:59:06 -08:00
2022-01-10 07:02:12 -08:00
self . lastConnectionError = " 🚨 BLE Connection Timeout after making \( timeoutTimerCount ) attempts to connect to \( name ) . "
print ( " 🚨 BLE Connection Timeout after making \( timeoutTimerCount ) attempts to connect to \( name ) . " )
if meshLoggingEnabled { MeshLogger . log ( " 🚨 BLE Connection Timeout after making \( timeoutTimerCount ) attempts to connect to \( String ( name ) ) . This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer. " ) }
2021-11-29 15:59:06 -08:00
2021-10-28 00:15:47 -07:00
self . timeoutTimerCount = 0
2021-11-04 08:36:55 -07:00
self . timeoutTimer ? . invalidate ( )
2021-11-29 15:59:06 -08:00
} else {
2022-01-11 06:43:41 -08:00
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-10-22 10:03:50 -07:00
}
}
2021-11-29 15:59:06 -08:00
2021-10-12 17:54:10 -07:00
// C o n n e c t t o a s p e c i f i c p e r i p h e r a l
func connectTo ( peripheral : CBPeripheral ) {
2021-11-29 15:59:06 -08:00
2021-12-21 00:33:22 -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
2021-10-22 20:58:20 -07:00
if self . connectedPeripheral != nil {
2022-01-11 06:43:41 -08:00
if meshLoggingEnabled { MeshLogger . log ( " ℹ ️ BLE Disconnecting from: \( self . connectedPeripheral . name ) to connect to \( peripheral . name ? ? " Unknown " ) " ) }
print ( " ℹ ️ BLE Disconnecting from: \( self . connectedPeripheral . name ) to connect to \( peripheral . name ? ? " Unknown " ) " )
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
2021-10-22 10:39:32 -07:00
self . centralManager ? . connect ( peripheral )
2021-11-29 15:59:06 -08:00
2021-10-23 10:27:10 -07:00
// U s e a t i m e r t o k e e p t r a c k o f c o n n e c t i n g p e r i p h e r a l s , c o n t e x t t o p a s s t h e r a d i o n a m e w i t h t h e t i m e r a n d t h e R u n L o o p t o p r e v e n t
// t h e t i m e r f r o m r u n n i n g o n t h e m a i n U I t h r e a d
2021-10-22 20:58:20 -07:00
let context = [ " name " : " @ \( peripheral . name ? ? " Unknown " ) " ]
2021-10-23 00:19:23 -07:00
self . timeoutTimer = Timer . scheduledTimer ( timeInterval : 2.0 , target : self , selector : #selector ( timeoutTimerFired ) , userInfo : context , repeats : true )
2021-10-23 10:27:10 -07:00
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
// D i s c o n n e c t C o n n e c t e d P e r i p h e r a l
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 )
2022-02-13 20:48:05 -08:00
self . isConnected = false
2021-09-10 21:50:54 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-25 23:48:12 -08:00
// C a l l e d e a c h t i m e a p e r i p h e r a l i s d i s c o v e r e d
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
2022-03-04 04:16:45 -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 , channelUtilization : nil , airTime : nil , lastUpdate : Date ( ) , subscribed : false , peripheral : peripheral )
2021-10-22 10:03:50 -07:00
let peripheralIndex = peripherals . firstIndex ( where : { $0 . id = = newPeripheral . id } )
2021-10-27 21:15:36 -07:00
2021-11-20 11:02:15 -08:00
if peripheralIndex != nil && newPeripheral . peripheral . state != CBPeripheralState . connected {
2021-10-28 00:15:47 -07:00
2021-11-07 12:59:03 -08:00
peripherals [ peripheralIndex ! ] = newPeripheral
2021-11-20 11:02:15 -08:00
peripherals . remove ( at : peripheralIndex ! )
peripherals . append ( newPeripheral )
2021-12-25 23:48:12 -08:00
2021-11-29 15:59:06 -08:00
} else {
2022-03-04 04:16:45 -08:00
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 )
2021-12-21 00:33:22 -08:00
print ( " ℹ ️ Adding peripheral: \( peripheralName ) " )
2021-11-21 13:48:28 -08:00
}
2021-10-22 10:03:50 -07:00
}
2022-03-04 04:16:45 -08:00
let today = Date ( )
let fiveMinutesAgo = Calendar . current . date ( byAdding : . minute , value : - 5 , to : today ) !
peripherals . removeAll ( where : { $0 . lastUpdate <= fiveMinutesAgo } )
2021-10-05 09:33:10 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
// C a l l e d w h e n a p e r i p h e r a l i s c o n n e c t e d
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
2021-10-23 10:27:10 -07:00
// I n v a l i d a t e a n d r e s e t c o n n e c t i o n t i m e r c o u n t , r e m o v e a n y c o n n e c t i o n e r r o r s
2021-11-21 13:48:28 -08:00
self . lastConnectionError = " "
2021-10-22 10:39:32 -07:00
self . timeoutTimer ! . invalidate ( )
2021-10-27 20:31:20 -07:00
self . timeoutTimerCount = 0
2021-11-29 15:59:06 -08:00
2021-10-23 10:27:10 -07:00
// M a p t h e p e r i p h e r a l t o t h e c o n n e c t e d N o d e a n d c o n n e c t e d P e r i p h e r a l O b s e r v e d O b j e c t s
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
2021-10-23 10:27:10 -07:00
// D i s c o v e r S e r v i c e s
2021-10-14 22:51:09 -07:00
peripheral . discoverServices ( [ meshtasticServiceCBUUID ] )
2021-12-21 00:33:22 -08:00
if meshLoggingEnabled { MeshLogger . log ( " ✅ BLE Connected: \( peripheral . name ? ? " Unknown " ) " ) }
print ( " ✅ BLE Connected: \( peripheral . name ? ? " Unknown " ) " )
2022-02-22 09:08:06 -10:00
2021-10-01 08:33:11 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-12 17:17:46 -08:00
// C a l l e d w h e n a P e r i p h e r a l f a i l s t o c o n n e c t
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
2021-12-21 00:33:22 -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
// D i s c o n n e c t P e r i p h e r a l E v e n t
2021-11-29 15:59:06 -08:00
func centralManager ( _ central : CBCentralManager , didDisconnectPeripheral peripheral : CBPeripheral , error : Error ? ) {
2021-10-23 00:19:23 -07:00
// S t a r t a s c a n s o t h e d i s c o n n e c t e d p e r i p h e r a l i s m o v e d t o t h e p e r i p h e r a l s [ ] i f i t i s a w a k e
2021-10-06 17:51:52 -07:00
self . startScanning ( )
2021-10-23 00:19:23 -07:00
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
2021-10-23 10:27:10 -07:00
// h t t p s : / / d e v e l o p e r . a p p l e . c o m / d o c u m e n t a t i o n / c o r e b l u e t o o t h / c b e r r o r / c o d e
2021-10-06 17:51:52 -07:00
let errorCode = ( e as NSError ) . code
2021-10-23 10:27:10 -07:00
// u n k n o w n = 0 ,
2021-11-29 15:59:06 -08:00
2021-10-23 10:27:10 -07:00
if errorCode = = 6 { // C B E r r o r . C o d e . c o n n e c t i o n T i m e o u t T h e c o n n e c t i o n h a s t i m e d o u t u n e x p e c t e d l y .
2021-11-29 15:59:06 -08:00
2021-10-22 10:03:50 -07:00
// H a p p e n s w h e n d e v i c e i s m a n u a l l y r e s e t / p o w e r e d o f f
2021-10-23 00:19:23 -07:00
// W e w i l l t r y a n d r e - c o n n e c t t o t h i s d e v i c e
2022-01-10 07:02:12 -08:00
lastConnectionError = " 🚨 \( e . localizedDescription ) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds. "
2021-10-23 01:00:42 -07:00
if peripheral . identifier . uuidString = = UserDefaults . standard . object ( forKey : " preferredPeripheralId " ) as ? String ? ? " " {
2021-12-24 21:50:10 -08:00
if meshLoggingEnabled { MeshLogger . log ( " ℹ ️ BLE Reconnecting: \( peripheral . name ? ? " Unknown " ) " ) }
print ( " ℹ ️ BLE Reconnecting: \( peripheral . name ? ? " Unknown " ) " )
2021-10-23 01:00:42 -07:00
self . connectTo ( peripheral : peripheral )
}
2021-11-29 15:59:06 -08:00
} else if errorCode = = 7 { // C B E r r o r . C o d e . p e r i p h e r a l D i s c o n n e c t e d T h e s p e c i f i e d d e v i c e h a s d i s c o n n e c t e d f r o m u s .
2021-10-12 17:54:10 -07:00
// S e e m s t o b e w h a t i s r e c e i v e d w h e n a t b e a m s l e e p s , i m m e d i a t e l y r e c c o n n e c t i n g d o e s n o t w o r k .
2021-10-22 10:03:50 -07:00
lastConnectionError = e . localizedDescription
2021-12-25 23:48:12 -08:00
2022-01-10 07:02: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 { // P e e r r e m o v e d p a i r i n g i n f o r m a t i o n
2021-10-12 17:54:10 -07:00
// F o r g e t t i n g a n d r e c o n n e c t i n g s e e m s t o b e n e c e s s a r y s o w e n e e d t o s h o w t h e u s e r a n e r r o r t e l l i n g t h e m t o d o t h a t
2022-01-10 07:02:12 -08:00
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
2022-01-10 07:02: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 {
2021-10-23 00:19:23 -07:00
lastConnectionError = e . localizedDescription
2021-12-25 23:48:12 -08:00
2022-01-10 07:02: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-10-23 00:19:23 -07:00
}
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
// D i s c o n n e c t e d w i t h o u t e r r o r w h i c h i n d i c a t e s u s e r i n t e n t t o d i s c o n n e c t
2021-10-23 00:19:23 -07:00
// H a p p e n s w h e n s w i p i n g t o d i s c o n n e c t
2021-12-21 00:33:22 -08:00
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: P e r i p h e r a l S e r v i c e s f u n c t i o n s
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
2021-12-21 00:33:22 -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-10-22 20:58:20 -07:00
2021-11-29 15:59:06 -08:00
for service in services {
if service . uuid = = meshtasticServiceCBUUID {
2021-12-21 00:33:22 -08:00
print ( " ✅ Meshtastic service discovered OK " )
if meshLoggingEnabled { MeshLogger . log ( " ✅ BLE Service for Meshtastic discovered by \( peripheral . name ? ? " Unknown " ) " ) }
2022-02-13 20:48:05 -08:00
// p e r i p h e r a l . d i s c o v e r C h a r a c t e r i s t i c s ( n i l , f o r : s e r v i c e )
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: D i s c o v e r C h a r a c t e r i s t i c s E v e n t
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
2021-12-21 00:33:22 -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 :
2021-12-21 00:33:22 -08:00
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 ( )
2022-04-07 19:26:40 -07:00
toRadio . wantConfigID = UInt32 . random ( in : UInt32 ( UInt8 . max ) . . < UInt32 . max )
2021-11-29 16:51:59 -08:00
let binaryData : Data = try ! toRadio . serializedData ( )
peripheral . writeValue ( binaryData , for : characteristic , type : . withResponse )
case FROMRADIO_UUID :
2021-12-21 00:33:22 -08:00
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 :
2021-12-24 21:50:10 -08:00
print ( " ✅ FROMNUM (Notify) characteristic OK " )
2021-12-21 22:50:37 -08:00
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
2021-10-27 20:31:20 -07:00
func peripheral ( _ peripheral : CBPeripheral , didUpdateNotificationStateFor characteristic : CBCharacteristic , error : Error ? ) {
2021-11-29 15:59:06 -08:00
2021-12-21 00:33:22 -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 {
2021-12-21 00:33:22 -08:00
print ( " 🚫 didUpdateNotificationStateFor error: \( errorText ) " )
2021-12-12 17:17:46 -08:00
}
2021-10-27 20:31:20 -07:00
}
2021-11-29 15:59:06 -08:00
2021-12-25 23:48:12 -08:00
// MARK: D a t a R e a d / U p d a t e C h a r a c t e r i s t i c E v e n t
2021-11-29 15:59:06 -08:00
func peripheral ( _ peripheral : CBPeripheral , didUpdateValueFor characteristic : CBCharacteristic , error : Error ? ) {
2021-12-31 09:43:37 -08:00
if let e = error {
print ( " 🚫 didUpdateValueFor Characteristic error \( e ) " )
let errorCode = ( e as NSError ) . code
if errorCode = = 5 { // C B A T T E r r o r D o m a i n C o d e = 5 " A u t h e n t i c a t i o n i s i n s u f f i c i e n t . "
// B L E P i n c o n n e c t i o n e r r o r
// W e w i l l t r y a n d r e - c o n n e c t t o t h i s d e v i c e
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-12-31 09:43:37 -08:00
}
2022-01-13 18:18:24 -08:00
if errorCode = = 15 { // C B A T T E r r o r D o m a i n C o d e = 1 5 " E n c r y p t i o n i s i n s u f f i c i e n t . "
// B L E P i n c o n n e c t i o n e r r o r
// W e w i l l t r y a n d r e - c o n n e c t t o t h i s d e v i c e
lastConnectionError = " 🚫 BLE \( e . localizedDescription ) This may be a Meshtastic Firmware bug affecting BLE 4.0 devices. "
if meshLoggingEnabled { MeshLogger . log ( " 🚫 BLE \( e . localizedDescription ) Please try connecting again. You may need to forget the device under Settings > General > Bluetooth. " ) }
self . centralManager ? . cancelPeripheralConnection ( peripheral )
}
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 FROMRADIO_UUID :
2022-04-11 15:58:11 -07:00
2021-11-29 17:09:27 -08:00
if characteristic . value = = nil || characteristic . value ! . isEmpty {
return
}
2022-03-04 03:54:25 -08:00
2021-11-29 17:09:27 -08:00
var decodedInfo = FromRadio ( )
decodedInfo = try ! FromRadio ( serializedData : characteristic . value ! )
2022-01-10 18:42:25 -08:00
// MARK: I n c o m i n g M y I n f o P a c k e t
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 ]
// N o t F o u n d I n s e r t
if fetchedMyInfo . isEmpty {
let myInfo = MyInfoEntity ( context : context ! )
myInfo . myNodeNum = Int64 ( decodedInfo . myInfo . myNodeNum )
myInfo . hasGps = decodedInfo . myInfo . hasGps_p
2022-04-11 15:58:11 -07:00
myInfo . bitrate = decodedInfo . myInfo . bitrate
2022-04-03 21:59:19 -07:00
2022-03-04 03:54:25 -08:00
// S w i f t d o e s s t r i n g s w e i r d , t h i s d o e s w o r k t o g e t t h e v e r s i o n w i t h o u t t h e g i t h u b h a s h
let lastDotIndex = decodedInfo . myInfo . firmwareVersion . lastIndex ( of : " . " )
2022-01-10 07:02:12 -08:00
var version = decodedInfo . myInfo . firmwareVersion [ . . . ( lastDotIndex ? ? String . Index ( utf16Offset : 6 , in : decodedInfo . myInfo . firmwareVersion ) ) ]
2022-01-04 22:57:33 -08:00
version = version . dropLast ( )
myInfo . firmwareVersion = String ( version )
2022-01-05 06:37:29 -08:00
lastConnnectionVersion = String ( version )
2022-01-04 22:57:33 -08:00
2021-12-12 17:17:46 -08:00
myInfo . messageTimeoutMsec = Int32 ( bitPattern : decodedInfo . myInfo . messageTimeoutMsec )
myInfo . minAppVersion = Int32 ( bitPattern : decodedInfo . myInfo . minAppVersion )
myInfo . maxChannels = Int32 ( bitPattern : decodedInfo . myInfo . maxChannels )
2022-02-13 21:49:58 -08:00
self . connectedPeripheral . num = myInfo . myNodeNum
self . connectedPeripheral . firmwareVersion = myInfo . firmwareVersion ? ? " Unknown "
self . connectedPeripheral . name = myInfo . bleName ? ? " Unknown "
2022-01-01 19:08:36 -08:00
let fetchBCUserRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " UserEntity " )
fetchBCUserRequest . predicate = NSPredicate ( format : " num == %lld " , Int64 ( decodedInfo . myInfo . myNodeNum ) )
do {
let fetchedUser = try context ? . fetch ( fetchBCUserRequest ) as ! [ UserEntity ]
if fetchedUser . isEmpty {
// S a v e t h e b r o a d c a s t u s e r i f i t d o e s n o t e x i s t
let bcu : UserEntity = UserEntity ( context : context ! )
bcu . shortName = " ALL "
bcu . longName = " All - Broadcast "
bcu . hwModel = " UNSET "
bcu . num = Int64 ( broadcastNodeNum )
bcu . userId = " BROADCASTNODE "
print ( " 💾 Saved the All - Broadcast User " )
}
2022-05-05 18:01:20 -07:00
// v a r s e t t i n g s C a l l e d = s e l f . g e t S e t t i n g s ( )
2022-04-11 15:58:11 -07:00
2022-05-05 18:01:20 -07:00
if false {
2022-04-11 15:58:11 -07:00
print ( " 💾 Called Get Settings " )
} else {
print ( " 💥 Get Settings Call Failed " )
}
2022-01-01 19:08:36 -08:00
} catch {
print ( " 💥 Error Saving the All - Broadcast User " )
}
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
2022-01-04 22:57:33 -08:00
let lastDotIndex = decodedInfo . myInfo . firmwareVersion . lastIndex ( of : " . " ) // . l a s t I n d e x ( o f : " . " , o f f s e t B y : - 1 )
2022-01-10 07:02:12 -08:00
var version = decodedInfo . myInfo . firmwareVersion [ . . . ( lastDotIndex ? ? String . Index ( utf16Offset : 6 , in : decodedInfo . myInfo . firmwareVersion ) ) ]
2022-01-04 22:57:33 -08:00
version = version . dropLast ( )
fetchedMyInfo [ 0 ] . firmwareVersion = String ( version )
2022-01-05 06:37:29 -08:00
lastConnnectionVersion = String ( version )
2021-12-15 23:53:45 -08:00
fetchedMyInfo [ 0 ] . messageTimeoutMsec = Int32 ( bitPattern : decodedInfo . myInfo . messageTimeoutMsec )
fetchedMyInfo [ 0 ] . minAppVersion = Int32 ( bitPattern : decodedInfo . myInfo . minAppVersion )
fetchedMyInfo [ 0 ] . maxChannels = Int32 ( bitPattern : decodedInfo . myInfo . maxChannels )
2022-02-16 07:37:08 -08:00
self . connectedPeripheral . num = fetchedMyInfo [ 0 ] . myNodeNum
self . connectedPeripheral . firmwareVersion = fetchedMyInfo [ 0 ] . firmwareVersion ? ? " Unknown "
self . connectedPeripheral . name = fetchedMyInfo [ 0 ] . bleName ? ? " Unknown "
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 ( )
2021-12-21 00:33:22 -08:00
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
2022-04-07 20:27:10 -07:00
print ( " 💥 Error Saving Core Data 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
2021-12-24 21:50:10 -08:00
print ( " 💥 Fetch MyInfo Error " )
2021-11-29 17:09:27 -08:00
}
2022-02-22 09:08:06 -10:00
2022-03-04 03:54:25 -08:00
// MARK: S h a r e L o c a t i o n P o s i t i o n U p d a t e T i m e r
// U s e c o n t e x t t o p a s s t h e r a d i o n a m e w i t h t h e t i m e r
// U s e a R u n L o o p t o p r e v e n t t h e t i m e r f r o m r u n n i n g o n t h e m a i n U I t h r e a d
if userSettings ? . provideLocation ? ? false {
if self . positionTimer != nil {
self . positionTimer ! . invalidate ( )
}
let context = [ " name " : " @ \( peripheral . name ? ? " Unknown " ) " ]
self . positionTimer = Timer . scheduledTimer ( timeInterval : TimeInterval ( ( userSettings ? . provideLocationInterval ? ? 900 ) ) , target : self , selector : #selector ( positionTimerFired ) , userInfo : context , repeats : true )
RunLoop . current . add ( self . positionTimer ! , forMode : . common )
2022-02-22 09:08:06 -10:00
}
2021-11-29 17:09:27 -08:00
}
2022-01-10 18:42:25 -08:00
// MARK: I n c o m i n g N o d e I n f o P a c k e t
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 ]
// N o t F o u n d I n s e r t
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 )
2022-04-03 21:59:19 -07:00
2022-04-02 06:02:21 -07:00
if decodedInfo . nodeInfo . hasDeviceMetrics {
2022-04-03 21:59:19 -07:00
2022-05-05 18:01:20 -07:00
let telemetry = TelemetryEntity ( context : context ! )
2022-04-19 22:56:59 -07:00
2022-05-05 18:01:20 -07:00
telemetry . batteryLevel = Int32 ( decodedInfo . nodeInfo . deviceMetrics . batteryLevel )
telemetry . voltage = decodedInfo . nodeInfo . deviceMetrics . voltage
telemetry . channelUtilization = decodedInfo . nodeInfo . deviceMetrics . channelUtilization
self . connectedPeripheral . channelUtilization = telemetry . channelUtilization
telemetry . airUtilTx = decodedInfo . nodeInfo . deviceMetrics . airUtilTx
2022-04-03 21:59:19 -07:00
self . connectedPeripheral . airTime = decodedInfo . nodeInfo . deviceMetrics . airUtilTx
2022-05-05 18:01:20 -07:00
var newTelemetries = [ TelemetryEntity ] ( )
newTelemetries . append ( telemetry )
newNode . telemetries ? = NSOrderedSet ( array : newTelemetries )
2022-04-02 06:02:21 -07:00
}
2022-04-03 21:59:19 -07:00
// FIXME: D a t e f r o m t h e n o d e i n f o , m a y b e a b a d d a t e , n e e d s t o b e f i x e d u p s t r e a m i n t h e f i r m w a r e
newNode . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( decodedInfo . nodeInfo . lastHeard ) ) )
2021-12-12 17:17:46 -08:00
newNode . snr = decodedInfo . nodeInfo . snr
2021-12-25 23:48:12 -08:00
2022-02-13 21:49:58 -08:00
if self . connectedPeripheral != nil && self . connectedPeripheral . num = = newNode . num {
2021-12-25 23:48:12 -08:00
2021-12-24 21:50:10 -08:00
if decodedInfo . nodeInfo . hasUser {
2022-02-13 21:49:58 -08:00
2021-12-24 21:50:10 -08:00
connectedPeripheral . name = decodedInfo . nodeInfo . user . longName
}
2021-12-19 19:40:16 -08:00
}
2022-04-03 21:59:19 -07: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 ( )
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
2022-03-13 20:09:42 -07:00
if decodedInfo . nodeInfo . position . time > 0 {
position . time = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( decodedInfo . nodeInfo . position . time ) ) )
}
else {
position . time = Date ( )
}
2021-12-18 20:49:50 -08:00
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
// L o o k f o r a M y I n f o
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 {
2021-12-21 00:33:22 -08:00
print ( " 💥 Fetch MyInfo Error " )
2021-12-12 17:17:46 -08:00
}
2021-12-25 23:48:12 -08:00
2022-02-13 20:48:05 -08:00
} else if decodedInfo . nodeInfo . hasUser && decodedInfo . nodeInfo . num > 0 {
2021-12-25 23:48:12 -08:00
2021-12-15 23:53:45 -08:00
fetchedNode [ 0 ] . id = Int64 ( decodedInfo . nodeInfo . num )
fetchedNode [ 0 ] . num = Int64 ( decodedInfo . nodeInfo . num )
2022-02-13 20:48:05 -08:00
if decodedInfo . nodeInfo . lastHeard = = 0 {
fetchedNode [ 0 ] . lastHeard = Date ( )
} else {
fetchedNode [ 0 ] . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( decodedInfo . nodeInfo . lastHeard ) ) )
}
2021-12-15 23:53:45 -08:00
fetchedNode [ 0 ] . snr = decodedInfo . nodeInfo . snr
2022-02-13 21:49:58 -08:00
if self . connectedPeripheral != nil && self . connectedPeripheral . num = = fetchedNode [ 0 ] . num {
if decodedInfo . nodeInfo . hasUser {
self . connectedPeripheral . name = fetchedNode [ 0 ] . user ! . longName ? ? " Unknown "
}
}
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
2022-02-13 20:48:05 -08:00
fetchedNode [ 0 ] . user ! . num = Int64 ( decodedInfo . nodeInfo . num )
2021-12-15 23:53:45 -08:00
fetchedNode [ 0 ] . user ! . longName = decodedInfo . nodeInfo . user . longName
fetchedNode [ 0 ] . user ! . shortName = decodedInfo . nodeInfo . user . shortName
2022-02-13 20:48:05 -08:00
fetchedNode [ 0 ] . user ! . macaddr = decodedInfo . nodeInfo . user . macaddr
2021-12-15 23:53:45 -08:00
fetchedNode [ 0 ] . user ! . hwModel = String ( describing : decodedInfo . nodeInfo . user . hwModel ) . uppercased ( )
2021-12-12 17:17:46 -08:00
}
2022-05-05 18:01:20 -07:00
if decodedInfo . nodeInfo . hasDeviceMetrics {
2022-03-13 20:09:42 -07:00
2022-05-05 18:01:20 -07:00
let newTelemetry = TelemetryEntity ( context : context ! )
newTelemetry . batteryLevel = Int32 ( decodedInfo . nodeInfo . deviceMetrics . batteryLevel )
newTelemetry . voltage = decodedInfo . nodeInfo . deviceMetrics . voltage
newTelemetry . channelUtilization = decodedInfo . nodeInfo . deviceMetrics . channelUtilization
self . connectedPeripheral . channelUtilization = newTelemetry . channelUtilization
newTelemetry . airUtilTx = decodedInfo . nodeInfo . deviceMetrics . airUtilTx
self . connectedPeripheral . airTime = decodedInfo . nodeInfo . deviceMetrics . airUtilTx
let mutableTelemetries = fetchedNode [ 0 ] . telemetries ! . mutableCopy ( ) as ! NSMutableOrderedSet
fetchedNode [ 0 ] . telemetries = mutableTelemetries . copy ( ) as ? NSOrderedSet
2022-03-13 20:09:42 -07:00
}
2022-05-05 18:01:20 -07:00
if decodedInfo . nodeInfo . hasPosition {
2021-12-25 23:48:12 -08:00
2022-05-05 18:01:20 -07: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
2022-05-05 18:01:20 -07:00
if decodedInfo . nodeInfo . position . time > 0 {
position . time = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( decodedInfo . nodeInfo . position . time ) ) )
}
else {
position . time = Date ( )
}
let mutablePositions = fetchedNode [ 0 ] . positions ! . mutableCopy ( ) as ! NSMutableOrderedSet
fetchedNode [ 0 ] . positions = mutablePositions . copy ( ) as ? NSOrderedSet
}
2021-12-25 23:48:12 -08:00
2021-12-12 17:17:46 -08:00
// L o o k f o r a M y I n f o
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 {
2021-12-21 00:33:22 -08:00
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 ( )
2021-12-21 00:33:22 -08:00
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
2022-04-07 20:27:10 -07:00
print ( " 💥 Error Saving Core Data 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
2021-12-21 00:33:22 -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
2021-12-21 00:33:22 -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
2021-12-21 00:33:22 -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
}
2022-02-22 09:08:06 -10:00
// H a n d l e o t h e r p a c k e t t y p e s
2021-11-29 17:09:27 -08:00
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
2022-01-10 18:42:25 -08:00
// MARK: I n c o m i n g P a c k e t f r o m t h e T E X T M E S S A G E _ A P P
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 ) {
2021-12-21 22:50:37 -08:00
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 )
2022-01-10 07:02:12 -08:00
if decodedInfo . packet . rxTime = = 0 {
newMessage . messageTimestamp = Int32 ( Date ( ) . timeIntervalSince1970 )
} else {
newMessage . messageTimestamp = Int32 ( bitPattern : decodedInfo . packet . rxTime )
}
2021-12-12 17:17:46 -08:00
newMessage . receivedACK = false
newMessage . direction = " IN "
2022-03-29 21:16:15 -07:00
newMessage . isEmoji = decodedInfo . packet . decoded . emoji = = 1
2022-01-10 07:02:12 -08:00
2022-02-04 02:26:58 -08:00
if decodedInfo . packet . decoded . replyID > 0 {
2022-01-10 07:02:12 -08:00
2022-02-04 02:26:58 -08:00
newMessage . replyID = Int64 ( decodedInfo . packet . decoded . replyID )
2022-01-10 07:02:12 -08:00
}
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
2021-12-24 21:50:10 -08:00
// S a v e t h e b r o a d c a s t u s e r i f i t d o e s n o t e x i s t
2021-12-19 19:40:16 -08:00
let bcu : UserEntity = UserEntity ( context : context ! )
2021-12-24 21:50:10 -08:00
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 ( )
2021-12-21 22:50:37 -08:00
print ( " 💾 Saved a new message for \( decodedInfo . packet . id ) " )
2022-02-18 06:22:02 -08:00
if meshLoggingEnabled { MeshLogger . log ( " 💾 Saved a new message for \( newMessage . messageId ) " ) }
2021-12-26 21:38:14 -08:00
2022-02-23 23:05:47 -10:00
if newMessage . toUser != nil && newMessage . toUser ! . num = = self . broadcastNodeNum || self . connectedPeripheral != nil && self . connectedPeripheral . num = = newMessage . toUser ! . num {
2021-12-26 21:38:14 -08:00
// C r e a t e a n i O S N o t i f i c a t i o n f o r t h e r e c e i v e d m e s s a g e a n d s c h e d u l e i t i m m e d i a t e l y
let manager = LocalNotificationManager ( )
manager . notifications = [
Notification (
2022-02-18 06:22:02 -08:00
id : ( " notification.id. \( newMessage . messageId ) " ) ,
2021-12-26 21:38:14 -08:00
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
2021-12-21 22:50:37 -08:00
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
2021-12-21 22:50:37 -08:00
print ( " 💥 Fetch Message To and From Users Error " )
2021-11-29 17:09:27 -08:00
}
}
2022-02-22 09:08:06 -10:00
// MARK: I n c o m i n g N O D E I N F O _ A P P P a c k e t
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 {
2021-12-16 22:45:18 -08:00
fetchedNode [ 0 ] . id = Int64 ( decodedInfo . packet . from )
fetchedNode [ 0 ] . num = Int64 ( decodedInfo . packet . from )
2021-12-26 21:38:14 -08:00
2022-01-01 22:55:25 -08:00
if decodedInfo . packet . rxTime > 0 {
2021-12-26 21:38:14 -08:00
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
2022-03-04 03:54:25 -08:00
if meshLoggingEnabled { MeshLogger . log ( " 💾 Updated NodeInfo SNR \( decodedInfo . packet . rxSnr ) and Time from Node Info App Packet For: \( fetchedNode [ 0 ] . num ) " ) }
print ( " 💾 Updated NodeInfo SNR \( decodedInfo . packet . rxSnr ) 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
2021-12-21 22:50:37 -08:00
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
2021-12-21 22:50:37 -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
2022-01-10 18:42:25 -08:00
// MARK: I n c o m i n g P a c k e t f r o m t h e P O S I T I O N _ A P P
2021-11-29 17:09:27 -08:00
} else if decodedInfo . packet . decoded . portnum = = PortNum . positionApp {
2021-10-11 07:28:28 -07:00
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 {
2021-12-16 22:45:18 -08:00
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
2022-01-18 17:09:23 +13:00
if let positionMessage = try ? Position ( serializedData : decodedInfo . packet . decoded . payload ) {
2022-03-15 09:11:31 -07:00
2022-01-18 17:09:23 +13:00
let position = PositionEntity ( context : context ! )
position . latitudeI = positionMessage . latitudeI
position . longitudeI = positionMessage . longitudeI
position . altitude = positionMessage . altitude
2022-03-15 09:11:31 -07:00
2022-03-19 23:45:01 -07:00
if positionMessage . time = = 0 {
2022-01-18 17:09:23 +13:00
2022-03-19 23:45:01 -07:00
position . time = Date ( )
2022-03-15 09:11:31 -07:00
2022-01-18 17:09:23 +13:00
} else {
2022-03-15 09:11:31 -07:00
position . time = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( positionMessage . time ) ) )
2022-01-18 17:09:23 +13:00
}
2022-03-15 09:11:31 -07:00
let mutablePositions = fetchedNode [ 0 ] . positions ! . mutableCopy ( ) as ! NSMutableOrderedSet
mutablePositions . add ( position )
fetchedNode [ 0 ] . positions = mutablePositions . copy ( ) as ? NSOrderedSet
2022-01-18 17:09:23 +13:00
}
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 {
2022-03-04 03:54:25 -08:00
MeshLogger . log ( " 💾 Updated NodeInfo Position Coordinates, SNR \( decodedInfo . packet . rxSnr ) and Time from Position App Packet For: \( fetchedNode [ 0 ] . num ) " )
2021-12-15 23:53:45 -08:00
}
2022-03-04 03:54:25 -08:00
print ( " 💾 Updated NodeInfo Position Coordinates, SNR \( decodedInfo . packet . rxSnr ) and Time from Position App 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
2022-02-04 02:26:58 -08:00
print ( " 💥 Error Saving NodeInfoEntity from POSITION_APP \( nsError ) " )
2021-12-15 23:53:45 -08:00
}
} catch {
2021-12-25 23:48:12 -08:00
2022-02-04 02:26:58 -08:00
print ( " 💥 Error Fetching NodeInfoEntity for POSITION_APP " )
2021-11-29 17:09:27 -08:00
}
2022-02-22 09:08:06 -10:00
// MARK: I n c o m i n g R O U T I N G _ A P P P a c k e t
2022-02-13 20:48:05 -08:00
} else if decodedInfo . packet . decoded . portnum = = PortNum . routingApp {
2022-03-19 23:45:01 -07:00
if let routingMessage = try ? Routing ( serializedData : decodedInfo . packet . decoded . payload ) {
print ( decodedInfo . packet . decoded . requestID )
print ( routingMessage )
2022-02-13 20:48:05 -08:00
2022-03-19 23:45:01 -07:00
let error = routingMessage . errorReason
2022-03-13 20:09:42 -07:00
2022-03-19 23:45:01 -07:00
var errorExplanation = " Unknown Routing Error "
2022-03-13 20:09:42 -07:00
2022-03-19 23:45:01 -07:00
switch error {
case Routing . Error . none :
errorExplanation = " This message is not a failure "
case Routing . Error . noRoute :
errorExplanation = " Our node doesn't have a route to the requested destination anymore. "
case Routing . Error . gotNak :
errorExplanation = " We received a nak while trying to forward on your behalf "
case Routing . Error . timeout :
errorExplanation = " Timeout "
case Routing . Error . noInterface :
errorExplanation = " No suitable interface could be found for delivering this packet "
case Routing . Error . maxRetransmit :
errorExplanation = " We reached the max retransmission count (typically for naive flood routing) "
case Routing . Error . noChannel :
errorExplanation = " No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) "
case Routing . Error . tooLarge :
errorExplanation = " The packet was too big for sending (exceeds interface MTU after encoding) "
case Routing . Error . noResponse :
errorExplanation = " The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions) "
case Routing . Error . badRequest :
errorExplanation = " The application layer service on the remote node received your request, but considered your request somehow invalid "
case Routing . Error . notAuthorized :
errorExplanation = " The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) "
fallthrough
default :
print ( error )
2022-03-13 20:09:42 -07:00
}
2022-03-19 23:45:01 -07:00
if meshLoggingEnabled { MeshLogger . log ( " 🕸️ ROUTING PACKET received for RequestID: \( decodedInfo . packet . decoded . requestID ) Error: \( errorExplanation ) " ) }
print ( " 🕸️ ROUTING PACKET received for RequestID: \( decodedInfo . packet . decoded . requestID ) Error: \( errorExplanation ) " )
2022-03-13 20:09:42 -07:00
2022-03-19 23:45:01 -07:00
if routingMessage . errorReason = = Routing . Error . none {
print ( " Priority ACK no Error " )
let fetchMessageRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " MessageEntity " )
fetchMessageRequest . predicate = NSPredicate ( format : " messageId == %lld " , Int64 ( decodedInfo . packet . decoded . requestID ) )
2022-02-23 23:05:47 -10:00
2022-03-19 23:45:01 -07:00
do {
let fetchedMessage = try context ? . fetch ( fetchMessageRequest ) [ 0 ] as ? MessageEntity
if fetchedMessage != nil {
fetchedMessage ! . receivedACK = true
fetchedMessage ! . ackSNR = decodedInfo . packet . rxSnr
if decodedInfo . packet . rxTime <= 0 {
fetchedMessage ! . ackTimestamp = Int32 ( Date ( ) . timeIntervalSince1970 )
} else {
fetchedMessage ! . ackTimestamp = Int32 ( decodedInfo . packet . rxTime )
}
fetchedMessage ! . objectWillChange . send ( )
}
try context ! . save ( )
if meshLoggingEnabled {
MeshLogger . log ( " 💾 ACK Received and saved for MessageID \( decodedInfo . packet . decoded . requestID ) " )
}
print ( " 💾 ACK Received and saved for MessageID \( decodedInfo . packet . decoded . requestID ) " )
} catch {
context ! . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving ACK for message MessageID \( decodedInfo . packet . id ) Error: \( nsError ) " )
}
}
2022-03-04 03:54:25 -08:00
}
2022-02-13 20:48:05 -08:00
2022-04-04 22:49:26 -07:00
// MARK: I n c o m i n g T E L E M E T R Y _ A P P P a c k e t
2022-03-29 18:53:33 -07:00
} else if decodedInfo . packet . decoded . portnum = = PortNum . telemetryApp {
2022-02-04 02:26:58 -08:00
2022-04-04 22:49:26 -07:00
if let telemetryMessage = try ? Telemetry ( serializedData : decodedInfo . packet . decoded . payload ) {
let telemetry = TelemetryEntity ( context : context ! )
print ( decodedInfo . packet . decoded . requestID )
print ( telemetryMessage )
}
2022-03-29 18:53:33 -07:00
if meshLoggingEnabled { MeshLogger . log ( " ℹ ️ MESH PACKET received for Telemetry App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " ) }
print ( " ℹ ️ MESH PACKET received for Telemetry App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " )
2022-02-04 02:26:58 -08:00
2022-01-02 23:28:51 -08:00
} else if decodedInfo . packet . decoded . portnum = = PortNum . storeForwardApp {
2022-02-04 02:26:58 -08:00
if meshLoggingEnabled { MeshLogger . log ( " ℹ ️ MESH PACKET received for Store Forward App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " ) }
2022-01-05 06:37:29 -08:00
print ( " ℹ ️ MESH PACKET received for Admin App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " )
2022-01-02 23:28:51 -08:00
} else if decodedInfo . packet . decoded . portnum = = PortNum . adminApp {
2021-11-29 15:59:06 -08:00
2022-02-04 02:26:58 -08:00
if meshLoggingEnabled { MeshLogger . log ( " ℹ ️ MESH PACKET received for Admin App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " ) }
2022-01-05 06:37:29 -08:00
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 {
2021-12-25 23:48:12 -08:00
2022-02-04 02:26:58 -08:00
if meshLoggingEnabled { MeshLogger . log ( " ℹ ️ MESH PACKET received for Other App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " ) }
2022-01-05 06:37:29 -08:00
print ( " ℹ ️ MESH PACKET received for Other App UNHANDLED \( try decodedInfo . packet . jsonString ( ) ) " )
2021-10-20 00:31:22 -07:00
}
2021-11-29 15:59:06 -08:00
2021-11-29 17:09:27 -08:00
} catch {
2021-12-21 22:50:37 -08:00
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
2021-12-21 00:33:22 -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
2021-12-18 01:01:56 -08:00
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 :
2022-03-20 08:10:25 -07:00
// L i k e l y F R O M N U M _ U U I D
2021-12-21 22:50:37 -08:00
print ( " 🚨 Unhandled Characteristic UUID: \( characteristic . uuid ) " )
2021-09-12 11:37:19 -07:00
}
peripheral . readValue ( for : FROMRADIO_characteristic )
2021-09-10 21:50:54 -07:00
}
2021-11-29 15:59:06 -08:00
2022-01-04 22:57:33 -08:00
// S e n d M e s s a g e
2022-03-29 21:16:15 -07:00
public func sendMessage ( message : String , toUserNum : Int64 , isEmoji : Bool , replyID : Int64 ) -> Bool {
2021-12-26 21:38:14 -08:00
2021-10-20 00:31:22 -07:00
var success = false
2021-11-29 15:59:06 -08:00
2021-10-20 00:31:22 -07:00
// R e t u r n f a l s e i f w e a r e n o t p r o p e r l y c o n n e c t e d t o a d e v i c e , h a n d l e r e t r y l o g i c i n t h e v i e w f o r n o w
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 ( )
2021-10-20 00:31:22 -07:00
self . startScanning ( )
2021-11-29 15:59:06 -08:00
2021-10-20 00:31:22 -07:00
// T r y a n d c o n n e c t t o t h e p r e f e r r e d P e r i p h e r i a l f i r s t
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 )
}
2022-01-04 22:57:33 -08:00
print ( " 🚫 Message Send Failed, not properly connected to \( preferredPeripheral ? . name ? ? " Unknown " ) " )
if meshLoggingEnabled { MeshLogger . log ( " 🚫 Message Send Failed, not properly connected to \( preferredPeripheral ? . name ? ? " Unknown " ) " ) }
2021-12-25 23:48:12 -08:00
2021-10-20 00:31:22 -07:00
success = false
2022-01-04 22:57:33 -08:00
2021-11-29 15:59:06 -08:00
} else if message . count < 1 {
2021-12-25 23:48:12 -08:00
2021-12-24 21:50:10 -08:00
// D o n ' t s e n d a n e m p t y m e s s a g e
print ( " 🚫 Don't Send an Empty Message " )
2021-10-20 00:31:22 -07:00
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
2021-12-24 21:50:10 -08:00
print ( " 🚫 Message Users Not Found, Fail " )
2021-12-18 20:49:50 -08:00
success = false
2022-01-10 07:29:48 -08:00
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 ) )
// n e w M e s s a g e . m e s s a g e I d = I n t 6 4 ( 0 x F F | U I n t 3 2 . r a n d o m ( i n : U I n t 3 2 ( U I n t 8 . m a x ) . . < U I n t 3 2 ( 1 1 4 7 4 8 3 6 4 7 ) ) )
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 } )
2022-03-29 21:16:15 -07:00
newMessage . isEmoji = isEmoji
2022-01-04 22:57:33 -08:00
if replyID > 0 {
newMessage . replyID = replyID
2022-01-01 22:55:25 -08:00
}
2021-12-21 00:33:22 -08:00
if newMessage . toUser = = nil {
2021-12-25 23:48:12 -08:00
2021-12-21 00:33:22 -08:00
let bcu : UserEntity = UserEntity ( context : context ! )
2021-12-24 21:50:10 -08:00
bcu . shortName = " ALL "
2021-12-28 09:11:24 -08:00
bcu . longName = " All - Broadcast "
2021-12-21 00:33:22 -08:00
bcu . hwModel = " UNSET "
bcu . num = Int64 ( broadcastNodeNum )
bcu . userId = " BROADCASTNODE "
newMessage . toUser = bcu
}
2022-01-04 22:57:33 -08:00
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 ( )
2022-02-22 18:06:50 -10:00
meshPacket . id = UInt32 ( newMessage . messageId )
2021-12-19 19:40:16 -08:00
meshPacket . to = UInt32 ( toUserNum )
meshPacket . from = UInt32 ( fromUserNum )
2022-01-15 17:16:00 -08:00
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
var toRadio : ToRadio !
toRadio = ToRadio ( )
toRadio . packet = meshPacket
let binaryData : Data = try ! toRadio . serializedData ( )
2021-12-25 23:48:12 -08:00
2022-02-13 20:48:05 -08:00
if meshLoggingEnabled { MeshLogger . log ( " 📲 New messageId \( newMessage . messageId ) sent to \( newMessage . toUser ? . longName ! ? ? " Unknown " ) " ) }
print ( " 📲 New messageId \( newMessage . messageId ) 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
2022-04-23 12:57:47 -07:00
print ( " 💥 Unresolved Core Data error in Send Message Function it is likely that your database is corrupted deleting and re-installing the app should clear the corrupted data. Error: \( nsError ) " )
2022-04-07 20:27:10 -07:00
if meshLoggingEnabled { MeshLogger . log ( " 💥 Unresolved Core Data 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
2021-10-20 00:31:22 -07:00
}
}
return success
}
2022-02-17 18:07:41 -08:00
2022-04-11 15:58:11 -07:00
// S e n d P o s i t i o n
2022-03-19 23:45:01 -07:00
public func sendPosition ( destNum : Int64 , wantResponse : Bool ) -> Bool {
2022-02-17 18:07:41 -08:00
var success = false
let fromNodeNum = connectedPeripheral . num
2022-02-24 07:57:08 -10:00
if fromNodeNum <= 0 || ( LocationHelper . currentLocation . latitude = = LocationHelper . DefaultLocation . latitude && LocationHelper . currentLocation . longitude = = LocationHelper . DefaultLocation . longitude ) {
2022-02-17 18:07:41 -08:00
return false
}
let fetchNode : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " NodeInfoEntity " )
fetchNode . predicate = NSPredicate ( format : " num == %lld " , fromNodeNum )
do {
let fetchedNode = try context ? . fetch ( fetchNode ) as ! [ NodeInfoEntity ]
2022-04-19 22:56:59 -07:00
2022-02-17 18:07:41 -08:00
if fetchedNode . count = = 1 {
2022-03-19 23:45:01 -07:00
2022-02-17 18:07:41 -08:00
var positionPacket = Position ( )
positionPacket . latitudeI = Int32 ( LocationHelper . currentLocation . latitude * 1e7 )
positionPacket . longitudeI = Int32 ( LocationHelper . currentLocation . longitude * 1e7 )
positionPacket . time = UInt32 ( Date ( ) . timeIntervalSince1970 )
positionPacket . altitude = Int32 ( LocationHelper . currentAltitude )
var meshPacket = MeshPacket ( )
2022-02-18 06:22:02 -08:00
meshPacket . to = UInt32 ( destNum )
2022-02-17 18:07:41 -08:00
meshPacket . from = UInt32 ( connectedPeripheral . num )
meshPacket . wantAck = wantResponse
var dataMessage = DataMessage ( )
dataMessage . payload = try ! positionPacket . serializedData ( )
dataMessage . portnum = PortNum . positionApp
meshPacket . decoded = dataMessage
var toRadio : ToRadio !
toRadio = ToRadio ( )
toRadio . packet = meshPacket
let binaryData : Data = try ! toRadio . serializedData ( )
2022-03-19 23:45:01 -07:00
if meshLoggingEnabled { MeshLogger . log ( " 📍 Sent a Position Packet from the phone to the device for node: \( fromNodeNum ) " ) }
print ( " 📍 Sent a Position Packet from the phone to the device for node: \( fromNodeNum ) " )
2022-02-17 18:07:41 -08:00
if connectedPeripheral ! . peripheral . state = = CBPeripheralState . connected {
2022-03-19 23:45:01 -07:00
2022-04-19 22:56:59 -07:00
connectedPeripheral . peripheral . writeValue ( binaryData , for : TORADIO_characteristic , type : . withResponse )
2022-03-19 23:45:01 -07:00
success = true
2022-02-17 18:07:41 -08:00
}
}
} catch {
2022-03-19 23:45:01 -07:00
success = false
2022-02-17 18:07:41 -08:00
}
return success
}
2022-02-22 09:08:06 -10:00
@objc func positionTimerFired ( timer : Timer ) {
// C h e c k f o r c o n n e c t e d n o d e
if connectedPeripheral != nil {
// S e n d a p o s i t i o n o u t t o t h e m e s h i f " s h a r e l o c a t i o n w i t h t h e m e s h " i s e n a b l e d i n s e t t i n g s
if userSettings ! . provideLocation {
2022-02-24 07:57:08 -10:00
2022-02-22 09:08:06 -10:00
let success = sendPosition ( destNum : connectedPeripheral . num , wantResponse : false )
if ! success {
print ( " Failed to send positon to device " )
}
}
}
}
2022-04-11 15:58:11 -07:00
// MARK: D e v i c e S e t t i n g s
public func getSettings ( ) -> Bool {
var adminPacket = AdminMessage ( )
adminPacket . getRadioRequest = true
var meshPacket : MeshPacket = MeshPacket ( )
meshPacket . to = UInt32 ( connectedPeripheral . num )
meshPacket . from = UInt32 ( connectedPeripheral . num )
meshPacket . id = UInt32 . random ( in : UInt32 ( UInt8 . max ) . . < UInt32 . max )
meshPacket . priority = MeshPacket . Priority . reliable
meshPacket . wantAck = true
meshPacket . hopLimit = 0
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 ( )
if connectedPeripheral ! . peripheral . state = = CBPeripheralState . connected {
connectedPeripheral . peripheral . writeValue ( binaryData , for : TORADIO_characteristic , type : . withResponse )
return true
}
return false
}
2021-09-10 07:41:26 -07:00
}