From 67900bfa512b4420bc8baa0ed2d86e274efdc2fd Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 28 Nov 2023 20:03:08 -0800 Subject: [PATCH] Remove GPS Update interval Remove GPS Attempt Time options Don't show positions with empty times in the elevation graph Hook up ambient lighitng config --- Meshtastic.xcodeproj/project.pbxproj | 4 + Meshtastic/Enums/PositionConfigEnums.swift | 87 +--------- Meshtastic/Helpers/BLEManager.swift | 54 ++++++ Meshtastic/Helpers/MeshPackets.swift | 8 +- Meshtastic/Persistence/UpdateCoreData.swift | 56 +++++++ .../Helpers/Map/PositionAltitudeChart.swift | 4 +- .../Config/Module/AmbientLightingConfig.swift | 157 ++++++++++++++++++ .../Settings/Config/PositionConfig.swift | 14 +- Meshtastic/Views/Settings/Settings.swift | 11 ++ 9 files changed, 293 insertions(+), 102 deletions(-) create mode 100644 Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 949ca08b..1ac11fbe 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ DD457188293C7E63000C49FB /* BLESignalStrengthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD457187293C7E63000C49FB /* BLESignalStrengthIndicator.swift */; }; DD4640202AFF10F4002A5ECB /* WaypointForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD46401F2AFF10F4002A5ECB /* WaypointForm.swift */; }; DD47E3D626F17ED900029299 /* CircleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D526F17ED900029299 /* CircleText.swift */; }; + DD4975A52B147BA90026544E /* AmbientLightingConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4975A42B147BA90026544E /* AmbientLightingConfig.swift */; }; DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4A911D2708C65400501B7E /* AppSettings.swift */; }; DD4F23CD28779A3C001D37CB /* EnvironmentMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */; }; DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; }; @@ -254,6 +255,7 @@ DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV5.xcdatamodel; sourceTree = ""; }; DD46401F2AFF10F4002A5ECB /* WaypointForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointForm.swift; sourceTree = ""; }; DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = ""; }; + DD4975A42B147BA90026544E /* AmbientLightingConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmbientLightingConfig.swift; sourceTree = ""; }; DD4A911D2708C65400501B7E /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentMetricsLog.swift; sourceTree = ""; }; DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = ""; }; @@ -592,6 +594,7 @@ DD61937B2863877A00E59241 /* Module */ = { isa = PBXGroup; children = ( + DD4975A42B147BA90026544E /* AmbientLightingConfig.swift */, DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */, DDC4C9FE2A8D982900CE201C /* DetectionSensorConfig.swift */, DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */, @@ -1216,6 +1219,7 @@ DDB75A212A12B954006ED576 /* LoRaSignalStrength.swift in Sources */, DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */, DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */, + DD4975A52B147BA90026544E /* AmbientLightingConfig.swift in Sources */, DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, DD5E5212298EE33B00D21B61 /* apponly.pb.swift in Sources */, diff --git a/Meshtastic/Enums/PositionConfigEnums.swift b/Meshtastic/Enums/PositionConfigEnums.swift index 69e155ba..171df3a5 100644 --- a/Meshtastic/Enums/PositionConfigEnums.swift +++ b/Meshtastic/Enums/PositionConfigEnums.swift @@ -53,52 +53,18 @@ enum GpsFormats: Int, CaseIterable, Identifiable { } } -enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { +enum GpsAttemptTimes: Int, CaseIterable, Identifiable { - case fiveSeconds = 5 - case tenSeconds = 10 - case fifteenSeconds = 15 - case twentySeconds = 20 - case twentyFiveSeconds = 25 - case thirtySeconds = 30 - case fortyFiveSeconds = 45 - case oneMinute = 60 - case twoMinutes = 120 - case fiveMinutes = 300 - case tenMinutes = 600 case fifteenMinutes = 900 case thirtyMinutes = 1800 case oneHour = 3600 case sixHours = 21600 case twelveHours = 43200 case twentyFourHours = 86400 - case maxInt32 = 2147483647 var id: Int { self.rawValue } var description: String { switch self { - case .fiveSeconds: - return "interval.five.seconds".localized - case .tenSeconds: - return "interval.ten.seconds".localized - case .fifteenSeconds: - return "interval.fifteen.seconds".localized - case .twentySeconds: - return "interval.twenty.seconds".localized - case .twentyFiveSeconds: - return "interval.twentyfive.seconds".localized - case .thirtySeconds: - return "interval.thirty.seconds".localized - case .fortyFiveSeconds: - return "interval.fortyfive.seconds".localized - case .oneMinute: - return "interval.one.minute".localized - case .twoMinutes: - return "interval.two.minutes".localized - case .fiveMinutes: - return "interval.five.minutes".localized - case .tenMinutes: - return "interval.ten.minutes".localized case .fifteenMinutes: return "interval.fifteen.minutes".localized case .thirtyMinutes: @@ -111,57 +77,6 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { return "interval.twelve.hours".localized case .twentyFourHours: return "interval.twentyfour.hours".localized - case .maxInt32: - return "on.boot" - } - } -} - -enum GpsAttemptTimes: Int, CaseIterable, Identifiable { - - case twoSeconds = 2 - case fiveSeconds = 5 - case tenSeconds = 10 - case fifteenSeconds = 15 - case twentySeconds = 20 - case twentyFiveSeconds = 25 - case thirtySeconds = 30 - case fortyFiveSeconds = 45 - case oneMinute = 60 - case twoMinutes = 120 - case fiveMinutes = 300 - case tenMinutes = 600 - case fifteenMinutes = 900 - - var id: Int { self.rawValue } - var description: String { - switch self { - case .twoSeconds: - return "interval.two.seconds".localized - case .fiveSeconds: - return "interval.five.seconds".localized - case .tenSeconds: - return "interval.ten.seconds".localized - case .fifteenSeconds: - return "interval.fifteen.seconds".localized - case .twentySeconds: - return "interval.twenty.seconds".localized - case .twentyFiveSeconds: - return "interval.twentyfive.seconds".localized - case .thirtySeconds: - return "interval.thirty.seconds".localized - case .fortyFiveSeconds: - return "interval.fortyfive.seconds".localized - case .oneMinute: - return "interval.one.minute".localized - case .twoMinutes: - return "interval.two.minutes".localized - case .fiveMinutes: - return "interval.five.minutes".localized - case .tenMinutes: - return "interval.ten.minutes".localized - case .fifteenMinutes: - return "interval.fifteen.minutes".localized } } } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index f49dcc78..bf5b0949 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1359,6 +1359,33 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate return 0 } + public func saveAmbientLightingModuleConfig(config: ModuleConfig.AmbientLightingConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 { + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.ambientLighting = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.channel = UInt32(adminIndex) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() @@ -1858,6 +1885,33 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate return false } + public func requestAmbientLightingConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.ambientlightingConfig + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { var adminPacket = AdminMessage() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index f11e57b0..eb8b1abd 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -56,7 +56,9 @@ func localConfig (config: Config, context: NSManagedObjectContext, nodeNum: Int6 func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { - if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) { + if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.ambientLighting(config.ambientLighting) { + upsertAmbientLightingModuleConfigPacket(config: config.ambientLighting, nodeNum: nodeNum, context: context) + } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) { upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum, context: context) } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.detectionSensor(config.detectionSensor) { upsertDetectionSensorModuleConfigPacket(config: config.detectionSensor, nodeNum: nodeNum, context: context) @@ -472,7 +474,9 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getModuleConfigResponse(adminMessage.getModuleConfigResponse) { let moduleConfig = adminMessage.getModuleConfigResponse - if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfig.cannedMessage) { + if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.ambientLighting(moduleConfig.ambientLighting) { + upsertAmbientLightingModuleConfigPacket(config: moduleConfig.ambientLighting, nodeNum: Int64(packet.from), context: context) + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfig.cannedMessage) { upsertCannedMessagesModuleConfigPacket(config: moduleConfig.cannedMessage, nodeNum: Int64(packet.from), context: context) } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.detectionSensor(moduleConfig.detectionSensor) { upsertDetectionSensorModuleConfigPacket(config: moduleConfig.detectionSensor, nodeNum: Int64(packet.from), context: context) diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index a02837c3..198226bc 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -624,6 +624,62 @@ func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNu } } +func upsertAmbientLightingModuleConfigPacket(config: Meshtastic.ModuleConfig.AmbientLightingConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat("mesh.log.ambientlighting.config %@".localized, String(nodeNum)) + MeshLogger.log("🏮 \(logString)") + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) + + do { + + guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else { + return + } + // Found a node, save Ambient Lighting Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].cannedMessageConfig == nil { + + let newAmbientLightingConfig = AmbientLightingConfigEntity(context: context) + + newAmbientLightingConfig.ledState = config.ledState + newAmbientLightingConfig.current = Int32(config.current) + newAmbientLightingConfig.red = Int32(config.red) + newAmbientLightingConfig.green = Int32(config.green) + newAmbientLightingConfig.blue = Int32(config.blue) + fetchedNode[0].ambientLightingConfig = newAmbientLightingConfig + + } else { + + if fetchedNode[0].ambientLightingConfig == nil { + fetchedNode[0].ambientLightingConfig = AmbientLightingConfigEntity(context: context) + } + fetchedNode[0].ambientLightingConfig?.ledState = config.ledState + fetchedNode[0].ambientLightingConfig?.current = Int32(config.current) + fetchedNode[0].ambientLightingConfig?.red = Int32(config.red) + fetchedNode[0].ambientLightingConfig?.green = Int32(config.green) + fetchedNode[0].ambientLightingConfig?.blue = Int32(config.blue) + } + + do { + try context.save() + print("💾 Updated Ambient Lighting Module Config for node number: \(String(nodeNum))") + } catch { + context.rollback() + let nsError = error as NSError + print("💥 Error Updating Core Data AmbientLightingConfigEntity: \(nsError)") + } + } else { + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Ambient Lighting Module Config") + } + } catch { + let nsError = error as NSError + print("💥 Fetching node for core data AmbientLightingConfigEntity failed: \(nsError)") + } +} + func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.CannedMessageConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat("mesh.log.cannedmessage.config %@".localized, String(nodeNum)) diff --git a/Meshtastic/Views/Nodes/Helpers/Map/PositionAltitudeChart.swift b/Meshtastic/Views/Nodes/Helpers/Map/PositionAltitudeChart.swift index eba287e7..4f69bd2a 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/PositionAltitudeChart.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/PositionAltitudeChart.swift @@ -23,8 +23,10 @@ struct PositionAltitudeChart: View { @State private var lineWidth = 2.0 var body: some View { + let fiveYearsAgo = Calendar.current.date(byAdding: .year, value: -5, to: Date()) let nodePositions = Array(node.positions!) as! [PositionEntity] - let data = nodePositions.map { PositionAltitude(time: $0.time ?? Date(), altitude: Measurement(value: Double($0.altitude), unit: .meters) ) } + let filteredPositions = nodePositions.filter({$0.time != nil && ($0.time ?? fiveYearsAgo!) > fiveYearsAgo!}) + let data = filteredPositions.map { PositionAltitude(time: $0.time ?? Date(), altitude: Measurement(value: Double($0.altitude), unit: .meters) ) } GroupBox(label: Label("Altitude", systemImage: "mountain.2")) { Chart(data, id: \.time) { diff --git a/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift b/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift new file mode 100644 index 00000000..17a44545 --- /dev/null +++ b/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift @@ -0,0 +1,157 @@ +// +// AmbientLightingConfig.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 11/26/23 +// + +import SwiftUI +@available(iOS 17.0, macOS 14.0, *) +struct AmbientLightingConfig: View { + @Environment(\.self) var environment + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + @Environment(\.dismiss) private var goBack + + var node: NodeInfoEntity? + + @State private var isPresentingSaveConfirm: Bool = false + @State var hasChanges = false + @State var ledState: Bool = false + @State var current = 10 + @State var red = 0 + @State var green = 0 + @State var blue = 0 + @State private var color = Color(red: 51, green: 199, blue: 88) // Color(.sRGB, red: 0.98, green: 0.9, blue: 0.2) + @State private var components: Color.Resolved? + var body: some View { + VStack { + Form { + if node != nil && node?.metadata == nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + Text("There has been no response to a request for device metadata over the admin channel for this node.") + .font(.callout) + .foregroundColor(.orange) + + } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { + // Let users know what is going on if they are using remote admin and don't have the config yet + if node?.rtttlConfig == nil { + Text("Ambient Lighting config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + .font(.callout) + .foregroundColor(.orange) + } else { + Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + .onAppear { + setAmbientLightingConfigValue() + } + } + } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { + Text("Configuration for: \(node?.user?.longName ?? "Unknown")") + .font(.title3) + } else { + Text("Please connect to a radio to configure settings.") + .font(.callout) + .foregroundColor(.orange) + } + Section(header: Text("options")) { + VStack { + Toggle(isOn: $ledState) { + Label("LED State", systemImage: ledState ? "lightbulb.led.fill" : "lightbulb.led") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + HStack { + Image(systemName: "eyedropper") + .foregroundColor(.accentColor) + ColorPicker("Color", selection: $color, supportsOpacity: false) + .padding(5) + } + HStack { + Image(systemName: "directcurrent") + .foregroundColor(.accentColor) + Stepper("Current: \(current)", value: $current, in: 0...31, step: 1) + .padding(5) + } + + } + .onChange(of: color, initial: true) { + components = color.resolve(in: environment) + hasChanges = true + } + } + } + //.disabled(self.bleManager.connectedPeripheral == nil || node?.ambientLightingConfig == nil) + Button { + isPresentingSaveConfirm = true + } label: { + Label("save", systemImage: "square.and.arrow.down") + } + .disabled(self.bleManager.connectedPeripheral == nil || !hasChanges) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + "are.you.sure", + isPresented: $isPresentingSaveConfirm, + titleVisibility: .visible + ) { + let nodeName = node?.user?.longName ?? "unknown".localized + let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName) + Button(buttonText) { + + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode != nil { + var al = ModuleConfig.AmbientLightingConfig() + al.ledState = ledState + al.current = UInt32(current) + if let components { + al.red = UInt32(components.red * 255) + al.green = UInt32(components.green * 255) + al.blue = UInt32(components.blue * 255) + } + + let adminMessageId = bleManager.saveAmbientLightingModuleConfig(config: al, 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() + } + } + } + } + message: { + Text("config.save.confirm") + } + .navigationTitle("ambient.lighting.config") + .navigationBarItems(trailing: + ZStack { + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") + }) + .onAppear { + self.bleManager.context = context + setAmbientLightingConfigValue() + // Need to request a Ambient Lighting Config from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.ambientLightingConfig == nil { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if node != nil && connectedNode != nil { + _ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + } + } + } + .onChange(of: ledState) { newLedState in + if node != nil && node!.ambientLightingConfig != nil { + if newLedState != node!.ambientLightingConfig!.ledState { hasChanges = true } + } + } + } + } + func setAmbientLightingConfigValue() { + self.ledState = node?.ambientLightingConfig?.ledState ?? false + self.current = Int(node?.ambientLightingConfig?.current ?? 10) + color = Color(red: Double((node?.ambientLightingConfig?.red ?? 255) / 255), + green: Double((node?.ambientLightingConfig?.green ?? 255) / 255), + blue: Double((node?.ambientLightingConfig?.blue ?? 255) / 255)) + self.hasChanges = false + } +} diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 8ad8397c..a0205ae2 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -212,13 +212,7 @@ struct PositionConfig: View { } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) if deviceGpsEnabled { - Picker("Update Interval", selection: $gpsUpdateInterval) { - ForEach(GpsUpdateIntervals.allCases) { ui in - Text(ui.description) - } - } - Text("How often should we try to get a GPS position.") - .font(.caption) + Picker("Attempt Time", selection: $gpsAttemptTime) { ForEach(GpsAttemptTimes.allCases) { at in Text(at.description) @@ -286,7 +280,6 @@ struct PositionConfig: View { pc.positionBroadcastSmartEnabled = smartPositionEnabled pc.gpsEnabled = deviceGpsEnabled pc.fixedPosition = fixedPosition - pc.gpsUpdateInterval = UInt32(gpsUpdateInterval) pc.gpsAttemptTime = UInt32(gpsAttemptTime) pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds) pc.broadcastSmartMinimumIntervalSecs = UInt32(broadcastSmartMinimumIntervalSecs) @@ -359,11 +352,6 @@ struct PositionConfig: View { if newGpsAttemptTime != node!.positionConfig!.gpsAttemptTime { hasChanges = true } } } - .onChange(of: gpsUpdateInterval) { newGpsUpdateInterval in - if node != nil && node!.positionConfig != nil { - if newGpsUpdateInterval != node!.positionConfig!.gpsUpdateInterval { hasChanges = true } - } - } .onChange(of: smartPositionEnabled) { newSmartPositionEnabled in if node != nil && node!.positionConfig != nil { if newSmartPositionEnabled != node!.positionConfig!.smartPositionEnabled { hasChanges = true } diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 7fb37da8..16423c60 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -27,6 +27,7 @@ struct Settings: View { case displayConfig case networkConfig case positionConfig + case ambientLightingConfig case cannedMessagesConfig case detectionSensorConfig case externalNotificationConfig @@ -187,6 +188,16 @@ struct Settings: View { .tag(SettingsSidebar.positionConfig) } Section("module.configuration") { + if #available(iOS 17.0, macOS 14.0, *) { + NavigationLink { + AmbientLightingConfig(node: nodes.first(where: { $0.num == selectedNode })) + } label: { + Image(systemName: "light.max") + .symbolRenderingMode(.hierarchical) + Text("ambient.lighting") + } + .tag(SettingsSidebar.ambientLightingConfig) + } NavigationLink { CannedMessagesConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: {