diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index a0ca88fd..99bb5608 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -25,8 +25,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph private var centralManager: CBCentralManager! - @Published var peripherals = [Peripheral]() - + @Published var peripherals: [Peripheral] @Published var connectedPeripheral: Peripheral! @Published var lastConnectionError: String @Published var minimumVersion = "1.3.43" @@ -79,9 +78,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // MARK: init BLEManager override init() { - //self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false self.lastConnectionError = "" self.connectedVersion = "0.0.0" + self.peripherals = [Peripheral]() super.init() // let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager") centralManager = CBCentralManager(delegate: self, queue: nil) @@ -102,23 +101,22 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // MARK: Scanning for BLE Devices // Scan for nearby BLE devices using the Meshtastic BLE service ID func startScanning() { - if isSwitchedOn { - centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: nil) - isScanning = centralManager.isScanning - + DispatchQueue.main.async { + self.isScanning = self.centralManager.isScanning + } print("✅ Scanning Started") } } // Stop Scanning For BLE Devices func stopScanning() { - if centralManager.isScanning { - centralManager.stopScan() - isScanning = centralManager.isScanning + DispatchQueue.main.async{ + self.isScanning = self.centralManager.isScanning + } print("🛑 Stopped Scanning") } } @@ -159,8 +157,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Connect to a specific peripheral func connectTo(peripheral: CBPeripheral) { stopScanning() - isConnecting = true - lastConnectionError = "" + DispatchQueue.main.async { + self.isConnecting = true + self.lastConnectionError = "" + } if connectedPeripheral != nil { MeshLogger.log("â„šī¸ BLE Disconnecting from: \(connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")") disconnectPeripheral() @@ -225,24 +225,31 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Called when a peripheral is connected func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { - - self.isConnecting = false - self.isConnected = true + DispatchQueue.main.async { + self.isConnecting = false + self.isConnected = true + } if userSettings?.preferredPeripheralId.count ?? 0 < 1 { - self.userSettings?.preferredPeripheralId = peripheral.identifier.uuidString - self.preferredPeripheral = true + DispatchQueue.main.async { + self.userSettings?.preferredPeripheralId = peripheral.identifier.uuidString + self.preferredPeripheral = true + } } else if userSettings!.preferredPeripheralId == peripheral.identifier.uuidString { - self.preferredPeripheral = true + DispatchQueue.main.async { + self.preferredPeripheral = true + } } else { self.preferredPeripheral = false print("Trying to connect a non prefered peripheral") } - // Invalidate and reset connection timer count, remove any connection errors - self.lastConnectionError = "" + // Invalidate and reset connection timer count + self.timeoutTimerCount = 0 if self.timeoutTimer != nil { self.timeoutTimer!.invalidate() } + // remove any connection errors + self.lastConnectionError = "" // Map the peripheral to the connectedPeripheral ObservedObjects connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first if connectedPeripheral != nil { @@ -485,12 +492,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // MyInfo if decodedInfo.myInfo.isInitialized && decodedInfo.myInfo.myNodeNum > 0 { - let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".") let version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))] - nowKnown = true - connectedVersion = String(version) + DispatchQueue.main.async { + self.connectedVersion = String(version) + } + let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame @@ -498,7 +506,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if !supportedVersion { invalidVersion = true - self.lastConnectionError = "🚨 Update your firmware" + lastConnectionError = "🚨 Update your firmware" return @@ -506,7 +514,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, peripheralId: self.connectedPeripheral.id, context: context!) - self.userSettings?.preferredNodeNum = myInfo?.myNodeNum ?? 0 + userSettings?.preferredNodeNum = myInfo?.myNodeNum ?? 0 if myInfo != nil { self.connectedPeripheral.num = myInfo!.myNodeNum @@ -632,10 +640,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce { - invalidVersion = false - lastConnectionError = "" + DispatchQueue.main.async { + self.invalidVersion = false + self.lastConnectionError = "" + self.isSubscribed = true + } MeshLogger.log("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)") - self.isSubscribed = true peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected }) // Config conplete returns so we don't read the characteristic again return @@ -1067,9 +1077,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if let decodedData = Data(base64Encoded: decodedString) { do { var channelSet: ChannelSet = try ChannelSet(serializedData: decodedData) + print(channelSet) var i:Int32 = 0 for cs in channelSet.settings { - i += 1 var chan = Channel() chan.settings = cs @@ -1086,7 +1096,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. 0 { let newChannel = ChannelEntity(context: context) newChannel.index = Int32(channel.index) @@ -776,14 +776,13 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo mutableChannels.add(newChannel) fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet + try context.save() + MeshLogger.log("💾 Updated MyInfo channel \(channel.index) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)") - } else { + } else if channel.role.rawValue > 0 { print("đŸ’Ĩ Trying to save a channel to a MyInfo that does not exist: \(fromNum)") } - try context.save() - MeshLogger.log("💾 Updated MyInfo channel \(channel.index) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)") - } catch { context.rollback() diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index c56fbe46..6c32f05c 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -82,7 +82,8 @@ struct AppSettings: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings - + + @State private var isPresentingCoreDataResetConfirm = false @State private var preferredDeviceConnected = false var perferredPeripheral: String { @@ -104,20 +105,6 @@ struct AppSettings: View { .keyboardType(.asciiCapable) .disableAutocorrection(true) .listRowSeparator(.visible) - -// HStack { -// Label("Radio", systemImage: "flipphone") -// Text(userSettings.preferredPeripheralName) -// .foregroundColor(.gray) -// -// } -// Text("This option is set via the preferred radio toggle for the connected device on the bluetooth tab.") -// .font(.caption) -// .listRowSeparator(.hidden) -// Text("The preferred radio will automatically reconnect if it becomes disconnected and is still within range.") -// .font(.caption2) -// .foregroundColor(.gray) - } Section(header: Text("Options")) { @@ -159,6 +146,26 @@ struct AppSettings: View { } } } + HStack { + + Button("Clear App Data", role: .destructive) { + isPresentingCoreDataResetConfirm = true + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + "Are you sure?", + isPresented: $isPresentingCoreDataResetConfirm, + titleVisibility: .visible + ) { + Button("Erase all app data?", role: .destructive) { + bleManager.disconnectPeripheral() + clearCoreDataDatabase(context: context) + } + } + } } .navigationTitle("App Settings") .navigationBarItems(trailing: diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index c4ce6e37..cfd14be3 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -206,6 +206,10 @@ struct PositionConfig: View { ) { Button("Save Position Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + if fixedPosition { + let sendPosition = bleManager.sendPosition(destNum: bleManager.connectedPeripheral.num, wantAck: true) + } + var pc = Config.PositionConfig() pc.positionBroadcastSmartEnabled = smartPositionEnabled pc.gpsEnabled = deviceGpsEnabled @@ -225,9 +229,6 @@ struct PositionConfig: View { if includeSpeed { pf.insert(.Speed) } if includeHeading { pf.insert(.Heading) } pc.positionFlags = UInt32(pf.rawValue) - if fixedPosition { - let sendPosition = bleManager.sendPosition(destNum: bleManager.connectedPeripheral.num, wantAck: true) - } let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 8d66ab3a..e4b8fa2e 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -79,64 +79,104 @@ struct ShareChannels: View { .labelsHidden() .disabled(channel.role == 1) Text((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 1 && channel.role > 0 { Toggle("Channel 1 Included", isOn: $includeChannel1) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 2 && channel.role > 0 { Toggle("Channel 2 Included", isOn: $includeChannel2) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 3 && channel.role > 0 { Toggle("Channel 3 Included", isOn: $includeChannel3) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 4 && channel.role > 0 { Toggle("Channel 4 Included", isOn: $includeChannel4) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 5 && channel.role > 0 { Toggle("Channel 5 Included", isOn: $includeChannel5) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 6 && channel.role > 0 { Toggle("Channel 6 Included", isOn: $includeChannel6) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } else if channel.index == 7 && channel.role > 0 { Toggle("Channel 7 Included", isOn: $includeChannel7) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") - Image(systemName: "lock.fill") + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") .foregroundColor(.green) + } } Spacer() }