mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #437 from meshtastic/2.2.16_Working_Changes
2.2.16 working changes
This commit is contained in:
commit
83d9ec66ef
12 changed files with 177 additions and 52 deletions
|
|
@ -21,6 +21,7 @@
|
|||
DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1925B828CDA93900720036 /* SerialConfigEnums.swift */; };
|
||||
DD1933762B0835D500771CD5 /* PositionAltitudeChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1933752B0835D500771CD5 /* PositionAltitudeChart.swift */; };
|
||||
DD1933782B084F4200771CD5 /* Measurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1933772B084F4200771CD5 /* Measurement.swift */; };
|
||||
DD1B8F402B35E2F10022AABC /* GPSStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */; };
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; };
|
||||
DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2160AE28C5552500C17253 /* MQTTConfig.swift */; };
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; };
|
||||
|
|
@ -235,6 +236,7 @@
|
|||
DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = "<group>"; };
|
||||
DD1933752B0835D500771CD5 /* PositionAltitudeChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAltitudeChart.swift; sourceTree = "<group>"; };
|
||||
DD1933772B084F4200771CD5 /* Measurement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Measurement.swift; sourceTree = "<group>"; };
|
||||
DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPSStatus.swift; sourceTree = "<group>"; };
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
|
||||
DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = "<group>"; };
|
||||
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -552,6 +554,7 @@
|
|||
DD3CC6B428E33FD100FA9159 /* ShareChannels.swift */,
|
||||
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */,
|
||||
DD61937A2863876A00E59241 /* Config */,
|
||||
DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1222,6 +1225,7 @@
|
|||
DDB75A112A059258006ED576 /* Url.swift in Sources */,
|
||||
DDAD49ED2AFB39DC00B4425D /* MeshMap.swift in Sources */,
|
||||
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */,
|
||||
DD1B8F402B35E2F10022AABC /* GPSStatus.swift in Sources */,
|
||||
DD8ED9C52898D51F00B3B0AB /* NetworkConfig.swift in Sources */,
|
||||
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */,
|
||||
DDDE5A1029AFE69700490C6C /* MeshActivityAttributes.swift in Sources */,
|
||||
|
|
@ -1482,7 +1486,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.15;
|
||||
MARKETING_VERSION = 2.2.16;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1516,7 +1520,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.15;
|
||||
MARKETING_VERSION = 2.2.16;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1638,7 +1642,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.15;
|
||||
MARKETING_VERSION = 2.2.16;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1671,7 +1675,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.15;
|
||||
MARKETING_VERSION = 2.2.16;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
|||
|
|
@ -14,13 +14,11 @@ extension NodeInfoEntity {
|
|||
}
|
||||
|
||||
var hasDeviceMetrics: Bool {
|
||||
|
||||
let deviceMetrics = telemetries?.filter{ ($0 as AnyObject).metricsType == 0 }
|
||||
return deviceMetrics?.count ?? 0 > 0
|
||||
}
|
||||
|
||||
var hasEnvironmentMetrics: Bool {
|
||||
|
||||
let environmentMetrics = telemetries?.filter{ ($0 as AnyObject).metricsType == 1 }
|
||||
return environmentMetrics?.count ?? 0 > 0
|
||||
}
|
||||
|
|
@ -28,8 +26,11 @@ extension NodeInfoEntity {
|
|||
return user?.sensorMessageList.count ?? 0 > 0
|
||||
}
|
||||
|
||||
var hasTraceRoutes: Bool {
|
||||
return traceRoutes?.count ?? 0 > 0
|
||||
}
|
||||
|
||||
var isOnline: Bool {
|
||||
|
||||
let fifteenMinutesAgo = Calendar.current.date(byAdding: .minute, value: -15, to: Date())
|
||||
if lastHeard?.compare(fifteenMinutesAgo!) == .orderedDescending {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -734,6 +734,19 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].storeForwardConfig?.enabled == true {
|
||||
wantStoreAndForwardPackets = true;
|
||||
}
|
||||
if fetchedNodeInfo.count == 1 {
|
||||
if !(fetchedNodeInfo[0].user?.vip ?? false) {
|
||||
fetchedNodeInfo[0].user?.vip = true
|
||||
do {
|
||||
try context!.save()
|
||||
|
||||
} catch {
|
||||
context!.rollback()
|
||||
let nsError = error as NSError
|
||||
print("💥 Core Data error. Error: \(nsError)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("Failed to find a node info for the connected node")
|
||||
|
|
@ -1311,6 +1324,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected{
|
||||
do {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
context!.delete(node.user!)
|
||||
context!.delete(node)
|
||||
try context!.save()
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import CoreLocation
|
|||
self.manager = CLLocationManager() // Creating a location manager instance is safe to call here in `MainActor`.
|
||||
locationsArray = [CLLocation]()
|
||||
enableSmartPosition = true
|
||||
self.manager.distanceFilter = 5
|
||||
}
|
||||
|
||||
func startLocationUpdates() {
|
||||
|
|
@ -66,7 +65,7 @@ import CoreLocation
|
|||
locationAdded = true
|
||||
}
|
||||
if !locationAdded {
|
||||
print("Bad Location \(self.count): \(loc)")
|
||||
//print("Bad Location \(self.count): \(loc)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,12 +84,15 @@ import CoreLocation
|
|||
func addLocation(_ location: CLLocation) -> Bool {
|
||||
let age = -location.timestamp.timeIntervalSinceNow
|
||||
if age > 10 {
|
||||
print("Bad Location \(self.count): Too Old \(age) seconds ago \(location)")
|
||||
return false
|
||||
}
|
||||
if location.horizontalAccuracy < 0 {
|
||||
print("Bad Location \(self.count): Horizontal Accuracy: \(location.horizontalAccuracy) \(location)")
|
||||
return false
|
||||
}
|
||||
if location.horizontalAccuracy > 100 {
|
||||
print("Bad Location \(self.count): Horizontal Accuracy: \(location.horizontalAccuracy) \(location)")
|
||||
return false
|
||||
}
|
||||
locationsArray.append(location)
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newUser.shortName = nodeInfo.user.shortName
|
||||
newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
newUser.isLicensed = nodeInfo.user.isLicensed
|
||||
newUser.role = Int32(nodeInfo.user.role.rawValue)
|
||||
newNode.user = newUser
|
||||
} else {
|
||||
let newUser = UserEntity(context: context)
|
||||
|
|
@ -337,6 +338,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
fetchedNode[0].user!.longName = nodeInfo.user.longName
|
||||
fetchedNode[0].user!.shortName = nodeInfo.user.shortName
|
||||
fetchedNode[0].user!.isLicensed = nodeInfo.user.isLicensed
|
||||
fetchedNode[0].user!.role = Int32(nodeInfo.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
} else {
|
||||
if (fetchedNode[0].user == nil) {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ import TipKit
|
|||
|
||||
@main
|
||||
struct MeshtasticAppleApp: App {
|
||||
|
||||
@UIApplicationDelegateAdaptor(MeshtasticAppDelegate.self) var appDelegate
|
||||
let persistenceController = PersistenceController.shared
|
||||
@ObservedObject private var bleManager: BLEManager = BLEManager()
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
|
||||
@State var saveChannels = false
|
||||
|
|
|
|||
|
|
@ -156,7 +156,9 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
// Update an existing node
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
if packet.rxTime > 0 {
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
}
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].rssi = packet.rxRssi
|
||||
|
||||
|
|
@ -268,7 +270,11 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
mutablePositions.add(position)
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
if positionMessage.time > 0 {
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
} else if packet.rxTime > 0 {
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
}
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].rssi = packet.rxRssi
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
|
|
@ -594,6 +600,8 @@ func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNu
|
|||
newPositionConfig.broadcastSmartMinimumIntervalSecs = Int32(config.broadcastSmartMinimumIntervalSecs)
|
||||
newPositionConfig.broadcastSmartMinimumDistance = Int32(config.broadcastSmartMinimumDistance)
|
||||
newPositionConfig.positionFlags = Int32(config.positionFlags)
|
||||
newPositionConfig.gpsAttemptTime = 900
|
||||
newPositionConfig.gpsUpdateInterval = 120
|
||||
fetchedNode[0].positionConfig = newPositionConfig
|
||||
} else {
|
||||
fetchedNode[0].positionConfig?.smartPositionEnabled = config.positionBroadcastSmartEnabled
|
||||
|
|
@ -605,6 +613,8 @@ func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNu
|
|||
fetchedNode[0].positionConfig?.positionBroadcastSeconds = Int32(config.positionBroadcastSecs)
|
||||
fetchedNode[0].positionConfig?.broadcastSmartMinimumIntervalSecs = Int32(config.broadcastSmartMinimumIntervalSecs)
|
||||
fetchedNode[0].positionConfig?.broadcastSmartMinimumDistance = Int32(config.broadcastSmartMinimumDistance)
|
||||
fetchedNode[0].positionConfig?.gpsAttemptTime = 900
|
||||
fetchedNode[0].positionConfig?.gpsUpdateInterval = 120
|
||||
fetchedNode[0].positionConfig?.positionFlags = Int32(config.positionFlags)
|
||||
}
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,14 @@ struct NodeListItem: View {
|
|||
LastHeardText(lastHeard: node.lastHeard)
|
||||
.font(.callout)
|
||||
}
|
||||
HStack {
|
||||
let role = DeviceRoles(rawValue: Int(node.user?.role ?? 0))
|
||||
Image(systemName: role?.systemName ?? "figure")
|
||||
.font(.callout)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Role: \(role?.name ?? "unknown".localized)")
|
||||
.font(.callout)
|
||||
}
|
||||
if node.positions?.count ?? 0 > 0 && connectedNode != node.num {
|
||||
HStack {
|
||||
let lastPostion = node.positions!.reversed()[0] as! PositionEntity
|
||||
|
|
@ -89,7 +97,6 @@ struct NodeListItem: View {
|
|||
.font(.callout)
|
||||
}
|
||||
}
|
||||
|
||||
if !connected {
|
||||
HStack {
|
||||
let preset = ModemPresets(rawValue: Int(modemPreset))
|
||||
|
|
@ -116,6 +123,11 @@ struct NodeListItem: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.callout)
|
||||
}
|
||||
if node.hasTraceRoutes {
|
||||
Image(systemName: "signpost.right.and.left")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
.padding(.top, 3)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ struct NodeList: View {
|
|||
@State private var columnVisibility = NavigationSplitViewVisibility.all
|
||||
@State private var selectedNode: NodeInfoEntity?
|
||||
@State private var isPresentingTraceRouteSentAlert = false
|
||||
@State private var isPresentingDeleteNodeAlert = false
|
||||
@State private var deleteNodeId: Int64 = 0
|
||||
|
||||
@SceneStorage("selectedDetailView") var selectedDetailView: String?
|
||||
|
||||
|
|
@ -85,10 +87,8 @@ struct NodeList: View {
|
|||
}
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
Button (role: .destructive) {
|
||||
let success = bleManager.removeNode(node: node, connectedNodeNum: Int64(connectedNodeNum))
|
||||
if !success {
|
||||
print("Failed to delete node \(node.user?.longName ?? "unknown".localized)")
|
||||
}
|
||||
deleteNodeId = node.num
|
||||
isPresentingDeleteNodeAlert = true
|
||||
} label: {
|
||||
Label("Delete Node", systemImage: "trash")
|
||||
}
|
||||
|
|
@ -107,7 +107,25 @@ struct NodeList: View {
|
|||
.searchable(text: nodesQuery, prompt: "Find a node")
|
||||
.navigationTitle(String.localizedStringWithFormat("nodes %@".localized, String(nodes.count)))
|
||||
.listStyle(.plain)
|
||||
|
||||
.confirmationDialog(
|
||||
|
||||
"are.you.sure",
|
||||
isPresented: $isPresentingDeleteNodeAlert,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete Node") {
|
||||
let deleteNode = getNodeInfo(id: deleteNodeId, context: context)
|
||||
if connectedNode != nil {
|
||||
|
||||
}
|
||||
if deleteNode != nil {
|
||||
let success = bleManager.removeNode(node: deleteNode!, connectedNodeNum: Int64(connectedNodeNum))
|
||||
if !success {
|
||||
print("Failed to delete node \(deleteNode?.user?.longName ?? "unknown".localized)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationSplitViewColumnWidth(min: 100, ideal: 250, max: 500)
|
||||
.navigationBarItems(leading:
|
||||
MeshtasticLogo(),
|
||||
|
|
|
|||
|
|
@ -46,9 +46,14 @@ struct TraceRouteLog: View {
|
|||
VStack {
|
||||
if selectedRoute != nil {
|
||||
if selectedRoute?.response ?? false && selectedRoute?.hops?.count ?? 0 > 0 {
|
||||
Text("Received by \(selectedRoute?.node?.user?.longName ?? "unknown".localized)")
|
||||
Text("Route: \(selectedRoute?.routeText ?? "unknown".localized)")
|
||||
.font(.title3)
|
||||
|
||||
Label {
|
||||
Text("Route: \(selectedRoute?.routeText ?? "unknown".localized)")
|
||||
} icon: {
|
||||
Image(systemName: "signpost.right.and.left")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
.font(.title2)
|
||||
} else if selectedRoute?.response ?? false {
|
||||
Label {
|
||||
Text("Trace route received directly by \(selectedRoute?.node?.user?.longName ?? "unknown".localized)")
|
||||
|
|
@ -56,7 +61,7 @@ struct TraceRouteLog: View {
|
|||
Image(systemName: "signpost.right.and.left")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
.font(.title3)
|
||||
.font(.title2)
|
||||
}
|
||||
|
||||
let hopsArray = selectedRoute?.hops?.array as? [TraceRouteHopEntity] ?? []
|
||||
|
|
@ -128,15 +133,6 @@ struct TraceRouteLog: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
.font(.title3)
|
||||
Divider()
|
||||
Label {
|
||||
Text("\(selectedRoute?.time?.formatted() ?? "") - No response")
|
||||
|
||||
} icon: {
|
||||
Image(systemName: "person.slash")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
.font(.callout)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,29 +32,33 @@ struct AppSettings: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
Section(header: Text("phone.gps")) {
|
||||
let accuracy = Measurement(value: locationHelper.locationManager.location?.horizontalAccuracy ?? 300, unit: UnitLength.meters)
|
||||
let altitiude = Measurement(value: locationHelper.locationManager.location?.altitude ?? 0, unit: UnitLength.meters)
|
||||
let speed = Measurement(value: locationHelper.locationManager.location?.speed ?? 0, unit: UnitSpeed.kilometersPerHour)
|
||||
HStack {
|
||||
Label("Accuracy \(accuracy.formatted())", systemImage: "scope")
|
||||
.font(.footnote)
|
||||
Label("Sats \(LocationHelper.satsInView)", systemImage: "sparkles")
|
||||
.font(.footnote)
|
||||
}
|
||||
Label("Coordinate \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.latitude ?? 0)), \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.longitude ?? 0))", systemImage: "mappin")
|
||||
.font(.footnote)
|
||||
.textSelection(.enabled)
|
||||
if locationHelper.locationManager.location?.verticalAccuracy ?? 0 > 0 {
|
||||
Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2")
|
||||
.font(.footnote)
|
||||
}
|
||||
if locationHelper.locationManager.location?.courseAccuracy ?? 0 > 0 {
|
||||
Label("Heading \(String(format: "%.2f", locationHelper.locationManager.location?.course ?? 0))°", systemImage: "location.circle")
|
||||
.font(.footnote)
|
||||
}
|
||||
if locationHelper.locationManager.location?.speedAccuracy ?? 0 > 0 {
|
||||
Label("Speed \(speed.formatted())", systemImage: "speedometer")
|
||||
if #available(iOS 17.0, macOS 14.0, *) {
|
||||
GPSStatus()
|
||||
} else {
|
||||
let accuracy = Measurement(value: locationHelper.locationManager.location?.horizontalAccuracy ?? 300, unit: UnitLength.meters)
|
||||
let altitiude = Measurement(value: locationHelper.locationManager.location?.altitude ?? 0, unit: UnitLength.meters)
|
||||
let speed = Measurement(value: locationHelper.locationManager.location?.speed ?? 0, unit: UnitSpeed.kilometersPerHour)
|
||||
HStack {
|
||||
Label("Accuracy \(accuracy.formatted())", systemImage: "scope")
|
||||
.font(.footnote)
|
||||
Label("Sats \(LocationHelper.satsInView)", systemImage: "sparkles")
|
||||
.font(.footnote)
|
||||
}
|
||||
Label("Coordinate \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.latitude ?? 0)), \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.longitude ?? 0))", systemImage: "mappin")
|
||||
.font(.footnote)
|
||||
.textSelection(.enabled)
|
||||
if locationHelper.locationManager.location?.verticalAccuracy ?? 0 > 0 {
|
||||
Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2")
|
||||
.font(.footnote)
|
||||
}
|
||||
if locationHelper.locationManager.location?.courseAccuracy ?? 0 > 0 {
|
||||
Label("Heading \(String(format: "%.2f", locationHelper.locationManager.location?.course ?? 0))°", systemImage: "location.circle")
|
||||
.font(.footnote)
|
||||
}
|
||||
if locationHelper.locationManager.location?.speedAccuracy ?? 0 > 0 {
|
||||
Label("Speed \(speed.formatted())", systemImage: "speedometer")
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
Section(header: Text("Location Settings")) {
|
||||
|
|
|
|||
60
Meshtastic/Views/Settings/GPSStatus.swift
Normal file
60
Meshtastic/Views/Settings/GPSStatus.swift
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// GPSStatus.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) by Garth Vander Houwen 12/22/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreLocation
|
||||
|
||||
@available(iOS 17.0, macOS 14.0, *)
|
||||
struct GPSStatus: View {
|
||||
|
||||
@ObservedObject var locationsHandler: LocationsHandler = LocationsHandler.shared
|
||||
var body: some View {
|
||||
let horizontalAccuracy = Measurement(value: locationsHandler.lastLocation.horizontalAccuracy, unit: UnitLength.meters)
|
||||
let verticalAccuracy = Measurement(value: locationsHandler.lastLocation.verticalAccuracy, unit: UnitLength.meters)
|
||||
let altitiude = Measurement(value: locationsHandler.lastLocation.altitude, unit: UnitLength.meters)
|
||||
let speed = Measurement(value: locationsHandler.lastLocation.speed, unit: UnitSpeed.kilometersPerHour)
|
||||
let speedAccuracy = Measurement(value: locationsHandler.lastLocation.speedAccuracy, unit: UnitSpeed.metersPerSecond)
|
||||
let courseAccuracy = Measurement(value: locationsHandler.lastLocation.courseAccuracy, unit: UnitAngle.degrees)
|
||||
Label("Coordinate \(String(format: "%.5f", locationsHandler.lastLocation.coordinate.latitude)), \(String(format: "%.5f", LocationsHandler.shared.lastLocation.coordinate.longitude))", systemImage: "mappin")
|
||||
.font(.footnote)
|
||||
.textSelection(.enabled)
|
||||
HStack {
|
||||
Label("Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope")
|
||||
.font(.footnote)
|
||||
Label("Sats Estimate \(LocationsHandler.satsInView)", systemImage: "sparkles")
|
||||
.font(.footnote)
|
||||
}
|
||||
HStack {
|
||||
if locationsHandler.lastLocation.verticalAccuracy > 0 {
|
||||
Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2")
|
||||
.font(.footnote)
|
||||
}
|
||||
Label("Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical")
|
||||
.font(.caption2)
|
||||
}
|
||||
HStack {
|
||||
let degrees = Angle.degrees(LocationsHandler.shared.lastLocation.course)
|
||||
Label {
|
||||
let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees)
|
||||
Text("Heading: \(heading.formatted())")
|
||||
} icon: {
|
||||
Image(systemName: "location.north")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.rotationEffect(degrees)
|
||||
}
|
||||
.font(.footnote)
|
||||
Label("Accuracy \(courseAccuracy.formatted())", systemImage: "safari")
|
||||
.font(.caption2)
|
||||
}
|
||||
HStack {
|
||||
Label("Speed \(speed.formatted())", systemImage: "speedometer")
|
||||
.font(.footnote)
|
||||
Label("Accuracy \(speedAccuracy.formatted())", systemImage: "gauge.with.dots.needle.bottom.50percent.badge.plus")
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue