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