mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
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
This commit is contained in:
parent
1c4b0c0cb3
commit
67900bfa51
9 changed files with 293 additions and 102 deletions
|
|
@ -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 = "<group>"; };
|
||||
DD46401F2AFF10F4002A5ECB /* WaypointForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointForm.swift; sourceTree = "<group>"; };
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = "<group>"; };
|
||||
DD4975A42B147BA90026544E /* AmbientLightingConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmbientLightingConfig.swift; sourceTree = "<group>"; };
|
||||
DD4A911D2708C65400501B7E /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||
DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentMetricsLog.swift; sourceTree = "<group>"; };
|
||||
DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved Ambient Lighting Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
upsertAmbientLightingModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> 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)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.channel = UInt32(adminIndex)
|
||||
meshPacket.wantAck = true
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
dataMessage.wantResponse = true
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Ambient Lighting Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func requestCannedMessagesModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<NSFetchRequestResult> = 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))
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue