diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 2d46d94e..86eb6681 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1578,7 +1578,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.3.5; + MARKETING_VERSION = 2.3.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1612,7 +1612,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.3.5; + MARKETING_VERSION = 2.3.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1685,7 +1685,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.3.5; + MARKETING_VERSION = 2.3.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1718,7 +1718,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.3.5; + MARKETING_VERSION = 2.3.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Meshtastic/Extensions/UserDefaults.swift b/Meshtastic/Extensions/UserDefaults.swift index 1a3e05b2..49757d69 100644 --- a/Meshtastic/Extensions/UserDefaults.swift +++ b/Meshtastic/Extensions/UserDefaults.swift @@ -86,7 +86,7 @@ extension UserDefaults { @UserDefault(.provideLocation, defaultValue: false) static var provideLocation: Bool - @UserDefault(.provideLocationInterval, defaultValue: 0) + @UserDefault(.provideLocationInterval, defaultValue: 30) static var provideLocationInterval: Int @UserDefault(.mapLayer, defaultValue: .standard) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 6c6956ff..084fe083 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -548,8 +548,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, peripheralId: self.connectedPeripheral.id, context: context!) if myInfo != nil { - UserDefaults.preferredPeripheralNum = Int(myInfo!.myNodeNum) - connectedPeripheral.num = myInfo!.myNodeNum + UserDefaults.preferredPeripheralNum = Int(myInfo?.myNodeNum ?? 0) + connectedPeripheral.num = myInfo?.myNodeNum ?? 0 connectedPeripheral.name = myInfo?.bleName ?? "unknown".localized connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized } @@ -561,7 +561,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, channel: decodedInfo.packet.channel, context: context!) if nodeInfo != nil { - if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num { + if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo?.num ?? -1 { if nodeInfo!.user != nil { connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "?" connectedPeripheral.longName = nodeInfo?.user?.longName ?? "unknown".localized @@ -1008,29 +1008,34 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate if let lastLocation = LocationsHandler.shared.locationsArray.last { - positionPacket.latitudeI = Int32(lastLocation.coordinate.latitude * 1e7) - positionPacket.longitudeI = Int32(lastLocation.coordinate.longitude * 1e7) - let timestamp = lastLocation.timestamp - positionPacket.time = UInt32(timestamp.timeIntervalSince1970) - positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) - positionPacket.altitude = Int32(lastLocation.altitude) - positionPacket.satsInView = UInt32(LocationsHandler.satsInView) - positionPacket.precisionBits = UInt32(fetchedChannel[0].positionPrecision) - let currentSpeed = lastLocation.speed - if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { - positionPacket.groundSpeed = UInt32(currentSpeed * 3.6) + if fetchedChannel.count > 0 { + positionPacket.latitudeI = Int32(lastLocation.coordinate.latitude * 1e7) + positionPacket.longitudeI = Int32(lastLocation.coordinate.longitude * 1e7) + let timestamp = lastLocation.timestamp + positionPacket.time = UInt32(timestamp.timeIntervalSince1970) + positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) + positionPacket.altitude = Int32(lastLocation.altitude) + positionPacket.satsInView = UInt32(LocationsHandler.satsInView) + positionPacket.precisionBits = UInt32(fetchedChannel[0].positionPrecision) + let currentSpeed = lastLocation.speed + if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { + positionPacket.groundSpeed = UInt32(currentSpeed * 3.6) + } + let currentHeading = lastLocation.course + if currentHeading > 0 && (!currentHeading.isNaN || !currentHeading.isInfinite) { + positionPacket.groundTrack = UInt32(currentHeading) + } } - let currentHeading = lastLocation.course - if currentHeading > 0 && (!currentHeading.isNaN || !currentHeading.isInfinite) { - positionPacket.groundTrack = UInt32(currentHeading) - } - } } else { if destNum <= 0 || LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) == 0.0 { return nil } + if fetchedChannel.count <= 0 { + return nil + } + positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) let timestamp = LocationHelper.shared.locationManager.location?.timestamp ?? Date() diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 90a35cce..0b5428fc 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -106,14 +106,24 @@ public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext } } -public func clearCoreDataDatabase(context: NSManagedObjectContext) { +public func clearCoreDataDatabase(context: NSManagedObjectContext, includeRoutes: Bool) { let persistenceController = PersistenceController.shared.container for i in 0...persistenceController.managedObjectModel.entities.count-1 { + let entity = persistenceController.managedObjectModel.entities[i] let query = NSFetchRequest(entityName: entity.name!) - let deleteRequest = NSBatchDeleteRequest(fetchRequest: query) - + var deleteRequest = NSBatchDeleteRequest(fetchRequest: query) + let entityName = entity.name ?? "UNK" + + if includeRoutes { + deleteRequest = NSBatchDeleteRequest(fetchRequest: query) + } else if !includeRoutes { + if !(entityName.contains("RouteEntity") || entityName.contains("LocationEntity")) { + print(entity.name?.lowercased()) + deleteRequest = NSBatchDeleteRequest(fetchRequest: query) + } + } do { try context.executeAndMergeChanges(using: deleteRequest) } catch let error as NSError { diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index da0b2a9e..498496ce 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -121,6 +121,14 @@ struct Connect: View { Text("Short Name: \(node?.user?.shortName ?? "?")") Text("Long Name: \(node?.user?.longName ?? "unknown".localized)") Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)") + Button { + if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!, adminIndex: node!.myInfo!.adminIndex) { + print("Shutdown Failed") + } + + } label: { + Label("Power Off", systemImage: "power") + } } } if isUnsetRegion { @@ -224,7 +232,7 @@ struct Connect: View { if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected { bleManager.disconnectPeripheral() } - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: false) let radio = bleManager.peripherals.first(where: { $0.peripheral.identifier.uuidString == selectedPeripherialId }) if radio != nil { @@ -234,7 +242,7 @@ struct Connect: View { } .textCase(nil) } - + } else { Text("bluetooth.off") .foregroundColor(.red) diff --git a/Meshtastic/Views/Messages/MessageContextMenuItems.swift b/Meshtastic/Views/Messages/MessageContextMenuItems.swift index d98eaeec..4fa31421 100644 --- a/Meshtastic/Views/Messages/MessageContextMenuItems.swift +++ b/Meshtastic/Views/Messages/MessageContextMenuItems.swift @@ -53,9 +53,14 @@ struct MessageContextMenuItems: View { let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) Text("\(messageDate.formattedDate(format: MessageText.dateFormatString))").foregroundColor(.gray) } - if !isCurrentUser { + if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) && message.fromUser?.userNode?.hopsAway ?? -1 == 0 { VStack { Text("SNR \(String(format: "%.2f", message.snr)) dB") + Text("RSSI \(String(format: "%.2f", message.rssi)) dBm") + } + } else if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) { + VStack { + Text("Hops Away \(message.fromUser?.userNode?.hopsAway ?? 0)) dB") } } if isCurrentUser && message.receivedACK { @@ -78,10 +83,6 @@ struct MessageContextMenuItems: View { if ackDate >= sixMonthsAgo! { Text("Ack Time: \(ackDate.formattedDate(format: "h:mm:ss.SSSS a"))") .foregroundColor(.gray) - } else { - Text("unknown.age") - .font(.caption2) - .foregroundColor(.gray) } } } diff --git a/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift b/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift index c8f1abaa..49580f02 100644 --- a/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift +++ b/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift @@ -18,7 +18,7 @@ struct TextMessageField: View { HStack { if destination.showAlertButton { Spacer() - AlertButton { typingMessage += "🔔 Alert Bell Character! \u{7}" } + AlertButton { typingMessage += "🔔 Alert Bell! \u{7}" } } Spacer() RequestPositionButton(action: requestPosition) @@ -131,7 +131,7 @@ private extension MessageDestination { var showAlertButton: Bool { switch self { - case .user: return false + case .user: return true case .channel: return true } } diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index cae61c55..07625a4e 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -122,12 +122,12 @@ struct DeviceMetricsLog: View { } else { ScrollView { let columns = [ - GridItem(.flexible(minimum: 25, maximum: 45), spacing: 0.1), GridItem(.flexible(minimum: 25, maximum: 50), spacing: 0.1), - GridItem(.flexible(minimum: 30, maximum: 65), spacing: 0.1), - GridItem(.flexible(minimum: 30, maximum: 65), spacing: 0.1), - GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1), - GridItem(.flexible(minimum: 130, maximum: 200), spacing: 0.1) + GridItem(.flexible(minimum: 25, maximum: 50), spacing: 0.1), + GridItem(.flexible(minimum: 25, maximum: 60), spacing: 0.1), + GridItem(.flexible(minimum: 25, maximum: 60), spacing: 0.1), + GridItem(.flexible(minimum: 25, maximum: 50), spacing: 0.1), + GridItem(.flexible(minimum: 130, maximum: 175), spacing: 0.1) ] LazyVGrid(columns: columns, alignment: .leading, spacing: 1) { GridRow { diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index e614153a..ba5d46f9 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -75,7 +75,7 @@ struct AppSettings: View { ) { Button("Erase all app data?", role: .destructive) { bleManager.disconnectPeripheral() - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: true) context.refreshAllObjects() UserDefaults.standard.reset() } diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 5f0f44fc..b71ff400 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -294,9 +294,13 @@ struct Channels: View { } func firstMissingChannelIndex(_ indexes: [Int]) -> Int { - for element in 1...indexes.count { - if !indexes.contains(element) { - return element + var smallestIndex = 1 + if indexes.isEmpty { return smallestIndex } + if smallestIndex <= indexes.count { + for element in smallestIndex...indexes.count { + if !indexes.contains(element) { + return element + } } } return indexes.count + 1 diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 68596dab..1d438cbe 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -153,7 +153,7 @@ struct DeviceConfig: View { if bleManager.sendNodeDBReset(fromUser: node!.user!, toUser: node!.user!) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { bleManager.disconnectPeripheral() - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: false) } } else { @@ -178,7 +178,7 @@ struct DeviceConfig: View { if bleManager.sendFactoryReset(fromUser: node!.user!, toUser: node!.user!) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { bleManager.disconnectPeripheral() - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: false) } } else { print("Factory Reset Failed") diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 42d601e0..f12ba7db 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -12,7 +12,7 @@ struct Firmware: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager var node: NodeInfoEntity? - @State var minimumVersion = "2.3.4" + @State var minimumVersion = "2.3.5" @State var version = "" @State private var currentDevice: DeviceHardware? @State private var latestStable: FirmwareRelease? diff --git a/Meshtastic/Views/Settings/RouteRecorder.swift b/Meshtastic/Views/Settings/RouteRecorder.swift index 63ca1697..766b72d5 100644 --- a/Meshtastic/Views/Settings/RouteRecorder.swift +++ b/Meshtastic/Views/Settings/RouteRecorder.swift @@ -248,6 +248,7 @@ struct RouteRecorder: View { let nsError = error as NSError print("💥 Error Saving RouteEntity from the Route Recorder \(nsError)") } + isShowingDetails = false } label: { Label("finish", systemImage: "flag.checkered") }