From ebf243f203ff794b37ed20f950202b13c020daa9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 23 Sep 2021 20:10:53 -0700 Subject: [PATCH] myInfo data for both peripheral and node info model fixed data loading and cleaned up models --- Meshtastic Client.xcodeproj/project.pbxproj | 4 ++ MeshtasticClient/Helpers/BLEManager.swift | 38 ++++++------ MeshtasticClient/Model/NodeInfoModel.swift | 2 +- MeshtasticClient/Model/PeripheralModel.swift | 16 +++++ .../Views/Bluetooth/Connect.swift | 20 +++++- MeshtasticClient/Views/Nodes/NodeDetail.swift | 36 +++++------ MeshtasticClient/Views/Nodes/NodeList.swift | 61 +++++++++++++------ MeshtasticClient/Views/Nodes/NodeMap.swift | 10 ++- MeshtasticClient/Views/Nodes/NodeRow.swift | 1 - 9 files changed, 127 insertions(+), 61 deletions(-) create mode 100644 MeshtasticClient/Model/PeripheralModel.swift diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 93a2b806..a1b15b8f 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; DD47E3CC26F0E51D00029299 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CB26F0E51D00029299 /* NodeDetail.swift */; }; DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; }; DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CF26F1073F00029299 /* NodeRow.swift */; }; @@ -65,6 +66,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; DD47E3CB26F0E51D00029299 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = ""; }; DD47E3CF26F1073F00029299 /* NodeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeRow.swift; sourceTree = ""; }; @@ -265,6 +267,7 @@ DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */, DD836AF026F8613500ABCC23 /* Color.swift */, DDF924CC26FCC97E009FE055 /* PacketModel.swift */, + DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */, ); path = Model; sourceTree = ""; @@ -465,6 +468,7 @@ DDF924CD26FCC97E009FE055 /* PacketModel.swift in Sources */, DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */, DD47E3DF26F39D9F00029299 /* MyInfoModel.swift in Sources */, + DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */, DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index c68b96c8..249ec849 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -3,20 +3,6 @@ import CoreData import CoreBluetooth import SwiftUI -final class Peripheral: Identifiable, ObservableObject { - @Published var id: String - @Published var index: Int - @Published var name: String - @Published var rssi: Int - - init(id: String, index: Int, name: String, rssi: Int) { - self.id = id - self.index = index - self.name = name - self.rssi = rssi - } -} - //--------------------------------------------------------------------------------------- // Meshtastic BLE Device Manager //--------------------------------------------------------------------------------------- @@ -27,7 +13,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph private var centralManager: CBCentralManager! @Published var connectedPeripheral: CBPeripheral! @Published var peripheralArray = [CBPeripheral]() - @Published var connectedNodeInfo: MyInfoModel! + @Published var connectedNodeInfo: Peripheral! + @Published var connectedNode: NodeInfoModel! //private var rssiArray = [NSNumber]() private var timer = Timer() @Published var isSwitchedOn = false @@ -89,6 +76,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph //--------------------------------------------------------------------------------------- func connectToDevice(id: String) { connectedPeripheral = peripheralArray.filter({ $0.identifier.uuidString == id }).first + connectedNodeInfo = Peripheral(id: connectedPeripheral.identifier.uuidString, name: connectedPeripheral.name ?? "Unknown", rssi: 0, myInfo: nil) self.centralManager?.connect(connectedPeripheral!) } @@ -148,7 +136,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } } - let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, index: peripherals.count, name: peripheralName, rssi: RSSI.intValue) + let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, name: peripheralName, rssi: RSSI.intValue, myInfo: nil) //print(newPeripheral) peripherals.append(newPeripheral) } @@ -260,11 +248,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if decodedInfo.myInfo.myNodeNum != 0 { - meshData.load() print("Save a myInfo") do { print(try decodedInfo.myInfo.jsonString()) - connectedNodeInfo = MyInfoModel( + + // Create a MyInfoModel + let myInfoModel = MyInfoModel( myNodeNum: decodedInfo.myInfo.myNodeNum, hasGps: decodedInfo.myInfo.hasGps_p, numBands: decodedInfo.myInfo.numBands, @@ -273,7 +262,18 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph rebootCount: decodedInfo.myInfo.rebootCount, messageTimeoutMsec: decodedInfo.myInfo.messageTimeoutMsec, minAppVersion: decodedInfo.myInfo.minAppVersion) - + // Save it to the connected nodeInfo + connectedNodeInfo.myInfo = myInfoModel + // Save it to the connected node + connectedNode = meshData.nodes.first(where: {$0.id == decodedInfo.myInfo.myNodeNum}) + if connectedNode != nil { + connectedNode.myInfo = myInfoModel + let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.myInfo.myNodeNum }) + meshData.nodes.remove(at: nodeIndex!) + meshData.nodes.append(connectedNode) + } + meshData.save() + } catch { fatalError("Failed to decode json") } diff --git a/MeshtasticClient/Model/NodeInfoModel.swift b/MeshtasticClient/Model/NodeInfoModel.swift index c27ae075..2acfb0fe 100644 --- a/MeshtasticClient/Model/NodeInfoModel.swift +++ b/MeshtasticClient/Model/NodeInfoModel.swift @@ -8,7 +8,7 @@ struct NodeInfoModel: Identifiable, Codable { var id: UInt32 var num: UInt32 - + var myInfo: MyInfoModel? var user: User struct User: Identifiable, Codable { var id: String diff --git a/MeshtasticClient/Model/PeripheralModel.swift b/MeshtasticClient/Model/PeripheralModel.swift new file mode 100644 index 00000000..251e4b8e --- /dev/null +++ b/MeshtasticClient/Model/PeripheralModel.swift @@ -0,0 +1,16 @@ +import Foundation + +final class Peripheral: Identifiable { + var id: String + var name: String + var rssi: Int + + var myInfo: MyInfoModel? + + init(id: String, name: String, rssi: Int, myInfo: MyInfoModel?) { + self.id = id + self.name = name + self.rssi = rssi + self.myInfo = myInfo + } +} diff --git a/MeshtasticClient/Views/Bluetooth/Connect.swift b/MeshtasticClient/Views/Bluetooth/Connect.swift index ca34e285..309bf35e 100644 --- a/MeshtasticClient/Views/Bluetooth/Connect.swift +++ b/MeshtasticClient/Views/Bluetooth/Connect.swift @@ -27,12 +27,28 @@ struct Connect: View { List { Section(header: Text("Connected Device").font(.title)) { if(bleManager.connectedPeripheral != nil){ - HStack{ + HStack { Image(systemName: "antenna.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical) .imageScale(.large).foregroundColor(.green) .padding(.trailing) - Text((bleManager.connectedPeripheral.name != nil) ? bleManager.connectedPeripheral.name! : "Unknown").font(.title2) + + if bleManager.connectedNodeInfo.myInfo != nil { + VStack (alignment: .leading) { + if bleManager.connectedNode != nil { + + Text(bleManager.connectedNode.user.longName).font(.title2) + } + else { + Text(String(bleManager.connectedNodeInfo.myInfo?.myNodeNum ?? 0)).font(.title2) + + } + Text("FW Version: ").font(.caption)+Text(bleManager.connectedNodeInfo.myInfo?.firmwareVersion ?? "(null)").font(.caption).foregroundColor(Color.gray) + } + } + else { + Text((bleManager.connectedPeripheral.name != nil) ? bleManager.connectedPeripheral.name! : "Unknown").font(.title2) + } } .padding() .swipeActions { diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index 9a466824..bed77362 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -54,22 +54,10 @@ struct NodeDetail: View { } ScrollView { - HStack { - - Image(node.user.hwModel) - .resizable() - .frame(width:70, height: 70) - .cornerRadius(5) - - Text("Model: " + String(node.user.hwModel)) - .font(.title) - } - .padding() - Divider() HStack { VStack(alignment: .center) { - Text("AKA").font(.title3) + Text("AKA").font(.title2) CircleText(text: node.user.shortName, color: Color.blue) .offset(y:10) } @@ -78,25 +66,37 @@ struct NodeDetail: View { VStack(alignment: .center) { Image(systemName: "waveform.path") - .font(.title2) + .font(.title) .foregroundColor(.blue) .symbolRenderingMode(.hierarchical) - Text("SNR").font(.title3) + Text("SNR").font(.title2) Text(String(node.snr ?? 0)) - .font(.title3) + .font(.title2) .foregroundColor(.gray) } Divider() VStack(alignment: .center) { BatteryIcon(batteryLevel: node.position.batteryLevel, font: .title, color: Color.blue) - Text("Battery").font(.title3) + Text("Battery").font(.title2) Text(String(node.position.batteryLevel!) + "%") - .font(.title3) + .font(.title2) .foregroundColor(.gray) .symbolRenderingMode(.hierarchical) } }.padding(4) Divider() + HStack { + + Image(node.user.hwModel) + .resizable() + .frame(width:60, height: 60) + .cornerRadius(5) + + Text("Model: " + String(node.user.hwModel)) + .font(.title3) + } + .padding() + Divider() HStack{ Image(systemName: "clock").font(.title2).foregroundColor(.blue) diff --git a/MeshtasticClient/Views/Nodes/NodeList.swift b/MeshtasticClient/Views/Nodes/NodeList.swift index a31d6bf7..5ff77027 100644 --- a/MeshtasticClient/Views/Nodes/NodeList.swift +++ b/MeshtasticClient/Views/Nodes/NodeList.swift @@ -6,7 +6,7 @@ // // Abstract: -// A view showing a list of devices that have been seen on the mesh network +// A view showing a list of devices that have been seen on the mesh network from the perspective of the connected device. import SwiftUI @@ -26,30 +26,57 @@ struct NodeList: View { NavigationView { List { - Toggle(isOn: $showLocationOnly) { - Text("Nodes with location only") + + if meshData.nodes.count == 0 { + Text("Scan for Radios").font(.largeTitle).listRowSeparator(.hidden) + Text("No LoRa Mesh Nodes Found").font(.title2).listRowSeparator(.hidden) + Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.") + .font(.body) + .listRowSeparator(.hidden) + Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.") + .listRowSeparator(.hidden) + Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.") + .listRowSeparator(.hidden) + Spacer().listRowSeparator(.hidden) + //Button(action: {}) { + // Text("Get Started") + // .font(.title) + // .frame(maxWidth: 300) + //} + //.buttonStyle(.borderedProminent) + //.buttonBorderShape(.automatic) + //.controlSize(.large) } - ForEach(filteredDevices.sorted(by: { $0.lastHeard > $1.lastHeard })) { node in - NavigationLink(destination: NodeDetail(node: node)) { - NodeRow(node: node, index : 0) - + else { + Toggle(isOn: $showLocationOnly) { + Text("Nodes with location only") } - .swipeActions { - Button { - let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == node.id }) - meshData.nodes.remove(at: nodeIndex!) - meshData.save() - } label: { - VStack { - Label("Delete from app", systemImage: "trash") - } + ForEach(filteredDevices.sorted(by: { $0.lastHeard > $1.lastHeard })) { node in + NavigationLink(destination: NodeDetail(node: node)) { + NodeRow(node: node, index : 0) + + } + .swipeActions { + Button { + let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == node.id }) + meshData.nodes.remove(at: nodeIndex!) + meshData.save() + } label: { + VStack { + Label("Delete from app", systemImage: "trash") + } + } + .tint(.red) } - .tint(.red) } } } .navigationTitle("All Nodes") } + .ignoresSafeArea(.all, edges: [.leading, .trailing]) + .onAppear{ + meshData.load() + } } } diff --git a/MeshtasticClient/Views/Nodes/NodeMap.swift b/MeshtasticClient/Views/Nodes/NodeMap.swift index 30c4b19f..cc2af207 100644 --- a/MeshtasticClient/Views/Nodes/NodeMap.swift +++ b/MeshtasticClient/Views/Nodes/NodeMap.swift @@ -33,7 +33,7 @@ struct NodeMap: View { let currentCoordinatePosition = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude) let regionBinding = Binding( get: { - MKCoordinateRegion(center: currentCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.09, longitudeDelta: 0.09)) + MKCoordinateRegion(center: currentCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.0359, longitudeDelta: 0.0359)) }, set: { _ in } ) @@ -52,11 +52,15 @@ struct NodeMap: View { CircleText(text: location.user.shortName, color: Color.blue) } ) - }.frame(maxHeight:.infinity) + } + .frame(maxHeight:.infinity) + .ignoresSafeArea(.all, edges: [.leading, .trailing]) } .navigationTitle("Mesh Map") .navigationBarTitleDisplayMode(.inline) - }.navigationViewStyle(StackNavigationViewStyle()) + } + .navigationViewStyle(StackNavigationViewStyle()) + } } diff --git a/MeshtasticClient/Views/Nodes/NodeRow.swift b/MeshtasticClient/Views/Nodes/NodeRow.swift index 2cdfccbe..831ad79b 100644 --- a/MeshtasticClient/Views/Nodes/NodeRow.swift +++ b/MeshtasticClient/Views/Nodes/NodeRow.swift @@ -19,7 +19,6 @@ struct NodeRow: View { Text(lastHeard, style: .relative).font(.caption).foregroundColor(.gray) } }.padding([.leading, .top, .bottom]) - } }