From 233fb0a1e060ddb596e36daf0d1c5c557d325502 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 25 May 2022 22:30:48 -0700 Subject: [PATCH] Display time only from mesh, remove all times set in the iOS app. Add Bitrate Format assorted things. Fix battery display on node details make node list more friendly on iPad and mac --- Meshtastic Client.xcodeproj/project.pbxproj | 4 ++ MeshtasticClient/Helpers/BLEManager.swift | 8 +++- MeshtasticClient/Model/PeripheralModel.swift | 4 +- .../Views/Bluetooth/Connect.swift | 8 +++- .../Views/Helpers/LastHeardText.swift | 22 ++++++++++ MeshtasticClient/Views/Nodes/NodeDetail.swift | 33 ++++++++++----- MeshtasticClient/Views/Nodes/NodeList.swift | 42 +++++++------------ 7 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 MeshtasticClient/Views/Helpers/LastHeardText.swift diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index c6e955ef..38d87e76 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -55,6 +55,7 @@ DDC2E17A26CE248F0042C5E4 /* MeshtasticClientUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E17926CE248F0042C5E4 /* MeshtasticClientUITests.swift */; }; DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */; }; DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */; }; + DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC3B273283F411B00AC321C /* LastHeardText.swift */; }; DDC4D568275499A500A4208E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4D567275499A500A4208E /* Persistence.swift */; }; DDCA31322826009C00207175 /* PassKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDCA31312826009C00207175 /* PassKit.framework */; }; DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF924C926FBB953009FE055 /* ConnectedDevice.swift */; }; @@ -133,6 +134,7 @@ DDC2E17B26CE248F0042C5E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = ""; }; + DDC3B273283F411B00AC321C /* LastHeardText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastHeardText.swift; sourceTree = ""; }; DDC4D567275499A500A4208E /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; DDCA31312826009C00207175 /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; DDF924C926FBB953009FE055 /* ConnectedDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectedDevice.swift; sourceTree = ""; }; @@ -356,6 +358,7 @@ DD47E3D826F3093800029299 /* MessageBubble.swift */, DD90860B26F684AF00DC5189 /* BatteryIcon.swift */, DDF924C926FBB953009FE055 /* ConnectedDevice.swift */, + DDC3B273283F411B00AC321C /* LastHeardText.swift */, ); path = Helpers; sourceTree = ""; @@ -574,6 +577,7 @@ C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, + DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */, DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */, DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */, diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index fec1ea2e..0ff0cb9d 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -176,7 +176,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph peripheralName = name } - 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) + let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, bitrate: nil, channelUtilization: nil, airTime: nil, lastUpdate: Date(), subscribed: false, peripheral: peripheral) let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id }) if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected { @@ -407,6 +407,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph myInfo.myNodeNum = Int64(decodedInfo.myInfo.myNodeNum) myInfo.hasGps = decodedInfo.myInfo.hasGps_p myInfo.bitrate = decodedInfo.myInfo.bitrate + self.connectedPeripheral.bitrate = myInfo.bitrate // Swift does strings weird, this does work to get the version without the github hash let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".") @@ -460,6 +461,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph fetchedMyInfo[0].myNodeNum = Int64(decodedInfo.myInfo.myNodeNum) fetchedMyInfo[0].hasGps = decodedInfo.myInfo.hasGps_p + fetchedMyInfo[0].bitrate = decodedInfo.myInfo.bitrate + let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1) var version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset:6, in: decodedInfo.myInfo.firmwareVersion))] version = version.dropLast() @@ -471,6 +474,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.connectedPeripheral.num = fetchedMyInfo[0].myNodeNum self.connectedPeripheral.firmwareVersion = fetchedMyInfo[0].firmwareVersion ?? "Unknown" self.connectedPeripheral.name = fetchedMyInfo[0].bleName ?? "Unknown" + self.connectedPeripheral.bitrate = fetchedMyInfo[0].bitrate + } do { @@ -812,7 +817,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // MARK: Incoming Packet from the POSITION_APP } else if decodedInfo.packet.decoded.portnum == PortNum.positionApp { - let fetchNodePositionRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from)) diff --git a/MeshtasticClient/Model/PeripheralModel.swift b/MeshtasticClient/Model/PeripheralModel.swift index 1b921104..18f00d6b 100644 --- a/MeshtasticClient/Model/PeripheralModel.swift +++ b/MeshtasticClient/Model/PeripheralModel.swift @@ -9,13 +9,14 @@ struct Peripheral: Identifiable { var longName: String var firmwareVersion: String var rssi: Int + var bitrate: Float? var channelUtilization: Float? var airTime: Float? var lastUpdate: Date var subscribed: Bool var peripheral: CBPeripheral - init(id: String, num: Int64, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, channelUtilization: Float?, airTime: Float?, lastUpdate: Date, subscribed: Bool, peripheral: CBPeripheral) { + init(id: String, num: Int64, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, bitrate: Float?, channelUtilization: Float?, airTime: Float?, lastUpdate: Date, subscribed: Bool, peripheral: CBPeripheral) { self.id = id self.num = num self.name = name @@ -23,6 +24,7 @@ struct Peripheral: Identifiable { self.longName = longName self.firmwareVersion = firmwareVersion self.rssi = rssi + self.bitrate = bitrate self.channelUtilization = channelUtilization self.airTime = airTime self.lastUpdate = lastUpdate diff --git a/MeshtasticClient/Views/Bluetooth/Connect.swift b/MeshtasticClient/Views/Bluetooth/Connect.swift index 49386672..84087dbe 100644 --- a/MeshtasticClient/Views/Bluetooth/Connect.swift +++ b/MeshtasticClient/Views/Bluetooth/Connect.swift @@ -74,9 +74,13 @@ struct Connect: View { if bleManager.connectedPeripheral != nil { Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.firmwareVersion) .font(.caption).foregroundColor(Color.gray) - Text("Channel Utilization: ").font(.caption)+Text(String(bleManager.connectedPeripheral.channelUtilization ?? 0.00)) + Text("Bitrate: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.bitrate ?? 0.00)) .font(.caption).foregroundColor(Color.gray) - Text("Air Time: ").font(.caption)+Text(String(bleManager.connectedPeripheral.airTime ?? 0.00)) + + + Text("Channel Utilization: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.channelUtilization ?? 0.00)) + .font(.caption).foregroundColor(Color.gray) + Text("Air Time: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.airTime ?? 0.00)) .font(.caption).foregroundColor(Color.gray) } if bleManager.connectedPeripheral.subscribed { diff --git a/MeshtasticClient/Views/Helpers/LastHeardText.swift b/MeshtasticClient/Views/Helpers/LastHeardText.swift new file mode 100644 index 00000000..c17a423d --- /dev/null +++ b/MeshtasticClient/Views/Helpers/LastHeardText.swift @@ -0,0 +1,22 @@ +import SwiftUI +// +// LastHeardText.swift +// Meshtastic Apple +// +// Created by Garth Vander Houwen on 5/25/22. +// +struct LastHeardText: View { + var lastHeard: Date? + let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) + + var body: some View { + if (lastHeard != nil && lastHeard! >= sixMonthsAgo!){ + + Text("Last Heard: \(lastHeard!, style: .relative) ago") + + } else { + + Text("Last Heard: Unknown Age") + } + } +} diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index e3bd2df1..637344a5 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -65,19 +65,19 @@ struct NodeDetail: View { ScrollView { - if node.lastHeard != nil { - HStack { + HStack { - Image(systemName: "clock.badge.checkmark.fill") - .font(.title) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Last Heard: \(node.lastHeard!, style: .relative) ago").font(.title3) - } - .padding() - Divider() + Image(systemName: "clock.badge.checkmark.fill") + .font(.title) + .foregroundColor(.accentColor) + .symbolRenderingMode(.hierarchical) + + LastHeardText(lastHeard: node.lastHeard).font(.title3) + } + .padding() + Divider() HStack { @@ -133,6 +133,15 @@ struct NodeDetail: View { BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .title, color: .accentColor) .padding(.bottom) + Text(String(mostRecent.batteryLevel) + "%") + .font(.title3) + .foregroundColor(.gray) + .fixedSize() + Text(String(format: "%.2f", mostRecent.voltage) + " V") + .font(.title3) + .foregroundColor(.gray) + .fixedSize() + } .padding(5) @@ -194,6 +203,10 @@ struct NodeDetail: View { Divider() ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in + if mappin.coordinate != nil { + + + } } // ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in diff --git a/MeshtasticClient/Views/Nodes/NodeList.swift b/MeshtasticClient/Views/Nodes/NodeList.swift index 3c043ea8..b608a3f3 100644 --- a/MeshtasticClient/Views/Nodes/NodeList.swift +++ b/MeshtasticClient/Views/Nodes/NodeList.swift @@ -65,11 +65,18 @@ struct NodeList: View { .padding(.bottom, 10) if connected { + HStack(alignment: .bottom) { Image(systemName: "repeat.circle.fill").font(.title3) .foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) - Text("Currently Connected").font(.title3).foregroundColor(Color.accentColor) + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + + Text("Currently Connected").font(.headline).foregroundColor(Color.accentColor) + } else { + + Text("Currently Connected").font(.title3).foregroundColor(Color.accentColor) + } } Spacer() } @@ -77,44 +84,23 @@ struct NodeList: View { HStack(alignment: .bottom) { Image(systemName: "clock.badge.checkmark.fill").font(.title3).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) - - if node.lastHeard != nil { - Text("Last Heard: \(node.lastHeard!, style: .relative) ago").font(.subheadline).foregroundColor(.gray) + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + + LastHeardText(lastHeard: node.lastHeard).font(.subheadline).foregroundColor(.gray) } else { - Text("Last Heard: Unknown").font(.subheadline).foregroundColor(.gray) + + LastHeardText(lastHeard: node.lastHeard).font(.title3).foregroundColor(.gray) } } } .padding([.leading, .top, .bottom]) } -// .swipeActions { -// -// Button { -// -// context.delete(node) -// -// do { -// -// try context.save() -// print("Successfully Deleted NodeInfoEntiy: \(node.num)") -// -// } catch { -// -// print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)") -// } -// -// } label: { -// -// Label("Delete from app", systemImage: "trash") -// } -// .tint(.red) -// } } } } .navigationTitle("All Nodes") .onAppear { - // self.nodes.returnsObjectsAsFaults = false + self.bleManager.context = context self.bleManager.userSettings = userSettings