Merge pull request #437 from meshtastic/2.2.16_Working_Changes

2.2.16 working changes
This commit is contained in:
Garth Vander Houwen 2023-12-22 19:10:18 -08:00 committed by GitHub
commit 83d9ec66ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 177 additions and 52 deletions

View file

@ -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 = "";

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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

View file

@ -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 {

View file

@ -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)
}

View file

@ -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(),

View file

@ -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()
}
}

View file

@ -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")) {

View 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)
}
}
}