From ebf9e8feec01daa7a3d21f497f843b91505607d4 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 18 Aug 2022 08:46:10 -0700 Subject: [PATCH] Initial BLE config view --- Meshtastic.xcodeproj/project.pbxproj | 8 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 2 +- .../contents | 231 ++++++++++++++++++ .../Settings/Config/BluetoothConfig.swift | 174 +++++++++++++ Meshtastic/Views/Settings/Settings.swift | 12 + 6 files changed, 426 insertions(+), 3 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents create mode 100644 Meshtastic/Views/Settings/Config/BluetoothConfig.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f8cf8487..03924cf8 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6D26ED19040058C060 /* Extensions.swift */; }; DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */; }; DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */; }; + DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; }; DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; }; DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; }; @@ -171,6 +172,8 @@ DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = telemetry.pb.swift; sourceTree = ""; }; DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 3.xcdatamodel"; sourceTree = ""; }; DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = ""; }; + DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = ""; }; + DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 7.xcdatamodel"; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; @@ -278,6 +281,7 @@ DD61937A2863876A00E59241 /* Config */ = { isa = PBXGroup; children = ( + DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */, DD41582528582E9B009B0E59 /* DeviceConfig.swift */, DD8EBF42285058FA00426DCA /* DisplayConfig.swift */, DD2553562855B02500E55709 /* LoRaConfig.swift */, @@ -664,6 +668,7 @@ DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */, DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */, DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */, + DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, DDC4D568275499A500A4208E /* Persistence.swift in Sources */, DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */, @@ -1102,6 +1107,7 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */, DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */, DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */, DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */, @@ -1109,7 +1115,7 @@ DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */; + currentVersion = DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 319888a5..bf45c105 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 6.xcdatamodel + MeshtasticDataModel v 7.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents index 6f9b7000..22353512 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents @@ -1,5 +1,5 @@ - + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents new file mode 100644 index 00000000..d58af5fd --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift new file mode 100644 index 00000000..1057f1eb --- /dev/null +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -0,0 +1,174 @@ +// +// BluetoothConfig.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 8/18/22. +// + +import SwiftUI + +enum BluetoothModes: Int, CaseIterable, Identifiable { + + case randomPin = 0 + case fixedPin = 1 + case noPin = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .randomPin: + return "Random" + case .fixedPin: + return "Fixed" + case .noPin: + return "None" + } + } + } + func protoEnumValue() -> Config.BluetoothConfig.PairingMode { + + switch self { + + case .randomPin: + return Config.BluetoothConfig.PairingMode.randomPin + case .fixedPin: + return Config.BluetoothConfig.PairingMode.fixedPin + case .noPin: + return Config.BluetoothConfig.PairingMode.noPin + } + } +} + +struct BluetoothConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + var node: NodeInfoEntity? + + @State private var isPresentingSaveConfirm: Bool = false + @State var initialLoad: Bool = true + @State var hasChanges = false + + @State var enabled = true + /// Determines the pairing strategy for the device + @State var mode = 0 + + /// Specified pin for PairingMode.FixedPin + @State var fixedPin = 123456 + + var body: some View { + + VStack { + + Form { + + Section(header: Text("Options")) { + + Toggle(isOn: $enabled) { + + Label("Enabled", systemImage: "antenna.radiowaves.left.and.right") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + + Picker("Pairing PIN", selection: $mode ) { + ForEach(BluetoothModes.allCases) { bm in + Text(bm.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + if mode == 2 { + + + } + } + } + .disabled(bleManager.connectedPeripheral == nil) + + Button { + + isPresentingSaveConfirm = true + + } label: { + + Label("Save", systemImage: "square.and.arrow.down") + } + .disabled(bleManager.connectedPeripheral == nil || !hasChanges) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + + "Are you sure?", + isPresented: $isPresentingSaveConfirm + ) { + Button("Save Bluetooth Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var bc = Config.BluetoothConfig() + bc.enabled = enabled + bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin + bc.fixedPin = UInt32(fixedPin) + + let adminMessageId = 0//bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!) + + if adminMessageId > 0 { + + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + + } else { + + } + } + } + } + .navigationTitle("Display Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + }) + .onAppear { + + if self.initialLoad{ + + self.bleManager.context = context + + self.enabled = node!.bluetoothConfig?.enabled ?? true + self.mode = Int(node!.bluetoothConfig?.mode ?? 0) + self.fixedPin = Int(node!.bluetoothConfig?.fixedPin ?? 123456) + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: enabled) { newEnabled in + + if node != nil && node!.bluetoothConfig != nil { + + if newEnabled != node!.bluetoothConfig!.enabled { hasChanges = true } + } + } + .onChange(of: mode) { newMode in + + if node != nil && node!.bluetoothConfig != nil { + + if newMode != node!.bluetoothConfig!.mode { hasChanges = true } + } + } + .onChange(of: fixedPin) { newFixedPin in + + if node != nil && node!.bluetoothConfig != nil { + + if newFixedPin != node!.bluetoothConfig!.fixedPin { hasChanges = true } + } + } + + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index e1887074..db8d7702 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -69,6 +69,18 @@ struct Settings: View { } .disabled(bleManager.connectedPeripheral == nil) + NavigationLink() { + + BluetoothConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + } label: { + + Image(systemName: "antenna.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) + + Text("Bluetooth (BLE)") + } + .disabled(bleManager.connectedPeripheral == nil) + NavigationLink { DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) } label: {