mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
291 lines
9.4 KiB
Swift
291 lines
9.4 KiB
Swift
//
|
|
// Connect.swift
|
|
// Meshtastic Apple
|
|
//
|
|
// Copyright(c) Garth Vander Houwen 8/18/21.
|
|
//
|
|
|
|
import SwiftUI
|
|
import MapKit
|
|
import CoreData
|
|
import CoreLocation
|
|
import CoreBluetooth
|
|
|
|
struct Connect: View {
|
|
|
|
@Environment(\.managedObjectContext) var context
|
|
@EnvironmentObject var bleManager: BLEManager
|
|
@EnvironmentObject var userSettings: UserSettings
|
|
@State var node: NodeInfoEntity? = nil
|
|
|
|
@State var isPreferredRadio: Bool = false
|
|
@State var isUnsetRegion = false
|
|
@State var invalidFirmwareVersion = false
|
|
|
|
var body: some View {
|
|
|
|
NavigationStack {
|
|
VStack {
|
|
List {
|
|
if bleManager.isSwitchedOn {
|
|
Section(header: Text("Connected Radio").font(.title)) {
|
|
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected {
|
|
HStack {
|
|
Image(systemName: "antenna.radiowaves.left.and.right")
|
|
.symbolRenderingMode(.hierarchical)
|
|
.imageScale(.large).foregroundColor(.green)
|
|
.padding(.trailing)
|
|
VStack(alignment: .leading) {
|
|
if node != nil {
|
|
Text(bleManager.connectedPeripheral.longName).font(.title2)
|
|
}
|
|
Text("BLE Name: ").font(.caption)+Text(bleManager.connectedPeripheral.peripheral.name ?? "Unknown")
|
|
.font(.caption).foregroundColor(Color.gray)
|
|
if node != nil {
|
|
Text("FW Version: ").font(.caption)+Text(node?.myInfo?.firmwareVersion ?? "Unknown")
|
|
.font(.caption).foregroundColor(Color.gray)
|
|
}
|
|
if bleManager.isSubscribed {
|
|
Text("Subscribed to mesh").font(.caption)
|
|
.foregroundColor(.green)
|
|
} else {
|
|
Text("Communicating with device. . . ").font(.caption)
|
|
.foregroundColor(.orange)
|
|
}
|
|
}
|
|
Spacer()
|
|
VStack(alignment: .center) {
|
|
Text("Preferred").font(.caption2)
|
|
Text("Radio").font(.caption2)
|
|
Toggle("Preferred Radio", isOn: $bleManager.preferredPeripheral)
|
|
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
|
.labelsHidden()
|
|
.onChange(of: bleManager.preferredPeripheral) { value in
|
|
if value {
|
|
if bleManager.connectedPeripheral != nil {
|
|
userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString
|
|
userSettings.preferredNodeNum = bleManager.connectedPeripheral!.num
|
|
bleManager.preferredPeripheral = true
|
|
isPreferredRadio = true
|
|
}
|
|
} else {
|
|
|
|
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId {
|
|
|
|
userSettings.preferredPeripheralId = ""
|
|
userSettings.preferredNodeNum = 0
|
|
bleManager.preferredPeripheral = false
|
|
isPreferredRadio = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.font(.caption).foregroundColor(Color.gray)
|
|
.padding([.top, .bottom])
|
|
.swipeActions {
|
|
|
|
Button(role: .destructive) {
|
|
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
|
bleManager.disconnectPeripheral()
|
|
isPreferredRadio = false
|
|
}
|
|
} label: {
|
|
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
|
}
|
|
}
|
|
.contextMenu{
|
|
|
|
if node != nil {
|
|
|
|
Text("Num: \(String(node!.num))")
|
|
Text("Short Name: \(node?.user?.shortName ?? "????")")
|
|
Text("Long Name: \(node?.user?.longName ?? "Unknown")")
|
|
Text("Max Channels: \(String(node?.myInfo?.maxChannels ?? 0))")
|
|
Text("Bitrate: \(String(format: "%.2f", node?.myInfo?.bitrate ?? 0.00))")
|
|
Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)")
|
|
|
|
}
|
|
}
|
|
if isUnsetRegion {
|
|
HStack {
|
|
NavigationLink {
|
|
LoRaConfig(node: node)
|
|
} label: {
|
|
Label("Set LoRa Region", systemImage: "globe.americas.fill")
|
|
.foregroundColor(.red)
|
|
.font(.title)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if bleManager.isConnecting {
|
|
HStack {
|
|
Image(systemName: "antenna.radiowaves.left.and.right")
|
|
.symbolRenderingMode(.hierarchical)
|
|
.imageScale(.large).foregroundColor(.orange)
|
|
.padding(.trailing)
|
|
if bleManager.timeoutTimerCount == 0 {
|
|
Text("Connecting . . .")
|
|
.font(.title3)
|
|
.foregroundColor(.orange)
|
|
} else {
|
|
VStack {
|
|
|
|
Text("Connection Attempt \(bleManager.timeoutTimerCount) of 10")
|
|
.font(.callout)
|
|
.foregroundColor(.orange)
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
|
|
} else {
|
|
|
|
if bleManager.lastConnectionError.count > 0 {
|
|
Text(bleManager.lastConnectionError).font(.callout).foregroundColor(.red)
|
|
}
|
|
HStack {
|
|
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
|
.symbolRenderingMode(.hierarchical)
|
|
.imageScale(.large).foregroundColor(.red)
|
|
.padding(.trailing)
|
|
Text("No device connected").font(.title3)
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
.textCase(nil)
|
|
|
|
if !self.bleManager.isConnected {
|
|
Section(header: Text("Available Radios").font(.title)) {
|
|
ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.name > $1.name })) { peripheral in
|
|
HStack {
|
|
Image(systemName: "circle.fill")
|
|
.imageScale(.large).foregroundColor(.gray)
|
|
.padding(.trailing)
|
|
Button(action: {
|
|
self.bleManager.stopScanning()
|
|
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
|
|
|
self.bleManager.disconnectPeripheral()
|
|
}
|
|
self.bleManager.connectTo(peripheral: peripheral.peripheral)
|
|
if userSettings.preferredPeripheralId == peripheral.peripheral.identifier.uuidString {
|
|
|
|
isPreferredRadio = true
|
|
} else {
|
|
|
|
isPreferredRadio = false
|
|
}
|
|
}) {
|
|
Text(peripheral.name).font(.title3)
|
|
}
|
|
Spacer()
|
|
VStack {
|
|
SignalStrengthIndicator(signalStrength: peripheral.getSignalStrength())
|
|
}
|
|
}.padding([.bottom, .top])
|
|
}
|
|
}.textCase(nil)
|
|
}
|
|
|
|
} else {
|
|
Text("Bluetooth: OFF")
|
|
.foregroundColor(.red)
|
|
.font(.title)
|
|
}
|
|
}
|
|
|
|
HStack(alignment: .center) {
|
|
Spacer()
|
|
|
|
#if targetEnvironment(macCatalyst)
|
|
|
|
if bleManager.connectedPeripheral != nil {
|
|
|
|
Button(role: .destructive, action: {
|
|
|
|
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
|
bleManager.disconnectPeripheral()
|
|
isPreferredRadio = false
|
|
}
|
|
|
|
}) {
|
|
|
|
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
|
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.buttonBorderShape(.capsule)
|
|
.controlSize(.large)
|
|
.padding()
|
|
}
|
|
#endif
|
|
Spacer()
|
|
}
|
|
.padding(.bottom, 10)
|
|
}
|
|
.navigationTitle("Bluetooth")
|
|
.navigationBarItems(leading: MeshtasticLogo(), trailing:
|
|
ZStack {
|
|
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
|
})
|
|
}
|
|
.sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) {
|
|
InvalidVersion(minimumVersion: self.bleManager.minimumVersion, version: self.bleManager.connectedVersion)
|
|
.presentationDetents([.large])
|
|
.presentationDragIndicator(.automatic)
|
|
}
|
|
.onChange(of: (self.bleManager.invalidVersion)) { cv in
|
|
invalidFirmwareVersion = self.bleManager.invalidVersion
|
|
}
|
|
.onChange(of: (self.bleManager.isSubscribed)) { sub in
|
|
|
|
if userSettings.preferredNodeNum > 0 && sub {
|
|
|
|
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
|
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(userSettings.preferredNodeNum))
|
|
|
|
do {
|
|
|
|
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
|
// Found a node, check it for a region
|
|
if !fetchedNode.isEmpty {
|
|
node = fetchedNode[0]
|
|
if node!.loRaConfig != nil && node!.loRaConfig?.regionCode ?? 0 == RegionCodes.unset.rawValue {
|
|
isUnsetRegion = true
|
|
} else {
|
|
isUnsetRegion = false
|
|
}
|
|
}
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
}
|
|
.onAppear(perform: {
|
|
self.bleManager.context = context
|
|
self.bleManager.userSettings = userSettings
|
|
|
|
// Ask for notification permission
|
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
|
|
if success {
|
|
print("Notifications are all set!")
|
|
} else if let error = error {
|
|
print(error.localizedDescription)
|
|
}
|
|
}
|
|
if self.bleManager.connectedPeripheral != nil && userSettings.preferredPeripheralId == self.bleManager.connectedPeripheral.id {
|
|
isPreferredRadio = true
|
|
} else {
|
|
isPreferredRadio = false
|
|
}
|
|
})
|
|
}
|
|
func didDismissSheet() {
|
|
bleManager.disconnectPeripheral()
|
|
}
|
|
}
|