diff --git a/MeshtasticApple/Helpers/BLEManager.swift b/MeshtasticApple/Helpers/BLEManager.swift index 4c80aaea..f0abf250 100644 --- a/MeshtasticApple/Helpers/BLEManager.swift +++ b/MeshtasticApple/Helpers/BLEManager.swift @@ -886,6 +886,41 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } + public func saveDisplayConfig(config: Config.DisplayConfig, destNum: Int64, wantResponse: Bool) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.setConfig.display = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { var adminPacket = AdminMessage() diff --git a/MeshtasticApple/Helpers/MeshPackets.swift b/MeshtasticApple/Helpers/MeshPackets.swift index 2fe55593..4d91a654 100644 --- a/MeshtasticApple/Helpers/MeshPackets.swift +++ b/MeshtasticApple/Helpers/MeshPackets.swift @@ -14,25 +14,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont // We don't care about any of the Power settings // We don't want to manage wifi from the phone app and disconnect our device //if meshlogging { MeshLogger.log("⚙️ Local Config version \(config.version) received for \(nodeLongName)") } - -// if (try! config.device.jsonString()) == "{}" { -// -// print("📟 Default Device config") -// -// } else { -// -// print("📟 Has Device config") -// } -// -// if (try! config.position.jsonString()) == "{}" { -// -// print("📍 Default Position config") -// -// } else { -// -// print("📍 Has Position config") -// } -// + // if (try! config.power.jsonString() == "{\"lsSecs\":300}") { // // print("📍 Default Power config") @@ -44,14 +26,6 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont // print(try! config.power.jsonString()) // } // -// if (try! config.display.jsonString()) == "{}" { -// -// print("🖥️ Default Display config") -// -// } else { -// -// print("🖥️ Has Display config") -// } if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { var isDefault = false @@ -59,6 +33,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont if (try! config.device.jsonString()) == "{}" { isDefault = true + print("📟 Default Device config") } let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -117,7 +92,79 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont context.rollback() let nsError = error as NSError - print("💥 Error Updating Core Data MyInfoEntity: \(nsError)") + print("💥 Error Updating Core Data DeviceConfigEntity: \(nsError)") + } + } + + } catch { + + } + } + + if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) { + + var isDefault = false + + if (try! config.display.jsonString()) == "{}" { + + isDefault = true + print("🖥️ Default Display config") + } + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) + + do { + + let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] + // Found a node, save Device Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].displayConfig == nil { + + let newDisplayConfig = DisplayConfigEntity(context: context) + + if isDefault { + + newDisplayConfig.screenOnSeconds = 0 + newDisplayConfig.screenCarouselInterval = 0 + newDisplayConfig.gpsFormat = 0 + + } else { + + newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue) + newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs) + newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) + } + fetchedNode[0].displayConfig = newDisplayConfig + + } else { + + if isDefault { + + fetchedNode[0].displayConfig?.screenOnSeconds = 0 + fetchedNode[0].displayConfig?.screenCarouselInterval = 0 + fetchedNode[0].displayConfig?.gpsFormat = 0 + + } else { + + fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue) + fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs) + fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) + } + } + + do { + + try context.save() + if meshlogging { MeshLogger.log("💾 Updated Display Config for node number: \(String(nodeNum))") } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)") } } diff --git a/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents b/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents index 1f15c865..5a300379 100644 --- a/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents +++ b/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents @@ -2,15 +2,22 @@ - + + + + + + + + - + @@ -61,6 +68,7 @@ + @@ -112,10 +120,11 @@ - + + \ No newline at end of file diff --git a/MeshtasticApple/Views/Settings/DisplayConfig.swift b/MeshtasticApple/Views/Settings/DisplayConfig.swift index d7c9936f..6c33fcb8 100644 --- a/MeshtasticApple/Views/Settings/DisplayConfig.swift +++ b/MeshtasticApple/Views/Settings/DisplayConfig.swift @@ -7,7 +7,7 @@ import SwiftUI -enum GpsFormat: Int, CaseIterable, Identifiable { +enum GpsFormats: Int, CaseIterable, Identifiable { case gpsFormatDec = 0 case gpsFormatDms = 1 @@ -35,6 +35,24 @@ enum GpsFormat: Int, CaseIterable, Identifiable { } } } + func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat { + + switch self { + + case .gpsFormatDec: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec + case .gpsFormatDms: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms + case .gpsFormatUtm: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm + case .gpsFormatMgrs: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs + case .gpsFormatOlc: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc + case .gpsFormatOsgr: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr + } + } } // Default of 0 is One Minute @@ -109,6 +127,11 @@ struct DisplayConfig: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + + var node: NodeInfoEntity + @State private var isPresentingSaveConfirm: Bool = false + @State var initialLoad: Bool = true + @State var hasChanges = false @State var screenOnSeconds = 0 @State var screenCarouselInterval = 0 @@ -146,7 +169,7 @@ struct DisplayConfig: View { } Section(header: Text("Format")) { Picker("GPS Format", selection: $gpsFormat ) { - ForEach(GpsFormat.allCases) { lu in + ForEach(GpsFormats.allCases) { lu in Text(lu.description) } } @@ -157,6 +180,43 @@ struct DisplayConfig: View { .listRowSeparator(.visible) } } + + Button { + + isPresentingSaveConfirm = true + + } label: { + + Label("Save", systemImage: "square.and.arrow.down") + } + .disabled(bleManager.connectedPeripheral == nil || !hasChanges) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + + "Are you sure?", + isPresented: $isPresentingSaveConfirm + ) { + Button("Save Display Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var dc = Config.DisplayConfig() + dc.gpsFormat = GpsFormats(rawValue: gpsFormat)!.protoEnumValue() + dc.screenOnSecs = UInt32(screenOnSeconds) + dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval) + + if bleManager.saveDisplayConfig(config: dc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + + // 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 + + } else { + + } + } + } } .navigationTitle("Display Config") .navigationBarItems(trailing: @@ -167,7 +227,37 @@ struct DisplayConfig: View { }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context + + self.gpsFormat = Int(node.displayConfig?.gpsFormat ?? 0) + self.screenOnSeconds = Int(node.displayConfig?.screenOnSeconds ?? 0) + self.screenCarouselInterval = Int(node.displayConfig?.screenCarouselInterval ?? 0) + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: screenOnSeconds) { newScreenSecs in + + if newScreenSecs != node.displayConfig!.screenOnSeconds { + + hasChanges = true + } + } + .onChange(of: screenCarouselInterval) { newCarouselSecs in + + if newCarouselSecs != node.displayConfig!.screenCarouselInterval { + + hasChanges = true + } + } + .onChange(of: gpsFormat) { newGpsFormat in + + if newGpsFormat != node.displayConfig!.gpsFormat { + + hasChanges = true + } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/MeshtasticApple/Views/Settings/Settings.swift b/MeshtasticApple/Views/Settings/Settings.swift index 1025f5e1..ba137128 100644 --- a/MeshtasticApple/Views/Settings/Settings.swift +++ b/MeshtasticApple/Views/Settings/Settings.swift @@ -58,7 +58,7 @@ struct Settings: View { .disabled(bleManager.connectedPeripheral == nil) NavigationLink { - DisplayConfig() + DisplayConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) } label: { Image(systemName: "display") @@ -93,7 +93,7 @@ struct Settings: View { Section("Module Configuration") { NavigationLink { - DisplayConfig() + PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) } label: { Image(systemName: "list.bullet.rectangle.fill")