Fixing linting errors caused by the bens

This commit is contained in:
Garth Vander Houwen 2025-07-16 15:54:53 -07:00
parent dde0ea080f
commit 2b0ce47f22
17 changed files with 54 additions and 130 deletions

View file

@ -1295,7 +1295,7 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1630;
LastUpgradeCheck = 1640;
TargetAttributes = {
25F5D5C62C4375A8008036E3 = {
CreatedOnToolsVersion = 15.4;
@ -1713,6 +1713,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@ -1778,6 +1779,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1630"
LastUpgradeVersion = "1640"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1630"
LastUpgradeVersion = "1640"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -11,10 +11,8 @@ import AppIntents
struct FactoryResetNodeIntent: AppIntent {
static var title: LocalizedStringResource = "Factory Reset"
static var description: IntentDescription = "Perform a factory reset on the node you are connected to"
@Parameter(title: "Hard Reset", description: "In addition to Config, Keys and BLE bonds will be wiped", default: false)
var hardReset: Bool
@Parameter(title: "Provide Confirmation", description: "Show a confirmation dialog before performing the factory reset", default: true)
var provideConfirmation: Bool

View file

@ -15,7 +15,6 @@ struct RestartNodeIntent: AppIntent {
func perform() async throws -> some IntentResult {
if !BLEManager.shared.isConnected {
throw AppIntentErrors.AppIntentError.notConnected
}

View file

@ -11,7 +11,7 @@ import AppIntents
import MeshtasticProtobufs
struct SendWaypointIntent: AppIntent {
var defaultDate = Date.now.addingTimeInterval(60 * 480)
static var title = LocalizedStringResource("Send a Waypoint")
@ -83,11 +83,9 @@ struct SendWaypointIntent: AppIntent {
newWaypoint.icon = unicode
newWaypoint.name = name
newWaypoint.description_p = description
if let expirationDate = expiration {
newWaypoint.expire = UInt32(expirationDate.timeIntervalSince1970)
}
if isLocked {
if let connectedPeripheral = BLEManager.shared.connectedPeripheral {
newWaypoint.lockedTo = UInt32(connectedPeripheral.num)

View file

@ -158,12 +158,13 @@ extension UserDefaults {
@UserDefault(.mapReportingOptIn, defaultValue: false)
static var mapReportingOptIn: Bool
@UserDefault(.usageDataAndCrashReporting, defaultValue: true)
static var usageDataAndCrashReporting: Bool
@UserDefault(.firstLaunch, defaultValue: true)
static var firstLaunch: Bool
@UserDefault(.showDeviceOnboarding, defaultValue: false)
static var showDeviceOnboarding: Bool

View file

@ -1142,8 +1142,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
sendWantConfig()
}
// MARK: Share Location Position Update Timer
// Use context to pass the radio name with the timer
// Use a RunLoop to prevent the timer from running on the main UI thread
@ -1159,7 +1157,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == NONCE_ONLY_DB {
Logger.mesh.info("🤜 [BLE] Want Config DB Complete. ID:\(decodedInfo.configCompleteID, privacy: .public)")
}
case FROMNUM_UUID:
Logger.services.info("🗞️ [BLE] (Notify) characteristic value will be read next")
default:

View file

@ -11,15 +11,11 @@ import TipKit
import MeshtasticProtobufs
struct ContactURLHandler {
static var minimumContactVersion = "2.6.9"
static func handleContactUrl(url: URL, bleManager: BLEManager) {
let supportedVersion = UserDefaults.firmwareVersion == "0.0.0" ||
minimumContactVersion.compare(UserDefaults.firmwareVersion, options: .numeric) == .orderedAscending ||
minimumContactVersion.compare(UserDefaults.firmwareVersion, options: .numeric) == .orderedSame
if !supportedVersion {
let alertController = UIAlertController(
title: "Firmware Upgrade Required",

View file

@ -46,9 +46,7 @@ struct MeshtasticAppleApp: App {
trackingConsent: UserDefaults.usageDataAndCrashReporting ? .granted : .notGranted,
)
DatadogCrashReporting.CrashReporting.enable()
Logs.enable()
Trace.enable(
with: Trace.Configuration(
sampleRate: 100, networkInfoEnabled: true // 100% sampling for development/testing, reduce for production

View file

@ -50,7 +50,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
case "messageNotification.thumbsUpAction":
if let channel = userInfo["channel"] as? Int32,
let replyID = userInfo["messageId"] as? Int64 {
let tapbackResponse = !BLEManager.shared.sendMessage (
let tapbackResponse = !BLEManager.shared.sendMessage(
message: Tapbacks.thumbsUp.emojiString,
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
channel: channel,
@ -64,7 +64,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
case "messageNotification.thumbsDownAction":
if let channel = userInfo["channel"] as? Int32,
let replyID = userInfo["messageId"] as? Int64 {
let tapbackResponse = !BLEManager.shared.sendMessage (
let tapbackResponse = !BLEManager.shared.sendMessage(
message: Tapbacks.thumbsDown.emojiString,
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
channel: channel,
@ -79,7 +79,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
if let userInput = (response as? UNTextInputNotificationResponse)?.userText,
let channel = userInfo["channel"] as? Int32,
let replyID = userInfo["messageId"] as? Int64 {
let tapbackResponse = !BLEManager.shared.sendMessage (
let tapbackResponse = !BLEManager.shared.sendMessage(
message: userInput,
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
channel: channel,

View file

@ -1369,7 +1369,6 @@ func upsertRangeTestModuleConfigPacket(config: ModuleConfig.RangeTestConfig, nod
do {
try context.save()
Logger.data.info("💾 [RangeTestConfigEntity] Updated for node: \(nodeNum.toHex(), privacy: .public)")
} catch {
context.rollback()
let nsError = error as NSError

View file

@ -7,19 +7,18 @@
import SwiftUI
struct ChannelLock: View {
@ObservedObject var channel: ChannelEntity
var body: some View {
/// Unencrypted - using no key at all or a known 1 byte key
if channel.psk?.hexDescription.count ?? 0 < 3 {
let preciseLoction = 17...32
// Using precise location and have MQTT uplink enabled
if channel.uplinkEnabled && preciseLoction ~= (Int(channel.positionPrecision)) {
if channel.uplinkEnabled && preciseLoction ~= (Int(channel.positionPrecision)) {
Image(systemName: "lock.open.trianglebadge.exclamationmark.fill")
.foregroundColor(.red)
// Using precise location
} else if preciseLoction ~= (Int(channel.positionPrecision)) {
} else if preciseLoction ~= (Int(channel.positionPrecision)) {
Image(systemName: "lock.open.fill")
.foregroundColor(.red)
// Just unencrypted without any location or MQTT

View file

@ -10,9 +10,7 @@ struct MessageText: View {
locale: Locale.current
)
static let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mm:ss:a")
@Environment(\.managedObjectContext) var context
let message: MessageEntity
let tapBackDestination: MessageDestination
let isCurrentUser: Bool
@ -21,7 +19,6 @@ struct MessageText: View {
@State private var saveChannels = false
@State private var channelSettings: String?
@State private var addChannels = false
@State private var isShowingDeleteConfirmation = false
var body: some View {
@ -41,10 +38,10 @@ struct MessageText: View {
HStack {
Spacer()
Image(systemName: "lock.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .green)
.font(.system(size: 20))
.offset(x: 8, y: 8)
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .green)
.font(.system(size: 20))
.offset(x: 8, y: 8)
}
}
}
@ -56,10 +53,10 @@ struct MessageText: View {
HStack {
Spacer()
Image(systemName: "envelope.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .gray)
.font(.system(size: 20))
.offset(x: 8, y: 8)
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .gray)
.font(.system(size: 20))
.offset(x: 8, y: 8)
}
}
}
@ -89,39 +86,32 @@ struct MessageText: View {
}
.environment(\.openURL, OpenURLAction { url in
channelSettings = nil
if url.absoluteString.lowercased().contains("meshtastic.org/v/#") {
// Handle contact URL
ContactURLHandler.handleContactUrl(url: url, bleManager: BLEManager.shared)
return .handled // Prevent default browser opening
} else if url.absoluteString.lowercased().contains("meshtastic.org/e/") {
// Handle channel URL
let components = url.absoluteString.components(separatedBy: "#")
guard !components.isEmpty, let lastComponent = components.last else {
Logger.services.error("No valid components found in channel URL: \(url.absoluteString, privacy: .public)")
return .discarded
}
self.addChannels = Bool(url.query?.contains("add=true") ?? false)
guard let lastComponent = components.last else {
Logger.services.error("Channel URL missing fragment component: \(url.absoluteString, privacy: .public)")
self.channelSettings = nil
return .discarded
}
self.channelSettings = lastComponent.components(separatedBy: "?").first ?? ""
Logger.services.debug("Add Channel: \(self.addChannels, privacy: .public)")
self.saveChannels = true
Logger.mesh.debug("Opening Channel Settings URL: \(url.absoluteString, privacy: .public)")
return .handled // Prevent default browser opening
}
return .systemAction // Open other URLs in browser
})
// Display sheet for channel settings
if url.absoluteString.lowercased().contains("meshtastic.org/v/#") {
// Handle contact URL
ContactURLHandler.handleContactUrl(url: url, bleManager: BLEManager.shared)
return .handled // Prevent default browser opening
} else if url.absoluteString.lowercased().contains("meshtastic.org/e/") {
// Handle channel URL
let components = url.absoluteString.components(separatedBy: "#")
guard !components.isEmpty, let lastComponent = components.last else {
Logger.services.error("No valid components found in channel URL: \(url.absoluteString, privacy: .public)")
return .discarded
}
self.addChannels = Bool(url.query?.contains("add=true") ?? false)
guard let lastComponent = components.last else {
Logger.services.error("Channel URL missing fragment component: \(url.absoluteString, privacy: .public)")
self.channelSettings = nil
return .discarded
}
self.channelSettings = lastComponent.components(separatedBy: "?").first ?? ""
Logger.services.debug("Add Channel: \(self.addChannels, privacy: .public)")
self.saveChannels = true
Logger.mesh.debug("Opening Channel Settings URL: \(url.absoluteString, privacy: .public)")
return .handled // Prevent default browser opening
}
return .systemAction // Open other URLs in browser
})
// Display sheet for channel settings
.sheet(isPresented: Binding(
get: {
saveChannels && !(channelSettings == nil)

View file

@ -19,22 +19,17 @@ struct NodeDetail: View {
var modemPreset: ModemPresets = ModemPresets(
rawValue: UserDefaults.modemPreset
) ?? ModemPresets.longFast
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@State private var showingShutdownConfirm: Bool = false
@State private var showingRebootConfirm: Bool = false
@State private var dateFormatRelative: Bool = true
// The node the device is currently connected to
var connectedNode: NodeInfoEntity?
// The node information being displayed on the detail screen
@ObservedObject
var node: NodeInfoEntity
var columnVisibility = NavigationSplitViewVisibility.all
var body: some View {
NavigationStack {
List {
@ -42,7 +37,6 @@ struct NodeDetail: View {
id: bleManager.connectedPeripheral?.num ?? -1,
context: context
)
Section("Hardware") {
NodeInfoItem(node: node)
}
@ -106,10 +100,9 @@ struct NodeDetail: View {
}
Spacer()
Text(String(node.num))
.textSelection(.enabled)
.textSelection(.enabled)
}
.accessibilityElement(children: .combine)
HStack {
Label {
Text("User Id")
@ -119,10 +112,9 @@ struct NodeDetail: View {
}
Spacer()
Text(node.num.toHex())
.textSelection(.enabled)
.textSelection(.enabled)
}
.accessibilityElement(children: .combine)
if node.user?.keyMatch ?? false {
if let publicKey = node.user?.publicKey {
HStack {
@ -134,7 +126,7 @@ struct NodeDetail: View {
}
Spacer()
Button(action: {
context.perform{
context.perform {
UIPasteboard.general.string = publicKey.base64EncodedString()
}
}) {
@ -147,7 +139,6 @@ struct NodeDetail: View {
.accessibilityElement(children: .combine)
}
}
if let metadata = node.metadata {
HStack {
Label {
@ -157,12 +148,10 @@ struct NodeDetail: View {
.symbolRenderingMode(.multicolor)
}
Spacer()
Text(metadata.firmwareVersion ?? "Unknown".localized)
}
.accessibilityElement(children: .combine)
}
if let role = node.user?.role, let deviceRole = DeviceRoles(rawValue: Int(role)) {
HStack {
Label {
@ -189,7 +178,6 @@ struct NodeDetail: View {
}
.accessibilityElement(children: .combine)
}
if let dm = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")).lastObject as? TelemetryEntity, let uptimeSeconds = dm.uptimeSeconds {
HStack {
Label {
@ -200,7 +188,6 @@ struct NodeDetail: View {
.symbolRenderingMode(.hierarchical)
}
Spacer()
let now = Date.now
let later = now + TimeInterval(uptimeSeconds)
let uptime = (now..<later).formatted(.components(style: .narrow))
@ -209,7 +196,6 @@ struct NodeDetail: View {
}
.accessibilityElement(children: .combine)
}
if let firstHeard = node.firstHeard, firstHeard.timeIntervalSince1970 > 0 && firstHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! {
HStack {
Label {
@ -232,7 +218,6 @@ struct NodeDetail: View {
dateFormatRelative.toggle()
}
}
if let lastHeard = node.lastHeard, lastHeard.timeIntervalSince1970 > 0 && lastHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! {
HStack {
Label {
@ -242,7 +227,6 @@ struct NodeDetail: View {
.symbolRenderingMode(.multicolor)
}
Spacer()
if dateFormatRelative, let text = Self.relativeFormatter.string(for: lastHeard) {
if lastHeard.formatted() != "Unknown Age".localized {
Text(text)
@ -259,7 +243,6 @@ struct NodeDetail: View {
}
}
}
// Note, as you add widgets, you should add to the `hasDataForLatestPositions` array
// This will make sure the "Environment" section is only displayed when the node has a position
// to use with WeatherKit, or has actual data in the most recent EnvironmentMetrics entity
@ -298,7 +281,7 @@ struct NodeDetail: View {
let windGust = node.latestEnvironmentMetrics?.windGust.map { Measurement(value: Double($0), unit: UnitSpeed.metersPerSecond) }
let direction = cardinalValue(from: Double(node.latestEnvironmentMetrics?.windDirection ?? 0))
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)
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
@ -370,7 +353,6 @@ struct NodeDetail: View {
}
}
.disabled(!node.hasDeviceMetrics)
NavigationLink {
NodeMapSwiftUI(node: node, showUserLocation: connectedNode?.num ?? 0 == node.num)
} label: {
@ -382,7 +364,6 @@ struct NodeDetail: View {
}
}
.disabled(!node.hasPositions)
NavigationLink {
PositionLog(node: node)
} label: {
@ -394,7 +375,6 @@ struct NodeDetail: View {
}
}
.disabled(!node.hasPositions)
NavigationLink {
EnvironmentMetricsLog(node: node)
} label: {
@ -406,7 +386,6 @@ struct NodeDetail: View {
}
}
.disabled(!node.hasEnvironmentMetrics)
NavigationLink {
TraceRouteLog(node: node)
} label: {
@ -418,7 +397,6 @@ struct NodeDetail: View {
}
}
.disabled(node.traceRoutes?.count ?? 0 == 0)
NavigationLink {
PowerMetricsLog(node: node)
} label: {
@ -430,7 +408,6 @@ struct NodeDetail: View {
}
}
.disabled(!node.hasPowerMetrics)
NavigationLink {
DetectionSensorLog(node: node)
} label: {
@ -442,7 +419,6 @@ struct NodeDetail: View {
}
}
.disabled(!node.hasDetectionSensorMetrics)
if node.hasPax {
NavigationLink {
PaxCounterLog(node: node)
@ -457,7 +433,6 @@ struct NodeDetail: View {
.disabled(!node.hasPax)
}
}
Section("Actions") {
if let user = node.user {
NodeAlertsButton(
@ -466,7 +441,6 @@ struct NodeDetail: View {
user: user
)
}
if let connectedNode {
FavoriteNodeButton(
bleManager: bleManager,
@ -491,7 +465,7 @@ struct NodeDetail: View {
}
if node.hasPositions {
NavigateToButton(node: node)
}
}
IgnoreNodeButton(
bleManager: bleManager,
context: context,
@ -506,7 +480,6 @@ struct NodeDetail: View {
}
}
}
if let metadata = node.metadata,
let connectedNode,
self.bleManager.connectedPeripheral != nil {
@ -529,7 +502,6 @@ struct NodeDetail: View {
}
}
}
if metadata.canShutdown {
Button {
showingShutdownConfirm = true
@ -549,7 +521,6 @@ struct NodeDetail: View {
}
}
}
Button {
showingRebootConfirm = true
} label: {

View file

@ -241,7 +241,6 @@ struct Channels: View {
#endif
}
}
if node?.myInfo?.channels?.array.count ?? 0 < 8 && node != nil {
Button {

View file

@ -12,18 +12,14 @@ import MeshtasticProtobufs
struct SaveChannelQRCode: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.managedObjectContext) var context
let channelSetLink: String
var addChannels: Bool = false
var bleManager: BLEManager
@State private var showError: Bool = false
@State private var errorMessage: String = ""
@State private var connectedToDevice: Bool = false
@State private var loraChanges: [String] = []
@State private var okToMQTT: Bool = false
var body: some View {
VStack {
Text("\(addChannels ? "Add" : "Replace all") Channels?")
@ -47,7 +43,6 @@ struct SaveChannelQRCode: View {
}
.padding()
}
if showError {
Text(errorMessage.isEmpty ? "Channels being added from the QR code did not save. When adding channels the names must be unique." : errorMessage)
.fixedSize(horizontal: false, vertical: true)
@ -55,7 +50,6 @@ struct SaveChannelQRCode: View {
.font(.callout)
.padding()
}
HStack {
if !showError {
Button {
@ -72,7 +66,6 @@ struct SaveChannelQRCode: View {
} else {
channelData = channelSetLink
}
let success = bleManager.saveChannelSet(base64UrlString: channelData, addChannels: addChannels, okToMQTT: okToMQTT)
if success {
dismiss()
@ -119,11 +112,8 @@ struct SaveChannelQRCode: View {
fetchLoRaConfigChanges()
}
}
private func extractChannelDataFromURL(_ urlString: String) -> String? {
Logger.data.info("Extracting channel data from URL: \(urlString)")
if let url = URL(string: urlString) {
// Get the fragment (part after #)
if let fragment = url.fragment, !fragment.isEmpty {
@ -131,7 +121,6 @@ struct SaveChannelQRCode: View {
return fragment
}
}
// Fallback: manually extract everything after the last #
if let hashIndex = urlString.lastIndex(of: "#") {
let startIndex = urlString.index(after: hashIndex)
@ -141,11 +130,9 @@ struct SaveChannelQRCode: View {
return channelData
}
}
Logger.data.error("Failed to extract channel data from URL: \(urlString)")
return nil
}
private func fetchLoRaConfigChanges() {
var currentLoRaConfig: Config.LoRaConfig?
@ -163,13 +150,10 @@ struct SaveChannelQRCode: View {
// Assume it's already the base64 data
channelData = channelSetLink
}
Logger.data.info("Processing channel data: \(channelData)")
// Fetch current LoRa config from Core Data
let fetchRequest = NodeInfoEntity.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "num == %lld", Int64(bleManager.connectedPeripheral?.num ?? 0))
do {
let nodes = try context.fetch(fetchRequest)
if let node = nodes.first {
@ -178,7 +162,6 @@ struct SaveChannelQRCode: View {
} catch {
Logger.data.error("Failed to fetch NodeInfoEntity: \(error.localizedDescription, privacy: .public)")
}
// Decode base64url string
let decodedString = channelData.base64urlToBase64()
guard let decodedData = Data(base64Encoded: decodedString) else {
@ -187,7 +170,6 @@ struct SaveChannelQRCode: View {
showError = true
return
}
do {
let channelSet = try ChannelSet(serializedBytes: decodedData)
let newLoRaConfig = channelSet.loraConfig
@ -244,7 +226,6 @@ struct SaveChannelQRCode: View {
} else {
// Compare against default values when no current config exists
let defaultConfig = getDefaultLoRaConfig()
if newLoRaConfig.hopLimit != defaultConfig.hopLimit {
changes.append("Hop Limit: \(defaultConfig.hopLimit) -> \(newLoRaConfig.hopLimit)")
}
@ -287,16 +268,13 @@ struct SaveChannelQRCode: View {
changes.append("Ignore MQTT: \(defaultConfig.ignoreMqtt) -> \(newLoRaConfig.ignoreMqtt)")
}
}
loraChanges = changes
} catch {
Logger.data.error("Failed to decode ChannelSet: \(error.localizedDescription, privacy: .public)")
errorMessage = "Failed to decode channel configuration"
showError = true
}
}
private func getDefaultLoRaConfig() -> Config.LoRaConfig {
var config = Config.LoRaConfig()
config.hopLimit = 3
@ -316,7 +294,6 @@ struct SaveChannelQRCode: View {
return config
}
}
extension LoRaConfigEntity {
func toProto() -> Config.LoRaConfig {
var config = Config.LoRaConfig()