From 169abf1561eb52987344899fe7ca372034aa1f98 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:38:35 -0800 Subject: [PATCH 01/29] Confirmation Dialogue for router roles --- Localizable.xcstrings | 6 ++++ .../GetEnviornmentMetricsIntent.swift | 8 +++++ .../Views/Settings/Config/DeviceConfig.swift | 35 ++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index e061cceb..5434aa45 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -5542,6 +5542,9 @@ } } } + }, + "Confirm" : { + }, "Connect to a Node" : { "localizations" : { @@ -29958,6 +29961,9 @@ } } } + }, + "The Router roles are designed for high vantage locations like mountaintops and towers. This node needs to be able to have a good direct connection to most of the nodes on the network or else this will significantly hurt the network." : { + }, "The secondary public key authorized to send admin messages to this node." : { "localizations" : { diff --git a/Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift b/Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift new file mode 100644 index 00000000..bca14a0c --- /dev/null +++ b/Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift @@ -0,0 +1,8 @@ +// +// GetEnviornmentMetricsIntent.swift +// Meshtastic +// +// Created by Benjamin Faershtein on 3/2/25. +// + +import Foundation diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 467b5901..abce6e9a 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -21,6 +21,7 @@ struct DeviceConfig: View { @State private var isPresentingFactoryResetConfirm = false @State var hasChanges = false @State var deviceRole = 0 + @State private var pendingDeviceRole = 0 @State var buzzerGPIO = 0 @State var buttonGPIO = 0 @State var rebroadcastMode = 0 @@ -29,6 +30,10 @@ struct DeviceConfig: View { @State var ledHeartbeatEnabled = true @State var tripleClickAsAdHocPing = true @State var tzdef = "" + + @State private var showRouterWarning = false + @State private var confirmWarning = false + var body: some View { VStack { @@ -39,9 +44,37 @@ struct DeviceConfig: View { VStack(alignment: .leading) { Picker("Device Role", selection: $deviceRole ) { ForEach(DeviceRoles.allCases) { dr in - Text(dr.name) + Text(dr.name).tag(dr.rawValue as Int) } } + .onChange(of: deviceRole) { oldValue, newValue in + if !confirmWarning { + if [2, 11].contains(newValue) { + pendingDeviceRole = newValue + deviceRole = oldValue // Reset selection until confirmed + showRouterWarning = true + } + } else { + confirmWarning = false + } + } + .confirmationDialog( + "Are you sure?", + isPresented: $showRouterWarning, + titleVisibility: .visible + ) { + + Button("Confirm") { + deviceRole = pendingDeviceRole + pendingDeviceRole = 0 + confirmWarning = true + } + Button("Cancel", role: .cancel) { + pendingDeviceRole = 0 + } + } message: { + Text("The Router roles are designed for high vantage locations like mountaintops and towers. This node needs to be able to have a good direct connection to most of the nodes on the network or else this will significantly hurt the network.") + } Text(DeviceRoles(rawValue: deviceRole)?.description ?? "") .foregroundColor(.gray) .font(.callout) From e70e8037391eff07ee3c518f17c9d4d7eb94ade2 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:41:08 -0800 Subject: [PATCH 02/29] Delete Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift --- Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift diff --git a/Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift b/Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift deleted file mode 100644 index bca14a0c..00000000 --- a/Meshtastic/AppIntents/GetEnviornmentMetricsIntent.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// GetEnviornmentMetricsIntent.swift -// Meshtastic -// -// Created by Benjamin Faershtein on 3/2/25. -// - -import Foundation From 223bf4eaa0410c6c85378cb3390239d9dfd0c863 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:41:26 -0800 Subject: [PATCH 03/29] Update ContentView.swift --- Meshtastic/Views/ContentView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Meshtastic/Views/ContentView.swift b/Meshtastic/Views/ContentView.swift index f593651e..d6b2fd6b 100644 --- a/Meshtastic/Views/ContentView.swift +++ b/Meshtastic/Views/ContentView.swift @@ -53,6 +53,5 @@ struct ContentView: View { } .tag(NavigationState.Tab.settings) } - .toolbarBackground(.visible, for: .tabBar) } } From 27ed32eb197244a34f00fba89025ef0634e53a35 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Wed, 12 Mar 2025 17:45:40 -0400 Subject: [PATCH 04/29] EnvironmentMetrics chart scale improvements --- Meshtastic.xcodeproj/project.pbxproj | 8 +- .../MetricsChartSeries.swift | 8 ++ .../MetricsSeriesList.swift | 78 +++++++++++++++++-- ...s.swift => EnvironmentDefaultSeries.swift} | 2 + 4 files changed, 84 insertions(+), 12 deletions(-) rename Meshtastic/Views/Nodes/Helpers/Metrics Columns/{EnviornmentDefaultSeries.swift => EnvironmentDefaultSeries.swift} (99%) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 60c6e50b..fadb81fe 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -17,7 +17,7 @@ 2344A2B12D68DFF800170A77 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C49D8F2C471AEA0024FBD1 /* Constants.swift */; }; 2373AE132D0A216C0086C749 /* MetricsChartSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */; }; 2373AE152D0A24930086C749 /* MetricsSeriesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */; }; - 2373AE172D0A26620086C749 /* EnviornmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnviornmentDefaultSeries.swift */; }; + 2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */; }; 251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */; }; 251926872C3BAE2200249DF5 /* NodeAlertsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926862C3BAE2200249DF5 /* NodeAlertsButton.swift */; }; 2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */; }; @@ -282,7 +282,7 @@ 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = ""; }; 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsChartSeries.swift; sourceTree = ""; }; 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsSeriesList.swift; sourceTree = ""; }; - 2373AE162D0A26620086C749 /* EnviornmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnviornmentDefaultSeries.swift; sourceTree = ""; }; + 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultSeries.swift; sourceTree = ""; }; 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteNodeButton.swift; sourceTree = ""; }; 251926862C3BAE2200249DF5 /* NodeAlertsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeAlertsButton.swift; sourceTree = ""; }; 251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExchangePositionsButton.swift; sourceTree = ""; }; @@ -600,7 +600,7 @@ isa = PBXGroup; children = ( 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */, - 2373AE162D0A26620086C749 /* EnviornmentDefaultSeries.swift */, + 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */, 231B3F262D0885240069A07D /* MetricsColumnDetail.swift */, ); path = "Metrics Columns"; @@ -1405,7 +1405,7 @@ DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */, 251926902C3CB44900249DF5 /* ClientHistoryButton.swift in Sources */, DDD5BB102C285FB3007E03CA /* AppLogFilter.swift in Sources */, - 2373AE172D0A26620086C749 /* EnviornmentDefaultSeries.swift in Sources */, + 2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */, DD4640202AFF10F4002A5ECB /* WaypointForm.swift in Sources */, DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, diff --git a/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift b/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift index b535099e..ab68c4cc 100644 --- a/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift +++ b/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift @@ -38,12 +38,18 @@ class MetricsChartSeries: ObservableObject { // Possibly converted to the proper units let valueClosure: (TelemetryEntity) -> Float? + // Used for scaling the Y-axis + let initialYAxisRange: ClosedRange? + let minumumYAxisSpan: Float? + // Main initializer init( id: String, keyPath: KeyPath, name: String, abbreviatedName: String, + initialYAxisRange: ClosedRange? = nil, + minumumYAxisSpan: Float? = nil, conversion: ((Value) -> Value)? = nil, visible: Bool = true, foregroundStyle: @escaping ((ClosedRange?) -> ForegroundStyle?) = { _ in nil }, @@ -54,6 +60,8 @@ class MetricsChartSeries: ObservableObject { self.id = id self.name = name self.abbreviatedName = abbreviatedName + self.initialYAxisRange = initialYAxisRange + self.minumumYAxisSpan = minumumYAxisSpan self.visible = visible // By saving these closures, MetricsChartSeries can be type agnostic diff --git a/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift b/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift index 049d1fb4..46ec6258 100644 --- a/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift +++ b/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift @@ -7,6 +7,7 @@ import Foundation import SwiftUI + class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplaceableCollection { @Published var series: [MetricsChartSeries] @@ -38,25 +39,86 @@ class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplacea return nil } + // Calculates the chartRange based on the series configuration and data provided + // Besides checkign the range of the data, this function also obeys some series-level + // configuraiton, such as: + // 1. starting with a desired fixed range + // 2. obeying a minimum span func chartRange(forData data: [TelemetryEntity]) -> ClosedRange { - var lower: Float? - var upper: Float? + var globalLower: Float = .infinity + var globalUpper: Float = -.infinity + + // Keep track of the range of each series + var range: [MetricsChartSeries: ClosedRange] = [:] + + // Determine if there is an initial fixed range. + // The range might exapand past this initial range if the data goes beyond. + for aSeries in self.visible { + if let thisRange = aSeries.initialYAxisRange { + range[aSeries] = thisRange + if thisRange.upperBound > globalUpper {globalUpper = thisRange.upperBound} + if thisRange.lowerBound < globalLower {globalLower = thisRange.lowerBound} + } + } + + // Iterate through all the data. It would be easier to iterate + // the series then the data, but this way we only iterate the data once for te in data { for aSeries in self.visible { + var seriesUpper = range[aSeries]?.upperBound ?? -.infinity + var seriesLower = range[aSeries]?.lowerBound ?? .infinity + if let value = aSeries.valueFor(te) { - if value > (upper ?? -.infinity) {upper = value} - if value < (lower ?? .infinity) {lower = value} + // Update the global bounds + if value > globalUpper {globalUpper = value} + if value < globalLower {globalLower = value} + + // Update the series bounds if necessary + if value > seriesUpper || value < seriesLower { + if value > seriesUpper { + seriesUpper = value + } + if value < seriesLower { + seriesLower = value + } + if seriesUpper.isFinite && seriesLower.isFinite { + range[aSeries] = seriesLower...seriesUpper + } + } } } } - - // Return default range if no data or nil - guard let lower, let upper else { + + // Go through each series one last time to obey the minimum span + for aSeries in self.visible { + if let minimumSpan = aSeries.minumumYAxisSpan, + let currentRange = range[aSeries] { + let currentSpan = currentRange.upperBound - currentRange.lowerBound + //Logger.data.info("Updated \(aSeries.id) to \(range[aSeries] ?? 0...0) span=\(currentSpan)") + if currentSpan < minimumSpan { + // Calculate the center of the range + let centerOfRange = currentRange.lowerBound + (currentSpan / 2) + let newLower = centerOfRange - (minimumSpan / 2.0) + let newUpper = centerOfRange + (minimumSpan / 2.0) + + if newUpper > globalUpper { + globalUpper = newUpper + } + if newLower < globalLower { + globalLower = newLower + } + } + } + } + + // Return default range if no data + if !globalLower.isFinite || !globalUpper.isFinite { return 0.0...100.0 } - return lower...upper + return globalLower...globalUpper } + // Collection conformance typealias Index = Int typealias Element = MetricsChartSeries diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnviornmentDefaultSeries.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift similarity index 99% rename from Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnviornmentDefaultSeries.swift rename to Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift index 8150f971..e9422ee4 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnviornmentDefaultSeries.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift @@ -19,6 +19,7 @@ extension MetricsSeriesList { keyPath: \.temperature, name: "Temperature", abbreviatedName: "Temp", + minumumYAxisSpan: 50.0, conversion: { t in t.map { Float($0.localeTemperature()) } }, foregroundStyle: { chartRange in let locale = NSLocale.current as NSLocale @@ -60,6 +61,7 @@ extension MetricsSeriesList { keyPath: \.relativeHumidity, name: "Relative Humidity", abbreviatedName: "Hum", + initialYAxisRange: 0.0...100.0, foregroundStyle: { _ in .linearGradient( colors: [Color(UIColor.purple.darker(componentDelta: 0.2)), .purple], From d16eec03acafa388f33eeee6c5406621b64fc72c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 14 Mar 2025 07:28:58 -0700 Subject: [PATCH 05/29] Bump version, adjust admin key handling --- Meshtastic.xcodeproj/project.pbxproj | 8 ++++---- .../Views/Settings/Config/SecurityConfig.swift | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 60c6e50b..29a7bb40 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1800,7 +1800,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.5.20; + MARKETING_VERSION = 2.5.21; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1834,7 +1834,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.5.20; + MARKETING_VERSION = 2.5.21; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1866,7 +1866,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.5.20; + MARKETING_VERSION = 2.5.21; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1899,7 +1899,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.5.20; + MARKETING_VERSION = 2.5.21; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Meshtastic/Views/Settings/Config/SecurityConfig.swift b/Meshtastic/Views/Settings/Config/SecurityConfig.swift index 6f16c8ff..c3d493c1 100644 --- a/Meshtastic/Views/Settings/Config/SecurityConfig.swift +++ b/Meshtastic/Views/Settings/Config/SecurityConfig.swift @@ -25,9 +25,9 @@ struct SecurityConfig: View { @State var hasValidPublicKey: Bool = false @State var privateKey = "" @State var hasValidPrivateKey: Bool = false - @State var adminKey = "" - @State var adminKey2 = "" - @State var adminKey3 = "" + @State var adminKey: String = "" + @State var adminKey2: String = "" + @State var adminKey3: String = "" @State var hasValidAdminKey: Bool = true @State var hasValidAdminKey2: Bool = true @State var hasValidAdminKey3: Bool = true @@ -259,9 +259,9 @@ struct SecurityConfig: View { func setSecurityValues() { self.publicKey = node?.securityConfig?.publicKey?.base64EncodedString() ?? "" self.privateKey = node?.securityConfig?.privateKey?.base64EncodedString() ?? "" - self.adminKey = node?.securityConfig?.adminKey?.base64EncodedString() ?? "" - self.adminKey2 = node?.securityConfig?.adminKey2?.base64EncodedString() ?? "" - self.adminKey3 = node?.securityConfig?.adminKey3?.base64EncodedString() ?? "" + self.adminKey = node?.securityConfig?.adminKey?.base64EncodedString(options: .lineLength64Characters) ?? "" + self.adminKey2 = node?.securityConfig?.adminKey2?.base64EncodedString(options: .lineLength64Characters) ?? "" + self.adminKey3 = node?.securityConfig?.adminKey3?.base64EncodedString(options: .lineLength64Characters) ?? "" self.isManaged = node?.securityConfig?.isManaged ?? false self.serialEnabled = node?.securityConfig?.serialEnabled ?? false self.debugLogApiEnabled = node?.securityConfig?.debugLogApiEnabled ?? false From 2ba9d09aed607c6e0a73b7e9362aa51da336654b Mon Sep 17 00:00:00 2001 From: Jake-B Date: Fri, 14 Mar 2025 12:47:31 -0400 Subject: [PATCH 06/29] Update to most recent meshtastic/protobufs --- Meshtastic/Helpers/BLEManager.swift | 2 + .../Settings/Config/Module/MQTTConfig.swift | 162 +++--- .../Sources/meshtastic/admin.pb.swift | 424 ++++++-------- .../Sources/meshtastic/apponly.pb.swift | 8 +- .../Sources/meshtastic/atak.pb.swift | 69 +-- .../meshtastic/cannedmessages.pb.swift | 8 +- .../Sources/meshtastic/channel.pb.swift | 38 +- .../Sources/meshtastic/clientonly.pb.swift | 8 +- .../Sources/meshtastic/config.pb.swift | 492 +++++++--------- .../meshtastic/connection_status.pb.swift | 23 +- .../Sources/meshtastic/device_ui.pb.swift | 209 +++++-- .../Sources/meshtastic/deviceonly.pb.swift | 238 +++++++- .../Sources/meshtastic/interdevice.pb.swift | 328 +++++++++++ .../Sources/meshtastic/localonly.pb.swift | 11 +- .../Sources/meshtastic/mesh.pb.swift | 539 +++++------------- .../Sources/meshtastic/module_config.pb.swift | 290 +++------- .../Sources/meshtastic/mqtt.pb.swift | 11 +- .../Sources/meshtastic/paxcount.pb.swift | 8 +- .../Sources/meshtastic/portnums.pb.swift | 25 +- .../Sources/meshtastic/powermon.pb.swift | 117 ++-- .../meshtastic/remote_hardware.pb.swift | 37 +- .../Sources/meshtastic/rtttl.pb.swift | 8 +- .../Sources/meshtastic/storeforward.pb.swift | 94 +-- .../Sources/meshtastic/telemetry.pb.swift | 143 ++--- .../Sources/meshtastic/xmodem.pb.swift | 40 +- 25 files changed, 1622 insertions(+), 1710 deletions(-) create mode 100644 MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 47db7ebd..89f57aa8 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1006,6 +1006,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate Logger.mesh.info("🕸️ MESH PACKET received for ATAK Plugin App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") case .powerstressApp: Logger.mesh.info("🕸️ MESH PACKET received for Power Stress App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") + case .reticulumTunnelApp: + Logger.mesh.info("🕸️ MESH PACKET received for Reticulum Tunnel App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") } if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce { diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 5114a7e4..765a8cab 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -230,31 +230,88 @@ struct MQTTConfig: View { } .scrollDismissesKeyboard(.interactively) .disabled(self.bleManager.connectedPeripheral == nil || node?.mqttConfig == nil) - } - SaveConfigButton(node: node, hasChanges: $hasChanges) { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context) - if connectedNode != nil { - var mqtt = ModuleConfig.MQTTConfig() - mqtt.enabled = self.enabled - mqtt.proxyToClientEnabled = self.proxyToClientEnabled - mqtt.address = self.address - mqtt.username = self.username - mqtt.password = self.password - mqtt.root = self.root - mqtt.encryptionEnabled = self.encryptionEnabled - mqtt.jsonEnabled = self.jsonEnabled - mqtt.tlsEnabled = self.tlsEnabled - mqtt.mapReportingEnabled = self.mapReportingEnabled - mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision) - mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs) - let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) - if adminMessageId > 0 { - // Should show a saved successfully alert once I know that to be true - // for now just disable the button after a successful save - hasChanges = false - goBack() + SaveConfigButton(node: node, hasChanges: $hasChanges) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context) + if connectedNode != nil { + var mqtt = ModuleConfig.MQTTConfig() + mqtt.enabled = self.enabled + mqtt.proxyToClientEnabled = self.proxyToClientEnabled + mqtt.address = self.address + mqtt.username = self.username + mqtt.password = self.password + mqtt.root = self.root + mqtt.encryptionEnabled = self.encryptionEnabled + mqtt.jsonEnabled = self.jsonEnabled + mqtt.tlsEnabled = self.tlsEnabled + mqtt.mapReportingEnabled = self.mapReportingEnabled + mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision) + mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs) + let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if adminMessageId > 0 { + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + goBack() + } } + }.onChange(of: enabled) { _, newEnabled in + if newEnabled != node?.mqttConfig?.enabled { hasChanges = true } + } + .onChange(of: proxyToClientEnabled) { _, newProxyToClientEnabled in + if newProxyToClientEnabled { + jsonEnabled = false + tlsEnabled = false + } + if newProxyToClientEnabled != node?.mqttConfig?.proxyToClientEnabled { hasChanges = true } + } + .onChange(of: address) { _, newAddress in + if newAddress != node?.mqttConfig?.address ?? "" { hasChanges = true } + } + .onChange(of: username) { _, newUsername in + if newUsername != node?.mqttConfig?.username ?? "" { hasChanges = true } + } + .onChange(of: password) { _, newPassword in + if newPassword != node?.mqttConfig?.password ?? "" { hasChanges = true } + } + .onChange(of: root) { _, newRoot in + if newRoot != node?.mqttConfig?.root ?? "" { hasChanges = true } + } + .onChange(of: selectedTopic) { _, newSelectedTopic in + root = newSelectedTopic + } + .onChange(of: encryptionEnabled) { _, newEncryptionEnabled in + if newEncryptionEnabled != node?.mqttConfig?.encryptionEnabled { hasChanges = true } + } + .onChange(of: jsonEnabled) { _, newJsonEnabled in + if newJsonEnabled { + proxyToClientEnabled = false + } + if newJsonEnabled != node?.mqttConfig?.jsonEnabled { hasChanges = true } + } + .onChange(of: tlsEnabled) { _, newTlsEnabled in + if address.lowercased() == "mqtt.meshtastic.org" { + tlsEnabled = false + } else { + if newTlsEnabled != node?.mqttConfig?.tlsEnabled { hasChanges = true } + } + } + .onChange(of: mqttConnected) { _, newMqttConnected in + if newMqttConnected == false { + if bleManager.mqttProxyConnected { + bleManager.mqttManager.disconnect() + } + } else { + if !bleManager.mqttProxyConnected && node != nil { + bleManager.mqttManager.connectFromConfigSettings(node: node!) + } + } + } + .onChange(of: mapReportingEnabled) { _, newMapReportingEnabled in + if newMapReportingEnabled != node?.mqttConfig?.mapReportingEnabled { hasChanges = true } + } + .onChange(of: mapPublishIntervalSecs) { _, newMapPublishIntervalSecs in + if newMapPublishIntervalSecs != node?.mqttConfig?.mapPublishIntervalSecs ?? -1 { hasChanges = true } } } .navigationTitle("mqtt.config") @@ -267,64 +324,6 @@ struct MQTTConfig: View { ) } ) - .onChange(of: enabled) { _, newEnabled in - if newEnabled != node?.mqttConfig?.enabled { hasChanges = true } - } - .onChange(of: proxyToClientEnabled) { _, newProxyToClientEnabled in - if newProxyToClientEnabled { - jsonEnabled = false - tlsEnabled = false - } - if newProxyToClientEnabled != node?.mqttConfig?.proxyToClientEnabled { hasChanges = true } - } - .onChange(of: address) { newAddress in - if newAddress != node?.mqttConfig?.address ?? "" { hasChanges = true } - } - .onChange(of: username) { newUsername in - if newUsername != node?.mqttConfig?.username ?? "" { hasChanges = true } - } - .onChange(of: password) { newPassword in - if newPassword != node?.mqttConfig?.password ?? "" { hasChanges = true } - } - .onChange(of: root) { _, newRoot in - if newRoot != node?.mqttConfig?.root ?? "" { hasChanges = true } - } - .onChange(of: selectedTopic) { _, newSelectedTopic in - root = newSelectedTopic - } - .onChange(of: encryptionEnabled) { _, newEncryptionEnabled in - if newEncryptionEnabled != node?.mqttConfig?.encryptionEnabled { hasChanges = true } - } - .onChange(of: jsonEnabled) { _, newJsonEnabled in - if newJsonEnabled { - proxyToClientEnabled = false - } - if newJsonEnabled != node?.mqttConfig?.jsonEnabled { hasChanges = true } - } - .onChange(of: tlsEnabled) { _, newTlsEnabled in - if address.lowercased() == "mqtt.meshtastic.org" { - tlsEnabled = false - } else { - if newTlsEnabled != node?.mqttConfig?.tlsEnabled { hasChanges = true } - } - } - .onChange(of: mqttConnected) { _, newMqttConnected in - if newMqttConnected == false { - if bleManager.mqttProxyConnected { - bleManager.mqttManager.disconnect() - } - } else { - if !bleManager.mqttProxyConnected && node != nil { - bleManager.mqttManager.connectFromConfigSettings(node: node!) - } - } - } - .onChange(of: mapReportingEnabled) { _, newMapReportingEnabled in - if newMapReportingEnabled != node?.mqttConfig?.mapReportingEnabled { hasChanges = true } - } - .onChange(of: mapPublishIntervalSecs) { _, newMapPublishIntervalSecs in - if newMapPublishIntervalSecs != node?.mqttConfig?.mapPublishIntervalSecs ?? -1 { hasChanges = true } - } .onFirstAppear { // Need to request a MqttModuleConfig from the remote node before allowing changes if let connectedPeripheral = bleManager.connectedPeripheral, let node { @@ -348,6 +347,7 @@ struct MQTTConfig: View { } } } + func setMqttValues() { nearbyTopics = [] diff --git a/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift index 1f51447d..5a9a75a5 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/admin.proto @@ -24,7 +25,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// This message is handled by the Admin module and is responsible for all settings/channel read/write operations. /// This message is used to do settings operations to both remote AND local nodes. /// (Prior to 1.2 these operations were done via special ToRadio operations) -public struct AdminMessage { +public struct AdminMessage: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -261,6 +262,36 @@ public struct AdminMessage { set {payloadVariant = .setScale(newValue)} } + /// + /// Backup the node's preferences + public var backupPreferences: AdminMessage.BackupLocation { + get { + if case .backupPreferences(let v)? = payloadVariant {return v} + return .flash + } + set {payloadVariant = .backupPreferences(newValue)} + } + + /// + /// Restore the node's preferences + public var restorePreferences: AdminMessage.BackupLocation { + get { + if case .restorePreferences(let v)? = payloadVariant {return v} + return .flash + } + set {payloadVariant = .restorePreferences(newValue)} + } + + /// + /// Remove backups of the node's preferences + public var removeBackupPreferences: AdminMessage.BackupLocation { + get { + if case .removeBackupPreferences(let v)? = payloadVariant {return v} + return .flash + } + set {payloadVariant = .removeBackupPreferences(newValue)} + } + /// /// Set the owner for this node public var setOwner: User { @@ -533,7 +564,7 @@ public struct AdminMessage { /// /// TODO: REPLACE - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Send the specified channel in the response to this message /// NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present) @@ -603,6 +634,15 @@ public struct AdminMessage { /// Set zero and offset for scale chips case setScale(UInt32) /// + /// Backup the node's preferences + case backupPreferences(AdminMessage.BackupLocation) + /// + /// Restore the node's preferences + case restorePreferences(AdminMessage.BackupLocation) + /// + /// Remove backups of the node's preferences + case removeBackupPreferences(AdminMessage.BackupLocation) + /// /// Set the owner for this node case setOwner(User) /// @@ -689,213 +729,11 @@ public struct AdminMessage { /// Tell the node to reset the nodedb. case nodedbReset(Int32) - #if !swift(>=4.1) - public static func ==(lhs: AdminMessage.OneOf_PayloadVariant, rhs: AdminMessage.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.getChannelRequest, .getChannelRequest): return { - guard case .getChannelRequest(let l) = lhs, case .getChannelRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getChannelResponse, .getChannelResponse): return { - guard case .getChannelResponse(let l) = lhs, case .getChannelResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getOwnerRequest, .getOwnerRequest): return { - guard case .getOwnerRequest(let l) = lhs, case .getOwnerRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getOwnerResponse, .getOwnerResponse): return { - guard case .getOwnerResponse(let l) = lhs, case .getOwnerResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getConfigRequest, .getConfigRequest): return { - guard case .getConfigRequest(let l) = lhs, case .getConfigRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getConfigResponse, .getConfigResponse): return { - guard case .getConfigResponse(let l) = lhs, case .getConfigResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getModuleConfigRequest, .getModuleConfigRequest): return { - guard case .getModuleConfigRequest(let l) = lhs, case .getModuleConfigRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getModuleConfigResponse, .getModuleConfigResponse): return { - guard case .getModuleConfigResponse(let l) = lhs, case .getModuleConfigResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return { - guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModuleMessagesResponse, .getCannedMessageModuleMessagesResponse): return { - guard case .getCannedMessageModuleMessagesResponse(let l) = lhs, case .getCannedMessageModuleMessagesResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceMetadataRequest, .getDeviceMetadataRequest): return { - guard case .getDeviceMetadataRequest(let l) = lhs, case .getDeviceMetadataRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceMetadataResponse, .getDeviceMetadataResponse): return { - guard case .getDeviceMetadataResponse(let l) = lhs, case .getDeviceMetadataResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getRingtoneRequest, .getRingtoneRequest): return { - guard case .getRingtoneRequest(let l) = lhs, case .getRingtoneRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getRingtoneResponse, .getRingtoneResponse): return { - guard case .getRingtoneResponse(let l) = lhs, case .getRingtoneResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceConnectionStatusRequest, .getDeviceConnectionStatusRequest): return { - guard case .getDeviceConnectionStatusRequest(let l) = lhs, case .getDeviceConnectionStatusRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceConnectionStatusResponse, .getDeviceConnectionStatusResponse): return { - guard case .getDeviceConnectionStatusResponse(let l) = lhs, case .getDeviceConnectionStatusResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setHamMode, .setHamMode): return { - guard case .setHamMode(let l) = lhs, case .setHamMode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getNodeRemoteHardwarePinsRequest, .getNodeRemoteHardwarePinsRequest): return { - guard case .getNodeRemoteHardwarePinsRequest(let l) = lhs, case .getNodeRemoteHardwarePinsRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getNodeRemoteHardwarePinsResponse, .getNodeRemoteHardwarePinsResponse): return { - guard case .getNodeRemoteHardwarePinsResponse(let l) = lhs, case .getNodeRemoteHardwarePinsResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.enterDfuModeRequest, .enterDfuModeRequest): return { - guard case .enterDfuModeRequest(let l) = lhs, case .enterDfuModeRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.deleteFileRequest, .deleteFileRequest): return { - guard case .deleteFileRequest(let l) = lhs, case .deleteFileRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setScale, .setScale): return { - guard case .setScale(let l) = lhs, case .setScale(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setOwner, .setOwner): return { - guard case .setOwner(let l) = lhs, case .setOwner(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setChannel, .setChannel): return { - guard case .setChannel(let l) = lhs, case .setChannel(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setConfig, .setConfig): return { - guard case .setConfig(let l) = lhs, case .setConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setModuleConfig, .setModuleConfig): return { - guard case .setModuleConfig(let l) = lhs, case .setModuleConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setCannedMessageModuleMessages, .setCannedMessageModuleMessages): return { - guard case .setCannedMessageModuleMessages(let l) = lhs, case .setCannedMessageModuleMessages(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setRingtoneMessage, .setRingtoneMessage): return { - guard case .setRingtoneMessage(let l) = lhs, case .setRingtoneMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeByNodenum, .removeByNodenum): return { - guard case .removeByNodenum(let l) = lhs, case .removeByNodenum(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setFavoriteNode, .setFavoriteNode): return { - guard case .setFavoriteNode(let l) = lhs, case .setFavoriteNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeFavoriteNode, .removeFavoriteNode): return { - guard case .removeFavoriteNode(let l) = lhs, case .removeFavoriteNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setFixedPosition, .setFixedPosition): return { - guard case .setFixedPosition(let l) = lhs, case .setFixedPosition(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeFixedPosition, .removeFixedPosition): return { - guard case .removeFixedPosition(let l) = lhs, case .removeFixedPosition(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setTimeOnly, .setTimeOnly): return { - guard case .setTimeOnly(let l) = lhs, case .setTimeOnly(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getUiConfigRequest, .getUiConfigRequest): return { - guard case .getUiConfigRequest(let l) = lhs, case .getUiConfigRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getUiConfigResponse, .getUiConfigResponse): return { - guard case .getUiConfigResponse(let l) = lhs, case .getUiConfigResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.storeUiConfig, .storeUiConfig): return { - guard case .storeUiConfig(let l) = lhs, case .storeUiConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setIgnoredNode, .setIgnoredNode): return { - guard case .setIgnoredNode(let l) = lhs, case .setIgnoredNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeIgnoredNode, .removeIgnoredNode): return { - guard case .removeIgnoredNode(let l) = lhs, case .removeIgnoredNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.beginEditSettings, .beginEditSettings): return { - guard case .beginEditSettings(let l) = lhs, case .beginEditSettings(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.commitEditSettings, .commitEditSettings): return { - guard case .commitEditSettings(let l) = lhs, case .commitEditSettings(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.factoryResetDevice, .factoryResetDevice): return { - guard case .factoryResetDevice(let l) = lhs, case .factoryResetDevice(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rebootOtaSeconds, .rebootOtaSeconds): return { - guard case .rebootOtaSeconds(let l) = lhs, case .rebootOtaSeconds(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.exitSimulator, .exitSimulator): return { - guard case .exitSimulator(let l) = lhs, case .exitSimulator(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rebootSeconds, .rebootSeconds): return { - guard case .rebootSeconds(let l) = lhs, case .rebootSeconds(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.shutdownSeconds, .shutdownSeconds): return { - guard case .shutdownSeconds(let l) = lhs, case .shutdownSeconds(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.factoryResetConfig, .factoryResetConfig): return { - guard case .factoryResetConfig(let l) = lhs, case .factoryResetConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.nodedbReset, .nodedbReset): return { - guard case .nodedbReset(let l) = lhs, case .nodedbReset(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// TODO: REPLACE - public enum ConfigType: SwiftProtobuf.Enum { + public enum ConfigType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -929,6 +767,9 @@ public struct AdminMessage { /// /// TODO: REPLACE case securityConfig // = 7 + + /// + /// Session key config case sessionkeyConfig // = 8 /// @@ -972,11 +813,25 @@ public struct AdminMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [AdminMessage.ConfigType] = [ + .deviceConfig, + .positionConfig, + .powerConfig, + .networkConfig, + .displayConfig, + .loraConfig, + .bluetoothConfig, + .securityConfig, + .sessionkeyConfig, + .deviceuiConfig, + ] + } /// /// TODO: REPLACE - public enum ModuleConfigType: SwiftProtobuf.Enum { + public enum ModuleConfigType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1074,53 +929,71 @@ public struct AdminMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [AdminMessage.ModuleConfigType] = [ + .mqttConfig, + .serialConfig, + .extnotifConfig, + .storeforwardConfig, + .rangetestConfig, + .telemetryConfig, + .cannedmsgConfig, + .audioConfig, + .remotehardwareConfig, + .neighborinfoConfig, + .ambientlightingConfig, + .detectionsensorConfig, + .paxcounterConfig, + ] + + } + + public enum BackupLocation: SwiftProtobuf.Enum, Swift.CaseIterable { + public typealias RawValue = Int + + /// + /// Backup to the internal flash + case flash // = 0 + + /// + /// Backup to the SD card + case sd // = 1 + case UNRECOGNIZED(Int) + + public init() { + self = .flash + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .flash + case 1: self = .sd + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .flash: return 0 + case .sd: return 1 + case .UNRECOGNIZED(let i): return i + } + } + + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [AdminMessage.BackupLocation] = [ + .flash, + .sd, + ] + } public init() {} } -#if swift(>=4.2) - -extension AdminMessage.ConfigType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [AdminMessage.ConfigType] = [ - .deviceConfig, - .positionConfig, - .powerConfig, - .networkConfig, - .displayConfig, - .loraConfig, - .bluetoothConfig, - .securityConfig, - .sessionkeyConfig, - .deviceuiConfig, - ] -} - -extension AdminMessage.ModuleConfigType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [AdminMessage.ModuleConfigType] = [ - .mqttConfig, - .serialConfig, - .extnotifConfig, - .storeforwardConfig, - .rangetestConfig, - .telemetryConfig, - .cannedmsgConfig, - .audioConfig, - .remotehardwareConfig, - .neighborinfoConfig, - .ambientlightingConfig, - .detectionsensorConfig, - .paxcounterConfig, - ] -} - -#endif // swift(>=4.2) - /// /// Parameters for setting up Meshtastic for ameteur radio usage -public struct HamParameters { +public struct HamParameters: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1150,7 +1023,7 @@ public struct HamParameters { /// /// Response envelope for node_remote_hardware_pins -public struct NodeRemoteHardwarePinsResponse { +public struct NodeRemoteHardwarePinsResponse: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1164,15 +1037,6 @@ public struct NodeRemoteHardwarePinsResponse { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension AdminMessage: @unchecked Sendable {} -extension AdminMessage.OneOf_PayloadVariant: @unchecked Sendable {} -extension AdminMessage.ConfigType: @unchecked Sendable {} -extension AdminMessage.ModuleConfigType: @unchecked Sendable {} -extension HamParameters: @unchecked Sendable {} -extension NodeRemoteHardwarePinsResponse: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" @@ -1203,6 +1067,9 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 21: .standard(proto: "enter_dfu_mode_request"), 22: .standard(proto: "delete_file_request"), 23: .standard(proto: "set_scale"), + 24: .standard(proto: "backup_preferences"), + 25: .standard(proto: "restore_preferences"), + 26: .standard(proto: "remove_backup_preferences"), 32: .standard(proto: "set_owner"), 33: .standard(proto: "set_channel"), 34: .standard(proto: "set_config"), @@ -1453,6 +1320,30 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat self.payloadVariant = .setScale(v) } }() + case 24: try { + var v: AdminMessage.BackupLocation? + try decoder.decodeSingularEnumField(value: &v) + if let v = v { + if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} + self.payloadVariant = .backupPreferences(v) + } + }() + case 25: try { + var v: AdminMessage.BackupLocation? + try decoder.decodeSingularEnumField(value: &v) + if let v = v { + if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} + self.payloadVariant = .restorePreferences(v) + } + }() + case 26: try { + var v: AdminMessage.BackupLocation? + try decoder.decodeSingularEnumField(value: &v) + if let v = v { + if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} + self.payloadVariant = .removeBackupPreferences(v) + } + }() case 32: try { var v: User? var hadOneofValue = false @@ -1796,6 +1687,18 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .setScale(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularUInt32Field(value: v, fieldNumber: 23) }() + case .backupPreferences?: try { + guard case .backupPreferences(let v)? = self.payloadVariant else { preconditionFailure() } + try visitor.visitSingularEnumField(value: v, fieldNumber: 24) + }() + case .restorePreferences?: try { + guard case .restorePreferences(let v)? = self.payloadVariant else { preconditionFailure() } + try visitor.visitSingularEnumField(value: v, fieldNumber: 25) + }() + case .removeBackupPreferences?: try { + guard case .removeBackupPreferences(let v)? = self.payloadVariant else { preconditionFailure() } + try visitor.visitSingularEnumField(value: v, fieldNumber: 26) + }() case .setOwner?: try { guard case .setOwner(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 32) @@ -1949,6 +1852,13 @@ extension AdminMessage.ModuleConfigType: SwiftProtobuf._ProtoNameProviding { ] } +extension AdminMessage.BackupLocation: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "FLASH"), + 1: .same(proto: "SD"), + ] +} + extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".HamParameters" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1980,7 +1890,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa if self.txPower != 0 { try visitor.visitSingularInt32Field(value: self.txPower, fieldNumber: 2) } - if self.frequency != 0 { + if self.frequency.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.frequency, fieldNumber: 3) } if !self.shortName.isEmpty { diff --git a/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift index 0457077c..52dac5ca 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/apponly.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -26,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// any SECONDARY channels. /// No DISABLED channels are included. /// This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL -public struct ChannelSet { +public struct ChannelSet: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -53,10 +53,6 @@ public struct ChannelSet { fileprivate var _loraConfig: Config.LoRaConfig? = nil } -#if swift(>=5.5) && canImport(_Concurrency) -extension ChannelSet: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift index 867648a9..06d6af88 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/atak.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum Team: SwiftProtobuf.Enum { +public enum Team: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -130,11 +131,6 @@ public enum Team: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Team: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Team] = [ .unspecifedColor, @@ -153,13 +149,12 @@ extension Team: CaseIterable { .darkGreen, .brown, ] -} -#endif // swift(>=4.2) +} /// /// Role of the group member -public enum MemberRole: SwiftProtobuf.Enum { +public enum MemberRole: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -233,11 +228,6 @@ public enum MemberRole: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension MemberRole: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [MemberRole] = [ .unspecifed, @@ -250,13 +240,12 @@ extension MemberRole: CaseIterable { .rto, .k9, ] -} -#endif // swift(>=4.2) +} /// /// Packets for the official ATAK Plugin -public struct TAKPacket { +public struct TAKPacket: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -337,7 +326,7 @@ public struct TAKPacket { /// /// The payload of the packet - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable { /// /// TAK position report case pli(PLI) @@ -349,28 +338,6 @@ public struct TAKPacket { /// May be compressed / truncated by the sender (EUD) case detail(Data) - #if !swift(>=4.1) - public static func ==(lhs: TAKPacket.OneOf_PayloadVariant, rhs: TAKPacket.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.pli, .pli): return { - guard case .pli(let l) = lhs, case .pli(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.chat, .chat): return { - guard case .chat(let l) = lhs, case .chat(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.detail, .detail): return { - guard case .detail(let l) = lhs, case .detail(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -382,7 +349,7 @@ public struct TAKPacket { /// /// ATAK GeoChat message -public struct GeoChat { +public struct GeoChat: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -424,7 +391,7 @@ public struct GeoChat { /// /// ATAK Group /// <__group role='Team Member' name='Cyan'/> -public struct Group { +public struct Group: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -446,7 +413,7 @@ public struct Group { /// /// ATAK EUD Status /// -public struct Status { +public struct Status: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -463,7 +430,7 @@ public struct Status { /// /// ATAK Contact /// -public struct Contact { +public struct Contact: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -483,7 +450,7 @@ public struct Contact { /// /// Position Location Information from ATAK -public struct PLI { +public struct PLI: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -515,18 +482,6 @@ public struct PLI { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension Team: @unchecked Sendable {} -extension MemberRole: @unchecked Sendable {} -extension TAKPacket: @unchecked Sendable {} -extension TAKPacket.OneOf_PayloadVariant: @unchecked Sendable {} -extension GeoChat: @unchecked Sendable {} -extension Group: @unchecked Sendable {} -extension Status: @unchecked Sendable {} -extension Contact: @unchecked Sendable {} -extension PLI: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift index 1b8c84de..ce1f0503 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/cannedmessages.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Canned message module configuration. -public struct CannedMessageModuleConfig { +public struct CannedMessageModuleConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -36,10 +36,6 @@ public struct CannedMessageModuleConfig { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension CannedMessageModuleConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift index 5b9c7e49..180cd698 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/channel.proto @@ -36,13 +37,15 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// FIXME: Add description of multi-channel support and how primary vs secondary channels are used. /// FIXME: explain how apps use channels for security. /// explain how remote settings and remote gpio are managed as an example -public struct ChannelSettings { +public struct ChannelSettings: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. /// /// Deprecated in favor of LoraConfig.channel_num + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var channelNum: UInt32 = 0 /// @@ -111,7 +114,7 @@ public struct ChannelSettings { /// /// This message is specifically for modules to store per-channel configuration data. -public struct ModuleSettings { +public struct ModuleSettings: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -132,7 +135,7 @@ public struct ModuleSettings { /// /// A pair of a channel number, mode and the (sharable) settings for that channel -public struct Channel { +public struct Channel: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -170,7 +173,7 @@ public struct Channel { /// cross band routing as needed. /// If a device has only a single radio (the common case) only one channel can be PRIMARY at a time /// (but any number of SECONDARY channels can't be sent received on that common frequency) - public enum Role: SwiftProtobuf.Enum { + public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -209,6 +212,13 @@ public struct Channel { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Channel.Role] = [ + .disabled, + .primary, + .secondary, + ] + } public init() {} @@ -216,26 +226,6 @@ public struct Channel { fileprivate var _settings: ChannelSettings? = nil } -#if swift(>=4.2) - -extension Channel.Role: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Channel.Role] = [ - .disabled, - .primary, - .secondary, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension ChannelSettings: @unchecked Sendable {} -extension ModuleSettings: @unchecked Sendable {} -extension Channel: @unchecked Sendable {} -extension Channel.Role: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift index f89a8e3c..d72c0ae1 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/clientonly.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -23,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// This abstraction is used to contain any configuration for provisioning a node on any client. /// It is useful for importing and exporting configurations. -public struct DeviceProfile { +public struct DeviceProfile: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -130,10 +130,6 @@ public struct DeviceProfile { fileprivate var _cannedMessages: String? = nil } -#if swift(>=5.5) && canImport(_Concurrency) -extension DeviceProfile: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift index a5f8cbc5..b3988aaa 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/config.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct Config { +public struct Config: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -113,7 +114,7 @@ public struct Config { /// /// Payload Variant - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { case device(Config.DeviceConfig) case position(Config.PositionConfig) case power(Config.PowerConfig) @@ -125,61 +126,11 @@ public struct Config { case sessionkey(Config.SessionkeyConfig) case deviceUi(DeviceUIConfig) - #if !swift(>=4.1) - public static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.device, .device): return { - guard case .device(let l) = lhs, case .device(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.position, .position): return { - guard case .position(let l) = lhs, case .position(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.power, .power): return { - guard case .power(let l) = lhs, case .power(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.network, .network): return { - guard case .network(let l) = lhs, case .network(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.display, .display): return { - guard case .display(let l) = lhs, case .display(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.lora, .lora): return { - guard case .lora(let l) = lhs, case .lora(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.bluetooth, .bluetooth): return { - guard case .bluetooth(let l) = lhs, case .bluetooth(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.security, .security): return { - guard case .security(let l) = lhs, case .security(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.sessionkey, .sessionkey): return { - guard case .sessionkey(let l) = lhs, case .sessionkey(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.deviceUi, .deviceUi): return { - guard case .deviceUi(let l) = lhs, case .deviceUi(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// Configuration - public struct DeviceConfig { + public struct DeviceConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -191,6 +142,8 @@ public struct Config { /// /// Disabling this will disable the SerialConsole by not initilizing the StreamAPI /// Moved to SecurityConfig + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var serialEnabled: Bool = false /// @@ -220,6 +173,8 @@ public struct Config { /// If true, device is considered to be "managed" by a mesh administrator /// Clients should then limit available configuration and administrative options inside the user interface /// Moved to SecurityConfig + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var isManaged: Bool = false /// @@ -231,14 +186,14 @@ public struct Config { public var tzdef: String = String() /// - /// If true, disable the default blinking LED (LED_PIN) behavior on the device + /// If true, disable the default blinking LED (LED_PIN) behavior on the device public var ledHeartbeatDisabled: Bool = false public var unknownFields = SwiftProtobuf.UnknownStorage() /// /// Defines the device's role on the Mesh network - public enum Role: SwiftProtobuf.Enum { + public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -256,6 +211,8 @@ public struct Config { /// The wifi radio and the oled screen will be put to sleep. /// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. case router // = 2 + + /// NOTE: This enum value was marked as deprecated in the .proto file case routerClient // = 3 /// @@ -356,11 +313,27 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DeviceConfig.Role] = [ + .client, + .clientMute, + .router, + .routerClient, + .repeater, + .tracker, + .sensor, + .tak, + .clientHidden, + .lostAndFound, + .takTracker, + .routerLate, + ] + } /// /// Defines the device's behavior for how messages are rebroadcast - public enum RebroadcastMode: SwiftProtobuf.Enum { + public enum RebroadcastMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -421,6 +394,16 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [ + .all, + .allSkipDecoding, + .localOnly, + .knownOnly, + .none, + .corePortnumsOnly, + ] + } public init() {} @@ -428,7 +411,7 @@ public struct Config { /// /// Position Config - public struct PositionConfig { + public struct PositionConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -450,6 +433,8 @@ public struct Config { /// /// Is GPS enabled for this node? + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var gpsEnabled: Bool = false /// @@ -460,6 +445,8 @@ public struct Config { /// /// Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var gpsAttemptTime: UInt32 = 0 /// @@ -500,7 +487,7 @@ public struct Config { /// are always included (also time if GPS-synced) /// NOTE: the more fields are included, the larger the message will be - /// leading to longer airtime and a higher risk of packet loss - public enum PositionFlags: SwiftProtobuf.Enum { + public enum PositionFlags: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -590,9 +577,24 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.PositionConfig.PositionFlags] = [ + .unset, + .altitude, + .altitudeMsl, + .geoidalSeparation, + .dop, + .hvdop, + .satinview, + .seqNo, + .timestamp, + .heading, + .speed, + ] + } - public enum GpsMode: SwiftProtobuf.Enum { + public enum GpsMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -630,6 +632,13 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.PositionConfig.GpsMode] = [ + .disabled, + .enabled, + .notPresent, + ] + } public init() {} @@ -638,13 +647,13 @@ public struct Config { /// /// Power Config\ /// See [Power Config](/docs/settings/config/power) for additional power config details. - public struct PowerConfig { + public struct PowerConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. /// - /// Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. + /// Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. /// Don't use this setting if you want to use your device with the phone apps or are using a device without a user button. /// Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles public var isPowerSaving: Bool = false @@ -698,7 +707,7 @@ public struct Config { /// /// Network Config - public struct NetworkConfig { + public struct NetworkConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -717,7 +726,7 @@ public struct Config { public var wifiPsk: String = String() /// - /// NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org` + /// NTP server to use if WiFi is conneced, defaults to `meshtastic.pool.ntp.org` public var ntpServer: String = String() /// @@ -749,7 +758,7 @@ public struct Config { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum AddressMode: SwiftProtobuf.Enum { + public enum AddressMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -781,11 +790,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.NetworkConfig.AddressMode] = [ + .dhcp, + .static, + ] + } /// /// Available flags auxiliary network protocols - public enum ProtocolFlags: SwiftProtobuf.Enum { + public enum ProtocolFlags: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -817,9 +832,15 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [ + .noBroadcast, + .udpBroadcast, + ] + } - public struct IpV4Config { + public struct IpV4Config: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -852,7 +873,7 @@ public struct Config { /// /// Display Config - public struct DisplayConfig { + public struct DisplayConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -913,7 +934,7 @@ public struct Config { /// /// How the GPS coordinates are displayed on the OLED screen. - public enum GpsCoordinateFormat: SwiftProtobuf.Enum { + public enum GpsCoordinateFormat: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -976,11 +997,21 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [ + .dec, + .dms, + .utm, + .mgrs, + .olc, + .osgr, + ] + } /// /// Unit display preference - public enum DisplayUnits: SwiftProtobuf.Enum { + public enum DisplayUnits: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1012,11 +1043,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.DisplayUnits] = [ + .metric, + .imperial, + ] + } /// /// Override OLED outo detect with this if it fails. - public enum OledType: SwiftProtobuf.Enum { + public enum OledType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1060,9 +1097,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.OledType] = [ + .oledAuto, + .oledSsd1306, + .oledSh1106, + .oledSh1107, + ] + } - public enum DisplayMode: SwiftProtobuf.Enum { + public enum DisplayMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1106,9 +1151,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.DisplayMode] = [ + .default, + .twocolor, + .inverted, + .color, + ] + } - public enum CompassOrientation: SwiftProtobuf.Enum { + public enum CompassOrientation: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1176,6 +1229,18 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.CompassOrientation] = [ + .degrees0, + .degrees90, + .degrees180, + .degrees270, + .degrees0Inverted, + .degrees90Inverted, + .degrees180Inverted, + .degrees270Inverted, + ] + } public init() {} @@ -1183,7 +1248,7 @@ public struct Config { /// /// Lora Config - public struct LoRaConfig { + public struct LoRaConfig: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1347,7 +1412,7 @@ public struct Config { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum RegionCode: SwiftProtobuf.Enum { + public enum RegionCode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1499,12 +1564,38 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.LoRaConfig.RegionCode] = [ + .unset, + .us, + .eu433, + .eu868, + .cn, + .jp, + .anz, + .kr, + .tw, + .ru, + .in, + .nz865, + .th, + .lora24, + .ua433, + .ua868, + .my433, + .my919, + .sg923, + .ph433, + .ph868, + .ph915, + ] + } /// /// Standard predefined channel settings /// Note: these mappings must match ModemPreset Choice in the device code. - public enum ModemPreset: SwiftProtobuf.Enum { + public enum ModemPreset: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1518,6 +1609,8 @@ public struct Config { /// /// Very Long Range - Slow /// Deprecated in 2.5: Works only with txco and is unusably slow + /// + /// NOTE: This enum value was marked as deprecated in the .proto file case veryLongSlow // = 2 /// @@ -1581,6 +1674,19 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.LoRaConfig.ModemPreset] = [ + .longFast, + .longSlow, + .veryLongSlow, + .mediumSlow, + .mediumFast, + .shortSlow, + .shortFast, + .longModerate, + .shortTurbo, + ] + } public init() {} @@ -1588,7 +1694,7 @@ public struct Config { fileprivate var _storage = _StorageClass.defaultInstance } - public struct BluetoothConfig { + public struct BluetoothConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1607,7 +1713,7 @@ public struct Config { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum PairingMode: SwiftProtobuf.Enum { + public enum PairingMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1645,12 +1751,19 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.BluetoothConfig.PairingMode] = [ + .randomPin, + .fixedPin, + .noPin, + ] + } public init() {} } - public struct SecurityConfig { + public struct SecurityConfig: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1694,7 +1807,7 @@ public struct Config { /// /// Blank config request, strictly for getting the session key - public struct SessionkeyConfig { + public struct SessionkeyConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1707,217 +1820,6 @@ public struct Config { public init() {} } -#if swift(>=4.2) - -extension Config.DeviceConfig.Role: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DeviceConfig.Role] = [ - .client, - .clientMute, - .router, - .routerClient, - .repeater, - .tracker, - .sensor, - .tak, - .clientHidden, - .lostAndFound, - .takTracker, - .routerLate, - ] -} - -extension Config.DeviceConfig.RebroadcastMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [ - .all, - .allSkipDecoding, - .localOnly, - .knownOnly, - .none, - .corePortnumsOnly, - ] -} - -extension Config.PositionConfig.PositionFlags: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.PositionConfig.PositionFlags] = [ - .unset, - .altitude, - .altitudeMsl, - .geoidalSeparation, - .dop, - .hvdop, - .satinview, - .seqNo, - .timestamp, - .heading, - .speed, - ] -} - -extension Config.PositionConfig.GpsMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.PositionConfig.GpsMode] = [ - .disabled, - .enabled, - .notPresent, - ] -} - -extension Config.NetworkConfig.AddressMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.NetworkConfig.AddressMode] = [ - .dhcp, - .static, - ] -} - -extension Config.NetworkConfig.ProtocolFlags: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [ - .noBroadcast, - .udpBroadcast, - ] -} - -extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [ - .dec, - .dms, - .utm, - .mgrs, - .olc, - .osgr, - ] -} - -extension Config.DisplayConfig.DisplayUnits: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.DisplayUnits] = [ - .metric, - .imperial, - ] -} - -extension Config.DisplayConfig.OledType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.OledType] = [ - .oledAuto, - .oledSsd1306, - .oledSh1106, - .oledSh1107, - ] -} - -extension Config.DisplayConfig.DisplayMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.DisplayMode] = [ - .default, - .twocolor, - .inverted, - .color, - ] -} - -extension Config.DisplayConfig.CompassOrientation: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.CompassOrientation] = [ - .degrees0, - .degrees90, - .degrees180, - .degrees270, - .degrees0Inverted, - .degrees90Inverted, - .degrees180Inverted, - .degrees270Inverted, - ] -} - -extension Config.LoRaConfig.RegionCode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.LoRaConfig.RegionCode] = [ - .unset, - .us, - .eu433, - .eu868, - .cn, - .jp, - .anz, - .kr, - .tw, - .ru, - .in, - .nz865, - .th, - .lora24, - .ua433, - .ua868, - .my433, - .my919, - .sg923, - .ph433, - .ph868, - .ph915, - ] -} - -extension Config.LoRaConfig.ModemPreset: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.LoRaConfig.ModemPreset] = [ - .longFast, - .longSlow, - .veryLongSlow, - .mediumSlow, - .mediumFast, - .shortSlow, - .shortFast, - .longModerate, - .shortTurbo, - ] -} - -extension Config.BluetoothConfig.PairingMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.BluetoothConfig.PairingMode] = [ - .randomPin, - .fixedPin, - .noPin, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension Config: @unchecked Sendable {} -extension Config.OneOf_PayloadVariant: @unchecked Sendable {} -extension Config.DeviceConfig: @unchecked Sendable {} -extension Config.DeviceConfig.Role: @unchecked Sendable {} -extension Config.DeviceConfig.RebroadcastMode: @unchecked Sendable {} -extension Config.PositionConfig: @unchecked Sendable {} -extension Config.PositionConfig.PositionFlags: @unchecked Sendable {} -extension Config.PositionConfig.GpsMode: @unchecked Sendable {} -extension Config.PowerConfig: @unchecked Sendable {} -extension Config.NetworkConfig: @unchecked Sendable {} -extension Config.NetworkConfig.AddressMode: @unchecked Sendable {} -extension Config.NetworkConfig.ProtocolFlags: @unchecked Sendable {} -extension Config.NetworkConfig.IpV4Config: @unchecked Sendable {} -extension Config.DisplayConfig: @unchecked Sendable {} -extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {} -extension Config.DisplayConfig.DisplayUnits: @unchecked Sendable {} -extension Config.DisplayConfig.OledType: @unchecked Sendable {} -extension Config.DisplayConfig.DisplayMode: @unchecked Sendable {} -extension Config.DisplayConfig.CompassOrientation: @unchecked Sendable {} -extension Config.LoRaConfig: @unchecked Sendable {} -extension Config.LoRaConfig.RegionCode: @unchecked Sendable {} -extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {} -extension Config.BluetoothConfig: @unchecked Sendable {} -extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {} -extension Config.SecurityConfig: @unchecked Sendable {} -extension Config.SessionkeyConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" @@ -2425,7 +2327,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.onBatteryShutdownAfterSecs != 0 { try visitor.visitSingularUInt32Field(value: self.onBatteryShutdownAfterSecs, fieldNumber: 2) } - if self.adcMultiplierOverride != 0 { + if self.adcMultiplierOverride.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.adcMultiplierOverride, fieldNumber: 3) } if self.waitBluetoothSecs != 0 { @@ -2892,7 +2794,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if _storage._codingRate != 0 { try visitor.visitSingularUInt32Field(value: _storage._codingRate, fieldNumber: 5) } - if _storage._frequencyOffset != 0 { + if _storage._frequencyOffset.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._frequencyOffset, fieldNumber: 6) } if _storage._region != .unset { @@ -2916,7 +2818,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if _storage._sx126XRxBoostedGain != false { try visitor.visitSingularBoolField(value: _storage._sx126XRxBoostedGain, fieldNumber: 13) } - if _storage._overrideFrequency != 0 { + if _storage._overrideFrequency.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._overrideFrequency, fieldNumber: 14) } if _storage._paFanDisabled != false { @@ -3133,8 +3035,8 @@ extension Config.SessionkeyConfig: SwiftProtobuf.Message, SwiftProtobuf._Message public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { diff --git a/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift index a2ec180e..6847c0e3 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/connection_status.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct DeviceConnectionStatus { +public struct DeviceConnectionStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -81,7 +81,7 @@ public struct DeviceConnectionStatus { /// /// WiFi connection status -public struct WifiConnectionStatus { +public struct WifiConnectionStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -114,7 +114,7 @@ public struct WifiConnectionStatus { /// /// Ethernet connection status -public struct EthernetConnectionStatus { +public struct EthernetConnectionStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -139,7 +139,7 @@ public struct EthernetConnectionStatus { /// /// Ethernet or WiFi connection status -public struct NetworkConnectionStatus { +public struct NetworkConnectionStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -167,7 +167,7 @@ public struct NetworkConnectionStatus { /// /// Bluetooth connection status -public struct BluetoothConnectionStatus { +public struct BluetoothConnectionStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -191,7 +191,7 @@ public struct BluetoothConnectionStatus { /// /// Serial connection status -public struct SerialConnectionStatus { +public struct SerialConnectionStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -209,15 +209,6 @@ public struct SerialConnectionStatus { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension DeviceConnectionStatus: @unchecked Sendable {} -extension WifiConnectionStatus: @unchecked Sendable {} -extension EthernetConnectionStatus: @unchecked Sendable {} -extension NetworkConnectionStatus: @unchecked Sendable {} -extension BluetoothConnectionStatus: @unchecked Sendable {} -extension SerialConnectionStatus: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift index 5e13b166..0281d6c1 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/device_ui.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum Theme: SwiftProtobuf.Enum { +public enum Theme: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -58,24 +59,18 @@ public enum Theme: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Theme: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Theme] = [ .dark, .light, .red, ] -} -#endif // swift(>=4.2) +} /// /// Localization -public enum Language: SwiftProtobuf.Enum { +public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -203,11 +198,6 @@ public enum Language: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Language: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Language] = [ .english, @@ -229,11 +219,10 @@ extension Language: CaseIterable { .simplifiedChinese, .traditionalChinese, ] + } -#endif // swift(>=4.2) - -public struct DeviceUIConfig { +public struct DeviceUIConfig: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -246,21 +235,21 @@ public struct DeviceUIConfig { } /// - /// TFT display brightness 1..255 + /// TFT display brightness 1..255 public var screenBrightness: UInt32 { get {return _storage._screenBrightness} set {_uniqueStorage()._screenBrightness = newValue} } /// - /// Screen timeout 0..900 + /// Screen timeout 0..900 public var screenTimeout: UInt32 { get {return _storage._screenTimeout} set {_uniqueStorage()._screenTimeout = newValue} } /// - /// Screen/Settings lock enabled + /// Screen/Settings lock enabled public var screenLock: Bool { get {return _storage._screenLock} set {_uniqueStorage()._screenLock = newValue} @@ -277,7 +266,7 @@ public struct DeviceUIConfig { } /// - /// Color theme + /// Color theme public var theme: Theme { get {return _storage._theme} set {_uniqueStorage()._theme = newValue} @@ -301,14 +290,14 @@ public struct DeviceUIConfig { } /// - /// Localization + /// Localization public var language: Language { get {return _storage._language} set {_uniqueStorage()._language = newValue} } /// - /// Node list filter + /// Node list filter public var nodeFilter: NodeFilter { get {return _storage._nodeFilter ?? NodeFilter()} set {_uniqueStorage()._nodeFilter = newValue} @@ -336,6 +325,17 @@ public struct DeviceUIConfig { set {_uniqueStorage()._calibrationData = newValue} } + /// + /// Map related data + public var mapData: Map { + get {return _storage._mapData ?? Map()} + set {_uniqueStorage()._mapData = newValue} + } + /// Returns true if `mapData` has been explicitly set. + public var hasMapData: Bool {return _storage._mapData != nil} + /// Clears the value of `mapData`. Subsequent reads from it will return its default value. + public mutating func clearMapData() {_uniqueStorage()._mapData = nil} + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -343,7 +343,7 @@ public struct DeviceUIConfig { fileprivate var _storage = _StorageClass.defaultInstance } -public struct NodeFilter { +public struct NodeFilter: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -381,7 +381,7 @@ public struct NodeFilter { public init() {} } -public struct NodeHighlight { +public struct NodeHighlight: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -411,13 +411,58 @@ public struct NodeHighlight { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension Theme: @unchecked Sendable {} -extension Language: @unchecked Sendable {} -extension DeviceUIConfig: @unchecked Sendable {} -extension NodeFilter: @unchecked Sendable {} -extension NodeHighlight: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) +public struct GeoPoint: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Zoom level + public var zoom: Int32 = 0 + + /// + /// Coordinate: latitude + public var latitude: Int32 = 0 + + /// + /// Coordinate: longitude + public var longitude: Int32 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Map: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Home coordinates + public var home: GeoPoint { + get {return _home ?? GeoPoint()} + set {_home = newValue} + } + /// Returns true if `home` has been explicitly set. + public var hasHome: Bool {return self._home != nil} + /// Clears the value of `home`. Subsequent reads from it will return its default value. + public mutating func clearHome() {self._home = nil} + + /// + /// Map tile style + public var style: String = String() + + /// + /// Map scroll follows GPS + public var followGps: Bool = false + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _home: GeoPoint? = nil +} // MARK: - Code below here is support for the SwiftProtobuf runtime. @@ -471,6 +516,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement 12: .standard(proto: "node_filter"), 13: .standard(proto: "node_highlight"), 14: .standard(proto: "calibration_data"), + 15: .standard(proto: "map_data"), ] fileprivate class _StorageClass { @@ -488,6 +534,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement var _nodeFilter: NodeFilter? = nil var _nodeHighlight: NodeHighlight? = nil var _calibrationData: Data = Data() + var _mapData: Map? = nil #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. @@ -516,6 +563,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement _nodeFilter = source._nodeFilter _nodeHighlight = source._nodeHighlight _calibrationData = source._calibrationData + _mapData = source._mapData } } @@ -548,6 +596,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement case 12: try { try decoder.decodeSingularMessageField(value: &_storage._nodeFilter) }() case 13: try { try decoder.decodeSingularMessageField(value: &_storage._nodeHighlight) }() case 14: try { try decoder.decodeSingularBytesField(value: &_storage._calibrationData) }() + case 15: try { try decoder.decodeSingularMessageField(value: &_storage._mapData) }() default: break } } @@ -602,6 +651,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement if !_storage._calibrationData.isEmpty { try visitor.visitSingularBytesField(value: _storage._calibrationData, fieldNumber: 14) } + try { if let v = _storage._mapData { + try visitor.visitSingularMessageField(value: v, fieldNumber: 15) + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -625,6 +677,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement if _storage._nodeFilter != rhs_storage._nodeFilter {return false} if _storage._nodeHighlight != rhs_storage._nodeHighlight {return false} if _storage._calibrationData != rhs_storage._calibrationData {return false} + if _storage._mapData != rhs_storage._mapData {return false} return true } if !storagesAreEqual {return false} @@ -757,3 +810,95 @@ extension NodeHighlight: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa return true } } + +extension GeoPoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GeoPoint" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "zoom"), + 2: .same(proto: "latitude"), + 3: .same(proto: "longitude"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularInt32Field(value: &self.zoom) }() + case 2: try { try decoder.decodeSingularInt32Field(value: &self.latitude) }() + case 3: try { try decoder.decodeSingularInt32Field(value: &self.longitude) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.zoom != 0 { + try visitor.visitSingularInt32Field(value: self.zoom, fieldNumber: 1) + } + if self.latitude != 0 { + try visitor.visitSingularInt32Field(value: self.latitude, fieldNumber: 2) + } + if self.longitude != 0 { + try visitor.visitSingularInt32Field(value: self.longitude, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: GeoPoint, rhs: GeoPoint) -> Bool { + if lhs.zoom != rhs.zoom {return false} + if lhs.latitude != rhs.latitude {return false} + if lhs.longitude != rhs.longitude {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Map: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".Map" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "home"), + 2: .same(proto: "style"), + 3: .standard(proto: "follow_gps"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._home) }() + case 2: try { try decoder.decodeSingularStringField(value: &self.style) }() + case 3: try { try decoder.decodeSingularBoolField(value: &self.followGps) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._home { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + if !self.style.isEmpty { + try visitor.visitSingularStringField(value: self.style, fieldNumber: 2) + } + if self.followGps != false { + try visitor.visitSingularBoolField(value: self.followGps, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Map, rhs: Map) -> Bool { + if lhs._home != rhs._home {return false} + if lhs.style != rhs.style {return false} + if lhs.followGps != rhs.followGps {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift index 34a33373..72248719 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/deviceonly.proto @@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Position with static location information only for NodeDBLite -public struct PositionLite { +public struct PositionLite: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -57,13 +58,15 @@ public struct PositionLite { public init() {} } -public struct UserLite { +public struct UserLite: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. /// /// This is the addr of the radio. + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var macaddr: Data = Data() /// @@ -102,7 +105,7 @@ public struct UserLite { public init() {} } -public struct NodeInfoLite { +public struct NodeInfoLite: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -205,7 +208,7 @@ public struct NodeInfoLite { } /// - /// Last byte of the node number of the node that should be used as the next hop to reach this node. + /// Last byte of the node number of the node that should be used as the next hop to reach this node. public var nextHop: UInt32 { get {return _storage._nextHop} set {_uniqueStorage()._nextHop = newValue} @@ -224,7 +227,7 @@ public struct NodeInfoLite { /// FIXME, since we write this each time we enter deep sleep (and have infinite /// flash) it would be better to use some sort of append only data structure for /// the receive queue and use the preferences store for the other stuff -public struct DeviceState { +public struct DeviceState: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -284,13 +287,18 @@ public struct DeviceState { /// Used only during development. /// Indicates developer is testing and changes should never be saved to flash. /// Deprecated in 2.3.1 + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var noSave: Bool { get {return _storage._noSave} set {_uniqueStorage()._noSave = newValue} } /// - /// Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. + /// Previously used to manage GPS factory resets. + /// Deprecated in 2.5.23 + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var didGpsReset: Bool { get {return _storage._didGpsReset} set {_uniqueStorage()._didGpsReset = newValue} @@ -316,13 +324,6 @@ public struct DeviceState { set {_uniqueStorage()._nodeRemoteHardwarePins = newValue} } - /// - /// New lite version of NodeDB to decrease memory footprint - public var nodeDbLite: [NodeInfoLite] { - get {return _storage._nodeDbLite} - set {_uniqueStorage()._nodeDbLite = newValue} - } - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -330,9 +331,29 @@ public struct DeviceState { fileprivate var _storage = _StorageClass.defaultInstance } +public struct NodeDatabase: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// A version integer used to invalidate old save files when we make + /// incompatible changes This integer is set at build time and is private to + /// NodeDB.cpp in the device code. + public var version: UInt32 = 0 + + /// + /// New lite version of NodeDB to decrease memory footprint + public var nodes: [NodeInfoLite] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + /// /// The on-disk saved channels -public struct ChannelFile { +public struct ChannelFile: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -352,13 +373,74 @@ public struct ChannelFile { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension PositionLite: @unchecked Sendable {} -extension UserLite: @unchecked Sendable {} -extension NodeInfoLite: @unchecked Sendable {} -extension DeviceState: @unchecked Sendable {} -extension ChannelFile: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) +/// +/// The on-disk backup of the node's preferences +public struct BackupPreferences: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// The version of the backup + public var version: UInt32 = 0 + + /// + /// The timestamp of the backup (if node has time) + public var timestamp: UInt32 = 0 + + /// + /// The node's configuration + public var config: LocalConfig { + get {return _config ?? LocalConfig()} + set {_config = newValue} + } + /// Returns true if `config` has been explicitly set. + public var hasConfig: Bool {return self._config != nil} + /// Clears the value of `config`. Subsequent reads from it will return its default value. + public mutating func clearConfig() {self._config = nil} + + /// + /// The node's module configuration + public var moduleConfig: LocalModuleConfig { + get {return _moduleConfig ?? LocalModuleConfig()} + set {_moduleConfig = newValue} + } + /// Returns true if `moduleConfig` has been explicitly set. + public var hasModuleConfig: Bool {return self._moduleConfig != nil} + /// Clears the value of `moduleConfig`. Subsequent reads from it will return its default value. + public mutating func clearModuleConfig() {self._moduleConfig = nil} + + /// + /// The node's channels + public var channels: ChannelFile { + get {return _channels ?? ChannelFile()} + set {_channels = newValue} + } + /// Returns true if `channels` has been explicitly set. + public var hasChannels: Bool {return self._channels != nil} + /// Clears the value of `channels`. Subsequent reads from it will return its default value. + public mutating func clearChannels() {self._channels = nil} + + /// + /// The node's user (owner) information + public var owner: User { + get {return _owner ?? User()} + set {_owner = newValue} + } + /// Returns true if `owner` has been explicitly set. + public var hasOwner: Bool {return self._owner != nil} + /// Clears the value of `owner`. Subsequent reads from it will return its default value. + public mutating func clearOwner() {self._owner = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _config: LocalConfig? = nil + fileprivate var _moduleConfig: LocalModuleConfig? = nil + fileprivate var _channels: ChannelFile? = nil + fileprivate var _owner: User? = nil +} // MARK: - Code below here is support for the SwiftProtobuf runtime. @@ -595,7 +677,7 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat try { if let v = _storage._position { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) } }() - if _storage._snr != 0 { + if _storage._snr.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4) } if _storage._lastHeard != 0 { @@ -664,7 +746,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 11: .standard(proto: "did_gps_reset"), 12: .standard(proto: "rx_waypoint"), 13: .standard(proto: "node_remote_hardware_pins"), - 14: .standard(proto: "node_db_lite"), ] fileprivate class _StorageClass { @@ -677,7 +758,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _didGpsReset: Bool = false var _rxWaypoint: MeshPacket? = nil var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = [] - var _nodeDbLite: [NodeInfoLite] = [] #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. @@ -701,7 +781,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati _didGpsReset = source._didGpsReset _rxWaypoint = source._rxWaypoint _nodeRemoteHardwarePins = source._nodeRemoteHardwarePins - _nodeDbLite = source._nodeDbLite } } @@ -729,7 +808,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }() case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }() case 13: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeRemoteHardwarePins) }() - case 14: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeDbLite) }() default: break } } @@ -769,9 +847,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if !_storage._nodeRemoteHardwarePins.isEmpty { try visitor.visitRepeatedMessageField(value: _storage._nodeRemoteHardwarePins, fieldNumber: 13) } - if !_storage._nodeDbLite.isEmpty { - try visitor.visitRepeatedMessageField(value: _storage._nodeDbLite, fieldNumber: 14) - } } try unknownFields.traverse(visitor: &visitor) } @@ -790,7 +865,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if _storage._didGpsReset != rhs_storage._didGpsReset {return false} if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false} if _storage._nodeRemoteHardwarePins != rhs_storage._nodeRemoteHardwarePins {return false} - if _storage._nodeDbLite != rhs_storage._nodeDbLite {return false} return true } if !storagesAreEqual {return false} @@ -800,6 +874,44 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati } } +extension NodeDatabase: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".NodeDatabase" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "version"), + 2: .same(proto: "nodes"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.version) }() + case 2: try { try decoder.decodeRepeatedMessageField(value: &self.nodes) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.version != 0 { + try visitor.visitSingularUInt32Field(value: self.version, fieldNumber: 1) + } + if !self.nodes.isEmpty { + try visitor.visitRepeatedMessageField(value: self.nodes, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: NodeDatabase, rhs: NodeDatabase) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.nodes != rhs.nodes {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChannelFile" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -837,3 +949,69 @@ extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati return true } } + +extension BackupPreferences: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BackupPreferences" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "version"), + 2: .same(proto: "timestamp"), + 3: .same(proto: "config"), + 4: .standard(proto: "module_config"), + 5: .same(proto: "channels"), + 6: .same(proto: "owner"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.version) }() + case 2: try { try decoder.decodeSingularFixed32Field(value: &self.timestamp) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._config) }() + case 4: try { try decoder.decodeSingularMessageField(value: &self._moduleConfig) }() + case 5: try { try decoder.decodeSingularMessageField(value: &self._channels) }() + case 6: try { try decoder.decodeSingularMessageField(value: &self._owner) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.version != 0 { + try visitor.visitSingularUInt32Field(value: self.version, fieldNumber: 1) + } + if self.timestamp != 0 { + try visitor.visitSingularFixed32Field(value: self.timestamp, fieldNumber: 2) + } + try { if let v = self._config { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() + try { if let v = self._moduleConfig { + try visitor.visitSingularMessageField(value: v, fieldNumber: 4) + } }() + try { if let v = self._channels { + try visitor.visitSingularMessageField(value: v, fieldNumber: 5) + } }() + try { if let v = self._owner { + try visitor.visitSingularMessageField(value: v, fieldNumber: 6) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: BackupPreferences, rhs: BackupPreferences) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.timestamp != rhs.timestamp {return false} + if lhs._config != rhs._config {return false} + if lhs._moduleConfig != rhs._moduleConfig {return false} + if lhs._channels != rhs._channels {return false} + if lhs._owner != rhs._owner {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift new file mode 100644 index 00000000..165ed685 --- /dev/null +++ b/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift @@ -0,0 +1,328 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// swiftlint:disable all +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: meshtastic/interdevice.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +public enum MessageType: SwiftProtobuf.Enum, Swift.CaseIterable { + public typealias RawValue = Int + case ack // = 0 + + /// in ms + case collectInterval // = 160 + + /// duration ms + case beepOn // = 161 + + /// cancel prematurely + case beepOff // = 162 + case shutdown // = 163 + case powerOn // = 164 + case scd41Temp // = 176 + case scd41Humidity // = 177 + case scd41Co2 // = 178 + case aht20Temp // = 179 + case aht20Humidity // = 180 + case tvocIndex // = 181 + case UNRECOGNIZED(Int) + + public init() { + self = .ack + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .ack + case 160: self = .collectInterval + case 161: self = .beepOn + case 162: self = .beepOff + case 163: self = .shutdown + case 164: self = .powerOn + case 176: self = .scd41Temp + case 177: self = .scd41Humidity + case 178: self = .scd41Co2 + case 179: self = .aht20Temp + case 180: self = .aht20Humidity + case 181: self = .tvocIndex + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .ack: return 0 + case .collectInterval: return 160 + case .beepOn: return 161 + case .beepOff: return 162 + case .shutdown: return 163 + case .powerOn: return 164 + case .scd41Temp: return 176 + case .scd41Humidity: return 177 + case .scd41Co2: return 178 + case .aht20Temp: return 179 + case .aht20Humidity: return 180 + case .tvocIndex: return 181 + case .UNRECOGNIZED(let i): return i + } + } + + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [MessageType] = [ + .ack, + .collectInterval, + .beepOn, + .beepOff, + .shutdown, + .powerOn, + .scd41Temp, + .scd41Humidity, + .scd41Co2, + .aht20Temp, + .aht20Humidity, + .tvocIndex, + ] + +} + +public struct SensorData: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The message type + public var type: MessageType = .ack + + /// The sensor data, either as a float or an uint32 + public var data: SensorData.OneOf_Data? = nil + + public var floatValue: Float { + get { + if case .floatValue(let v)? = data {return v} + return 0 + } + set {data = .floatValue(newValue)} + } + + public var uint32Value: UInt32 { + get { + if case .uint32Value(let v)? = data {return v} + return 0 + } + set {data = .uint32Value(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// The sensor data, either as a float or an uint32 + public enum OneOf_Data: Equatable, Sendable { + case floatValue(Float) + case uint32Value(UInt32) + + } + + public init() {} +} + +public struct InterdeviceMessage: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The message data + public var data: InterdeviceMessage.OneOf_Data? = nil + + public var nmea: String { + get { + if case .nmea(let v)? = data {return v} + return String() + } + set {data = .nmea(newValue)} + } + + public var sensor: SensorData { + get { + if case .sensor(let v)? = data {return v} + return SensorData() + } + set {data = .sensor(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// The message data + public enum OneOf_Data: Equatable, Sendable { + case nmea(String) + case sensor(SensorData) + + } + + public init() {} +} + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "meshtastic" + +extension MessageType: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "ACK"), + 160: .same(proto: "COLLECT_INTERVAL"), + 161: .same(proto: "BEEP_ON"), + 162: .same(proto: "BEEP_OFF"), + 163: .same(proto: "SHUTDOWN"), + 164: .same(proto: "POWER_ON"), + 176: .same(proto: "SCD41_TEMP"), + 177: .same(proto: "SCD41_HUMIDITY"), + 178: .same(proto: "SCD41_CO2"), + 179: .same(proto: "AHT20_TEMP"), + 180: .same(proto: "AHT20_HUMIDITY"), + 181: .same(proto: "TVOC_INDEX"), + ] +} + +extension SensorData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SensorData" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "type"), + 2: .standard(proto: "float_value"), + 3: .standard(proto: "uint32_value"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.type) }() + case 2: try { + var v: Float? + try decoder.decodeSingularFloatField(value: &v) + if let v = v { + if self.data != nil {try decoder.handleConflictingOneOf()} + self.data = .floatValue(v) + } + }() + case 3: try { + var v: UInt32? + try decoder.decodeSingularUInt32Field(value: &v) + if let v = v { + if self.data != nil {try decoder.handleConflictingOneOf()} + self.data = .uint32Value(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.type != .ack { + try visitor.visitSingularEnumField(value: self.type, fieldNumber: 1) + } + switch self.data { + case .floatValue?: try { + guard case .floatValue(let v)? = self.data else { preconditionFailure() } + try visitor.visitSingularFloatField(value: v, fieldNumber: 2) + }() + case .uint32Value?: try { + guard case .uint32Value(let v)? = self.data else { preconditionFailure() } + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3) + }() + case nil: break + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: SensorData, rhs: SensorData) -> Bool { + if lhs.type != rhs.type {return false} + if lhs.data != rhs.data {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension InterdeviceMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".InterdeviceMessage" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "nmea"), + 2: .same(proto: "sensor"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: String? + try decoder.decodeSingularStringField(value: &v) + if let v = v { + if self.data != nil {try decoder.handleConflictingOneOf()} + self.data = .nmea(v) + } + }() + case 2: try { + var v: SensorData? + var hadOneofValue = false + if let current = self.data { + hadOneofValue = true + if case .sensor(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.data = .sensor(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + switch self.data { + case .nmea?: try { + guard case .nmea(let v)? = self.data else { preconditionFailure() } + try visitor.visitSingularStringField(value: v, fieldNumber: 1) + }() + case .sensor?: try { + guard case .sensor(let v)? = self.data else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + }() + case nil: break + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: InterdeviceMessage, rhs: InterdeviceMessage) -> Bool { + if lhs.data != rhs.data {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift index 0af27466..c3356286 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/localonly.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct LocalConfig { +public struct LocalConfig: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -129,7 +129,7 @@ public struct LocalConfig { fileprivate var _storage = _StorageClass.defaultInstance } -public struct LocalModuleConfig { +public struct LocalModuleConfig: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -293,11 +293,6 @@ public struct LocalModuleConfig { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=5.5) && canImport(_Concurrency) -extension LocalConfig: @unchecked Sendable {} -extension LocalModuleConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift index bea1d14d..8c869ba8 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/mesh.proto @@ -25,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// bin/build-all.sh script. /// Because they will be used to find firmware filenames in the android app for OTA updates. /// To match the old style filenames, _ is converted to -, p is converted to . -public enum HardwareModel: SwiftProtobuf.Enum { +public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -290,7 +291,7 @@ public enum HardwareModel: SwiftProtobuf.Enum { case cdebyteEoraS3 // = 61 /// - /// TWC_MESH_V4 + /// TWC_MESH_V4 /// Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS case twcMeshV4 // = 62 @@ -402,6 +403,10 @@ public enum HardwareModel: SwiftProtobuf.Enum { /// https://www.loraitalia.it case meshlink // = 87 + /// + /// Seeed XIAO nRF52840 + Wio SX1262 kit + case xiaoNrf52Kit // = 88 + /// /// ------------------------------------------------------------------------------------------------------------------------------------------ /// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. @@ -503,6 +508,7 @@ public enum HardwareModel: SwiftProtobuf.Enum { case 85: self = .routastic case 86: self = .meshTab case 87: self = .meshlink + case 88: self = .xiaoNrf52Kit case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } @@ -598,16 +604,12 @@ public enum HardwareModel: SwiftProtobuf.Enum { case .routastic: return 85 case .meshTab: return 86 case .meshlink: return 87 + case .xiaoNrf52Kit: return 88 case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } } -} - -#if swift(>=4.2) - -extension HardwareModel: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [HardwareModel] = [ .unset, @@ -698,15 +700,15 @@ extension HardwareModel: CaseIterable { .routastic, .meshTab, .meshlink, + .xiaoNrf52Kit, .privateHw, ] -} -#endif // swift(>=4.2) +} /// /// Shared constants between device and phone -public enum Constants: SwiftProtobuf.Enum { +public enum Constants: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -741,26 +743,20 @@ public enum Constants: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Constants: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Constants] = [ .zero, .dataPayloadLen, ] -} -#endif // swift(>=4.2) +} /// /// Error codes for critical errors /// The device might report these fault codes on the screen. /// If you encounter a fault code, please post on the meshtastic.discourse.group /// and we'll try to help. -public enum CriticalErrorCode: SwiftProtobuf.Enum { +public enum CriticalErrorCode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -869,11 +865,6 @@ public enum CriticalErrorCode: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension CriticalErrorCode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [CriticalErrorCode] = [ .none, @@ -891,15 +882,14 @@ extension CriticalErrorCode: CaseIterable { .flashCorruptionRecoverable, .flashCorruptionUnrecoverable, ] -} -#endif // swift(>=4.2) +} /// /// Enum for modules excluded from a device's configuration. /// Each value represents a ModuleConfigType that can be toggled as excluded /// by setting its corresponding bit in the `excluded_modules` bitmask field. -public enum ExcludedModules: SwiftProtobuf.Enum { +public enum ExcludedModules: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1003,11 +993,6 @@ public enum ExcludedModules: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension ExcludedModules: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [ExcludedModules] = [ .excludedNone, @@ -1025,13 +1010,12 @@ extension ExcludedModules: CaseIterable { .detectionsensorConfig, .paxcounterConfig, ] -} -#endif // swift(>=4.2) +} /// /// A GPS Position -public struct Position { +public struct Position: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1248,7 +1232,7 @@ public struct Position { /// /// How the location was acquired: manual, onboard GPS, external (EUD) GPS - public enum LocSource: SwiftProtobuf.Enum { + public enum LocSource: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1292,12 +1276,20 @@ public struct Position { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Position.LocSource] = [ + .locUnset, + .locManual, + .locInternal, + .locExternal, + ] + } /// /// How the altitude was acquired: manual, GPS int/ext, etc /// Default: same as location_source if present - public enum AltSource: SwiftProtobuf.Enum { + public enum AltSource: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1347,6 +1339,15 @@ public struct Position { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Position.AltSource] = [ + .altUnset, + .altManual, + .altInternal, + .altExternal, + .altBarometric, + ] + } public init() {} @@ -1354,31 +1355,6 @@ public struct Position { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=4.2) - -extension Position.LocSource: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Position.LocSource] = [ - .locUnset, - .locManual, - .locInternal, - .locExternal, - ] -} - -extension Position.AltSource: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Position.AltSource] = [ - .altUnset, - .altManual, - .altInternal, - .altExternal, - .altBarometric, - ] -} - -#endif // swift(>=4.2) - /// /// Broadcast when a newly powered mesh node wants to find a node num it can use /// Sent from the phone over bluetooth to set the user id for the owner of this node. @@ -1400,7 +1376,7 @@ extension Position.AltSource: CaseIterable { /// A few nodenums are reserved and will never be requested: /// 0xff - broadcast /// 0 through 3 - for future use -public struct User { +public struct User: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1425,6 +1401,8 @@ public struct User { /// Deprecated in Meshtastic 2.1.x /// This is the addr of the radio. /// Not populated by the phone, but added by the esp32 when broadcasting + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var macaddr: Data = Data() /// @@ -1456,7 +1434,7 @@ public struct User { /// /// A message used in a traceroute -public struct RouteDiscovery { +public struct RouteDiscovery: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1484,7 +1462,7 @@ public struct RouteDiscovery { /// /// A Routing control Data packet handled by the routing module -public struct Routing { +public struct Routing: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1524,7 +1502,7 @@ public struct Routing { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_Variant: Equatable { + public enum OneOf_Variant: Equatable, Sendable { /// /// A route request going from the requester case routeRequest(RouteDiscovery) @@ -1536,34 +1514,12 @@ public struct Routing { /// in addition to ack.fail_id to provide details on the type of failure). case errorReason(Routing.Error) - #if !swift(>=4.1) - public static func ==(lhs: Routing.OneOf_Variant, rhs: Routing.OneOf_Variant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.routeRequest, .routeRequest): return { - guard case .routeRequest(let l) = lhs, case .routeRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.routeReply, .routeReply): return { - guard case .routeReply(let l) = lhs, case .routeReply(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.errorReason, .errorReason): return { - guard case .errorReason(let l) = lhs, case .errorReason(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide /// details on the type of failure). - public enum Error: SwiftProtobuf.Enum { + public enum Error: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1681,42 +1637,36 @@ public struct Routing { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Routing.Error] = [ + .none, + .noRoute, + .gotNak, + .timeout, + .noInterface, + .maxRetransmit, + .noChannel, + .tooLarge, + .noResponse, + .dutyCycleLimit, + .badRequest, + .notAuthorized, + .pkiFailed, + .pkiUnknownPubkey, + .adminBadSessionKey, + .adminPublicKeyUnauthorized, + ] + } public init() {} } -#if swift(>=4.2) - -extension Routing.Error: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Routing.Error] = [ - .none, - .noRoute, - .gotNak, - .timeout, - .noInterface, - .maxRetransmit, - .noChannel, - .tooLarge, - .noResponse, - .dutyCycleLimit, - .badRequest, - .notAuthorized, - .pkiFailed, - .pkiUnknownPubkey, - .adminBadSessionKey, - .adminPublicKeyUnauthorized, - ] -} - -#endif // swift(>=4.2) - /// /// (Formerly called SubPacket) /// The payload portion fo a packet, this is the actual bytes that are sent /// inside a radio packet (because from/to are broken out by the comms library) -public struct DataMessage { +public struct DataMessage: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1783,7 +1733,7 @@ public struct DataMessage { /// /// Waypoint message, used to share arbitrary locations across the mesh -public struct Waypoint { +public struct Waypoint: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1845,7 +1795,7 @@ public struct Waypoint { /// /// This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server -public struct MqttClientProxyMessage { +public struct MqttClientProxyMessage: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1886,7 +1836,7 @@ public struct MqttClientProxyMessage { /// /// The actual service envelope payload or text for mqtt pub / sub - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable { /// /// Bytes case data(Data) @@ -1894,24 +1844,6 @@ public struct MqttClientProxyMessage { /// Text case text(String) - #if !swift(>=4.1) - public static func ==(lhs: MqttClientProxyMessage.OneOf_PayloadVariant, rhs: MqttClientProxyMessage.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.data, .data): return { - guard case .data(let l) = lhs, case .data(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.text, .text): return { - guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -1921,7 +1853,7 @@ public struct MqttClientProxyMessage { /// A packet envelope sent/received over the mesh /// only payload_variant is sent in the payload portion of the LORA packet. /// The other fields are either not sent at all, or sent in the special 16 byte LORA header. -public struct MeshPacket { +public struct MeshPacket: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2055,6 +1987,8 @@ public struct MeshPacket { /// /// Describe if this message is delayed + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var delayed: MeshPacket.Delayed { get {return _storage._delayed} set {_uniqueStorage()._delayed = newValue} @@ -2090,7 +2024,7 @@ public struct MeshPacket { } /// - /// Last byte of the node number of the node that should be used as the next hop in routing. + /// Last byte of the node number of the node that should be used as the next hop in routing. /// Set by the firmware internally, clients are not supposed to set this. public var nextHop: UInt32 { get {return _storage._nextHop} @@ -2116,7 +2050,7 @@ public struct MeshPacket { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable { /// /// TODO: REPLACE case decoded(DataMessage) @@ -2124,24 +2058,6 @@ public struct MeshPacket { /// TODO: REPLACE case encrypted(Data) - #if !swift(>=4.1) - public static func ==(lhs: MeshPacket.OneOf_PayloadVariant, rhs: MeshPacket.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.decoded, .decoded): return { - guard case .decoded(let l) = lhs, case .decoded(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.encrypted, .encrypted): return { - guard case .encrypted(let l) = lhs, case .encrypted(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// @@ -2163,7 +2079,7 @@ public struct MeshPacket { /// So I bit the bullet and implemented a new (internal - not sent over the air) /// field in MeshPacket called 'priority'. /// And the transmission queue in the router object is now a priority queue. - public enum Priority: SwiftProtobuf.Enum { + public enum Priority: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -2247,11 +2163,25 @@ public struct MeshPacket { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [MeshPacket.Priority] = [ + .unset, + .min, + .background, + .default, + .reliable, + .response, + .high, + .alert, + .ack, + .max, + ] + } /// /// Identify if this is a delayed packet - public enum Delayed: SwiftProtobuf.Enum { + public enum Delayed: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -2289,6 +2219,13 @@ public struct MeshPacket { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [MeshPacket.Delayed] = [ + .noDelay, + .broadcast, + .direct, + ] + } public init() {} @@ -2296,35 +2233,6 @@ public struct MeshPacket { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=4.2) - -extension MeshPacket.Priority: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [MeshPacket.Priority] = [ - .unset, - .min, - .background, - .default, - .reliable, - .response, - .high, - .alert, - .ack, - .max, - ] -} - -extension MeshPacket.Delayed: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [MeshPacket.Delayed] = [ - .noDelay, - .broadcast, - .direct, - ] -} - -#endif // swift(>=4.2) - /// /// The bluetooth to device link: /// Old BTLE protocol docs from TODO, merge in above and make real docs... @@ -2342,7 +2250,7 @@ extension MeshPacket.Delayed: CaseIterable { /// level etc) SET_CONFIG (switches device to a new set of radio params and /// preshared key, drops all existing nodes, force our node to rejoin this new group) /// Full information about a node on the mesh -public struct NodeInfo { +public struct NodeInfo: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2455,7 +2363,7 @@ public struct NodeInfo { /// Unique local debugging info for this node /// Note: we don't include position or the user info, because that will come in the /// Sent to the phone in response to WantNodes. -public struct MyNodeInfo { +public struct MyNodeInfo: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2494,7 +2402,7 @@ public struct MyNodeInfo { /// on the message it is assumed to be a continuation of the previously sent message. /// This allows the device code to use fixed maxlen 64 byte strings for messages, /// and then extend as needed by emitting multiple records. -public struct LogRecord { +public struct LogRecord: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2519,7 +2427,7 @@ public struct LogRecord { /// /// Log levels, chosen to match python logging conventions. - public enum Level: SwiftProtobuf.Enum { + public enum Level: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -2581,29 +2489,23 @@ public struct LogRecord { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [LogRecord.Level] = [ + .unset, + .critical, + .error, + .warning, + .info, + .debug, + .trace, + ] + } public init() {} } -#if swift(>=4.2) - -extension LogRecord.Level: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [LogRecord.Level] = [ - .unset, - .critical, - .error, - .warning, - .info, - .debug, - .trace, - ] -} - -#endif // swift(>=4.2) - -public struct QueueStatus { +public struct QueueStatus: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2630,7 +2532,7 @@ public struct QueueStatus { /// It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? /// It will sit in that descriptor until consumed by the phone, /// at which point the next item in the FIFO will be populated. -public struct FromRadio { +public struct FromRadio: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2816,7 +2718,7 @@ public struct FromRadio { /// /// Log levels, chosen to match python logging conventions. - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Log levels, chosen to match python logging conventions. case packet(MeshPacket) @@ -2874,80 +2776,6 @@ public struct FromRadio { /// Persistent data for device-ui case deviceuiConfig(DeviceUIConfig) - #if !swift(>=4.1) - public static func ==(lhs: FromRadio.OneOf_PayloadVariant, rhs: FromRadio.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.packet, .packet): return { - guard case .packet(let l) = lhs, case .packet(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.myInfo, .myInfo): return { - guard case .myInfo(let l) = lhs, case .myInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.nodeInfo, .nodeInfo): return { - guard case .nodeInfo(let l) = lhs, case .nodeInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.config, .config): return { - guard case .config(let l) = lhs, case .config(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.logRecord, .logRecord): return { - guard case .logRecord(let l) = lhs, case .logRecord(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.configCompleteID, .configCompleteID): return { - guard case .configCompleteID(let l) = lhs, case .configCompleteID(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rebooted, .rebooted): return { - guard case .rebooted(let l) = lhs, case .rebooted(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.moduleConfig, .moduleConfig): return { - guard case .moduleConfig(let l) = lhs, case .moduleConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.channel, .channel): return { - guard case .channel(let l) = lhs, case .channel(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.queueStatus, .queueStatus): return { - guard case .queueStatus(let l) = lhs, case .queueStatus(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.xmodemPacket, .xmodemPacket): return { - guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.metadata, .metadata): return { - guard case .metadata(let l) = lhs, case .metadata(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.mqttClientProxyMessage, .mqttClientProxyMessage): return { - guard case .mqttClientProxyMessage(let l) = lhs, case .mqttClientProxyMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.fileInfo, .fileInfo): return { - guard case .fileInfo(let l) = lhs, case .fileInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.clientNotification, .clientNotification): return { - guard case .clientNotification(let l) = lhs, case .clientNotification(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.deviceuiConfig, .deviceuiConfig): return { - guard case .deviceuiConfig(let l) = lhs, case .deviceuiConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -2958,7 +2786,7 @@ public struct FromRadio { /// To be used for important messages that should to be displayed to the user /// in the form of push notifications or validation messages when saving /// invalid configuration. -public struct ClientNotification { +public struct ClientNotification: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -2995,7 +2823,7 @@ public struct ClientNotification { /// /// Individual File info for the device -public struct FileInfo { +public struct FileInfo: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3016,7 +2844,7 @@ public struct FileInfo { /// /// Packets/commands to the radio will be written (reliably) to the toRadio characteristic. /// Once the write completes the phone can assume it is handled. -public struct ToRadio { +public struct ToRadio: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3096,7 +2924,7 @@ public struct ToRadio { /// /// Log levels, chosen to match python logging conventions. - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Send this packet on the mesh case packet(MeshPacket) @@ -3123,40 +2951,6 @@ public struct ToRadio { /// Heartbeat message (used to keep the device connection awake on serial) case heartbeat(Heartbeat) - #if !swift(>=4.1) - public static func ==(lhs: ToRadio.OneOf_PayloadVariant, rhs: ToRadio.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.packet, .packet): return { - guard case .packet(let l) = lhs, case .packet(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.wantConfigID, .wantConfigID): return { - guard case .wantConfigID(let l) = lhs, case .wantConfigID(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.disconnect, .disconnect): return { - guard case .disconnect(let l) = lhs, case .disconnect(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.xmodemPacket, .xmodemPacket): return { - guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.mqttClientProxyMessage, .mqttClientProxyMessage): return { - guard case .mqttClientProxyMessage(let l) = lhs, case .mqttClientProxyMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.heartbeat, .heartbeat): return { - guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -3164,7 +2958,7 @@ public struct ToRadio { /// /// Compressed message payload -public struct Compressed { +public struct Compressed: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3184,7 +2978,7 @@ public struct Compressed { /// /// Full info on edges for a single node -public struct NeighborInfo { +public struct NeighborInfo: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3212,7 +3006,7 @@ public struct NeighborInfo { /// /// A single edge in the mesh -public struct Neighbor { +public struct Neighbor: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3242,7 +3036,7 @@ public struct Neighbor { /// /// Device metadata response -public struct DeviceMetadata { +public struct DeviceMetadata: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3304,7 +3098,7 @@ public struct DeviceMetadata { /// /// A heartbeat message is sent to the node from the client to keep the connection alive. /// This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI. -public struct Heartbeat { +public struct Heartbeat: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3316,7 +3110,7 @@ public struct Heartbeat { /// /// RemoteHardwarePins associated with a node -public struct NodeRemoteHardwarePin { +public struct NodeRemoteHardwarePin: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3343,7 +3137,7 @@ public struct NodeRemoteHardwarePin { fileprivate var _pin: RemoteHardwarePin? = nil } -public struct ChunkedPayload { +public struct ChunkedPayload: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3371,7 +3165,7 @@ public struct ChunkedPayload { /// /// Wrapper message for broken repeated oneof support -public struct resend_chunks { +public struct resend_chunks: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3385,7 +3179,7 @@ public struct resend_chunks { /// /// Responses to a ChunkedPayload request -public struct ChunkedPayloadResponse { +public struct ChunkedPayloadResponse: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -3428,7 +3222,7 @@ public struct ChunkedPayloadResponse { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Request to transfer chunked payload case requestTransfer(Bool) @@ -3439,77 +3233,11 @@ public struct ChunkedPayloadResponse { /// Request missing indexes in the chunked payload case resendChunks(resend_chunks) - #if !swift(>=4.1) - public static func ==(lhs: ChunkedPayloadResponse.OneOf_PayloadVariant, rhs: ChunkedPayloadResponse.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.requestTransfer, .requestTransfer): return { - guard case .requestTransfer(let l) = lhs, case .requestTransfer(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.acceptTransfer, .acceptTransfer): return { - guard case .acceptTransfer(let l) = lhs, case .acceptTransfer(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.resendChunks, .resendChunks): return { - guard case .resendChunks(let l) = lhs, case .resendChunks(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension HardwareModel: @unchecked Sendable {} -extension Constants: @unchecked Sendable {} -extension CriticalErrorCode: @unchecked Sendable {} -extension ExcludedModules: @unchecked Sendable {} -extension Position: @unchecked Sendable {} -extension Position.LocSource: @unchecked Sendable {} -extension Position.AltSource: @unchecked Sendable {} -extension User: @unchecked Sendable {} -extension RouteDiscovery: @unchecked Sendable {} -extension Routing: @unchecked Sendable {} -extension Routing.OneOf_Variant: @unchecked Sendable {} -extension Routing.Error: @unchecked Sendable {} -extension DataMessage: @unchecked Sendable {} -extension Waypoint: @unchecked Sendable {} -extension MqttClientProxyMessage: @unchecked Sendable {} -extension MqttClientProxyMessage.OneOf_PayloadVariant: @unchecked Sendable {} -extension MeshPacket: @unchecked Sendable {} -extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {} -extension MeshPacket.Priority: @unchecked Sendable {} -extension MeshPacket.Delayed: @unchecked Sendable {} -extension NodeInfo: @unchecked Sendable {} -extension MyNodeInfo: @unchecked Sendable {} -extension LogRecord: @unchecked Sendable {} -extension LogRecord.Level: @unchecked Sendable {} -extension QueueStatus: @unchecked Sendable {} -extension FromRadio: @unchecked Sendable {} -extension FromRadio.OneOf_PayloadVariant: @unchecked Sendable {} -extension ClientNotification: @unchecked Sendable {} -extension FileInfo: @unchecked Sendable {} -extension ToRadio: @unchecked Sendable {} -extension ToRadio.OneOf_PayloadVariant: @unchecked Sendable {} -extension Compressed: @unchecked Sendable {} -extension NeighborInfo: @unchecked Sendable {} -extension Neighbor: @unchecked Sendable {} -extension DeviceMetadata: @unchecked Sendable {} -extension Heartbeat: @unchecked Sendable {} -extension NodeRemoteHardwarePin: @unchecked Sendable {} -extension ChunkedPayload: @unchecked Sendable {} -extension resend_chunks: @unchecked Sendable {} -extension ChunkedPayloadResponse: @unchecked Sendable {} -extension ChunkedPayloadResponse.OneOf_PayloadVariant: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" @@ -3604,6 +3332,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 85: .same(proto: "ROUTASTIC"), 86: .same(proto: "MESH_TAB"), 87: .same(proto: "MESHLINK"), + 88: .same(proto: "XIAO_NRF52_KIT"), 255: .same(proto: "PRIVATE_HW"), ] } @@ -4559,7 +4288,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if _storage._rxTime != 0 { try visitor.visitSingularFixed32Field(value: _storage._rxTime, fieldNumber: 7) } - if _storage._rxSnr != 0 { + if _storage._rxSnr.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._rxSnr, fieldNumber: 8) } if _storage._hopLimit != 0 { @@ -4761,7 +4490,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB try { if let v = _storage._position { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) } }() - if _storage._snr != 0 { + if _storage._snr.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4) } if _storage._lastHeard != 0 { @@ -5640,7 +5369,7 @@ extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if self.nodeID != 0 { try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1) } - if self.snr != 0 { + if self.snr.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 2) } if self.lastRxTime != 0 { @@ -5765,8 +5494,8 @@ extension Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { diff --git a/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift index bcf4041c..0138ccff 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/module_config.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum RemoteHardwarePinType: SwiftProtobuf.Enum { +public enum RemoteHardwarePinType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -58,24 +58,18 @@ public enum RemoteHardwarePinType: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension RemoteHardwarePinType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [RemoteHardwarePinType] = [ .unknown, .digitalRead, .digitalWrite, ] -} -#endif // swift(>=4.2) +} /// /// Module Config -public struct ModuleConfig { +public struct ModuleConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -218,7 +212,7 @@ public struct ModuleConfig { /// /// TODO: REPLACE - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// TODO: REPLACE case mqtt(ModuleConfig.MQTTConfig) @@ -259,73 +253,11 @@ public struct ModuleConfig { /// TODO: REPLACE case paxcounter(ModuleConfig.PaxcounterConfig) - #if !swift(>=4.1) - public static func ==(lhs: ModuleConfig.OneOf_PayloadVariant, rhs: ModuleConfig.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.mqtt, .mqtt): return { - guard case .mqtt(let l) = lhs, case .mqtt(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.serial, .serial): return { - guard case .serial(let l) = lhs, case .serial(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.externalNotification, .externalNotification): return { - guard case .externalNotification(let l) = lhs, case .externalNotification(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.storeForward, .storeForward): return { - guard case .storeForward(let l) = lhs, case .storeForward(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rangeTest, .rangeTest): return { - guard case .rangeTest(let l) = lhs, case .rangeTest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.telemetry, .telemetry): return { - guard case .telemetry(let l) = lhs, case .telemetry(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.cannedMessage, .cannedMessage): return { - guard case .cannedMessage(let l) = lhs, case .cannedMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.audio, .audio): return { - guard case .audio(let l) = lhs, case .audio(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.remoteHardware, .remoteHardware): return { - guard case .remoteHardware(let l) = lhs, case .remoteHardware(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.neighborInfo, .neighborInfo): return { - guard case .neighborInfo(let l) = lhs, case .neighborInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.ambientLighting, .ambientLighting): return { - guard case .ambientLighting(let l) = lhs, case .ambientLighting(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.detectionSensor, .detectionSensor): return { - guard case .detectionSensor(let l) = lhs, case .detectionSensor(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.paxcounter, .paxcounter): return { - guard case .paxcounter(let l) = lhs, case .paxcounter(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// MQTT Client Config - public struct MQTTConfig { + public struct MQTTConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -400,7 +332,7 @@ public struct ModuleConfig { /// /// Settings for reporting unencrypted information about our node to a map via MQTT - public struct MapReportSettings { + public struct MapReportSettings: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -420,7 +352,7 @@ public struct ModuleConfig { /// /// RemoteHardwareModule Config - public struct RemoteHardwareConfig { + public struct RemoteHardwareConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -444,7 +376,7 @@ public struct ModuleConfig { /// /// NeighborInfoModule Config - public struct NeighborInfoConfig { + public struct NeighborInfoConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -470,7 +402,7 @@ public struct ModuleConfig { /// /// Detection Sensor Module Config - public struct DetectionSensorConfig { + public struct DetectionSensorConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -517,7 +449,7 @@ public struct ModuleConfig { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum TriggerType: SwiftProtobuf.Enum { + public enum TriggerType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// Event is triggered if pin is low @@ -569,6 +501,16 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [ + .logicLow, + .logicHigh, + .fallingEdge, + .risingEdge, + .eitherEdgeActiveLow, + .eitherEdgeActiveHigh, + ] + } public init() {} @@ -576,7 +518,7 @@ public struct ModuleConfig { /// /// Audio Config for codec2 voice - public struct AudioConfig { + public struct AudioConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -613,7 +555,7 @@ public struct ModuleConfig { /// /// Baudrate for codec2 voice - public enum Audio_Baud: SwiftProtobuf.Enum { + public enum Audio_Baud: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case codec2Default // = 0 case codec23200 // = 1 @@ -660,6 +602,19 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [ + .codec2Default, + .codec23200, + .codec22400, + .codec21600, + .codec21400, + .codec21300, + .codec21200, + .codec2700, + .codec2700B, + ] + } public init() {} @@ -667,7 +622,7 @@ public struct ModuleConfig { /// /// Config for the Paxcounter Module - public struct PaxcounterConfig { + public struct PaxcounterConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -693,7 +648,7 @@ public struct ModuleConfig { /// /// Serial Config - public struct SerialConfig { + public struct SerialConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -736,7 +691,7 @@ public struct ModuleConfig { /// /// TODO: REPLACE - public enum Serial_Baud: SwiftProtobuf.Enum { + public enum Serial_Baud: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case baudDefault // = 0 case baud110 // = 1 @@ -804,11 +759,31 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [ + .baudDefault, + .baud110, + .baud300, + .baud600, + .baud1200, + .baud2400, + .baud4800, + .baud9600, + .baud19200, + .baud38400, + .baud57600, + .baud115200, + .baud230400, + .baud460800, + .baud576000, + .baud921600, + ] + } /// /// TODO: REPLACE - public enum Serial_Mode: SwiftProtobuf.Enum { + public enum Serial_Mode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case `default` // = 0 case simple // = 1 @@ -853,6 +828,17 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [ + .default, + .simple, + .proto, + .textmsg, + .nmea, + .caltopo, + .ws85, + ] + } public init() {} @@ -860,7 +846,7 @@ public struct ModuleConfig { /// /// External Notifications Config - public struct ExternalNotificationConfig { + public struct ExternalNotificationConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -943,7 +929,7 @@ public struct ModuleConfig { /// /// Store and Forward Module Config - public struct StoreForwardConfig { + public struct StoreForwardConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -979,7 +965,7 @@ public struct ModuleConfig { /// /// Preferences for the RangeTestModule - public struct RangeTestConfig { + public struct RangeTestConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1004,7 +990,7 @@ public struct ModuleConfig { /// /// Configuration for both device and environment metrics - public struct TelemetryConfig { + public struct TelemetryConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1073,7 +1059,7 @@ public struct ModuleConfig { /// /// Canned Messages Module Config - public struct CannedMessageConfig { + public struct CannedMessageConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1128,7 +1114,7 @@ public struct ModuleConfig { /// /// TODO: REPLACE - public enum InputEventChar: SwiftProtobuf.Enum { + public enum InputEventChar: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1196,6 +1182,18 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [ + .none, + .up, + .down, + .left, + .right, + .select, + .back, + .cancel, + ] + } public init() {} @@ -1204,7 +1202,7 @@ public struct ModuleConfig { /// ///Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. ///Initially created for the RAK14001 RGB LED module. - public struct AmbientLightingConfig { + public struct AmbientLightingConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1237,89 +1235,9 @@ public struct ModuleConfig { public init() {} } -#if swift(>=4.2) - -extension ModuleConfig.DetectionSensorConfig.TriggerType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [ - .logicLow, - .logicHigh, - .fallingEdge, - .risingEdge, - .eitherEdgeActiveLow, - .eitherEdgeActiveHigh, - ] -} - -extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [ - .codec2Default, - .codec23200, - .codec22400, - .codec21600, - .codec21400, - .codec21300, - .codec21200, - .codec2700, - .codec2700B, - ] -} - -extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [ - .baudDefault, - .baud110, - .baud300, - .baud600, - .baud1200, - .baud2400, - .baud4800, - .baud9600, - .baud19200, - .baud38400, - .baud57600, - .baud115200, - .baud230400, - .baud460800, - .baud576000, - .baud921600, - ] -} - -extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [ - .default, - .simple, - .proto, - .textmsg, - .nmea, - .caltopo, - .ws85, - ] -} - -extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [ - .none, - .up, - .down, - .left, - .right, - .select, - .back, - .cancel, - ] -} - -#endif // swift(>=4.2) - /// /// A GPIO pin definition for remote hardware module -public struct RemoteHardwarePin { +public struct RemoteHardwarePin: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1341,32 +1259,6 @@ public struct RemoteHardwarePin { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension RemoteHardwarePinType: @unchecked Sendable {} -extension ModuleConfig: @unchecked Sendable {} -extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {} -extension ModuleConfig.MQTTConfig: @unchecked Sendable {} -extension ModuleConfig.MapReportSettings: @unchecked Sendable {} -extension ModuleConfig.RemoteHardwareConfig: @unchecked Sendable {} -extension ModuleConfig.NeighborInfoConfig: @unchecked Sendable {} -extension ModuleConfig.DetectionSensorConfig: @unchecked Sendable {} -extension ModuleConfig.DetectionSensorConfig.TriggerType: @unchecked Sendable {} -extension ModuleConfig.AudioConfig: @unchecked Sendable {} -extension ModuleConfig.AudioConfig.Audio_Baud: @unchecked Sendable {} -extension ModuleConfig.PaxcounterConfig: @unchecked Sendable {} -extension ModuleConfig.SerialConfig: @unchecked Sendable {} -extension ModuleConfig.SerialConfig.Serial_Baud: @unchecked Sendable {} -extension ModuleConfig.SerialConfig.Serial_Mode: @unchecked Sendable {} -extension ModuleConfig.ExternalNotificationConfig: @unchecked Sendable {} -extension ModuleConfig.StoreForwardConfig: @unchecked Sendable {} -extension ModuleConfig.RangeTestConfig: @unchecked Sendable {} -extension ModuleConfig.TelemetryConfig: @unchecked Sendable {} -extension ModuleConfig.CannedMessageConfig: @unchecked Sendable {} -extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable {} -extension ModuleConfig.AmbientLightingConfig: @unchecked Sendable {} -extension RemoteHardwarePin: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift index efe6cdd5..006fd9c8 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/mqtt.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// This message wraps a MeshPacket with extra metadata about the sender and how it arrived. -public struct ServiceEnvelope { +public struct ServiceEnvelope: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -57,7 +57,7 @@ public struct ServiceEnvelope { /// /// Information about a node intended to be reported unencrypted to a map using MQTT. -public struct MapReport { +public struct MapReport: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -121,11 +121,6 @@ public struct MapReport { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension ServiceEnvelope: @unchecked Sendable {} -extension MapReport: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift index cf8aa463..e24ed371 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/paxcount.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// TODO: REPLACE -public struct Paxcount { +public struct Paxcount: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -44,10 +44,6 @@ public struct Paxcount { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension Paxcount: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift index 3f9afc46..cac96bc4 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/portnums.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -33,7 +33,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// Note: This was formerly a Type enum named 'typ' with the same id # /// We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. /// This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. -public enum PortNum: SwiftProtobuf.Enum { +public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -193,6 +193,11 @@ public enum PortNum: SwiftProtobuf.Enum { /// PowerStress based monitoring support (for automated power consumption testing) case powerstressApp // = 74 + /// + /// Reticulum Network Stack Tunnel App + /// ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface + case reticulumTunnelApp // = 76 + /// /// Private applications should use portnums >= 256. /// To simplify initial development and testing you can use "PRIVATE_APP" @@ -241,6 +246,7 @@ public enum PortNum: SwiftProtobuf.Enum { case 72: self = .atakPlugin case 73: self = .mapReportApp case 74: self = .powerstressApp + case 76: self = .reticulumTunnelApp case 256: self = .privateApp case 257: self = .atakForwarder case 511: self = .max @@ -276,6 +282,7 @@ public enum PortNum: SwiftProtobuf.Enum { case .atakPlugin: return 72 case .mapReportApp: return 73 case .powerstressApp: return 74 + case .reticulumTunnelApp: return 76 case .privateApp: return 256 case .atakForwarder: return 257 case .max: return 511 @@ -283,11 +290,6 @@ public enum PortNum: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension PortNum: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [PortNum] = [ .unknownApp, @@ -316,18 +318,14 @@ extension PortNum: CaseIterable { .atakPlugin, .mapReportApp, .powerstressApp, + .reticulumTunnelApp, .privateApp, .atakForwarder, .max, ] + } -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension PortNum: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. extension PortNum: SwiftProtobuf._ProtoNameProviding { @@ -358,6 +356,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding { 72: .same(proto: "ATAK_PLUGIN"), 73: .same(proto: "MAP_REPORT_APP"), 74: .same(proto: "POWERSTRESS_APP"), + 76: .same(proto: "RETICULUM_TUNNEL_APP"), 256: .same(proto: "PRIVATE_APP"), 257: .same(proto: "ATAK_FORWARDER"), 511: .same(proto: "MAX"), diff --git a/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift index 5f51e948..58c21701 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/powermon.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs). ///But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) -public struct PowerMon { +public struct PowerMon: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -31,7 +31,7 @@ public struct PowerMon { /// Any significant power changing event in meshtastic should be tagged with a powermon state transition. ///If you are making new meshtastic features feel free to add new entries at the end of this definition. - public enum State: SwiftProtobuf.Enum { + public enum State: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case none // = 0 case cpuDeepSleep // = 1 @@ -104,37 +104,31 @@ public struct PowerMon { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [PowerMon.State] = [ + .none, + .cpuDeepSleep, + .cpuLightSleep, + .vext1On, + .loraRxon, + .loraTxon, + .loraRxactive, + .btOn, + .ledOn, + .screenOn, + .screenDrawing, + .wifiOn, + .gpsActive, + ] + } public init() {} } -#if swift(>=4.2) - -extension PowerMon.State: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [PowerMon.State] = [ - .none, - .cpuDeepSleep, - .cpuLightSleep, - .vext1On, - .loraRxon, - .loraTxon, - .loraRxactive, - .btOn, - .ledOn, - .screenOn, - .screenDrawing, - .wifiOn, - .gpsActive, - ] -} - -#endif // swift(>=4.2) - /// /// PowerStress testing support via the C++ PowerStress module -public struct PowerStressMessage { +public struct PowerStressMessage: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -151,7 +145,7 @@ public struct PowerStressMessage { /// What operation would we like the UUT to perform. ///note: senders should probably set want_response in their request packets, so that they can know when the state ///machine has started processing their request - public enum Opcode: SwiftProtobuf.Enum { + public enum Opcode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -272,48 +266,35 @@ public struct PowerStressMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [PowerStressMessage.Opcode] = [ + .unset, + .printInfo, + .forceQuiet, + .endQuiet, + .screenOn, + .screenOff, + .cpuIdle, + .cpuDeepsleep, + .cpuFullon, + .ledOn, + .ledOff, + .loraOff, + .loraTx, + .loraRx, + .btOff, + .btOn, + .wifiOff, + .wifiOn, + .gpsOff, + .gpsOn, + ] + } public init() {} } -#if swift(>=4.2) - -extension PowerStressMessage.Opcode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [PowerStressMessage.Opcode] = [ - .unset, - .printInfo, - .forceQuiet, - .endQuiet, - .screenOn, - .screenOff, - .cpuIdle, - .cpuDeepsleep, - .cpuFullon, - .ledOn, - .ledOff, - .loraOff, - .loraTx, - .loraRx, - .btOff, - .btOn, - .wifiOff, - .wifiOn, - .gpsOff, - .gpsOn, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension PowerMon: @unchecked Sendable {} -extension PowerMon.State: @unchecked Sendable {} -extension PowerStressMessage: @unchecked Sendable {} -extension PowerStressMessage.Opcode: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" @@ -323,8 +304,8 @@ extension PowerMon: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { @@ -379,7 +360,7 @@ extension PowerStressMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.cmd != .unset { try visitor.visitSingularEnumField(value: self.cmd, fieldNumber: 1) } - if self.numSeconds != 0 { + if self.numSeconds.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.numSeconds, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) diff --git a/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift index ac6eeb26..d23dc07b 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/remote_hardware.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -30,7 +30,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// because no security yet (beyond the channel mechanism). /// It should be off by default and then protected based on some TBD mechanism /// (a special channel once multichannel support is included?) -public struct HardwareMessage { +public struct HardwareMessage: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -52,7 +52,7 @@ public struct HardwareMessage { /// /// TODO: REPLACE - public enum TypeEnum: SwiftProtobuf.Enum { + public enum TypeEnum: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -110,32 +110,21 @@ public struct HardwareMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [HardwareMessage.TypeEnum] = [ + .unset, + .writeGpios, + .watchGpios, + .gpiosChanged, + .readGpios, + .readGpiosReply, + ] + } public init() {} } -#if swift(>=4.2) - -extension HardwareMessage.TypeEnum: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [HardwareMessage.TypeEnum] = [ - .unset, - .writeGpios, - .watchGpios, - .gpiosChanged, - .readGpios, - .readGpiosReply, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension HardwareMessage: @unchecked Sendable {} -extension HardwareMessage.TypeEnum: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift index 6fdf3208..38d0c880 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/rtttl.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Canned message module configuration. -public struct RTTTLConfig { +public struct RTTTLConfig: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -36,10 +36,6 @@ public struct RTTTLConfig { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension RTTTLConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift index 54efa77b..deb96569 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/storeforward.proto @@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// TODO: REPLACE -public struct StoreAndForward { +public struct StoreAndForward: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -79,7 +80,7 @@ public struct StoreAndForward { /// /// TODO: REPLACE - public enum OneOf_Variant: Equatable { + public enum OneOf_Variant: Equatable, @unchecked Sendable { /// /// TODO: REPLACE case stats(StoreAndForward.Statistics) @@ -93,38 +94,12 @@ public struct StoreAndForward { /// Text from history message. case text(Data) - #if !swift(>=4.1) - public static func ==(lhs: StoreAndForward.OneOf_Variant, rhs: StoreAndForward.OneOf_Variant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.stats, .stats): return { - guard case .stats(let l) = lhs, case .stats(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.history, .history): return { - guard case .history(let l) = lhs, case .history(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.heartbeat, .heartbeat): return { - guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.text, .text): return { - guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// 001 - 063 = From Router /// 064 - 127 = From Client - public enum RequestResponse: SwiftProtobuf.Enum { + public enum RequestResponse: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -242,11 +217,31 @@ public struct StoreAndForward { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [StoreAndForward.RequestResponse] = [ + .unset, + .routerError, + .routerHeartbeat, + .routerPing, + .routerPong, + .routerBusy, + .routerHistory, + .routerStats, + .routerTextDirect, + .routerTextBroadcast, + .clientError, + .clientHistory, + .clientStats, + .clientPing, + .clientPong, + .clientAbort, + ] + } /// /// TODO: REPLACE - public struct Statistics { + public struct Statistics: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -294,7 +289,7 @@ public struct StoreAndForward { /// /// TODO: REPLACE - public struct History { + public struct History: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -319,7 +314,7 @@ public struct StoreAndForward { /// /// TODO: REPLACE - public struct Heartbeat { + public struct Heartbeat: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -340,41 +335,6 @@ public struct StoreAndForward { public init() {} } -#if swift(>=4.2) - -extension StoreAndForward.RequestResponse: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [StoreAndForward.RequestResponse] = [ - .unset, - .routerError, - .routerHeartbeat, - .routerPing, - .routerPong, - .routerBusy, - .routerHistory, - .routerStats, - .routerTextDirect, - .routerTextBroadcast, - .clientError, - .clientHistory, - .clientStats, - .clientPing, - .clientPong, - .clientAbort, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension StoreAndForward: @unchecked Sendable {} -extension StoreAndForward.OneOf_Variant: @unchecked Sendable {} -extension StoreAndForward.RequestResponse: @unchecked Sendable {} -extension StoreAndForward.Statistics: @unchecked Sendable {} -extension StoreAndForward.History: @unchecked Sendable {} -extension StoreAndForward.Heartbeat: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" diff --git a/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift index e652a89c..4cd8e939 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/telemetry.proto @@ -7,7 +8,6 @@ // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ -import Foundation import SwiftProtobuf // If the compiler emits an error on this type, it is because this file @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Supported I2C Sensors for telemetry in Meshtastic -public enum TelemetrySensorType: SwiftProtobuf.Enum { +public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -122,7 +122,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { case aht10 // = 23 /// - /// DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) + /// DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) case dfrobotLark // = 24 /// @@ -146,7 +146,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { case customSensor // = 29 /// - /// MAX30102 Pulse Oximeter and Heart-Rate Sensor + /// MAX30102 Pulse Oximeter and Heart-Rate Sensor case max30102 // = 30 /// @@ -168,6 +168,14 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { /// /// DFRobot Gravity tipping bucket rain gauge case dfrobotRain // = 35 + + /// + /// Infineon DPS310 High accuracy pressure and temperature + case dps310 // = 36 + + /// + /// RAKWireless RAK12035 Soil Moisture Sensor Module + case rak12035 // = 37 case UNRECOGNIZED(Int) public init() { @@ -212,6 +220,8 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { case 33: self = .radsens case 34: self = .ina226 case 35: self = .dfrobotRain + case 36: self = .dps310 + case 37: self = .rak12035 default: self = .UNRECOGNIZED(rawValue) } } @@ -254,15 +264,12 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { case .radsens: return 33 case .ina226: return 34 case .dfrobotRain: return 35 + case .dps310: return 36 + case .rak12035: return 37 case .UNRECOGNIZED(let i): return i } } -} - -#if swift(>=4.2) - -extension TelemetrySensorType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [TelemetrySensorType] = [ .sensorUnset, @@ -301,14 +308,15 @@ extension TelemetrySensorType: CaseIterable { .radsens, .ina226, .dfrobotRain, + .dps310, + .rak12035, ] -} -#endif // swift(>=4.2) +} /// /// Key native device metrics such as battery level -public struct DeviceMetrics { +public struct DeviceMetrics: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -381,7 +389,7 @@ public struct DeviceMetrics { /// /// Weather station or other environmental metrics -public struct EnvironmentMetrics { +public struct EnvironmentMetrics: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -452,7 +460,7 @@ public struct EnvironmentMetrics { /// Clears the value of `current`. Subsequent reads from it will return its default value. public mutating func clearCurrent() {_uniqueStorage()._current = nil} - /// + /// /// relative scale IAQ value as measured by Bosch BME680 . value 0-500. /// Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. public var iaq: UInt32 { @@ -608,6 +616,28 @@ public struct EnvironmentMetrics { /// Clears the value of `rainfall24H`. Subsequent reads from it will return its default value. public mutating func clearRainfall24H() {_uniqueStorage()._rainfall24H = nil} + /// + /// Soil moisture measured (% 1-100) + public var soilMoisture: UInt32 { + get {return _storage._soilMoisture ?? 0} + set {_uniqueStorage()._soilMoisture = newValue} + } + /// Returns true if `soilMoisture` has been explicitly set. + public var hasSoilMoisture: Bool {return _storage._soilMoisture != nil} + /// Clears the value of `soilMoisture`. Subsequent reads from it will return its default value. + public mutating func clearSoilMoisture() {_uniqueStorage()._soilMoisture = nil} + + /// + /// Soil temperature measured (*C) + public var soilTemperature: Float { + get {return _storage._soilTemperature ?? 0} + set {_uniqueStorage()._soilTemperature = newValue} + } + /// Returns true if `soilTemperature` has been explicitly set. + public var hasSoilTemperature: Bool {return _storage._soilTemperature != nil} + /// Clears the value of `soilTemperature`. Subsequent reads from it will return its default value. + public mutating func clearSoilTemperature() {_uniqueStorage()._soilTemperature = nil} + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -617,7 +647,7 @@ public struct EnvironmentMetrics { /// /// Power Metrics (voltage / current / etc) -public struct PowerMetrics { +public struct PowerMetrics: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -702,7 +732,7 @@ public struct PowerMetrics { /// /// Air quality metrics -public struct AirQualityMetrics { +public struct AirQualityMetrics: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -871,7 +901,7 @@ public struct AirQualityMetrics { /// /// Local device mesh statistics -public struct LocalStats { +public struct LocalStats: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -929,7 +959,7 @@ public struct LocalStats { /// /// Health telemetry metrics -public struct HealthMetrics { +public struct HealthMetrics: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -978,7 +1008,7 @@ public struct HealthMetrics { /// /// Types of Measurements the telemetry module is equipped to handle -public struct Telemetry { +public struct Telemetry: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1051,7 +1081,7 @@ public struct Telemetry { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_Variant: Equatable { + public enum OneOf_Variant: Equatable, Sendable { /// /// Key native device metrics such as battery level case deviceMetrics(DeviceMetrics) @@ -1071,40 +1101,6 @@ public struct Telemetry { /// Health telemetry metrics case healthMetrics(HealthMetrics) - #if !swift(>=4.1) - public static func ==(lhs: Telemetry.OneOf_Variant, rhs: Telemetry.OneOf_Variant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.deviceMetrics, .deviceMetrics): return { - guard case .deviceMetrics(let l) = lhs, case .deviceMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.environmentMetrics, .environmentMetrics): return { - guard case .environmentMetrics(let l) = lhs, case .environmentMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.airQualityMetrics, .airQualityMetrics): return { - guard case .airQualityMetrics(let l) = lhs, case .airQualityMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.powerMetrics, .powerMetrics): return { - guard case .powerMetrics(let l) = lhs, case .powerMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.localStats, .localStats): return { - guard case .localStats(let l) = lhs, case .localStats(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.healthMetrics, .healthMetrics): return { - guard case .healthMetrics(let l) = lhs, case .healthMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -1112,7 +1108,7 @@ public struct Telemetry { /// /// NAU7802 Telemetry configuration, for saving to flash -public struct Nau7802Config { +public struct Nau7802Config: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -1130,19 +1126,6 @@ public struct Nau7802Config { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension TelemetrySensorType: @unchecked Sendable {} -extension DeviceMetrics: @unchecked Sendable {} -extension EnvironmentMetrics: @unchecked Sendable {} -extension PowerMetrics: @unchecked Sendable {} -extension AirQualityMetrics: @unchecked Sendable {} -extension LocalStats: @unchecked Sendable {} -extension HealthMetrics: @unchecked Sendable {} -extension Telemetry: @unchecked Sendable {} -extension Telemetry.OneOf_Variant: @unchecked Sendable {} -extension Nau7802Config: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" @@ -1185,6 +1168,8 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { 33: .same(proto: "RADSENS"), 34: .same(proto: "INA226"), 35: .same(proto: "DFROBOT_RAIN"), + 36: .same(proto: "DPS310"), + 37: .same(proto: "RAK12035"), ] } @@ -1271,6 +1256,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple 18: .same(proto: "radiation"), 19: .standard(proto: "rainfall_1h"), 20: .standard(proto: "rainfall_24h"), + 21: .standard(proto: "soil_moisture"), + 22: .standard(proto: "soil_temperature"), ] fileprivate class _StorageClass { @@ -1294,6 +1281,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple var _radiation: Float? = nil var _rainfall1H: Float? = nil var _rainfall24H: Float? = nil + var _soilMoisture: UInt32? = nil + var _soilTemperature: Float? = nil #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. @@ -1328,6 +1317,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple _radiation = source._radiation _rainfall1H = source._rainfall1H _rainfall24H = source._rainfall24H + _soilMoisture = source._soilMoisture + _soilTemperature = source._soilTemperature } } @@ -1366,6 +1357,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple case 18: try { try decoder.decodeSingularFloatField(value: &_storage._radiation) }() case 19: try { try decoder.decodeSingularFloatField(value: &_storage._rainfall1H) }() case 20: try { try decoder.decodeSingularFloatField(value: &_storage._rainfall24H) }() + case 21: try { try decoder.decodeSingularUInt32Field(value: &_storage._soilMoisture) }() + case 22: try { try decoder.decodeSingularFloatField(value: &_storage._soilTemperature) }() default: break } } @@ -1438,6 +1431,12 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple try { if let v = _storage._rainfall24H { try visitor.visitSingularFloatField(value: v, fieldNumber: 20) } }() + try { if let v = _storage._soilMoisture { + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 21) + } }() + try { if let v = _storage._soilTemperature { + try visitor.visitSingularFloatField(value: v, fieldNumber: 22) + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -1467,6 +1466,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if _storage._radiation != rhs_storage._radiation {return false} if _storage._rainfall1H != rhs_storage._rainfall1H {return false} if _storage._rainfall24H != rhs_storage._rainfall24H {return false} + if _storage._soilMoisture != rhs_storage._soilMoisture {return false} + if _storage._soilTemperature != rhs_storage._soilTemperature {return false} return true } if !storagesAreEqual {return false} @@ -1692,10 +1693,10 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if self.uptimeSeconds != 0 { try visitor.visitSingularUInt32Field(value: self.uptimeSeconds, fieldNumber: 1) } - if self.channelUtilization != 0 { + if self.channelUtilization.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.channelUtilization, fieldNumber: 2) } - if self.airUtilTx != 0 { + if self.airUtilTx.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.airUtilTx, fieldNumber: 3) } if self.numPacketsTx != 0 { @@ -1962,7 +1963,7 @@ extension Nau7802Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa if self.zeroOffset != 0 { try visitor.visitSingularInt32Field(value: self.zeroOffset, fieldNumber: 1) } - if self.calibrationFactor != 0 { + if self.calibrationFactor.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.calibrationFactor, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) diff --git a/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift index 1f41fe0b..46907a58 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/xmodem.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct XModem { +public struct XModem: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -35,7 +36,7 @@ public struct XModem { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum Control: SwiftProtobuf.Enum { + public enum Control: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case nul // = 0 case soh // = 1 @@ -79,34 +80,23 @@ public struct XModem { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [XModem.Control] = [ + .nul, + .soh, + .stx, + .eot, + .ack, + .nak, + .can, + .ctrlz, + ] + } public init() {} } -#if swift(>=4.2) - -extension XModem.Control: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [XModem.Control] = [ - .nul, - .soh, - .stx, - .eot, - .ack, - .nak, - .can, - .ctrlz, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension XModem: @unchecked Sendable {} -extension XModem.Control: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" From d847e7543767cf7ce4e904827b2d2a3d7eab6503 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Fri, 14 Mar 2025 13:02:52 -0400 Subject: [PATCH 07/29] Database updates for new attributes in Telemetry protobufs --- Meshtastic.xcodeproj/project.pbxproj | 4 +- .../ManagedAttributePropertyWrapper.swift | 2 + Meshtastic/Helpers/MeshPackets.swift | 10 + .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 502 ++++++++++++++++++ .../TelemetryEntity+CoreDataClass.swift | 9 + .../Weather/CurrentConditionsCompact.swift | 33 -- .../Weather/LocalWeatherConditions.swift | 107 ---- .../Views/Nodes/Helpers/NodeDetail.swift | 20 +- 9 files changed, 546 insertions(+), 143 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents delete mode 100644 Meshtastic/Views/Helpers/Weather/CurrentConditionsCompact.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index fadb81fe..fc1539f2 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -277,6 +277,7 @@ 231B3F202D087A4C0069A07D /* MetricTableColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricTableColumn.swift; sourceTree = ""; }; 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultColumns.swift; sourceTree = ""; }; 231B3F262D0885240069A07D /* MetricsColumnDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsColumnDetail.swift; sourceTree = ""; }; + 233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 50.xcdatamodel"; sourceTree = ""; }; 2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAttributePropertyWrapper.swift; sourceTree = ""; }; 2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataClass.swift"; sourceTree = ""; }; 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = ""; }; @@ -2011,6 +2012,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */, 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */, DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */, DDDFE7402D0D4A070044463C /* MeshtasticDataModelV 47.xcdatamodel */, @@ -2061,7 +2063,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */; + currentVersion = 233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Extensions/CoreData/ManagedAttributePropertyWrapper.swift b/Meshtastic/Extensions/CoreData/ManagedAttributePropertyWrapper.swift index 3b123207..5c5fc71f 100644 --- a/Meshtastic/Extensions/CoreData/ManagedAttributePropertyWrapper.swift +++ b/Meshtastic/Extensions/CoreData/ManagedAttributePropertyWrapper.swift @@ -29,6 +29,8 @@ public struct ManagedAttribute { converter = { $0.int32Value as? Value } } else if Value.self == Int64.self { converter = { $0.int64Value as? Value } + } else if Value.self == UInt32.self { + converter = { $0.uint32Value as? Value } } else { fatalError("Unsupported type: \(Value.self)") } diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 093066ef..b6c8db17 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -723,10 +723,20 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage telemetry.current = telemetryMessage.environmentMetrics.hasCurrent.then(telemetryMessage.environmentMetrics.current) telemetry.voltage = telemetryMessage.environmentMetrics.hasVoltage.then(telemetryMessage.environmentMetrics.voltage) telemetry.weight = telemetryMessage.environmentMetrics.hasWeight.then(telemetryMessage.environmentMetrics.weight) + telemetry.distance = telemetryMessage.environmentMetrics.hasDistance.then(telemetryMessage.environmentMetrics.distance) telemetry.windSpeed = telemetryMessage.environmentMetrics.hasWindSpeed.then(telemetryMessage.environmentMetrics.windSpeed) telemetry.windGust = telemetryMessage.environmentMetrics.hasWindGust.then(telemetryMessage.environmentMetrics.windGust) telemetry.windLull = telemetryMessage.environmentMetrics.hasWindLull.then(telemetryMessage.environmentMetrics.windLull) telemetry.windDirection = telemetryMessage.environmentMetrics.hasWindDirection.then(Int32(truncatingIfNeeded: telemetryMessage.environmentMetrics.windDirection)) + telemetry.irLux = telemetryMessage.environmentMetrics.hasIrLux.then(telemetryMessage.environmentMetrics.irLux) + telemetry.lux = telemetryMessage.environmentMetrics.hasLux.then(telemetryMessage.environmentMetrics.lux) + telemetry.whiteLux = telemetryMessage.environmentMetrics.hasWhiteLux.then(telemetryMessage.environmentMetrics.whiteLux) + telemetry.uvLux = telemetryMessage.environmentMetrics.hasUvLux.then(telemetryMessage.environmentMetrics.uvLux) + telemetry.radiation = telemetryMessage.environmentMetrics.hasRadiation.then(telemetryMessage.environmentMetrics.radiation) + telemetry.rainfall1H = telemetryMessage.environmentMetrics.hasRainfall1H.then(telemetryMessage.environmentMetrics.rainfall1H) + telemetry.rainfall24H = telemetryMessage.environmentMetrics.hasRainfall24H.then(telemetryMessage.environmentMetrics.rainfall24H) + telemetry.soilTemperature = telemetryMessage.environmentMetrics.hasSoilTemperature.then(telemetryMessage.environmentMetrics.soilTemperature) + telemetry.soilMoisture = telemetryMessage.environmentMetrics.hasSoilMoisture.then(telemetryMessage.environmentMetrics.soilMoisture) telemetry.metricsType = 1 } else if telemetryMessage.variant == Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) { // Local Stats for Live activity diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 0b4b8e13..cd530ab2 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV 49.xcdatamodel + MeshtasticDataModelV 50.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents new file mode 100644 index 00000000..52f1a59f --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents @@ -0,0 +1,502 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Model/CoreData/TelemetryEntity+CoreDataClass.swift b/Meshtastic/Model/CoreData/TelemetryEntity+CoreDataClass.swift index bcbaf4b9..48f8263b 100644 --- a/Meshtastic/Model/CoreData/TelemetryEntity+CoreDataClass.swift +++ b/Meshtastic/Model/CoreData/TelemetryEntity+CoreDataClass.swift @@ -42,5 +42,14 @@ public class TelemetryEntity: NSManagedObject, Identifiable { @ManagedAttribute(attributeName: "windGust") public var windGust: Float? @ManagedAttribute(attributeName: "windLull") public var windLull: Float? @ManagedAttribute(attributeName: "windSpeed") public var windSpeed: Float? + @ManagedAttribute(attributeName: "irLux") public var irLux: Float? + @ManagedAttribute(attributeName: "lux") public var lux: Float? + @ManagedAttribute(attributeName: "uvLux") public var uvLux: Float? + @ManagedAttribute(attributeName: "whiteLux") public var whiteLux: Float? + @ManagedAttribute(attributeName: "radiation") public var radiation: Float? + @ManagedAttribute(attributeName: "rainfall1H") public var rainfall1H: Float? + @ManagedAttribute(attributeName: "rainfall24H") public var rainfall24H: Float? + @ManagedAttribute(attributeName: "soilTemperature") public var soilTemperature: Float? + @ManagedAttribute(attributeName: "soilMoisture") public var soilMoisture: UInt32? } diff --git a/Meshtastic/Views/Helpers/Weather/CurrentConditionsCompact.swift b/Meshtastic/Views/Helpers/Weather/CurrentConditionsCompact.swift deleted file mode 100644 index fe166e48..00000000 --- a/Meshtastic/Views/Helpers/Weather/CurrentConditionsCompact.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// CurrentConditionsCompact.swift -// Meshtastic -// -// Copyright(c) Garth Vander Houwen 2/5/23. -// -import SwiftUI - -struct CurrentConditionsCompact: View { - var temp: Float - var condition: WeatherConditions - - var body: some View { - Label("\(String(temp.formattedTemperature()))", systemImage: condition.symbolName) - .font(.caption) - .foregroundColor(.gray) - .symbolRenderingMode(.multicolor) - } -} -struct CurrentConditionsCompact_Previews: PreviewProvider { - static var previews: some View { - - VStack { - CurrentConditionsCompact(temp: 22, condition: WeatherConditions.clear) - CurrentConditionsCompact(temp: 17, condition: WeatherConditions.cloudy) - CurrentConditionsCompact(temp: -5, condition: WeatherConditions.frigid) - CurrentConditionsCompact(temp: 38, condition: WeatherConditions.hot) - CurrentConditionsCompact(temp: 10, condition: WeatherConditions.rain) - CurrentConditionsCompact(temp: 30, condition: WeatherConditions.smoky) - CurrentConditionsCompact(temp: -2, condition: WeatherConditions.snow) - } - } -} diff --git a/Meshtastic/Views/Helpers/Weather/LocalWeatherConditions.swift b/Meshtastic/Views/Helpers/Weather/LocalWeatherConditions.swift index b4e5336c..c2879aec 100644 --- a/Meshtastic/Views/Helpers/Weather/LocalWeatherConditions.swift +++ b/Meshtastic/Views/Helpers/Weather/LocalWeatherConditions.swift @@ -89,113 +89,6 @@ struct LocalWeatherConditions: View { } } -struct WeatherConditionsCompactWidget: View { - let temperature: String - let symbolName: String - let description: String - var body: some View { - VStack(alignment: .leading) { - HStack(spacing: 5.0) { - Image(systemName: symbolName) - .foregroundColor(.accentColor) - .font(.callout) - Text(description) - .lineLimit(2) - .allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) - .fixedSize(horizontal: false, vertical: true) - .font(.caption) - } - Text(temperature) - .font(temperature.length < 4 ? .system(size: 72) : .system(size: 54) ) - } - .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) - .padding() - .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) - } -} - -struct HumidityCompactWidget: View { - let humidity: Int - let dewPoint: String? - var body: some View { - VStack(alignment: .leading) { - HStack(spacing: 5.0) { - Image(systemName: "humidity") - .foregroundColor(.accentColor) - .font(.callout) - Text("Humidity") - .textCase(.uppercase) - .font(.caption) - } - Text("\(humidity)%") - .font(.largeTitle) - .padding(.bottom, 5) - if let dewPoint { - Text("The dew point is \(dewPoint) right now.") - .lineLimit(3) - .allowsTightening(true) - .fixedSize(horizontal: false, vertical: true) - .font(.caption2) - } - } - .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) - .padding() - .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) - } -} - -struct PressureCompactWidget: View { - let pressure: String - let unit: String - let low: Bool - var body: some View { - VStack(alignment: .leading) { - HStack(spacing: 5.0) { - Image(systemName: "gauge") - .foregroundColor(.accentColor) - .font(.callout) - Text("Pressure") - .textCase(.uppercase) - .font(.caption) - } - Text(pressure) - .font(pressure.length < 7 ? .system(size: 35) : .system(size: 30) ) - Text(low ? "LOW" : "HIGH") - .padding(.bottom, 10) - Text(unit) - } - .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) - .padding() - .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) - } -} - -struct WindCompactWidget: View { - let speed: String - let gust: String? - let direction: String? - - var body: some View { - let hasGust = ((gust ?? "").isEmpty == false) - VStack(alignment: .leading) { - Label { Text("Wind").textCase(.uppercase) } icon: { Image(systemName: "wind").foregroundColor(.accentColor) } - if let direction { - Text("\(direction)") - .font(!hasGust ? .callout : .caption) - .padding(.bottom, 10) - } - Text(speed) - .font(!hasGust ? .system(size: 45) : .system(size: 35)) - if let gust, !gust.isEmpty { - Text("Gusts \(gust)") - } - } - .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) - .padding() - .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) - } -} - /// Magnus Formula func calculateDewPoint(temp: Float, relativeHumidity: Float) -> Double { let a: Float = 17.27 diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index 485bbdd6..958f4bfc 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -212,7 +212,7 @@ struct NodeDetail: View { // to use with WeatherKit, or has actual data in the most recent EnvironmentMetrics entity // that will be rendered in this section. if node.hasPositions && UserDefaults.environmentEnableWeatherKit - || node.hasDataForLatestEnvironmentMetrics(attributes: ["iaq", "temperature", "relativeHumidity", "barometricPressure", "windSpeed"]) { + || node.hasDataForLatestEnvironmentMetrics(attributes: ["iaq", "temperature", "relativeHumidity", "barometricPressure", "windSpeed", "radiation", "weight", "distance", "soilTemperature", "soilMoisture"]) { Section("Environment") { if !node.hasEnvironmentMetrics { LocalWeatherConditions(location: node.latestPosition?.nodeLocation) @@ -245,6 +245,24 @@ struct NodeDetail: View { WindCompactWidget(speed: windSpeedMeasurement.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))), gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction) } + if let radiation = node.latestEnvironmentMetrics?.radiation { + RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(2))), unit: "µR/hr") + } + if let weight = node.latestEnvironmentMetrics?.weight { + WeightCompactWidget(weight: weight.formatted(.number.precision(.fractionLength(1))), unit: "kg") + } + if let distance = node.latestEnvironmentMetrics?.distance { + DistanceCompactWidget(distance: distance.formatted(.number.precision(.fractionLength(0))), unit: "mm") + } + if let soilTemperature = node.latestEnvironmentMetrics?.soilTemperature { + let locale = NSLocale.current as NSLocale + let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey")) + let unit = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? "°F" : "°C" + SoilTemperatureCompactWidget(temperature: soilTemperature.localeTemperature().formatted(.number.precision(.fractionLength(0))), unit: unit) + } + if let soilMoisture = node.latestEnvironmentMetrics?.soilMoisture { + SoilMoistureCompactWidget(moisture: soilMoisture.formatted(.number.precision(.fractionLength(0))), unit: "%") + } } .padding(node.latestEnvironmentMetrics?.iaq ?? -1 > 0 ? .bottom : .vertical) } From 77504bb8d43eba6b647416c72fa65ff56d57a5cc Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 07:58:44 -0400 Subject: [PATCH 08/29] Update Environment Metrics to support latest Telemetry protobufs --- Localizable.xcstrings | 29 +- Meshtastic.xcodeproj/project.pbxproj | 46 ++- .../soil.moisture.symbolset/Contents.json | 12 + .../soilMoisture.variable.svg | 366 ++++++++++++++++++ .../soil.temperature.symbolset/Contents.json | 12 + .../soilTemp.variable.svg | 363 +++++++++++++++++ .../Compact Widgets/CompactWidget.swift | 36 ++ .../CurrentConditionsCompact.swift | 34 ++ .../DistanceCompactWidget.swift | 39 ++ .../HumidityCompactWidget.swift | 47 +++ .../PressureCompactWidget.swift | 43 ++ .../RadiationCompactWidget.swift | 39 ++ .../Compact Widgets/SoilCompactWidgets.swift | 72 ++++ .../WeatherConditionsCompactWidget.swift | 42 ++ .../Compact Widgets/WeightCompactWidget.swift | 39 ++ .../Compact Widgets/WindCompactWidget.swift | 45 +++ .../EnvironmentDefaultColumns.swift | 145 +++++++ .../EnvironmentDefaultSeries.swift | 263 ++++++++++++- .../Views/Nodes/Helpers/NodeDetail.swift | 2 +- 19 files changed, 1641 insertions(+), 33 deletions(-) create mode 100644 Meshtastic/Assets.xcassets/soil.moisture.symbolset/Contents.json create mode 100644 Meshtastic/Assets.xcassets/soil.moisture.symbolset/soilMoisture.variable.svg create mode 100644 Meshtastic/Assets.xcassets/soil.temperature.symbolset/Contents.json create mode 100644 Meshtastic/Assets.xcassets/soil.temperature.symbolset/soilTemp.variable.svg create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/CurrentConditionsCompact.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/DistanceCompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/HumidityCompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/PressureCompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/SoilCompactWidgets.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/WeightCompactWidget.swift create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/WindCompactWidget.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 6c2731d1..068905ec 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -12138,23 +12138,6 @@ } } }, - "HUMIDITY" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "LUFTFEUCHTIGKEIT" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "ВЛАЖНОСТ" - } - } - } - }, "hybrid" : { "extractionState" : "migrated", "localizations" : { @@ -23607,6 +23590,9 @@ } } } + }, + "Radiation" : { + }, "Radio Disconnected" : { "extractionState" : "manual", @@ -28513,6 +28499,12 @@ } } } + }, + "Soil Moisture" : { + + }, + "Soil Temp" : { + }, "Specifies how long the monitored GPIO should output." : { "localizations" : { @@ -32676,6 +32668,9 @@ } } } + }, + "Weight" : { + }, "What does the lock mean?" : { "localizations" : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index fc1539f2..bf000340 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -11,6 +11,15 @@ 231B3F222D087A4C0069A07D /* MetricsColumnList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F1F2D087A4C0069A07D /* MetricsColumnList.swift */; }; 231B3F252D087C3C0069A07D /* EnvironmentDefaultColumns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */; }; 231B3F272D0885240069A07D /* MetricsColumnDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F262D0885240069A07D /* MetricsColumnDetail.swift */; }; + 233E99B62D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99B52D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift */; }; + 233E99B82D849C6500CC3A77 /* HumidityCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99B72D849C6500CC3A77 /* HumidityCompactWidget.swift */; }; + 233E99BA2D849C7000CC3A77 /* PressureCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99B92D849C7000CC3A77 /* PressureCompactWidget.swift */; }; + 233E99BC2D849C8C00CC3A77 /* WindCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99BB2D849C8C00CC3A77 /* WindCompactWidget.swift */; }; + 233E99BE2D849D3200CC3A77 /* RadiationCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99BD2D849D3200CC3A77 /* RadiationCompactWidget.swift */; }; + 233E99C12D849D6000CC3A77 /* DistanceCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */; }; + 233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */; }; + 233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C42D84A0B600CC3A77 /* CompactWidget.swift */; }; + 233E99C72D84A70900CC3A77 /* SoilCompactWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */; }; 2344A2AB2D66974300170A77 /* ManagedAttributePropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */; }; 2344A2AF2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */; }; 2344A2B02D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */; }; @@ -278,6 +287,15 @@ 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultColumns.swift; sourceTree = ""; }; 231B3F262D0885240069A07D /* MetricsColumnDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsColumnDetail.swift; sourceTree = ""; }; 233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 50.xcdatamodel"; sourceTree = ""; }; + 233E99B52D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherConditionsCompactWidget.swift; sourceTree = ""; }; + 233E99B72D849C6500CC3A77 /* HumidityCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HumidityCompactWidget.swift; sourceTree = ""; }; + 233E99B92D849C7000CC3A77 /* PressureCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PressureCompactWidget.swift; sourceTree = ""; }; + 233E99BB2D849C8C00CC3A77 /* WindCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindCompactWidget.swift; sourceTree = ""; }; + 233E99BD2D849D3200CC3A77 /* RadiationCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadiationCompactWidget.swift; sourceTree = ""; }; + 233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceCompactWidget.swift; sourceTree = ""; }; + 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightCompactWidget.swift; sourceTree = ""; }; + 233E99C42D84A0B600CC3A77 /* CompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactWidget.swift; sourceTree = ""; }; + 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoilCompactWidgets.swift; sourceTree = ""; }; 2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAttributePropertyWrapper.swift; sourceTree = ""; }; 2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataClass.swift"; sourceTree = ""; }; 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = ""; }; @@ -607,6 +625,23 @@ path = "Metrics Columns"; sourceTree = ""; }; + 233E99B42D849C2D00CC3A77 /* Compact Widgets */ = { + isa = PBXGroup; + children = ( + 233E99C42D84A0B600CC3A77 /* CompactWidget.swift */, + 233E99B72D849C6500CC3A77 /* HumidityCompactWidget.swift */, + 233E99B52D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift */, + 233E99B92D849C7000CC3A77 /* PressureCompactWidget.swift */, + 233E99BB2D849C8C00CC3A77 /* WindCompactWidget.swift */, + DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */, + 233E99BD2D849D3200CC3A77 /* RadiationCompactWidget.swift */, + 233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */, + 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */, + 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */, + ); + path = "Compact Widgets"; + sourceTree = ""; + }; 2344A2AC2D66978000170A77 /* CoreData */ = { isa = PBXGroup; children = ( @@ -778,7 +813,6 @@ isa = PBXGroup; children = ( DD5E523E298F5A9E00D21B61 /* AirQualityIndex.swift */, - DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */, DDA9515D2BC6F56F00CEA535 /* IndoorAirQuality.swift */, DD354FD82BD96A0B0061A25F /* IAQScale.swift */, DD41A61429AB0035003C5A37 /* NodeWeatherForecast.swift */, @@ -1039,6 +1073,7 @@ DDC2E18D26CE25CB0042C5E4 /* Helpers */ = { isa = PBXGroup; children = ( + 233E99B42D849C2D00CC3A77 /* Compact Widgets */, DD6F65772C6EAB860053C113 /* Help */, DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */, DD3CC24B2C498D6C001BD3A2 /* BatteryCompact.swift */, @@ -1407,7 +1442,9 @@ 251926902C3CB44900249DF5 /* ClientHistoryButton.swift in Sources */, DDD5BB102C285FB3007E03CA /* AppLogFilter.swift in Sources */, 2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */, + 233E99B82D849C6500CC3A77 /* HumidityCompactWidget.swift in Sources */, DD4640202AFF10F4002A5ECB /* WaypointForm.swift in Sources */, + 233E99C12D849D6000CC3A77 /* DistanceCompactWidget.swift in Sources */, DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, DD15E4F32B8BA56E00654F61 /* PaxCounterConfig.swift in Sources */, @@ -1437,6 +1474,7 @@ DDE9659C2B1C3B6A00531070 /* RouteRecorder.swift in Sources */, B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */, DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */, + 233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */, DDDB26482AACD6D1003AFCB7 /* NodeMapMapkit.swift in Sources */, DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */, DD1BD0F32C63C65E008C0C70 /* SecurityConfig.swift in Sources */, @@ -1454,6 +1492,7 @@ DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */, DD3619152B1EF9F900C41C8C /* LocationsHandler.swift in Sources */, DD6F65792C6EADE60053C113 /* DirectMessagesHelp.swift in Sources */, + 233E99B62D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift in Sources */, 25F5D5C02C3F6DA6008036E3 /* Router.swift in Sources */, DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */, 25C49D902C471AEA0024FBD1 /* Constants.swift in Sources */, @@ -1492,6 +1531,7 @@ DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, DD97E96828EFE9A00056DDA4 /* About.swift in Sources */, DDDB444029F79AB000EE2349 /* UserDefaults.swift in Sources */, + 233E99BA2D849C7000CC3A77 /* PressureCompactWidget.swift in Sources */, DDB6ABE028B13AC700384BA1 /* DeviceEnums.swift in Sources */, DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */, D93068DD2B81CA820066FBC8 /* ConfigHeader.swift in Sources */, @@ -1501,6 +1541,7 @@ DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */, BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */, DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, + 233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */, DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */, DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */, DDB75A112A059258006ED576 /* Url.swift in Sources */, @@ -1536,6 +1577,7 @@ 8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */, 2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */, DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */, + 233E99BE2D849D3200CC3A77 /* RadiationCompactWidget.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */, DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */, @@ -1567,12 +1609,14 @@ DDDB26442AAC0206003AFCB7 /* NodeDetail.swift in Sources */, DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */, DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */, + 233E99C72D84A70900CC3A77 /* SoilCompactWidgets.swift in Sources */, BCE2D3C92C7C377F008E6199 /* FactoryResetNodeIntent.swift in Sources */, DD93800B2BA3F968008BEC06 /* NodeMapContent.swift in Sources */, DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */, DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */, DDDB444429F8A8DD00EE2349 /* Float.swift in Sources */, DDAB580F2B0DAFBC00147258 /* LocationEntityExtension.swift in Sources */, + 233E99BC2D849C8C00CC3A77 /* WindCompactWidget.swift in Sources */, B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */, BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */, D93068D72B8146690066FBC8 /* MessageText.swift in Sources */, diff --git a/Meshtastic/Assets.xcassets/soil.moisture.symbolset/Contents.json b/Meshtastic/Assets.xcassets/soil.moisture.symbolset/Contents.json new file mode 100644 index 00000000..c5fa326a --- /dev/null +++ b/Meshtastic/Assets.xcassets/soil.moisture.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "soilMoisture.variable.svg", + "idiom" : "universal" + } + ] +} diff --git a/Meshtastic/Assets.xcassets/soil.moisture.symbolset/soilMoisture.variable.svg b/Meshtastic/Assets.xcassets/soil.moisture.symbolset/soilMoisture.variable.svg new file mode 100644 index 00000000..346bc90e --- /dev/null +++ b/Meshtastic/Assets.xcassets/soil.moisture.symbolset/soilMoisture.variable.svg @@ -0,0 +1,366 @@ + + + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.6.0 + Requires Xcode 16 or greater + Generated from thermometer.variable + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meshtastic/Assets.xcassets/soil.temperature.symbolset/Contents.json b/Meshtastic/Assets.xcassets/soil.temperature.symbolset/Contents.json new file mode 100644 index 00000000..d136a31b --- /dev/null +++ b/Meshtastic/Assets.xcassets/soil.temperature.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "soilTemp.variable.svg", + "idiom" : "universal" + } + ] +} diff --git a/Meshtastic/Assets.xcassets/soil.temperature.symbolset/soilTemp.variable.svg b/Meshtastic/Assets.xcassets/soil.temperature.symbolset/soilTemp.variable.svg new file mode 100644 index 00000000..b5501a8a --- /dev/null +++ b/Meshtastic/Assets.xcassets/soil.temperature.symbolset/soilTemp.variable.svg @@ -0,0 +1,363 @@ + + + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.6.0 + Requires Xcode 16 or greater + Generated from thermometer.variable + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift new file mode 100644 index 00000000..6bb35a0f --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift @@ -0,0 +1,36 @@ +// +// CompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// + +import SwiftUI + +// This file was created for the purpose of previewing +// all of the Compact Widgets in one place. + +// In the future, it could be used for a CompactWidget superclass, if desired. + +#Preview { + + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + HumidityCompactWidget(humidity: 27, dewPoint: "32°") + HumidityCompactWidget(humidity: 27, dewPoint: nil) + WeatherConditionsCompactWidget(temperature: "24°F", symbolName: "sun.rain.fill", description: "Raining") + PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: true) + PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: false) + WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: "SW") + WindCompactWidget(speed: "12 mph", gust: nil, direction: "SW") + WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: nil) + WindCompactWidget(speed: "12 mph", gust: nil, direction: nil) + RadiationCompactWidget(radiation: "15", unit: "µR/hr") + DistanceCompactWidget(distance: "123", unit: "mm") + WeightCompactWidget(weight: "123", unit: "kg") + SoilTemperatureCompactWidget(temperature: "23", unit: "°C") + SoilMoistureCompactWidget(moisture: "23", unit: "%") + } + } +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/CurrentConditionsCompact.swift b/Meshtastic/Views/Helpers/Compact Widgets/CurrentConditionsCompact.swift new file mode 100644 index 00000000..5f0edf46 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/CurrentConditionsCompact.swift @@ -0,0 +1,34 @@ +// +// CurrentConditionsCompact.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 2/5/23. +// +import SwiftUI + +struct CurrentConditionsCompact: View { + var temp: Float + var condition: WeatherConditions + + var body: some View { + Label("\(String(temp.formattedTemperature()))", systemImage: condition.symbolName) + .font(.caption) + .foregroundColor(.gray) + .symbolRenderingMode(.multicolor) + } +} + +struct CurrentConditionsCompact_Previews: PreviewProvider { + static var previews: some View { + + VStack { + CurrentConditionsCompact(temp: 22, condition: WeatherConditions.clear) + CurrentConditionsCompact(temp: 17, condition: WeatherConditions.cloudy) + CurrentConditionsCompact(temp: -5, condition: WeatherConditions.frigid) + CurrentConditionsCompact(temp: 38, condition: WeatherConditions.hot) + CurrentConditionsCompact(temp: 10, condition: WeatherConditions.rain) + CurrentConditionsCompact(temp: 30, condition: WeatherConditions.smoky) + CurrentConditionsCompact(temp: -2, condition: WeatherConditions.snow) + } + } +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/DistanceCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/DistanceCompactWidget.swift new file mode 100644 index 00000000..9f04406e --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/DistanceCompactWidget.swift @@ -0,0 +1,39 @@ +// +// DistanceCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// + +import SwiftUI + +struct DistanceCompactWidget: View { + let distance: String + let unit: String + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { + Image(systemName: "ruler") + .imageScale(.small) + .foregroundColor(.accentColor) + Text("Distance") + .textCase(.uppercase) + .font(.callout) + } + HStack { + Text("\(distance)") + .font(distance.length < 4 ? .system(size: 50) : .system(size: 40) ) + Text(unit) + .font(.system(size: 14)) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + DistanceCompactWidget(distance: "123", unit: "mm") +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/HumidityCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/HumidityCompactWidget.swift new file mode 100644 index 00000000..b20981dd --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/HumidityCompactWidget.swift @@ -0,0 +1,47 @@ +// +// HumidityCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// +import SwiftUI + +struct HumidityCompactWidget: View { + let humidity: Int + let dewPoint: String? + var body: some View { + VStack(alignment: .leading) { + HStack(spacing: 5.0) { + Image(systemName: "humidity") + .foregroundColor(.accentColor) + .font(.callout) + Text("Humidity") + .textCase(.uppercase) + .font(.caption) + } + Text("\(humidity)%") + .font(.largeTitle) + .padding(.bottom, 5) + if let dewPoint { + Text("The dew point is \(dewPoint) right now.") + .lineLimit(3) + .allowsTightening(true) + .fixedSize(horizontal: false, vertical: true) + .font(.caption2) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + HumidityCompactWidget(humidity: 27, dewPoint: "32°") + HumidityCompactWidget(humidity: 27, dewPoint: nil) + } + } +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/PressureCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/PressureCompactWidget.swift new file mode 100644 index 00000000..6f2f6583 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/PressureCompactWidget.swift @@ -0,0 +1,43 @@ +// +// PressureCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// +import SwiftUI + +struct PressureCompactWidget: View { + let pressure: String + let unit: String + let low: Bool + var body: some View { + VStack(alignment: .leading) { + HStack(spacing: 5.0) { + Image(systemName: "gauge") + .foregroundColor(.accentColor) + .font(.callout) + Text("Pressure") + .textCase(.uppercase) + .font(.caption) + } + Text(pressure) + .font(pressure.length < 7 ? .system(size: 35) : .system(size: 30) ) + Text(low ? "LOW" : "HIGH") + .padding(.bottom, 10) + Text(unit) + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: true) + PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: false) + } + } +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift new file mode 100644 index 00000000..b5cd7232 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift @@ -0,0 +1,39 @@ +// +// RadiationCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// + +import SwiftUI + +struct RadiationCompactWidget: View { + let radiation: String + let unit: String + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { + Text(verbatim: "☢") + .font(.system(size: 30, design: .monospaced)) + .foregroundColor(.accentColor) + Text("Radiation") + .textCase(.uppercase) + .font(.callout) + } + HStack { + Text("\(radiation)") + .font(radiation.length < 4 ? .system(size: 50) : .system(size: 34) ) + Text(unit) + .font(.system(size: 14)) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + RadiationCompactWidget(radiation: "15", unit: "µR/hr") +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/SoilCompactWidgets.swift b/Meshtastic/Views/Helpers/Compact Widgets/SoilCompactWidgets.swift new file mode 100644 index 00000000..7f51d21e --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/SoilCompactWidgets.swift @@ -0,0 +1,72 @@ +// +// SoilCompactWidgets.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// + +import SwiftUI + +struct SoilTemperatureCompactWidget: View { + let temperature: String + let unit: String + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { + Image("soil.temperature") + .imageScale(.small) + .foregroundColor(.accentColor) + Text("Soil Temp") + .textCase(.uppercase) + .font(.callout) + } + HStack { + Text("\(temperature)") + .font(temperature.length < 4 ? .system(size: 50) : .system(size: 40) ) + Text(unit) + .font(.system(size: 14)) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +struct SoilMoistureCompactWidget: View { + let moisture: String + let unit: String + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { + Image("soil.moisture") + .imageScale(.small) + .foregroundColor(.accentColor) + Text("Soil Moisture") + .textCase(.uppercase) + .font(.callout) + } + HStack { + Text("\(moisture)") + .font(moisture.length < 4 ? .system(size: 50) : .system(size: 40) ) + Text(unit) + .font(.system(size: 14)) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + SoilTemperatureCompactWidget(temperature: "23", unit: "°C") + SoilMoistureCompactWidget(moisture: "23", unit: "%") + } + } +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift new file mode 100644 index 00000000..4918c7b6 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift @@ -0,0 +1,42 @@ +// +// WeatherConditionsCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// +import SwiftUI + +struct WeatherConditionsCompactWidget: View { + let temperature: String + let symbolName: String + let description: String + var body: some View { + VStack(alignment: .leading) { + HStack(spacing: 5.0) { + Image(systemName: symbolName) + .foregroundColor(.accentColor) + .font(.callout) + Text(description) + .lineLimit(2) + .allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) + .fixedSize(horizontal: false, vertical: true) + .font(.caption) + } + Text(temperature) + .font(temperature.length < 4 ? .system(size: 72) : .system(size: 54) ) + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + WeatherConditionsCompactWidget(temperature: "24°F", symbolName: "sun.rain.fill", description: "Raining") + } + } +} + diff --git a/Meshtastic/Views/Helpers/Compact Widgets/WeightCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/WeightCompactWidget.swift new file mode 100644 index 00000000..f6b6df81 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/WeightCompactWidget.swift @@ -0,0 +1,39 @@ +// +// WeightCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// + +import SwiftUI + +struct WeightCompactWidget: View { + let weight: String + let unit: String + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { + Image(systemName: "scalemass") + .imageScale(.small) + .foregroundColor(.accentColor) + Text("Weight") + .textCase(.uppercase) + .font(.callout) + } + HStack { + Text("\(weight)") + .font(weight.length < 4 ? .system(size: 50) : .system(size: 40) ) + Text(unit) + .font(.system(size: 14)) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + WeightCompactWidget(weight: "123", unit: "kg") +} diff --git a/Meshtastic/Views/Helpers/Compact Widgets/WindCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/WindCompactWidget.swift new file mode 100644 index 00000000..3b3c5273 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/WindCompactWidget.swift @@ -0,0 +1,45 @@ +// +// WindCompactWidget.swift +// Meshtastic +// +// Created by Jake Bordens on 3/14/25. +// +import SwiftUI + +struct WindCompactWidget: View { + let speed: String + let gust: String? + let direction: String? + + var body: some View { + let hasGust = ((gust ?? "").isEmpty == false) + VStack(alignment: .leading) { + Label { Text("Wind").textCase(.uppercase) } icon: { Image(systemName: "wind").foregroundColor(.accentColor) } + if let direction { + Text("\(direction)") + .font(!hasGust ? .callout : .caption) + .padding(.bottom, 10) + } + Text(speed) + .font(.system(size: 35)) + if let gust, !gust.isEmpty { + Text("Gusts \(gust)") + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: "SW") + WindCompactWidget(speed: "12 mph", gust: nil, direction: "SW") + WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: nil) + WindCompactWidget(speed: "12 mph", gust: nil, direction: nil) + } + } +} diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift index 245c2f9c..fe221f5a 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift @@ -73,6 +73,77 @@ extension MetricsColumnList { } }), + // Various Lux + MetricsTableColumn( + id: "lux", + keyPath: \.lux, + name: "Lux", + abbreviatedName: "Lux", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, lux in + lux.map { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))") + } ?? Text(Constants.nilValueIndicator) + }), + + MetricsTableColumn( + id: "whiteLux", + keyPath: \.whiteLux, + name: "White Lux", + abbreviatedName: "White", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, lux in + lux.map { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))") + } ?? Text(Constants.nilValueIndicator) + }), + + MetricsTableColumn( + id: "uvLux", + keyPath: \.uvLux, + name: "UV Lux", + abbreviatedName: "UV", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, lux in + lux.map { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))") + } ?? Text(Constants.nilValueIndicator) + }), + + MetricsTableColumn( + id: "irLux", + keyPath: \.irLux, + name: "IR Lux", + abbreviatedName: "IR", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, lux in + lux.map { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))") + } ?? Text(Constants.nilValueIndicator) + }), + + // Radiation + MetricsTableColumn( + id: "radiation", + keyPath: \.radiation, + name: "Radiation", + abbreviatedName: "☢️", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, radiation in + radiation.map { + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + Text(verbatim: "\($0.formatted(.number.grouping(.never).precision(.fractionLength(1)))) µR/h") + } else { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))") + } + } ?? Text(Constants.nilValueIndicator) + }), + // Wind Direction Series Configuration MetricsTableColumn( id: "windDirection", @@ -127,6 +198,80 @@ extension MetricsColumnList { } ?? Text(verbatim: Constants.nilValueIndicator) }), + // Weight + MetricsTableColumn( + id: "weight", + keyPath: \.weight, + name: "Weight", + abbreviatedName: "kg", + minWidth: 30, maxWidth: 60, + visible: false, + tableBody: { _, weight in + weight.map { + let weight = Measurement( + value: Double($0), unit: UnitMass.kilograms) + return Text( + weight.formatted( + .measurement( + width: .abbreviated, + numberFormatStyle: .number.grouping(.never) + .precision( + .fractionLength(0)))) + ) + } ?? Text(Constants.nilValueIndicator) + }), + + // Distance sensor, often used for water level + MetricsTableColumn( + id: "distance", + keyPath: \.distance, + name: "Distance", + abbreviatedName: "Dist", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, distance in + distance.map { + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + Text(verbatim: "\($0.formatted(.number.grouping(.never).precision(.fractionLength(1)))) mm") + } else { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))") + } + } ?? Text(Constants.nilValueIndicator) + }), + + // Soil Temperature + MetricsTableColumn( + id: "soilTemperature", + keyPath: \.soilTemperature, + name: "Soil Temperature", + abbreviatedName: "Soil Temp", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, soilTemperature in + soilTemperature.map { + Text($0.formattedTemperature()) + } ?? Text(verbatim: Constants.nilValueIndicator) + + }), + + // Soil Moisture + MetricsTableColumn( + id: "soilMoisture", + keyPath: \.soilMoisture, + name: "Soil Moisture", + abbreviatedName: "Moist", + minWidth: 30, maxWidth: 50, + visible: false, + tableBody: { _, moisture in + moisture.map { + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(0))))%") + } else { + Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(0))))") + } + } ?? Text(Constants.nilValueIndicator) + }), + // Timestamp Series Configuration -- for use in table only MetricsTableColumn( id: "time", diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift index e9422ee4..1db9b025 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift @@ -63,9 +63,9 @@ extension MetricsSeriesList { abbreviatedName: "Hum", initialYAxisRange: 0.0...100.0, foregroundStyle: { _ in - .linearGradient( - colors: [Color(UIColor.purple.darker(componentDelta: 0.2)), .purple], - startPoint: .bottom, endPoint: .top + .linearGradient( + colors: [Color(UIColor.purple.darker(componentDelta: 0.2)), .purple], + startPoint: .bottom, endPoint: .top ) }, chartBody: { series, _, time, humidity in @@ -80,7 +80,7 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), - + // Barometric Pressure Series Configuration MetricsChartSeries( id: "barometricPressure", @@ -89,10 +89,10 @@ extension MetricsSeriesList { abbreviatedName: "Bar", visible: false, foregroundStyle: { _ in - .linearGradient( - colors: [Color(UIColor.green.darker(componentDelta: 0.3)), .green], - startPoint: .bottom, endPoint: .top - ) + .linearGradient( + colors: [Color(UIColor.green.darker(componentDelta: 0.3)), .green], + startPoint: .bottom, endPoint: .top + ) }, chartBody: { series, _, time, pressure in if let pressure { @@ -106,7 +106,7 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), - + // Indoor Air Quality Series Configuration MetricsChartSeries( id: "iaq", @@ -134,6 +134,241 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), + + // Lux + MetricsChartSeries( + id: "lux", + keyPath: \.lux, + name: "Lux", + abbreviatedName: "Lux", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.cyan.lighter(componentDelta: 0.3)), .cyan], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, lux in + if let lux { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, lux) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // White Lux + MetricsChartSeries( + id: "whiteLux", + keyPath: \.whiteLux, + name: "White Lux", + abbreviatedName: "White", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.cyan.lighter(componentDelta: 0.5)), Color(UIColor.cyan.lighter(componentDelta: 0.2))], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, lux in + if let lux { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, lux) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // UV Lux + MetricsChartSeries( + id: "uvLux", + keyPath: \.uvLux, + name: "UV Lux", + abbreviatedName: "UV", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.systemIndigo.lighter(componentDelta: 0.4)), Color(UIColor.systemIndigo.lighter(componentDelta: 0.2))], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, lux in + if let lux { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, lux) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // IR Lux + MetricsChartSeries( + id: "irLux", + keyPath: \.irLux, + name: "IR Lux", + abbreviatedName: "IR", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.red.darker(componentDelta: 0.5)), .red], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, lux in + if let lux { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, lux) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // Radiation + MetricsChartSeries( + id: "radiation", + keyPath: \.radiation, + name: "Radiation", + abbreviatedName: "☢️", + minumumYAxisSpan: 20.0, + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.orange.darker(componentDelta: 0.4)), .orange], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, radiation in + if let radiation { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, radiation) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // Weight + MetricsChartSeries( + id: "weight", + keyPath: \.weight, + name: "Weight", + abbreviatedName: "kg", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.systemPink.darker(componentDelta: 0.5)), .pink], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, weight in + if let weight { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, weight) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // Distance + MetricsChartSeries( + id: "distance", + keyPath: \.distance, + name: "Distance", + abbreviatedName: "Dist", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.systemTeal.darker(componentDelta: 0.7)), Color(UIColor.systemTeal)], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, distance in + if let distance { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, distance) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // Soil Temperature + MetricsChartSeries( + id: "soilTemperature", + keyPath: \.soilTemperature, + name: "Soil Temperature", + abbreviatedName: "Soil Temp", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.brown.darker(componentDelta: 0.4)), .brown], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, soilTemp in + if let soilTemp { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, soilTemp) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // Soil Temperature + MetricsChartSeries( + id: "soilMoisture", + keyPath: \.soilMoisture, + name: "Soil Moisture", + abbreviatedName: "Moist", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.blue.darker(componentDelta: 0.4)), .brown], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, soilMoisture in + if let soilMoisture { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, soilMoisture) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), // Combined Wind Speed and Direction Series Configuration -- For use in Chart only MetricsChartSeries( @@ -143,10 +378,10 @@ extension MetricsSeriesList { abbreviatedName: "Speed/Dir", visible: false, foregroundStyle: { _ in - .linearGradient( - colors: [Color(UIColor.yellow.darker(componentDelta: 0.3)), Color(UIColor.yellow.darker(componentDelta: 0.1))], - startPoint: .bottom, endPoint: .top - ) + .linearGradient( + colors: [Color(UIColor.yellow.darker(componentDelta: 0.3)), Color(UIColor.yellow.darker(componentDelta: 0.1))], + startPoint: .bottom, endPoint: .top + ) }, chartBody: { series, _, time, wsad in if let wsad { @@ -216,7 +451,7 @@ func generateStops(minTemp: Double, maxTemp: Double, tempUnit: UnitTemperature, ((tempUnit == .celsius ? 20 : 68), .yellow), ((tempUnit == .celsius ? 30 : 86), .orange), ((tempUnit == .celsius ? 55 : 125), .red) - ] + ] for (stopValue, color) in stopTargets { let stopLocation = transform(stopValue, from: minTemp...maxTemp, to: 0...1) gradientStops.append(Gradient.Stop(color: color.opacity(opacity), location: stopLocation)) diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index 958f4bfc..caaf4a33 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -246,7 +246,7 @@ struct NodeDetail: View { gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction) } if let radiation = node.latestEnvironmentMetrics?.radiation { - RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(2))), unit: "µR/hr") + RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(1))), unit: "µR/hr") } if let weight = node.latestEnvironmentMetrics?.weight { WeightCompactWidget(weight: weight.formatted(.number.precision(.fractionLength(1))), unit: "kg") From e610ce547307a9fd9e53648cd05e37a2afa234a6 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 08:18:27 -0400 Subject: [PATCH 09/29] Added rainfall to Environment Metrics graph and chart --- .../EnvironmentDefaultColumns.swift | 46 ++++++ .../EnvironmentDefaultSeries.swift | 132 ++++++++++++------ 2 files changed, 138 insertions(+), 40 deletions(-) diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift index fe221f5a..ffd86987 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift @@ -198,6 +198,52 @@ extension MetricsColumnList { } ?? Text(verbatim: Constants.nilValueIndicator) }), + // Rainfall 1-hour + MetricsTableColumn( + id: "rainfall1H", + keyPath: \.rainfall1H, + name: "Rainfall (1H)", + abbreviatedName: "Rain 1H", + minWidth: 30, maxWidth: 60, + visible: false, + tableBody: { _, rainfall in + rainfall.map { + let rain = Measurement( + value: Double($0), unit: UnitLength.millimeters) + return Text( + rain.formatted( + .measurement( + width: .abbreviated, + numberFormatStyle: .number.grouping(.never) + .precision( + .fractionLength(0)))) + ) + } ?? Text(Constants.nilValueIndicator) + }), + + // Rainfall 24-hour + MetricsTableColumn( + id: "rainfall24H", + keyPath: \.rainfall24H, + name: "Rainfall (24H)", + abbreviatedName: "Rain 24H", + minWidth: 30, maxWidth: 60, + visible: false, + tableBody: { _, rainfall in + rainfall.map { + let rain = Measurement( + value: Double($0), unit: UnitLength.millimeters) + return Text( + rain.formatted( + .measurement( + width: .abbreviated, + numberFormatStyle: .number.grouping(.never) + .precision( + .fractionLength(0)))) + ) + } ?? Text(Constants.nilValueIndicator) + }), + // Weight MetricsTableColumn( id: "weight", diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift index 1db9b025..d3762845 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift @@ -266,6 +266,98 @@ extension MetricsSeriesList { } }), + // Combined Wind Speed and Direction Series Configuration -- For use in Chart only + MetricsChartSeries( + id: "windSpeedAndDirection", + keyPath: \.windSpeedAndDirection, + name: "Wind Speed/Direction", + abbreviatedName: "Speed/Dir", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.yellow.darker(componentDelta: 0.3)), Color(UIColor.yellow.darker(componentDelta: 0.1))], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, wsad in + if let wsad { + // debug data: var wsad = WindSpeedAndDirection(windSpeed:Float.random(in:0...25), windDirection: Int32.random(in:0..<3)*90 ) + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, wsad.windSpeed) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + PointMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, wsad.windSpeed) + ) + .symbol { + if let wd = wsad.windDirection { + Image(systemName: "location.north.circle.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(Color.white, Color(UIColor.yellow.darker(componentDelta: 0.3))) + .rotationEffect( + .degrees(Double(wd))) + } + }.foregroundStyle(.yellow) + } + }), + + // Rainfaill 1-hour + MetricsChartSeries( + id: "rainfall1H", + keyPath: \.rainfall1H, + name: "Rainfall 1H", + abbreviatedName: "Rain 1H", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.systemBlue.darker(componentDelta: 0.5)), .blue], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, rainfall in + if let rainfall { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, rainfall) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + + // Rainfaill 24-hour + MetricsChartSeries( + id: "rainfall24H", + keyPath: \.rainfall24H, + name: "Rainfall 24H", + abbreviatedName: "Rain 24H", + visible: false, + foregroundStyle: { _ in + .linearGradient( + colors: [Color(UIColor.systemBlue.darker(componentDelta: 0.5)), .cyan], + startPoint: .bottom, endPoint: .top + ) + }, + chartBody: { series, _, time, rainfall in + if let rainfall { + LineMark( + x: .value("Time", time), + y: .value(series.abbreviatedName, rainfall) + ) + .interpolationMethod(.catmullRom) + .foregroundStyle(by: .value("Series", series.abbreviatedName)) + .lineStyle(StrokeStyle(lineWidth: 4)) + .alignsMarkStylesWithPlotArea() + } + }), + // Weight MetricsChartSeries( id: "weight", @@ -369,46 +461,6 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), - - // Combined Wind Speed and Direction Series Configuration -- For use in Chart only - MetricsChartSeries( - id: "windSpeedAndDirection", - keyPath: \.windSpeedAndDirection, - name: "Wind Speed/Direction", - abbreviatedName: "Speed/Dir", - visible: false, - foregroundStyle: { _ in - .linearGradient( - colors: [Color(UIColor.yellow.darker(componentDelta: 0.3)), Color(UIColor.yellow.darker(componentDelta: 0.1))], - startPoint: .bottom, endPoint: .top - ) - }, - chartBody: { series, _, time, wsad in - if let wsad { - // debug data: var wsad = WindSpeedAndDirection(windSpeed:Float.random(in:0...25), windDirection: Int32.random(in:0..<3)*90 ) - LineMark( - x: .value("Time", time), - y: .value(series.abbreviatedName, wsad.windSpeed) - ) - .interpolationMethod(.catmullRom) - .foregroundStyle(by: .value("Series", series.abbreviatedName)) - .lineStyle(StrokeStyle(lineWidth: 4)) - .alignsMarkStylesWithPlotArea() - PointMark( - x: .value("Time", time), - y: .value(series.abbreviatedName, wsad.windSpeed) - ) - .symbol { - if let wd = wsad.windDirection { - Image(systemName: "location.north.circle.fill") - .symbolRenderingMode(.palette) - .foregroundStyle(Color.white, Color(UIColor.yellow.darker(componentDelta: 0.3))) - .rotationEffect( - .degrees(Double(wd))) - } - }.foregroundStyle(.yellow) - } - }) ]) } } From 52a6620df6d24656d0e4c016bbc61b2ffd7aedfa Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 09:05:36 -0400 Subject: [PATCH 10/29] Rainfall Compact Widgets --- Meshtastic.xcodeproj/project.pbxproj | 4 ++ .../Compact Widgets/CompactWidget.swift | 11 ++++ .../RainfallCompactWidget.swift | 66 +++++++++++++++++++ .../Views/Nodes/Helpers/NodeDetail.swift | 20 ++++++ 4 files changed, 101 insertions(+) create mode 100644 Meshtastic/Views/Helpers/Compact Widgets/RainfallCompactWidget.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index bf000340..19798229 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */; }; 233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C42D84A0B600CC3A77 /* CompactWidget.swift */; }; 233E99C72D84A70900CC3A77 /* SoilCompactWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */; }; + 233E99CB2D85AAA900CC3A77 /* RainfallCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99CA2D85AAA900CC3A77 /* RainfallCompactWidget.swift */; }; 2344A2AB2D66974300170A77 /* ManagedAttributePropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */; }; 2344A2AF2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */; }; 2344A2B02D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */; }; @@ -296,6 +297,7 @@ 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightCompactWidget.swift; sourceTree = ""; }; 233E99C42D84A0B600CC3A77 /* CompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactWidget.swift; sourceTree = ""; }; 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoilCompactWidgets.swift; sourceTree = ""; }; + 233E99CA2D85AAA900CC3A77 /* RainfallCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RainfallCompactWidget.swift; sourceTree = ""; }; 2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAttributePropertyWrapper.swift; sourceTree = ""; }; 2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataClass.swift"; sourceTree = ""; }; 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = ""; }; @@ -638,6 +640,7 @@ 233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */, 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */, 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */, + 233E99CA2D85AAA900CC3A77 /* RainfallCompactWidget.swift */, ); path = "Compact Widgets"; sourceTree = ""; @@ -1528,6 +1531,7 @@ DD1BD0EE2C603C91008C0C70 /* CustomFormatters.swift in Sources */, DD0BE3102CB9FDC4000BA445 /* DetectionSensorEnums.swift in Sources */, DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */, + 233E99CB2D85AAA900CC3A77 /* RainfallCompactWidget.swift in Sources */, DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, DD97E96828EFE9A00056DDA4 /* About.swift in Sources */, DDDB444029F79AB000EE2349 /* UserDefaults.swift in Sources */, diff --git a/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift index 6bb35a0f..42411cc1 100644 --- a/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift +++ b/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift @@ -31,6 +31,17 @@ import SwiftUI WeightCompactWidget(weight: "123", unit: "kg") SoilTemperatureCompactWidget(temperature: "23", unit: "°C") SoilMoistureCompactWidget(moisture: "23", unit: "%") + + let rain: Float = 10.1 + let locale = NSLocale.current as NSLocale + let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) + let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches + let unitLabel = usesMetricSystem ? "mm" : "in" + let measurement = Measurement(value: Double(rain), unit: UnitLength.millimeters) + let decimals = usesMetricSystem ? 0 : 1 + let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals))) + RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel) + RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel) } } } diff --git a/Meshtastic/Views/Helpers/Compact Widgets/RainfallCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/RainfallCompactWidget.swift new file mode 100644 index 00000000..9e71d213 --- /dev/null +++ b/Meshtastic/Views/Helpers/Compact Widgets/RainfallCompactWidget.swift @@ -0,0 +1,66 @@ +// +// RainfallCompactWidgets.swift +// Meshtastic +// +// Created by Jake Bordens on 3/15/25. +// + +import SwiftUI + +struct RainfallCompactWidget: View { + enum RainfallTimeSpan: String { + case rainfall1H = "Rainfall 1H" + case rainfall24H = "Rainfall 24H" + } + + let timespan: RainfallTimeSpan + let rainfall: String + let unit: String + + private var icon: Image { + if timespan == .rainfall1H { + return Image(systemName: "cloud.rain.fill") + } + return Image(systemName: "cloud.heavyrain.fill") + } + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { + icon.imageScale(.small) + .foregroundColor(.accentColor) + Text(timespan.rawValue) + .textCase(.uppercase) + .font(.callout) + } + HStack { + Text("\(rainfall)") + .font(rainfall.length < 4 ? .system(size: 50) : .system(size: 40) ) + Text(unit) + .font(.system(size: 14)) + } + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} + +#Preview { + let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + Form { + LazyVGrid(columns: gridItemLayout) { + let rain: Float = 10.1 + let locale = NSLocale.current as NSLocale + let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) + let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches + let unitLabel = usesMetricSystem ? "mm" : "in" + let measurement = Measurement(value: Double(rain), unit: UnitLength.millimeters) + let decimals = usesMetricSystem ? 0 : 1 + let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals))) + + RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel) + RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel) + } + } +} diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index caaf4a33..d3439b3f 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -245,6 +245,26 @@ struct NodeDetail: View { WindCompactWidget(speed: windSpeedMeasurement.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))), gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction) } + if let rainfall1h = node.latestEnvironmentMetrics?.rainfall1H { + let locale = NSLocale.current as NSLocale + let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) + let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches + let unitLabel = usesMetricSystem ? "mm" : "in" + let measurement = Measurement(value: Double(rainfall1h), unit: UnitLength.millimeters) + let decimals = usesMetricSystem ? 0 : 1 + let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals))) + RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel) + } + if let rainfall24h = node.latestEnvironmentMetrics?.rainfall24H { + let locale = NSLocale.current as NSLocale + let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) + let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches + let unitLabel = usesMetricSystem ? "mm" : "in" + let measurement = Measurement(value: Double(rainfall24h), unit: UnitLength.millimeters) + let decimals = usesMetricSystem ? 0 : 1 + let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals))) + RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel) + } if let radiation = node.latestEnvironmentMetrics?.radiation { RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(1))), unit: "µR/hr") } From 5188152f2f1d78aa573a5d907613bf8ea19e8948 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 09:47:52 -0400 Subject: [PATCH 11/29] Improvements to emoji handling for node names --- Meshtastic/Extensions/String.swift | 26 +++++++++++++++++++ Meshtastic/Views/Helpers/CircleText.swift | 2 +- .../Views/Helpers/ConnectedDevice.swift | 2 +- Meshtastic/Views/Settings/UserConfig.swift | 8 +++--- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Meshtastic/Extensions/String.swift b/Meshtastic/Extensions/String.swift index c7c6385b..aa28fa71 100644 --- a/Meshtastic/Extensions/String.swift +++ b/Meshtastic/Extensions/String.swift @@ -101,4 +101,30 @@ extension String { .map { String($0) } .joined() } + + // Adds variation selectors to prefer the graphical form of emoji. + // Looks ahead to make sure that the variation selector is not already applied. + var addingVariationSelectors: String { + var result = "" + var scalars = self.unicodeScalars + var index = scalars.startIndex + while index < scalars.endIndex { + let currentScalar = scalars[index] + result += String(currentScalar) + if currentScalar.properties.isEmoji && !currentScalar.properties.isEmojiPresentation { + // Check if the next scalar is U+FE0F + let nextIndex = scalars.index(after: index) + if nextIndex < scalars.endIndex && scalars[nextIndex].value == 0xFE0F { + // Already has variation selector; skip the next scalar + index = nextIndex + } else { + // Append variation selector + result += String(UnicodeScalar(0xFE0F)!) + } + } + // Move to the next scalar + index = scalars.index(after: index) + } + return result + } } diff --git a/Meshtastic/Views/Helpers/CircleText.swift b/Meshtastic/Views/Helpers/CircleText.swift index b7e4238d..b8f74842 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -16,7 +16,7 @@ struct CircleText: View { Circle() .fill(color) .frame(width: circleSize, height: circleSize) - Text(text) + Text(text.addingVariationSelectors) .frame(width: circleSize * 0.9, height: circleSize * 0.9, alignment: .center) .foregroundColor(color.isLight() ? .black : .white) .minimumScaleFactor(0.001) diff --git a/Meshtastic/Views/Helpers/ConnectedDevice.swift b/Meshtastic/Views/Helpers/ConnectedDevice.swift index 42b0ac70..c795b1b0 100644 --- a/Meshtastic/Views/Helpers/ConnectedDevice.swift +++ b/Meshtastic/Views/Helpers/ConnectedDevice.swift @@ -28,7 +28,7 @@ struct ConnectedDevice: View { .imageScale(.large) .foregroundColor(.green) .symbolRenderingMode(.hierarchical) - Text(name).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray) + Text(name.addingVariationSelectors).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray) } else { Image(systemName: "antenna.radiowaves.left.and.right.slash") .imageScale(.medium) diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index ea64e36e..644c0077 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -50,12 +50,14 @@ struct UserConfig: View { TextField("Long Name", text: $longName) .onChange(of: longName) { - var totalBytes = longName.utf8.count + var newValue = longName.withoutVariationSelectors + var totalBytes = newValue.utf8.count // Only mess with the value if it is too big while totalBytes > (isLicensed ? 6 : 36) { - longName = String(longName.dropLast()) - totalBytes = longName.utf8.count + newValue = String(newValue.dropLast()) + totalBytes = newValue.utf8.count } + longName = newValue } } .keyboardType(.default) From f730a93082354fa140dfe9a1dc7c518840ba6e95 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 10:13:19 -0400 Subject: [PATCH 12/29] Do not apply variation selectors to ASCII --- Meshtastic/Extensions/String.swift | 2 +- Meshtastic/Views/Nodes/Helpers/NodeListItem.swift | 2 +- Meshtastic/Views/Nodes/NodeList.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Meshtastic/Extensions/String.swift b/Meshtastic/Extensions/String.swift index aa28fa71..6dd3cad5 100644 --- a/Meshtastic/Extensions/String.swift +++ b/Meshtastic/Extensions/String.swift @@ -111,7 +111,7 @@ extension String { while index < scalars.endIndex { let currentScalar = scalars[index] result += String(currentScalar) - if currentScalar.properties.isEmoji && !currentScalar.properties.isEmojiPresentation { + if currentScalar.properties.isEmoji && !currentScalar.properties.isEmojiPresentation && !currentScalar.isASCII { // Check if the next scalar is U+FE0F let nextIndex = scalars.index(after: index) if nextIndex < scalars.endIndex && scalars[nextIndex].value == 0xFE0F { diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index 9819ef38..e57d99cc 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -64,7 +64,7 @@ struct NodeListItem: View { let (image, color) = userKeyStatus IconAndText(systemName: image, imageColor: color, - text: node.user?.longName ?? "unknown".localized, + text: node.user?.longName?.addingVariationSelectors ?? "unknown".localized, textColor: .primary) if node.favorite { Spacer() diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 12c76cd2..002fc695 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -264,7 +264,7 @@ struct NodeList: View { columnVisibility: columnVisibility ) .edgesIgnoringSafeArea([.leading, .trailing]) - .navigationBarTitle(String(node.user?.longName ?? "unknown".localized), displayMode: .inline) + .navigationBarTitle(String(node.user?.longName?.addingVariationSelectors ?? "unknown".localized), displayMode: .inline) .navigationBarItems( trailing: ZStack { if UIDevice.current.userInterfaceIdiom != .phone { From dcd061eece4f7b04ffc8c1230eb70267242135e4 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 11:15:51 -0400 Subject: [PATCH 13/29] Add variation selectors to longName in more spots --- Meshtastic/Views/Bluetooth/Connect.swift | 4 ++-- Meshtastic/Views/Settings/Settings.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 2d86e15a..6d804e3f 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -61,9 +61,9 @@ struct Connect: View { .padding(.trailing) VStack(alignment: .leading) { if node != nil { - Text(connectedPeripheral.longName).font(.title2) + Text(connectedPeripheral.longName.addingVariationSelectors).font(.title2) } - Text("BLE Name").font(.callout)+Text(": \(bleManager.connectedPeripheral?.peripheral.name ?? "unknown".localized)") + Text("BLE Name").font(.callout)+Text(": \(bleManager.connectedPeripheral?.peripheral.name?.addingVariationSelectors ?? "unknown".localized)") .font(.callout).foregroundColor(Color.gray) if node != nil { Text("firmware.version").font(.callout)+Text(": \(node?.metadata?.firmwareVersion ?? "unknown".localized)") diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index abf8a0ec..8e24e500 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -363,7 +363,7 @@ struct Settings: View { .tag(Int(node.num)) } else if UserDefaults.enableAdministration && node.user?.pkiEncrypted ?? false { Label { - Text("Request PKI Admin: \(node.user?.longName ?? "unknown".localized)") + Text("Request PKI Admin: \(node.user?.longName?.addingVariationSelectors ?? "unknown".localized)") } icon: { Image(systemName: "rectangle.and.hand.point.up.left") } @@ -395,7 +395,7 @@ struct Settings: View { TipView(AdminChannelTip(), arrowEdge: .top) } else { if bleManager.connectedPeripheral != nil { - Text("Connected Node \(node?.user?.longName ?? "unknown".localized)") + Text("Connected Node \(node?.user?.longName?.addingVariationSelectors ?? "unknown".localized)") } } } From d06dd5e8bacb93371aa8ece2d72ebed1d3170f78 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 15 Mar 2025 11:39:46 -0400 Subject: [PATCH 14/29] Better handling for numeric emojis --- Meshtastic/Extensions/String.swift | 23 ++++++++++++++++++----- Meshtastic/Views/Bluetooth/Connect.swift | 4 ++-- Meshtastic/Views/Settings/Settings.swift | 4 ++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Meshtastic/Extensions/String.swift b/Meshtastic/Extensions/String.swift index 6dd3cad5..d2ae1e5a 100644 --- a/Meshtastic/Extensions/String.swift +++ b/Meshtastic/Extensions/String.swift @@ -93,11 +93,24 @@ extension String { // Filter out variation selectors from the string var withoutVariationSelectors: String { - return self.unicodeScalars - .filter { scalar in - return !scalar.properties.isVariationSelector + var scalars: [UnicodeScalar] = [] + var previousWasASCII = false + + for scalar in self.unicodeScalars { + if scalar.properties.isVariationSelector { + // Only keep variation selector if the previous character was ASCII + if previousWasASCII { + scalars.append(scalar) + } + // No need to update previousWasASCII since variation selectors aren't characters + // Shouldn't have 2 in a row + } else { + scalars.append(scalar) + previousWasASCII = scalar.isASCII } - .compactMap { UnicodeScalar($0) } + } + + return scalars.compactMap { UnicodeScalar($0) } .map { String($0) } .joined() } @@ -106,7 +119,7 @@ extension String { // Looks ahead to make sure that the variation selector is not already applied. var addingVariationSelectors: String { var result = "" - var scalars = self.unicodeScalars + let scalars = self.unicodeScalars var index = scalars.startIndex while index < scalars.endIndex { let currentScalar = scalars[index] diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 6d804e3f..09af4418 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -120,7 +120,7 @@ struct Connect: View { #endif Text("Num: \(String(node!.num))") Text("Short Name: \(node?.user?.shortName ?? "?")") - Text("Long Name: \(node?.user?.longName ?? "unknown".localized)") + Text("Long Name: \(node?.user?.longName?.addingVariationSelectors ?? "unknown".localized)") Text("BLE RSSI: \(connectedPeripheral.rssi)") Button { @@ -333,7 +333,7 @@ struct Connect: View { let localStats = node?.telemetries?.filtered(using: NSPredicate(format: "metricsType == 4")) let mostRecent = localStats?.lastObject as? TelemetryEntity - let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName ?? "unknown") + let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName?.addingVariationSelectors ?? "unknown") let future = Date(timeIntervalSinceNow: Double(timerSeconds)) let initialContentState = MeshActivityAttributes.ContentState(uptimeSeconds: UInt32(mostRecent?.uptimeSeconds ?? 0), diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 8e24e500..8e0dcfc3 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -341,7 +341,7 @@ struct Settings: View { /// Connected Node if node.num == bleManager.connectedPeripheral?.num ?? 0 { Label { - Text("BLE: \(node.user?.longName ?? "unknown".localized)") + Text("BLE: \(node.user?.longName?.addingVariationSelectors ?? "unknown".localized)") } icon: { Image(systemName: "antenna.radiowaves.left.and.right") } @@ -370,7 +370,7 @@ struct Settings: View { .tag(Int(node.num)) } else if !UserDefaults.enableAdministration { Label { - Text("Request Legacy Admin: \(node.user?.longName ?? "unknown".localized)") + Text("Request Legacy Admin: \(node.user?.longName?.addingVariationSelectors ?? "unknown".localized)") } icon: { Image(systemName: "rectangle.and.hand.point.up.left") } From 471aaa3b2ddea2ac3ae17a9d864782ca0ae8777c Mon Sep 17 00:00:00 2001 From: Matt Grieser Date: Fri, 21 Mar 2025 08:26:41 -0400 Subject: [PATCH 15/29] Fix typo in RAK 4631 Firmware Updates text --- Meshtastic/Views/Settings/Firmware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 76922d54..b0c9c08f 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -138,7 +138,7 @@ struct Firmware: View { .font(.callout) .padding(.bottom) } else { - Text("OTA Updates are not supported on the this NRF Device.") + Text("OTA Updates are not supported on this NRF Device.") .font(.title3) Link("Drag & Drop Firmware Update", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/drag-n-drop")!) .font(.callout) From db57a862d4187bdea3fdd29818709003dd035d56 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Fri, 21 Mar 2025 16:06:56 -0400 Subject: [PATCH 16/29] Added UDP Broadcast to network settings --- Localizable.xcstrings | 6 + .../contents | 3 +- Meshtastic/Persistence/UpdateCoreData.swift | 2 + .../Views/Settings/Config/NetworkConfig.swift | 123 +++++++++++------- 4 files changed, 86 insertions(+), 48 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 809b2e8e..49e5ffe0 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -9300,6 +9300,9 @@ } } } + }, + "Enable broadcasting packets via UDP over the local network." : { + }, "Enable Notifications" : { "localizations" : { @@ -31501,6 +31504,9 @@ } } } + }, + "UDP Broadcast" : { + }, "Ukraine 433mhz" : { "extractionState" : "manual", diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents index 52f1a59f..3eb01148 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -208,6 +208,7 @@ + diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 0c367968..5e90cd9e 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -658,12 +658,14 @@ func upsertNetworkConfigPacket(config: Config.NetworkConfig, nodeNum: Int64, ses newNetworkConfig.wifiSsid = config.wifiSsid newNetworkConfig.wifiPsk = config.wifiPsk newNetworkConfig.ethEnabled = config.ethEnabled + newNetworkConfig.enabledProtocols = Int32(config.enabledProtocols) fetchedNode[0].networkConfig = newNetworkConfig } else { fetchedNode[0].networkConfig?.ethEnabled = config.ethEnabled fetchedNode[0].networkConfig?.wifiEnabled = config.wifiEnabled fetchedNode[0].networkConfig?.wifiSsid = config.wifiSsid fetchedNode[0].networkConfig?.wifiPsk = config.wifiPsk + fetchedNode[0].networkConfig?.enabledProtocols = Int32(config.enabledProtocols) } if sessionPasskey != nil { fetchedNode[0].sessionPasskey = sessionPasskey diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 84f48c41..c63e95be 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -24,66 +24,78 @@ struct NetworkConfig: View { @State var ntpServer = "" @State var ethEnabled = false @State var ethMode = 0 + @State var udpEnabled = false var body: some View { VStack { Form { ConfigHeader(title: "Network", config: \.networkConfig, node: node, onAppear: setNetworkValues) - if node != nil && node?.metadata?.hasWifi ?? false { - Section(header: Text("WiFi Options")) { + if let node { + if node.metadata?.hasWifi ?? false { + Section(header: Text("WiFi Options")) { - Toggle(isOn: $wifiEnabled) { - Label("enabled", systemImage: "wifi") - Text("Enabling WiFi will disable the bluetooth connection to the app.") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Toggle(isOn: $wifiEnabled) { + Label("enabled", systemImage: "wifi") + Text("Enabling WiFi will disable the bluetooth connection to the app.") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - HStack { - Label("ssid", systemImage: "network") - TextField("ssid", text: $wifiSsid) - .foregroundColor(.gray) - .autocapitalization(.none) - .disableAutocorrection(true) - .onChange(of: wifiSsid) { - var totalBytes = wifiSsid.utf8.count - // Only mess with the value if it is too big - while totalBytes > 32 { - wifiSsid = String(wifiSsid.dropLast()) - totalBytes = wifiSsid.utf8.count + HStack { + Label("ssid", systemImage: "network") + TextField("ssid", text: $wifiSsid) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: wifiSsid) { + var totalBytes = wifiSsid.utf8.count + // Only mess with the value if it is too big + while totalBytes > 32 { + wifiSsid = String(wifiSsid.dropLast()) + totalBytes = wifiSsid.utf8.count + } + hasChanges = true } - hasChanges = true - } - .foregroundColor(.gray) - } - .keyboardType(.default) - HStack { - Label("password", systemImage: "wallet.pass") - TextField("password", text: $wifiPsk) - .foregroundColor(.gray) - .autocapitalization(.none) - .disableAutocorrection(true) - .onChange(of: wifiPsk) { - var totalBytes = wifiPsk.utf8.count - // Only mess with the value if it is too big - while totalBytes > 63 { - wifiPsk = String(wifiPsk.dropLast()) - totalBytes = wifiPsk.utf8.count + .foregroundColor(.gray) + } + .keyboardType(.default) + HStack { + Label("password", systemImage: "wallet.pass") + TextField("password", text: $wifiPsk) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: wifiPsk) { + var totalBytes = wifiPsk.utf8.count + // Only mess with the value if it is too big + while totalBytes > 63 { + wifiPsk = String(wifiPsk.dropLast()) + totalBytes = wifiPsk.utf8.count + } + hasChanges = true } - hasChanges = true - } - .foregroundColor(.gray) + .foregroundColor(.gray) + } + .keyboardType(.default) } - .keyboardType(.default) } - } - if node != nil && node?.metadata?.hasEthernet ?? false { - Section(header: Text("Ethernet Options")) { - Toggle(isOn: $ethEnabled) { - Label("enabled", systemImage: "network") - Text("Enabling Ethernet will disable the bluetooth connection to the app.") + if node.metadata?.hasEthernet ?? false { + Section(header: Text("Ethernet Options")) { + Toggle(isOn: $ethEnabled) { + Label("enabled", systemImage: "network") + Text("Enabling Ethernet will disable the bluetooth connection to the app.") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + } + } + + if node.metadata?.hasEthernet ?? false || node.metadata?.hasWifi ?? false { + Section(header: Text("UDP Broadcast")) { + Toggle(isOn: $udpEnabled) { + Label("enabled", systemImage: "point.3.connected.trianglepath.dotted") + Text("Enable broadcasting packets via UDP over the local network.") + } } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } } @@ -98,6 +110,7 @@ struct NetworkConfig: View { network.wifiSsid = self.wifiSsid network.wifiPsk = self.wifiPsk network.ethEnabled = self.ethEnabled + network.enabledProtocols = self.udpEnabled ? UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) : UInt32(Config.NetworkConfig.ProtocolFlags.noBroadcast.rawValue) // network.addressMode = Config.NetworkConfig.AddressMode.dhcp let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) @@ -166,14 +179,30 @@ struct NetworkConfig: View { } .onChange(of: ethEnabled) { _, newEthEnabled in if newEthEnabled != node?.networkConfig?.ethEnabled { hasChanges = true } + }.onChange(of: udpEnabled) {_, newUdpEnabled in + if let netConfig = node?.networkConfig { + let newValue: UInt32 + if newUdpEnabled { + newValue = UInt32(netConfig.enabledProtocols) | UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) + } else { + newValue = UInt32(netConfig.enabledProtocols) & ~UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) + } + if netConfig.enabledProtocols != Int32(newValue) { + netConfig.enabledProtocols = Int32(newValue) + hasChanges = true + } + } } } + func setNetworkValues() { self.wifiEnabled = node?.networkConfig?.wifiEnabled ?? false self.wifiSsid = node?.networkConfig?.wifiSsid ?? "" self.wifiPsk = node?.networkConfig?.wifiPsk ?? "" self.wifiMode = Int(node?.networkConfig?.wifiMode ?? 0) self.ethEnabled = node?.networkConfig?.ethEnabled ?? false + let enabledProtocols = UInt32(node?.networkConfig?.enabledProtocols ?? Int32(Config.NetworkConfig.ProtocolFlags.noBroadcast.rawValue)) + self.udpEnabled = enabledProtocols & UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) != 0 self.hasChanges = false } } From 9c8015e6cfbf283f9eac240cc2cd8476c5d46aad Mon Sep 17 00:00:00 2001 From: Matt Grieser Date: Fri, 21 Mar 2025 09:58:54 -0400 Subject: [PATCH 17/29] Update references to GitLab to GitHub instead --- CONTRIBUTING.md | 34 +++++++++++++++------------- MeshtasticProtobufs/Package.resolved | 15 ++++++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 MeshtasticProtobufs/Package.resolved diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94ade3d3..60f3057e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,29 +4,31 @@ Thank you for considering contributing to Meshtastic! We appreciate your time an ## Table of Contents -1. [Getting Started](#getting-started) -2. [Development Workflow](#development-workflow) - - [Targeting `main`](#targeting-main) - - [Small, Incremental Changes](#small-incremental-changes) - - [Rebase Commits](#rebase-commits) -3. [Creating a Branch](#creating-a-branch) -4. [Making Changes](#making-changes) -5. [Commit Messages](#commit-messages) -6. [Merging Changes](#merging-changes) -7. [Testing](#testing) -8. [Code Review](#code-review) -9. [Documentation](#documentation) -10. [Style Guides](#style-guides) +- [Contributing to Meshtastic](#contributing-to-meshtastic) + - [Table of Contents](#table-of-contents) + - [Getting Started](#getting-started) + - [Development Workflow](#development-workflow) + - [Targeting `main`](#targeting-main) + - [Small, Incremental Changes](#small-incremental-changes) + - [Rebase Commits](#rebase-commits) + - [Creating a Branch](#creating-a-branch) + - [Making Changes](#making-changes) + - [Commit Messages](#commit-messages) + - [Merging Changes](#merging-changes) + - [Testing](#testing) + - [Code Review](#code-review) + - [Documentation](#documentation) + - [Style Guides](#style-guides) - [Git Commit Messages](#git-commit-messages) - [Code Style](#code-style) -11. [Community](#community) + - [Community](#community) ## Getting Started -1. Fork the repository on GitLab. +1. Fork the repository on GitHub. 2. Clone your fork to your local machine: ```sh - git clone https://gitlab.com//Meshtastic-Apple.git + git clone https://github.com//Meshtastic-Apple.git ``` 3. Navigate to the project directory: ```sh diff --git a/MeshtasticProtobufs/Package.resolved b/MeshtasticProtobufs/Package.resolved new file mode 100644 index 00000000..a679a95e --- /dev/null +++ b/MeshtasticProtobufs/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "a2385deee281bd55bce80722a1f2b020f7b745c02005befa8ccbf58a39ef4002", + "pins" : [ + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", + "version" : "1.29.0" + } + } + ], + "version" : 3 +} From bcc2970a335be324071fa6a20325333eca5ba536 Mon Sep 17 00:00:00 2001 From: Matt Grieser <8972327+mattgrieser@users.noreply.github.com> Date: Sat, 22 Mar 2025 13:16:37 -0400 Subject: [PATCH 18/29] Improve formatting of Getting Started section in README --- README.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index bbaf49b0..d2ab6c35 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,22 @@ SwiftUI client applications for iOS, iPadOS and macOS. This project always uses the latest release version of XCode. 1. Clone the repo. -2. Set up git hooks to automatically lint the project when you commit changes. -2. Open `Meshtastic.xcworkspace` -2. Build and run the `Meshtastic` target. - -```sh -git clone git@github.com:meshtastic/Meshtastic-Apple.git -cd Meshtastic-Apple -./scripts/setup-hooks.sh -open Meshtastic.xcworkspace -``` + ```sh + git clone git@github.com:meshtastic/Meshtastic-Apple.git + ``` +2. Open the local directory. + ```sh + cd Meshtastic-Apple + ``` +3. Set up git hooks to automatically lint the project when you commit changes. + ```sh + ./scripts/setup-hooks.sh + ``` +4. Open `Meshtastic.xcworkspace` + ```sh + open Meshtastic.xcworkspace + ``` +5. Build and run the `Meshtastic` target. ## Technical Standards From 556dab6e9d39f89bccda3b3493b22a8a60bcad4c Mon Sep 17 00:00:00 2001 From: Matt Grieser <8972327+mattgrieser@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:24:37 -0400 Subject: [PATCH 19/29] Exclude Package.resolved files as requested --- .gitignore | 3 ++ .../xcshareddata/swiftpm/Package.resolved | 51 ------------------- .../xcshareddata/swiftpm/Package.resolved | 51 ------------------- MeshtasticProtobufs/Package.resolved | 15 ------ 4 files changed, 3 insertions(+), 117 deletions(-) delete mode 100644 Meshtastic.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 MeshtasticProtobufs/Package.resolved diff --git a/.gitignore b/.gitignore index a6d2222f..b4360ace 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,6 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ + +# Exclude Package.resolved files +*.resolved diff --git a/Meshtastic.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Meshtastic.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index d528f985..00000000 --- a/Meshtastic.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,51 +0,0 @@ -{ - "originHash" : "2d0b85469585b0d6079eac292d63864096062c24848a49380b9d9727f0ceb96c", - "pins" : [ - { - "identity" : "cocoamqtt", - "kind" : "remoteSourceControl", - "location" : "https://github.com/emqx/CocoaMQTT", - "state" : { - "revision" : "85387a2478551ad84f39be8a3c8587d34dd2bcf5", - "version" : "2.1.5" - } - }, - { - "identity" : "mqttcocoaasyncsocket", - "kind" : "remoteSourceControl", - "location" : "https://github.com/leeway1208/MqttCocoaAsyncSocket", - "state" : { - "revision" : "ce3e18607fd01079495f86ff6195d8a3ca469f73", - "version" : "1.0.8" - } - }, - { - "identity" : "sqlite.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/stephencelis/SQLite.swift.git", - "state" : { - "revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb", - "version" : "0.14.1" - } - }, - { - "identity" : "starscream", - "kind" : "remoteSourceControl", - "location" : "https://github.com/daltoniam/Starscream.git", - "state" : { - "revision" : "a063fda2b8145a231953c20e7a646be254365396", - "version" : "3.1.2" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "9f0c76544701845ad98716f3f6a774a892152bcb", - "version" : "1.26.0" - } - } - ], - "version" : 3 -} diff --git a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 3465793d..00000000 --- a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,51 +0,0 @@ -{ - "originHash" : "1571e0d09fede5d57a2c415019f30868d90fde5a53a863cc277593881c2dc4a5", - "pins" : [ - { - "identity" : "cocoamqtt", - "kind" : "remoteSourceControl", - "location" : "https://github.com/emqx/CocoaMQTT", - "state" : { - "revision" : "aff43422925cc30b9af319f4c4dce4f52859baf4", - "version" : "2.1.8" - } - }, - { - "identity" : "mqttcocoaasyncsocket", - "kind" : "remoteSourceControl", - "location" : "https://github.com/leeway1208/MqttCocoaAsyncSocket", - "state" : { - "revision" : "ce3e18607fd01079495f86ff6195d8a3ca469f73", - "version" : "1.0.8" - } - }, - { - "identity" : "sqlite.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/stephencelis/SQLite.swift.git", - "state" : { - "revision" : "a95fc6df17d108bd99210db5e8a9bac90fe984b8", - "version" : "0.15.3" - } - }, - { - "identity" : "starscream", - "kind" : "remoteSourceControl", - "location" : "https://github.com/daltoniam/Starscream.git", - "state" : { - "revision" : "c6bfd1af48efcc9a9ad203665db12375ba6b145a", - "version" : "4.0.8" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", - "version" : "1.28.1" - } - } - ], - "version" : 3 -} diff --git a/MeshtasticProtobufs/Package.resolved b/MeshtasticProtobufs/Package.resolved deleted file mode 100644 index a679a95e..00000000 --- a/MeshtasticProtobufs/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "a2385deee281bd55bce80722a1f2b020f7b745c02005befa8ccbf58a39ef4002", - "pins" : [ - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", - "version" : "1.29.0" - } - } - ], - "version" : 3 -} From 1df57e22626f2c9ec6f307063aca954fd4957582 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 28 Mar 2025 08:39:06 -0700 Subject: [PATCH 20/29] Update device hardware file --- Meshtastic/Resources/DeviceHardware.json | 163 +++++++++++++++++++---- 1 file changed, 134 insertions(+), 29 deletions(-) diff --git a/Meshtastic/Resources/DeviceHardware.json b/Meshtastic/Resources/DeviceHardware.json index a4a6949c..00b80aea 100644 --- a/Meshtastic/Resources/DeviceHardware.json +++ b/Meshtastic/Resources/DeviceHardware.json @@ -87,7 +87,8 @@ "images": [ "t-echo.svg" ], - "requiresDfu": true + "requiresDfu": true, + "hasInkHud": true }, { "hwModel": 8, @@ -153,17 +154,21 @@ "images": [ "tbeam-s3-core.svg" ], - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 13, "hwModelSlug": "RAK11200", "platformioTarget": "rak11200", "architecture": "esp32", - "activelySupported": false, + "activelySupported": true, "displayName": "RAK WisBlock 11200", "tags": [ "RAK" + ], + "images": [ + "rak11200.svg" ] }, { @@ -188,7 +193,7 @@ "displayName": "LILYGO T-LoRa V2.1-1.8", "tags": [ "LilyGo", - "2.4G LoRA" + "2.4GHz" ], "images": [ "tlora-v2-1-1_8.svg" @@ -338,7 +343,8 @@ "requiresDfu": true, "images": [ "station-g2.svg" - ] + ], + "partitionScheme": "16MB" }, { "hwModel": 39, @@ -404,7 +410,8 @@ "images": [ "heltec-v3.svg", "heltec-v3-case.svg" - ] + ], + "partitionScheme": "8MB" }, { "hwModel": 44, @@ -419,7 +426,8 @@ ], "images": [ "heltec-wsl-v3.svg" - ] + ], + "partitionScheme": "8MB" }, { "hwModel": 47, @@ -469,7 +477,8 @@ "images": [ "heltec-wireless-tracker.svg" ], - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 58, @@ -482,7 +491,8 @@ "images": [ "heltec-wireless-tracker.svg" ], - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 49, @@ -497,7 +507,9 @@ ], "images": [ "heltec-wireless-paper.svg" - ] + ], + "hasInkHud": true, + "partitionScheme": "8MB" }, { "hwModel": 50, @@ -513,7 +525,9 @@ "images": [ "t-deck.svg" ], - "requiresDfu": true + "requiresDfu": true, + "hasMui": true, + "partitionScheme": "16MB" }, { "hwModel": 51, @@ -528,7 +542,8 @@ ], "images": [ "t-watch-s3.svg" - ] + ], + "partitionScheme": "16MB" }, { "hwModel": 52, @@ -537,7 +552,9 @@ "architecture": "esp32-s3", "activelySupported": true, "supportLevel": 3, - "displayName": "Pi Computer S3" + "displayName": "Pi Computer S3", + "hasMui": true, + "partitionScheme": "8MB" }, { "hwModel": 53, @@ -567,7 +584,8 @@ "displayName": "Heltec Wireless Paper V1.0", "images": [ "heltec-wireless-paper-v1_0.svg" - ] + ], + "partitionScheme": "8MB" }, { "hwModel": 59, @@ -577,36 +595,41 @@ "activelySupported": true, "supportLevel": 3, "displayName": "unPhone", - "requiresDfu": true + "requiresDfu": true, + "hasMui": true, + "partitionScheme": "8MB" }, { "hwModel": 48, "hwModelSlug": "HELTEC_WIRELESS_TRACKER", "platformioTarget": "tracksenger", "architecture": "esp32-s3", - "activelySupported": true, + "activelySupported": false, "supportLevel": 3, "displayName": "TrackSenger (small TFT)", - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 48, "hwModelSlug": "HELTEC_WIRELESS_TRACKER", "platformioTarget": "tracksenger-lcd", "architecture": "esp32-s3", - "activelySupported": true, + "activelySupported": false, "supportLevel": 3, "displayName": "TrackSenger (big TFT)", - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 48, "hwModelSlug": "HELTEC_WIRELESS_TRACKER", "platformioTarget": "tracksenger-oled", "architecture": "esp32-s3", - "activelySupported": true, + "activelySupported": false, "supportLevel": 3, - "displayName": "TrackSenger (big OLED)" + "displayName": "TrackSenger (big OLED)", + "partitionScheme": "8MB" }, { "hwModel": 61, @@ -647,7 +670,8 @@ "images": [ "heltec-vision-master-t190.svg" ], - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 67, @@ -663,7 +687,9 @@ "images": [ "heltec-vision-master-e213.svg" ], - "requiresDfu": true + "requiresDfu": true, + "hasInkHud": true, + "partitionScheme": "8MB" }, { "hwModel": 68, @@ -679,7 +705,9 @@ "images": [ "heltec-vision-master-e290.svg" ], - "requiresDfu": true + "requiresDfu": true, + "hasInkHud": true, + "partitionScheme": "8MB" }, { "hwModel": 69, @@ -711,7 +739,9 @@ ], "images": [ "seeed-sensecap-indicator.svg" - ] + ], + "hasMui": true, + "partitionScheme": "8MB" }, { "hwModel": 71, @@ -730,8 +760,8 @@ "requiresDfu": true }, { - "hwModel": 72, - "hwModelSlug": "Seeed_XIAO_S3", + "hwModel": 81, + "hwModelSlug": "SEEED_XIAO_S3", "platformioTarget": "seeed-xiao-s3", "architecture": "esp32-s3", "activelySupported": true, @@ -743,14 +773,15 @@ "images": [ "seeed-xiao-s3.svg" ], - "requiresDfu": true + "requiresDfu": true, + "partitionScheme": "8MB" }, { "hwModel": 84, "hwModelSlug": "WISMESH_TAP", "platformioTarget": "rak_wismeshtap", "architecture": "nrf52840", - "activelySupported": false, + "activelySupported": true, "supportLevel": 1, "displayName": "RAK WisMesh Tap", "tags": [ @@ -760,5 +791,79 @@ "rak-wismeshtap.svg" ], "requiresDfu": true + }, + { + "hwModel": 22, + "hwModelSlug": "WISMESH_HUB", + "platformioTarget": "rak2560", + "architecture": "nrf52840", + "activelySupported": true, + "supportLevel": 1, + "displayName": "RAK WisMesh Repeater", + "tags": [ + "RAK" + ], + "images": [ + "rak2560.svg" + ], + "requiresDfu": true + }, + { + "hwModel": 63, + "hwModelSlug": "NRF52_PROMICRO_DIY", + "platformioTarget": "nrf52_promicro_diy_tcxo", + "architecture": "nrf52840", + "activelySupported": true, + "supportLevel": 3, + "displayName": "NRF52 Pro-micro DIY", + "tags": [ + "DIY" + ], + "images": [ + "promicro.svg" + ], + "requiresDfu": true + }, + { + "hwModel": 88, + "hwModelSlug": "XIAO_NRF52_KIT", + "platformioTarget": "seeed_xiao_nrf52840_kit", + "architecture": "nrf52840", + "activelySupported": true, + "supportLevel": 1, + "displayName": "Seeed Xiao NRF52840 Kit", + "tags": [ + "Seeed" + ], + "requiresDfu": true, + "images": [ + "seeed_xiao_nrf52_kit.svg" + ] + }, + { + "hwModel": 89, + "hwModelSlug": "THINKNODE_M1", + "platformioTarget": "thinknode_m1", + "architecture": "nrf52840", + "activelySupported": false, + "supportLevel": 1, + "displayName": "ThinkNode M1", + "tags": [ + "Elecrow" + ], + "requiresDfu": true + }, + { + "hwModel": 90, + "hwModelSlug": "THINKNODE_M2", + "platformioTarget": "thinknode_m2", + "architecture": "esp32-s3", + "activelySupported": false, + "supportLevel": 1, + "displayName": "ThinkNode M2", + "tags": [ + "Elecrow" + ], + "requiresDfu": false } ] From a550e6b6d91892be71a14f0730924a8735062df9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 28 Mar 2025 09:43:54 -0700 Subject: [PATCH 21/29] Fix date regressions from #1089 --- Localizable.xcstrings | 4 ++++ Meshtastic/Extensions/Date.swift | 6 +++--- Meshtastic/Views/Nodes/Helpers/NodeDetail.swift | 10 ++++++---- Meshtastic/Views/Nodes/Helpers/NodeListItem.swift | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 49e5ffe0..eb4cd488 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -22204,6 +22204,7 @@ } }, "OTA Updates are not supported on the this NRF Device." : { + "extractionState" : "stale", "localizations" : { "sr" : { "stringUnit" : { @@ -22218,6 +22219,9 @@ } } } + }, + "OTA Updates are not supported on this NRF Device." : { + }, "OTA Updates are not supported on your platform." : { "localizations" : { diff --git a/Meshtastic/Extensions/Date.swift b/Meshtastic/Extensions/Date.swift index 0736fc63..2cbd37b3 100644 --- a/Meshtastic/Extensions/Date.swift +++ b/Meshtastic/Extensions/Date.swift @@ -10,17 +10,17 @@ import Foundation extension Date { var lastHeard: String { - if timeIntervalSince1970 > 0 { + if self.timeIntervalSince1970 > 0 && self < Calendar.current.date(byAdding: .day, value: 1, to: Date())! { formatted() } else { - "unknown" + "unknown.age".localized } } func formattedDate(format: String) -> String { let dateformat = DateFormatter() dateformat.dateFormat = format - if self > Calendar.current.date(byAdding: .year, value: -5, to: Date())! { + if self.timeIntervalSince1970 > 0 && self < Calendar.current.date(byAdding: .day, value: 1, to: Date())! { return dateformat.string(from: self) } else { return "unknown.age".localized diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index d3439b3f..e48a9acc 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -163,7 +163,7 @@ struct NodeDetail: View { } } - if let firstHeard = node.firstHeard, firstHeard.timeIntervalSince1970 > 0 { + if let firstHeard = node.firstHeard, firstHeard.timeIntervalSince1970 > 0 && firstHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! { HStack { Label { Text("First heard") @@ -184,7 +184,7 @@ struct NodeDetail: View { } } - if let lastHeard = node.lastHeard, lastHeard.timeIntervalSince1970 > 0 { + if let lastHeard = node.lastHeard, lastHeard.timeIntervalSince1970 > 0 && lastHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! { HStack { Label { Text("Last heard") @@ -195,8 +195,10 @@ struct NodeDetail: View { Spacer() if dateFormatRelative, let text = Self.relativeFormatter.string(for: lastHeard) { - Text(text) - .textSelection(.enabled) + if lastHeard.formatted() != "unknown.age".localized { + Text(text) + .textSelection(.enabled) + } } else { Text(lastHeard.formatted()) .textSelection(.enabled) diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index e57d99cc..cabfabc6 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -77,10 +77,10 @@ struct NodeListItem: View { imageColor: .green, text: "connected".localized) } - if node.lastHeard?.timeIntervalSince1970 ?? 0 > 0 { + if node.lastHeard?.timeIntervalSince1970 ?? 0 > 0 && node.lastHeard! < Calendar.current.date(byAdding: .year, value: 1, to: Date())!{ IconAndText(systemName: node.isOnline ? "checkmark.circle.fill" : "moon.circle.fill", imageColor: node.isOnline ? .green : .orange, - text: node.lastHeard?.formatted() ?? "unknown") + text: node.lastHeard?.formatted() ?? "unknown.age".localized) } let role = DeviceRoles(rawValue: Int(node.user?.role ?? 0)) IconAndText(systemName: role?.systemName ?? "figure", From 18db01eb7f380749f547689a3a65d37f8fd602df Mon Sep 17 00:00:00 2001 From: Jake-B Date: Fri, 28 Mar 2025 15:53:48 -0400 Subject: [PATCH 22/29] Filter module settings based on excluded_modules field --- Localizable.xcstrings | 6 + Meshtastic/Helpers/MeshPackets.swift | 1 + .../contents | 1 + Meshtastic/Views/Settings/Settings.swift | 177 ++++++++++++------ 4 files changed, 123 insertions(+), 62 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 49e5ffe0..99535751 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -6366,6 +6366,9 @@ } } } + }, + "Currently showing modules that may not be supported by this node." : { + }, "Currently the recommended way to update ESP32 devices is using the web flasher on a desktop computer from a chrome based browser. It does not work on mobile devices or over BLE." : { "localizations" : { @@ -30354,6 +30357,9 @@ } } } + }, + "This node does not support any configurable modules." : { + }, "This will disable fixed position and remove the currently set position." : { "localizations" : { diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index b6c8db17..a52580df 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -226,6 +226,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPass newMetadata.hasEthernet = metadata.hasEthernet_p newMetadata.role = Int32(metadata.role.rawValue) newMetadata.positionFlags = Int32(metadata.positionFlags) + newMetadata.excludedModules = Int32(metadata.excludedModules) // Swift does strings weird, this does work to get the version without the github hash let lastDotIndex = metadata.firmwareVersion.lastIndex(of: ".") var version = metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: metadata.firmwareVersion))] diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents index 3eb01148..9578507a 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 50.xcdatamodel/contents @@ -76,6 +76,7 @@ + diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 8e0dcfc3..28a69c92 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -8,6 +8,7 @@ import SwiftUI import OSLog import TipKit +import MeshtasticProtobufs struct Settings: View { @Environment(\.managedObjectContext) var context @@ -25,10 +26,21 @@ struct Settings: View { @State private var selectedNode: Int = 0 @State private var preferredNodeNum: Int = 0 + @State private var moduleOverride: Bool = false @ObservedObject var router: Router + // MARK: Helper + + private func isModuleSupported(_ module: ExcludedModules) -> Bool { + return moduleOverride || Int(nodes.first(where: { $0.num == preferredNodeNum })?.metadata?.excludedModules ?? Int32.zero) & module.rawValue == 0 + } + + private func isAnySupported(_ modules: [ExcludedModules]) -> Bool { + return modules.map(isModuleSupported).contains(true) + } + // MARK: Views var radioConfigurationSection: some View { @@ -153,57 +165,70 @@ struct Settings: View { } var moduleConfigurationSection: some View { - Section("module.configuration") { - NavigationLink(value: SettingsNavigationState.ambientLighting) { - Label { - Text("Ambient Lighting") - } icon: { - Image(systemName: "light.max") + Section { + if isModuleSupported(.ambientlightingConfig) { + NavigationLink(value: SettingsNavigationState.ambientLighting) { + Label { + Text("Ambient Lighting") + } icon: { + Image(systemName: "light.max") + } } } - NavigationLink(value: SettingsNavigationState.cannedMessages) { - Label { - Text("Canned Messages") - } icon: { - Image(systemName: "list.bullet.rectangle.fill") + if isModuleSupported(.cannedmsgConfig) { + NavigationLink(value: SettingsNavigationState.cannedMessages) { + Label { + Text("Canned Messages") + } icon: { + Image(systemName: "list.bullet.rectangle.fill") + } } } - NavigationLink(value: SettingsNavigationState.detectionSensor) { - Label { - Text("detection.sensor") - } icon: { - Image(systemName: "sensor") - } - } - - NavigationLink(value: SettingsNavigationState.externalNotification) { - Label { - Text("external.notification") - } icon: { - Image(systemName: "megaphone") - } - } - - NavigationLink(value: SettingsNavigationState.mqtt) { - Label { - Text("mqtt") - } icon: { - Image(systemName: "dot.radiowaves.up.forward") - } - } - - NavigationLink(value: SettingsNavigationState.rangeTest) { - Label { - Text("range.test") - } icon: { - Image(systemName: "point.3.connected.trianglepath.dotted") + if isModuleSupported(.detectionsensorConfig) { + NavigationLink(value: SettingsNavigationState.detectionSensor) { + Label { + Text("detection.sensor") + } icon: { + Image(systemName: "sensor") + } } } if let node = nodes.first(where: { $0.num == preferredNodeNum }), - node.metadata?.hasWifi ?? false { + node.metadata?.hasWifi ?? false, isModuleSupported(.extnotifConfig) { + NavigationLink(value: SettingsNavigationState.externalNotification) { + Label { + Text("external.notification") + } icon: { + Image(systemName: "megaphone") + } + } + } + + if isModuleSupported(.mqttConfig) { + NavigationLink(value: SettingsNavigationState.mqtt) { + Label { + Text("mqtt") + } icon: { + Image(systemName: "dot.radiowaves.up.forward") + } + } + } + + if isModuleSupported(.rangetestConfig) { + NavigationLink(value: SettingsNavigationState.rangeTest) { + Label { + Text("range.test") + } icon: { + Image(systemName: "point.3.connected.trianglepath.dotted") + } + } + } + + if let node = nodes.first(where: { $0.num == preferredNodeNum }), + node.metadata?.hasWifi ?? false, isModuleSupported(.paxcounterConfig) { NavigationLink(value: SettingsNavigationState.paxCounter) { Label { Text("config.module.paxcounter.settings") @@ -213,37 +238,62 @@ struct Settings: View { } } - NavigationLink(value: SettingsNavigationState.ringtone) { - Label { - Text("ringtone") - } icon: { - Image(systemName: "music.note.list") + if isModuleSupported(.audioConfig) { + NavigationLink(value: SettingsNavigationState.ringtone) { + Label { + Text("ringtone") + } icon: { + Image(systemName: "music.note.list") + } } } - NavigationLink(value: SettingsNavigationState.serial) { - Label { - Text("serial") - } icon: { - Image(systemName: "terminal") + if let node = nodes.first(where: { $0.num == preferredNodeNum }), + node.metadata?.hasWifi ?? false, isModuleSupported(.serialConfig) { + NavigationLink(value: SettingsNavigationState.serial) { + Label { + Text("serial") + } icon: { + Image(systemName: "terminal") + } } } - NavigationLink(value: SettingsNavigationState.storeAndForward) { - Label { - Text("Store & Forward") - } icon: { - Image(systemName: "envelope.arrow.triangle.branch") + if isModuleSupported(.storeforwardConfig) { + NavigationLink(value: SettingsNavigationState.storeAndForward) { + Label { + Text("Store & Forward") + } icon: { + Image(systemName: "envelope.arrow.triangle.branch") + } } } - NavigationLink(value: SettingsNavigationState.telemetry) { - Label { - Text("telemetry") - } icon: { - Image(systemName: "chart.xyaxis.line") + if isModuleSupported(.telemetryConfig) { + NavigationLink(value: SettingsNavigationState.telemetry) { + Label { + Text("telemetry") + } icon: { + Image(systemName: "chart.xyaxis.line") + } } } + + // Update this list with the modules that are shown above. If all are not supported + // Then show a message. + if !isAnySupported([.ambientlightingConfig, .cannedmsgConfig, + .detectionsensorConfig, .extnotifConfig, + .mqttConfig, .rangetestConfig, .paxcounterConfig, + .audioConfig, .serialConfig, .storeforwardConfig, + .telemetryConfig]) { + Text("This node does not support any configurable modules.") + } + } header: { + Text("module.configuration") + } footer: { + if moduleOverride { + Text("Currently showing modules that may not be supported by this node.") + } } } @@ -497,7 +547,10 @@ struct Settings: View { } .navigationTitle("settings") .navigationBarItems( - leading: MeshtasticLogo() + leading: MeshtasticLogo().onLongPressGesture(minimumDuration: 1.0) { + self.moduleOverride.toggle() + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + } ) } } From cfde995eb1ccab9b2b2694c29ced26ec7267a7ad Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 28 Mar 2025 13:55:34 -0700 Subject: [PATCH 23/29] Remove unused dependancy --- Meshtastic.xcodeproj/project.pbxproj | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index e72f8c99..3156850d 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -66,7 +66,6 @@ BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */; }; BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */; }; BCE2D3C92C7C377F008E6199 /* FactoryResetNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */; }; - C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; }; D93068D32B8129510066FBC8 /* MessageContextMenuItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D22B8129510066FBC8 /* MessageContextMenuItems.swift */; }; D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D42B812B700066FBC8 /* MessageDestination.swift */; }; D93068D72B8146690066FBC8 /* MessageText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D62B8146690066FBC8 /* MessageText.swift */; }; @@ -588,7 +587,6 @@ buildActionMask = 2147483647; files = ( 25A978BA2C13F8ED0003AAE7 /* MeshtasticProtobufs in Frameworks */, - C9697FA527933B8C00250207 /* SQLite in Frameworks */, DD0D3D222A55CEB10066DB71 /* CocoaMQTT in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1252,7 +1250,6 @@ ); name = Meshtastic; packageProductDependencies = ( - C9697FA427933B8C00250207 /* SQLite */, DD0D3D212A55CEB10066DB71 /* CocoaMQTT */, 25A978B92C13F8ED0003AAE7 /* MeshtasticProtobufs */, ); @@ -1322,7 +1319,6 @@ ); mainGroup = DDC2E14B26CE248E0042C5E4; packageReferences = ( - C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */, DD0D3D202A55CEB10066DB71 /* XCRemoteSwiftPackageReference "CocoaMQTT" */, 25A978B82C13F8ED0003AAE7 /* XCLocalSwiftPackageReference "MeshtasticProtobufs" */, 259792242C2F10B600AD1659 /* XCRemoteSwiftPackageReference "swift-protobuf" */, @@ -2017,14 +2013,6 @@ minimumVersion = 1.26.0; }; }; - C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/stephencelis/SQLite.swift.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.9.2; - }; - }; DD0D3D202A55CEB10066DB71 /* XCRemoteSwiftPackageReference "CocoaMQTT" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/emqx/CocoaMQTT"; @@ -2044,11 +2032,6 @@ isa = XCSwiftPackageProductDependency; productName = MeshtasticProtobufs; }; - C9697FA427933B8C00250207 /* SQLite */ = { - isa = XCSwiftPackageProductDependency; - package = C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */; - productName = SQLite; - }; DD0D3D212A55CEB10066DB71 /* CocoaMQTT */ = { isa = XCSwiftPackageProductDependency; package = DD0D3D202A55CEB10066DB71 /* XCRemoteSwiftPackageReference "CocoaMQTT" */; From 17b4caae41f9255f7e056f63bca629a6c1709c02 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 28 Mar 2025 14:02:41 -0700 Subject: [PATCH 24/29] Update .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index b4360ace..a6d2222f 100644 --- a/.gitignore +++ b/.gitignore @@ -90,6 +90,3 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ - -# Exclude Package.resolved files -*.resolved From ce5bb5cbdc81a9d2c8adf2a84f2273dc4be73e12 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 28 Mar 2025 15:01:02 -0700 Subject: [PATCH 25/29] Package updates --- .../xcshareddata/WorkspaceSettings.xcsettings | 5 +++ .../xcshareddata/swiftpm/Package.resolved | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Meshtastic.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Meshtastic.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Meshtastic.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..0c67376e --- /dev/null +++ b/Meshtastic.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..8cb1b6ba --- /dev/null +++ b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,42 @@ +{ + "originHash" : "a3033aea781828906c453276e3723177901ce64df5757de7ada28c854c9662eb", + "pins" : [ + { + "identity" : "cocoamqtt", + "kind" : "remoteSourceControl", + "location" : "https://github.com/emqx/CocoaMQTT", + "state" : { + "revision" : "aff43422925cc30b9af319f4c4dce4f52859baf4", + "version" : "2.1.8" + } + }, + { + "identity" : "mqttcocoaasyncsocket", + "kind" : "remoteSourceControl", + "location" : "https://github.com/leeway1208/MqttCocoaAsyncSocket", + "state" : { + "revision" : "ce3e18607fd01079495f86ff6195d8a3ca469f73", + "version" : "1.0.8" + } + }, + { + "identity" : "starscream", + "kind" : "remoteSourceControl", + "location" : "https://github.com/daltoniam/Starscream.git", + "state" : { + "revision" : "c6bfd1af48efcc9a9ad203665db12375ba6b145a", + "version" : "4.0.8" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", + "version" : "1.29.0" + } + } + ], + "version" : 3 +} From bebeda8c743fd48751668843247e5745f33f669e Mon Sep 17 00:00:00 2001 From: Jake-B Date: Sat, 29 Mar 2025 10:16:55 -0400 Subject: [PATCH 26/29] Fix for External Notification and Serial module settings --- Meshtastic/Views/Settings/Settings.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 28a69c92..51a7e587 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -196,8 +196,7 @@ struct Settings: View { } } - if let node = nodes.first(where: { $0.num == preferredNodeNum }), - node.metadata?.hasWifi ?? false, isModuleSupported(.extnotifConfig) { + if isModuleSupported(.extnotifConfig) { NavigationLink(value: SettingsNavigationState.externalNotification) { Label { Text("external.notification") @@ -248,8 +247,7 @@ struct Settings: View { } } - if let node = nodes.first(where: { $0.num == preferredNodeNum }), - node.metadata?.hasWifi ?? false, isModuleSupported(.serialConfig) { + if isModuleSupported(.serialConfig) { NavigationLink(value: SettingsNavigationState.serial) { Label { Text("serial") From eafbac23c98e8dc9e72559d337437b20dff483fc Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 30 Mar 2025 08:51:59 -0700 Subject: [PATCH 27/29] Remove wifi check from pax config --- Meshtastic/Views/Settings/Settings.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 28a69c92..ec3667b3 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -227,8 +227,7 @@ struct Settings: View { } } - if let node = nodes.first(where: { $0.num == preferredNodeNum }), - node.metadata?.hasWifi ?? false, isModuleSupported(.paxcounterConfig) { + if isModuleSupported(.paxcounterConfig) { NavigationLink(value: SettingsNavigationState.paxCounter) { Label { Text("config.module.paxcounter.settings") From 02d13c7ba35bf68012880d6ad784f9e6d0d4745f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 30 Mar 2025 10:21:18 -0700 Subject: [PATCH 28/29] Remove unused core data backup functionality --- Localizable.xcstrings | 2 + Meshtastic/Helpers/BLEManager.swift | 19 +--- Meshtastic/Persistence/Persistence.swift | 119 ----------------------- Meshtastic/Views/Bluetooth/Connect.swift | 12 --- Meshtastic/Views/Settings/AppData.swift | 42 -------- 5 files changed, 3 insertions(+), 191 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 66552059..edbdda71 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -2204,6 +2204,7 @@ } }, "Backup Database" : { + "extractionState" : "stale", "localizations" : { "sr" : { "stringUnit" : { @@ -24694,6 +24695,7 @@ } }, "restore" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 89f57aa8..7077a26d 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -690,24 +690,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized let newConnection = Int64(UserDefaults.preferredPeripheralNum) != Int64(decodedInfo.myInfo.myNodeNum) if newConnection { - let container = NSPersistentContainer(name: "Meshtastic") - if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { - let databasePath = url.appendingPathComponent("backup") - .appendingPathComponent("\(UserDefaults.preferredPeripheralNum)") - .appendingPathComponent("Meshtastic.sqlite") - if FileManager.default.fileExists(atPath: databasePath.path) { - do { - disconnectPeripheral(reconnect: false) - try container.restorePersistentStore(from: databasePath) - UserDefaults.preferredPeripheralNum = Int(myInfo?.myNodeNum ?? 0) - context.refreshAllObjects() - Logger.data.notice("🗂️ Restored Core data for /\(UserDefaults.preferredPeripheralNum, privacy: .public)") - connectTo(peripheral: peripheral) - } catch { - Logger.data.error("🗂️ Restore Core data copy error: \(error, privacy: .public)") - } - } - } + // Onboard a new device connection here } } tryClearExistingChannels() diff --git a/Meshtastic/Persistence/Persistence.swift b/Meshtastic/Persistence/Persistence.swift index 70e9f898..e86278e3 100644 --- a/Meshtastic/Persistence/Persistence.swift +++ b/Meshtastic/Persistence/Persistence.swift @@ -103,123 +103,4 @@ extension NSPersistentContainer { case invalidSource(String) } - /// Restore backup persistent stores located in the directory referenced by `backupURL`. - /// **Be very careful with this**. To restore a persistent store, the current persistent store must be removed from the container. When that happens, **all currently loaded Core Data objects** will become invalid. Using them after restoring will cause your app to crash. - /// When calling this method you **must** ensure that you do not continue to use any previously fetched managed objects or existing fetched results controllers. **If this method does not throw, that does not mean your app is safe.** You need to take extra steps to prevent crashes. The details vary depending on the nature of your app. - /// - Parameter backupURL: A file URL containing backup copies of all currently loaded persistent stores. - /// - Throws: `CopyPersistentStoreError` in various situations. - /// - Returns: Nothing. If no errors are thrown, the restore is complete. - func restorePersistentStore(from backupURL: URL) throws { - guard backupURL.isFileURL else { - throw CopyPersistentStoreErrors.invalidSource("Backup URL must be a file URL") - } - - var isDirectory: ObjCBool = false - if FileManager.default.fileExists(atPath: backupURL.path, isDirectory: &isDirectory) { - if !isDirectory.boolValue { - throw CopyPersistentStoreErrors.invalidSource("Source URL must be a directory") - } - } else { - throw CopyPersistentStoreErrors.invalidSource("Source URL must exist") - } - - for persistentStoreDescription in persistentStoreDescriptions { - guard let loadedStoreURL = persistentStoreDescription.url else { - continue - } - let backupStoreURL = backupURL.appendingPathComponent(loadedStoreURL.lastPathComponent) - guard FileManager.default.fileExists(atPath: backupStoreURL.path) else { - throw CopyPersistentStoreErrors.invalidSource("Missing backup store for \(backupStoreURL)") - } - do { - let storeOptions = persistentStoreDescription.options - let configurationName = persistentStoreDescription.configuration - let storeType = persistentStoreDescription.type - // Replace the current store with the backup copy. This has a side effect of removing the current store from the Core Data stack. - // When restoring, it's necessary to use the current persistent store coordinator. - try persistentStoreCoordinator.replacePersistentStore(at: loadedStoreURL, destinationOptions: storeOptions, withPersistentStoreFrom: backupStoreURL, sourceOptions: storeOptions, ofType: storeType) - // Add the persistent store at the same location we've been using, because it was removed in the previous step. - try persistentStoreCoordinator.addPersistentStore(ofType: storeType, configurationName: configurationName, at: loadedStoreURL, options: storeOptions) - } catch { - throw CopyPersistentStoreErrors.copyStoreError("Could not restore: \(error.localizedDescription)") - } - } - } - - /// Copy all loaded persistent stores to a new directory. Each currently loaded file-based persistent store will be copied (including journal files, external binary storage, and anything else Core Data needs) into the destination directory to a persistent store with the same name and type as the existing store. In-memory stores, if any, are skipped. - /// - Parameters: - /// - destinationURL: Destination for new persistent store files. Must be a file URL. If `overwriting` is `false` and `destinationURL` exists, it must be a directory. - /// - overwriting: If `true`, any existing copies of the persistent store will be replaced or updated. If `false`, existing copies will not be changed or remoted. When this is `false`, the destination persistent store file must not already exist. - /// - Throws: `CopyPersistentStoreError` - /// - Returns: Nothing. If no errors are thrown, all loaded persistent stores will be copied to the destination directory. - func copyPersistentStores(to destinationURL: URL, overwriting: Bool = false) throws { - - guard !destinationURL.relativeString.contains("/0/") else { - throw CopyPersistentStoreErrors.invalidDestination("Invalid 0 Node Id") - } - - guard destinationURL.isFileURL else { - throw CopyPersistentStoreErrors.invalidDestination("Destination URL must be a file URL") - } - // If the destination exists and we aren't overwriting it, then it must be a directory. (If we are overwriting, we'll remove it anyway, so it doesn't matter whether it's a directory). - var isDirectory: ObjCBool = false - if !overwriting && FileManager.default.fileExists(atPath: destinationURL.path, isDirectory: &isDirectory) { - if !isDirectory.boolValue { - throw CopyPersistentStoreErrors.invalidDestination("Destination URL must be a directory") - } - // Don't check if destination stores exist in the destination dir, that comes later on a per-store basis. - } - // If we're overwriting, remove the destination. - if overwriting && FileManager.default.fileExists(atPath: destinationURL.path) { - do { - try FileManager.default.removeItem(at: destinationURL) - } catch { - throw CopyPersistentStoreErrors.destinationNotRemoved("Can't overwrite destination at \(destinationURL)") - } - } - // Create the destination directory - do { - try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil) - } catch { - throw CopyPersistentStoreErrors.destinationError("Could not create destination directory at \(destinationURL)") - } - - for persistentStoreDescription in persistentStoreDescriptions { - guard let storeURL = persistentStoreDescription.url else { - continue - } - guard persistentStoreDescription.type != NSInMemoryStoreType else { - continue - } - let destinationStoreURL = destinationURL.appendingPathComponent(storeURL.lastPathComponent) - - if !overwriting && FileManager.default.fileExists(atPath: destinationStoreURL.path) { - // If the destination exists, the replacePersistentStore call will update it in place. That's fine unless we're not overwriting. - throw CopyPersistentStoreErrors.destinationError("Destination already exists at \(destinationStoreURL)") - } - do { - // Replace an existing backup, if any, with a new one with the same options and type. This doesn't affect the current Core Data stack. - // The function name says "replace", but it works if there's nothing at the destination yet. In that case it creates a new persistent store. - // Note that for backup, it doesn't matter if the persistent store coordinator is the one currently in use or a different one. It could be a class function, for this use. - try persistentStoreCoordinator.replacePersistentStore(at: destinationStoreURL, destinationOptions: persistentStoreDescription.options, withPersistentStoreFrom: storeURL, sourceOptions: persistentStoreDescription.options, ofType: persistentStoreDescription.type) - /// Cleanup extra files - let directory = destinationStoreURL.deletingLastPathComponent() - /// Delete -wal file - do { - try FileManager.default.removeItem(at: directory.appendingPathComponent("Meshtastic.sqlite-wal")) - /// Delete -shm file - do { - try FileManager.default.removeItem(at: directory.appendingPathComponent("Meshtastic.sqlite-shm")) - } catch { - Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-shm file \(error, privacy: .public)") - } - } catch { - Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-wal file \(error, privacy: .public)") - } - } catch { - Logger.services.error("🗄 Error Deleting Meshtastic.sqlite file \(error, privacy: .public)") - throw CopyPersistentStoreErrors.copyStoreError("\(error.localizedDescription)") - } - } - } } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 09af4418..72b1a6ae 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -214,18 +214,6 @@ struct Connect: View { if let connectedPeripheral = bleManager.connectedPeripheral, connectedPeripheral.peripheral.state == CBPeripheralState.connected { bleManager.disconnectPeripheral() } - let container = NSPersistentContainer(name: "Meshtastic") - guard let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { - Logger.data.error("nil File path for back") - return - } - do { - try container.copyPersistentStores(to: url.appendingPathComponent("backup").appendingPathComponent("\(UserDefaults.preferredPeripheralNum)"), overwriting: true) - Logger.data.notice("🗂️ Made a core data backup to backup/\(UserDefaults.preferredPeripheralNum)") - - } catch { - Logger.data.error("🗂️ Core data backup copy error: \(error, privacy: .public)") - } clearCoreDataDatabase(context: context, includeRoutes: false) } UserDefaults.preferredPeripheralId = selectedPeripherialId diff --git a/Meshtastic/Views/Settings/AppData.swift b/Meshtastic/Views/Settings/AppData.swift index 3f1ebf0e..f1777989 100644 --- a/Meshtastic/Views/Settings/AppData.swift +++ b/Meshtastic/Views/Settings/AppData.swift @@ -25,34 +25,6 @@ struct AppData: View { GPSStatus() } Divider() - Button(action: { - let container = NSPersistentContainer(name: "Meshtastic") - guard let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { - Logger.data.error("nil File path for back") - return - } - do { - try container.copyPersistentStores(to: url.appendingPathComponent("backup").appendingPathComponent("\(UserDefaults.preferredPeripheralNum)"), overwriting: true) - loadFiles() - Logger.data.notice("🗂️ Made a core data backup to backup/\(UserDefaults.preferredPeripheralNum)") - } catch { - Logger.data.error("🗂️ Core data backup copy error: \(error, privacy: .public)") - } - }) { - Label { - Text("Backup Database") - .font(idiom == .phone ? .callout : .title) - } icon: { - Image(systemName: "cylinder.split.1x2") - .symbolRenderingMode(.hierarchical) - .font(idiom == .phone ? .callout : .title) - .frame(width: 35) - } - } - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.large) - Divider() } List(files, id: \.self) { file in @@ -62,20 +34,6 @@ struct AppData: View { Label { Text("Node Core Data Backup \(file.pathComponents[(idiom == .phone || idiom == .pad) ? 9 : 10])/\(file.lastPathComponent) - \(file.creationDate?.formatted() ?? "") - \(file.fileSizeString)") .swipeActions { - Button(role: .none) { - bleManager.disconnectPeripheral(reconnect: false) - let container = NSPersistentContainer(name: "Meshtastic") - do { - try container.restorePersistentStore(from: file.absoluteURL) - UserDefaults.preferredPeripheralId = "" - UserDefaults.preferredPeripheralNum = Int(file.pathComponents[(idiom == .phone || idiom == .pad) ? 9 : 10]) ?? 0 - Logger.data.notice("🗂️ Restored a core data backup to backup/\(UserDefaults.preferredPeripheralNum, privacy: .public)") - } catch { - Logger.data.error("🗂️ Core data restore copy error: \(error, privacy: .public)") - } - } label: { - Label("restore", systemImage: "arrow.counterclockwise") - } Button(role: .destructive) { do { try FileManager.default.removeItem(at: file) From 4259ac112a3eab945235b1dc5bb43e05e87b9626 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 30 Mar 2025 10:23:16 -0700 Subject: [PATCH 29/29] Delete stale translation keys --- Localizable.xcstrings | 116 ------------------------------------------ 1 file changed, 116 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index edbdda71..d3cc60ca 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -2203,23 +2203,6 @@ } } }, - "Backup Database" : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Резервна база података" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "备份数据库" - } - } - } - }, "Bandwidth" : { "localizations" : { "de" : { @@ -17133,71 +17116,6 @@ } } }, - "mesh.log" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh Log" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh Log" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Journal du maillage" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "יומן מש" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dziennik Sieci" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Log do Mesh" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh-logg" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Логови мреже" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh 日志" - } - }, - "zh-Hant-TW" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh 紀錄檔" - } - } - } - }, "mesh.log.ambientlighting.config %@" : { "extractionState" : "migrated", "localizations" : { @@ -22207,23 +22125,6 @@ } } }, - "OTA Updates are not supported on the this NRF Device." : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "ОТА ажурирања нису подржана на овом NRF уређају." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "OTA 更新不支持 NRF 设备" - } - } - } - }, "OTA Updates are not supported on this NRF Device." : { }, @@ -24694,23 +24595,6 @@ } } }, - "restore" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wiederherstellen" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обнова" - } - } - } - }, "resume" : { "localizations" : { "de" : {