From c7b98cb7f4f7246f6c5d70aceacd687d08283e22 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 21 Jun 2022 02:43:37 -0700 Subject: [PATCH] Device Config --- MeshtasticApple/Helpers/BLEManager.swift | 38 ++++- MeshtasticApple/Helpers/MeshPackets.swift | 77 +++++++++- .../contents | 11 +- .../Views/Settings/DeviceConfig.swift | 131 +++++++++++++++--- .../Views/Settings/LoRaConfig.swift | 1 - MeshtasticApple/Views/Settings/Settings.swift | 2 +- 6 files changed, 233 insertions(+), 27 deletions(-) diff --git a/MeshtasticApple/Helpers/BLEManager.swift b/MeshtasticApple/Helpers/BLEManager.swift index d815eb0c..4c80aaea 100644 --- a/MeshtasticApple/Helpers/BLEManager.swift +++ b/MeshtasticApple/Helpers/BLEManager.swift @@ -688,7 +688,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970) positionPacket.altitude = Int32(LocationHelper.currentAltitude) - + positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed) + positionPacket.groundTrack = UInt32(LocationHelper.currentHeading) var meshPacket = MeshPacket() meshPacket.to = UInt32(destNum) @@ -850,6 +851,41 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } + public func saveDeviceConfig(config: Config.DeviceConfig, destNum: Int64, wantResponse: Bool) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.setConfig.device = 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 bb8a0861..2fe55593 100644 --- a/MeshtasticApple/Helpers/MeshPackets.swift +++ b/MeshtasticApple/Helpers/MeshPackets.swift @@ -52,6 +52,79 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont // // print("🖥️ Has Display config") // } + if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { + + var isDefault = false + + if (try! config.device.jsonString()) == "{}" { + + isDefault = true + } + + 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].deviceConfig == nil { + + let newDeviceConfig = DeviceConfigEntity(context: context) + + if isDefault { + + // Client default protobuf value of 0 + newDeviceConfig.role = 0 + newDeviceConfig.serialEnabled = true + newDeviceConfig.debugLogEnabled = false + + } else { + + // Client default protobuf value of 0 + newDeviceConfig.role = Int32(config.device.role.rawValue) + newDeviceConfig.serialEnabled = !config.device.serialDisabled + newDeviceConfig.debugLogEnabled = config.device.debugLogEnabled + } + fetchedNode[0].deviceConfig = newDeviceConfig + + } else { + + if isDefault { + + // Client default protobuf value of 0 + fetchedNode[0].deviceConfig?.role = 0 + fetchedNode[0].deviceConfig?.serialEnabled = true + fetchedNode[0].deviceConfig?.debugLogEnabled = false + + } else { + // Client default protobuf value of 0 + fetchedNode[0].deviceConfig?.role = Int32(config.device.role.rawValue) + fetchedNode[0].deviceConfig?.serialEnabled = !config.device.serialDisabled + fetchedNode[0].deviceConfig?.debugLogEnabled = config.device.debugLogEnabled + } + } + + do { + + try context.save() + if meshlogging { MeshLogger.log("💾 Updated Device Config for node number: \(String(nodeNum))") } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Updating Core Data MyInfoEntity: \(nsError)") + } + } + + } catch { + + } + } if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { @@ -119,14 +192,14 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont do { try context.save() - if meshlogging { MeshLogger.log("💾 Updated LoRaConfig for node number: \(String(nodeNum))") } + if meshlogging { MeshLogger.log("💾 Updated LoRa Config for node number: \(String(nodeNum))") } } catch { context.rollback() let nsError = error as NSError - print("💥 Error Updating Core Data MyInfoEntity: \(nsError)") + print("💥 Error Updating Core Data LoRaConfigEntity: \(nsError)") } } diff --git a/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents b/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents index 3039ae3b..1f15c865 100644 --- a/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents +++ b/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents @@ -1,5 +1,12 @@ + + + + + + + @@ -53,6 +60,7 @@ + @@ -104,9 +112,10 @@ - + + \ No newline at end of file diff --git a/MeshtasticApple/Views/Settings/DeviceConfig.swift b/MeshtasticApple/Views/Settings/DeviceConfig.swift index 75099874..795f34ab 100644 --- a/MeshtasticApple/Views/Settings/DeviceConfig.swift +++ b/MeshtasticApple/Views/Settings/DeviceConfig.swift @@ -20,29 +20,48 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { switch self { case .client: - return "Client (default)" + return "Client (default) - App connected client." case .clientMute: return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh." case .router: - return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep." + return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep." case .routerClient: return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client." } } } + func protoEnumValue() -> Config.DeviceConfig.Role { + + switch self { + + case .client: + return Config.DeviceConfig.Role.client + case .clientMute: + return Config.DeviceConfig.Role.clientMute + case .router: + return Config.DeviceConfig.Role.router + case .routerClient: + return Config.DeviceConfig.Role.routerClient + } + } } struct DeviceConfig: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager - + + var node: NodeInfoEntity + + @State private var isPresentingFactoryResetConfirm: Bool = false + @State private var isPresentingSaveConfirm: Bool = false + @State var initialLoad: Bool = true + @State var hasChanges = false + @State var deviceRole = 0 @State var serialEnabled = true @State var debugLogEnabled = false - @State private var isPresentingFactoryResetConfirm: Bool = false - var body: some View { VStack { @@ -77,24 +96,64 @@ struct DeviceConfig: View { } } - Button("Factory Reset", role: .destructive) { + HStack { - isPresentingFactoryResetConfirm = true - } - .disabled(bleManager.connectedPeripheral == nil) - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.large) - .padding() - .confirmationDialog( - "Are you sure?", - isPresented: $isPresentingFactoryResetConfirm - ) { - Button("Erase all device settings?", role: .destructive) { + Button { + + isPresentingSaveConfirm = true - if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + } 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 Device Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { - print("Factory Reset Failed") + var dc = Config.DeviceConfig() + dc.role = DeviceRoles(rawValue: deviceRole)!.protoEnumValue() + dc.serialDisabled = !serialEnabled + dc.debugLogEnabled = debugLogEnabled + + if bleManager.saveDeviceConfig(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 { + + } + } + } + + Button("Factory Reset", role: .destructive) { + + isPresentingFactoryResetConfirm = true + } + .disabled(bleManager.connectedPeripheral == nil) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + "Are you sure?", + isPresented: $isPresentingFactoryResetConfirm + ) { + Button("Erase all device settings?", role: .destructive) { + + if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + + print("Factory Reset Failed") + } } } } @@ -110,7 +169,37 @@ struct DeviceConfig: View { }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context + + self.deviceRole = Int(node.deviceConfig?.role ?? 0) + self.serialEnabled = (node.deviceConfig?.serialEnabled ?? true) + self.debugLogEnabled = node.deviceConfig?.debugLogEnabled ?? false + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: deviceRole) { newRole in + + if newRole != node.deviceConfig!.role { + + hasChanges = true + } + } + .onChange(of: serialEnabled) { newSerial in + + if newSerial != node.deviceConfig!.serialEnabled { + + hasChanges = true + } + } + .onChange(of: debugLogEnabled) { newDebugLog in + + if newDebugLog != node.deviceConfig!.debugLogEnabled { + + hasChanges = true + } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/MeshtasticApple/Views/Settings/LoRaConfig.swift b/MeshtasticApple/Views/Settings/LoRaConfig.swift index 5395ccfa..de7f76e3 100644 --- a/MeshtasticApple/Views/Settings/LoRaConfig.swift +++ b/MeshtasticApple/Views/Settings/LoRaConfig.swift @@ -287,7 +287,6 @@ struct LoRaConfig: View { if self.initialLoad{ self.bleManager.context = context - print("got hops \(node.loRaConfig?.hopLimit ?? 0)") self.hopLimit = Int(node.loRaConfig?.hopLimit ?? 0) self.region = Int(node.loRaConfig?.regionCode ?? 0) self.modemPreset = Int(node.loRaConfig?.modemPreset ?? 0) diff --git a/MeshtasticApple/Views/Settings/Settings.swift b/MeshtasticApple/Views/Settings/Settings.swift index d43f4982..1025f5e1 100644 --- a/MeshtasticApple/Views/Settings/Settings.swift +++ b/MeshtasticApple/Views/Settings/Settings.swift @@ -48,7 +48,7 @@ struct Settings: View { Section("Radio Configuration") { NavigationLink { - DeviceConfig() + DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) } label: { Image(systemName: "flipphone")