From 921f95cc63f0e36cc467c020f82cdfc4a1680793 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 31 Jan 2023 10:50:17 -0800 Subject: [PATCH 1/6] Assorted updates --- Meshtastic/Enums/LoraConfigEnums.swift | 19 +++- Meshtastic/Helpers/MeshPackets.swift | 8 +- Meshtastic/Persistence/QueryCoreData.swift | 32 +++++++ Meshtastic/Persistence/UpdateCoreData.swift | 47 +++++----- Meshtastic/Protobufs/config.pb.swift | 93 ++++++++++++++++++- Meshtastic/Protobufs/deviceonly.pb.swift | 41 ++++++++ Meshtastic/Views/Bluetooth/Connect.swift | 2 +- Meshtastic/Views/Nodes/NodeMap.swift | 8 +- .../Views/Settings/Config/LoRaConfig.swift | 11 ++- Meshtastic/Views/Settings/Settings.swift | 5 +- Meshtastic/Views/Settings/UserConfig.swift | 6 +- 11 files changed, 227 insertions(+), 45 deletions(-) diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift index 1da71b0d..ca076fd3 100644 --- a/Meshtastic/Enums/LoraConfigEnums.swift +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -22,6 +22,8 @@ enum RegionCodes : Int, CaseIterable, Identifiable { case `in` = 10 case nz865 = 11 case th = 12 + case ua433 = 14 + case ua868 = 15 case lora24 = 13 var id: Int { self.rawValue } @@ -54,6 +56,10 @@ enum RegionCodes : Int, CaseIterable, Identifiable { return "New Zealand 865mhz" case .th: return "Thailand" + case .ua433: + return "Ukraine 433mhz" + case .ua868: + return "Ukraine 868mhz" case .lora24: return "2.4 GHZ" } @@ -90,6 +96,10 @@ enum RegionCodes : Int, CaseIterable, Identifiable { return Config.LoRaConfig.RegionCode.nz865 case .th: return Config.LoRaConfig.RegionCode.th + case .ua433: + return Config.LoRaConfig.RegionCode.ua433 + case .ua868: + return Config.LoRaConfig.RegionCode.ua868 case .lora24: return Config.LoRaConfig.RegionCode.lora24 } @@ -100,6 +110,7 @@ enum ModemPresets : Int, CaseIterable, Identifiable { case LongFast = 0 case LongSlow = 1 + case LongModerate = 7 case VLongSlow = 2 case MedSlow = 3 case MedFast = 4 @@ -115,6 +126,8 @@ enum ModemPresets : Int, CaseIterable, Identifiable { return "Long Range - Fast" case .LongSlow: return "Long Range - Slow" + case .LongModerate: + return "Long Range - Moderate" case .VLongSlow: return "Very Long Range - Slow" case .MedSlow: @@ -136,10 +149,12 @@ enum ModemPresets : Int, CaseIterable, Identifiable { return Config.LoRaConfig.ModemPreset.longFast case .LongSlow: return Config.LoRaConfig.ModemPreset.longSlow + case .LongModerate: + return Config.LoRaConfig.ModemPreset.longModerate case .VLongSlow: - return Config.LoRaConfig.ModemPreset.veryLongSlow + return Config.LoRaConfig.ModemPreset.veryLongSlow case .MedSlow: - return Config.LoRaConfig.ModemPreset.mediumSlow + return Config.LoRaConfig.ModemPreset.mediumSlow case .MedFast: return Config.LoRaConfig.ModemPreset.mediumFast case .ShortSlow: diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index cbd9a56c..e98df340 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -46,7 +46,7 @@ func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64 } else if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) { upsertDisplayConfigPacket(config: config, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { - upsertLoRaConfigPacket(config: config, nodeNum: nodeNum, context: context) + upsertLoRaConfigPacket(config: config.lora, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { upsertNetworkConfigPacket(config: config, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { @@ -759,8 +759,6 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) { - - if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getCannedMessageModuleMessagesResponse(adminMessage.getCannedMessageModuleMessagesResponse) { if let cmmc = try? CannedMessageModuleConfig(serializedData: packet.decoded.payload) { @@ -811,7 +809,9 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { upsertDeviceConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { - upsertLoRaConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) + + let lc = try? Config.LoRaConfig(serializedData: packet.decoded.payload) + upsertLoRaConfigPacket(config: lc!, nodeNum: Int64(packet.from), context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { upsertNetworkConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) diff --git a/Meshtastic/Persistence/QueryCoreData.swift b/Meshtastic/Persistence/QueryCoreData.swift index 72692819..cb30f04f 100644 --- a/Meshtastic/Persistence/QueryCoreData.swift +++ b/Meshtastic/Persistence/QueryCoreData.swift @@ -7,6 +7,38 @@ import CoreData +public func getNodeInfo(id: Int64, context: NSManagedObjectContext) -> NodeInfoEntity { + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(id)) + + do { + let fetchNodeInfo = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] + if fetchNodeInfo.count == 1 { + return fetchNodeInfo[0] + } + } catch { + return NodeInfoEntity(context: context) + } + return NodeInfoEntity(context: context) +} + +public func getUser(id: Int64, context: NSManagedObjectContext) -> UserEntity { + + let fetchUserRequest: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") + fetchUserRequest.predicate = NSPredicate(format: "num == %lld", Int64(id)) + + do { + let fetchedUser = try context.fetch(fetchUserRequest) as! [UserEntity] + if fetchedUser.count == 1 { + return fetchedUser[0] + } + } catch { + return UserEntity(context: context) + } + return UserEntity(context: context) +} + public func getWaypoint(id: Int64, context: NSManagedObjectContext) -> WaypointEntity { let fetchWaypointRequest: NSFetchRequest = NSFetchRequest.init(entityName: "WaypointEntity") diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 6552e54a..77eda04e 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -304,7 +304,7 @@ func upsertDisplayConfigPacket(config: Config, nodeNum: Int64, context: NSManage } } -func upsertLoRaConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { +func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.lora.config %@", comment: "LoRa config received: %@"), String(nodeNum)) MeshLogger.log("πŸ“» \(logString)") @@ -320,33 +320,34 @@ func upsertLoRaConfigPacket(config: Config, nodeNum: Int64, context: NSManagedOb if fetchedNode[0].loRaConfig == nil { // No lora config for node, save a new lora config let newLoRaConfig = LoRaConfigEntity(context: context) - newLoRaConfig.regionCode = Int32(config.lora.region.rawValue) - newLoRaConfig.usePreset = config.lora.usePreset - newLoRaConfig.modemPreset = Int32(config.lora.modemPreset.rawValue) - newLoRaConfig.bandwidth = Int32(config.lora.bandwidth) - newLoRaConfig.spreadFactor = Int32(config.lora.spreadFactor) - newLoRaConfig.codingRate = Int32(config.lora.codingRate) - newLoRaConfig.frequencyOffset = config.lora.frequencyOffset - newLoRaConfig.hopLimit = Int32(config.lora.hopLimit) - newLoRaConfig.txPower = Int32(config.lora.txPower) - newLoRaConfig.txEnabled = config.lora.txEnabled - newLoRaConfig.channelNum = Int32(config.lora.channelNum) + newLoRaConfig.regionCode = Int32(config.region.rawValue) + newLoRaConfig.usePreset = config.usePreset + newLoRaConfig.modemPreset = Int32(config.modemPreset.rawValue) + newLoRaConfig.bandwidth = Int32(config.bandwidth) + newLoRaConfig.spreadFactor = Int32(config.spreadFactor) + newLoRaConfig.codingRate = Int32(config.codingRate) + newLoRaConfig.frequencyOffset = config.frequencyOffset + newLoRaConfig.hopLimit = Int32(config.hopLimit) + newLoRaConfig.txPower = Int32(config.txPower) + newLoRaConfig.txEnabled = config.txEnabled + newLoRaConfig.channelNum = Int32(config.channelNum) fetchedNode[0].loRaConfig = newLoRaConfig } else { - fetchedNode[0].loRaConfig?.regionCode = Int32(config.lora.region.rawValue) - fetchedNode[0].loRaConfig?.usePreset = config.lora.usePreset - fetchedNode[0].loRaConfig?.modemPreset = Int32(config.lora.modemPreset.rawValue) - fetchedNode[0].loRaConfig?.bandwidth = Int32(config.lora.bandwidth) - fetchedNode[0].loRaConfig?.spreadFactor = Int32(config.lora.spreadFactor) - fetchedNode[0].loRaConfig?.codingRate = Int32(config.lora.codingRate) - fetchedNode[0].loRaConfig?.frequencyOffset = config.lora.frequencyOffset - fetchedNode[0].loRaConfig?.hopLimit = Int32(config.lora.hopLimit) - fetchedNode[0].loRaConfig?.txPower = Int32(config.lora.txPower) - fetchedNode[0].loRaConfig?.txEnabled = config.lora.txEnabled - fetchedNode[0].loRaConfig?.channelNum = Int32(config.lora.channelNum) + fetchedNode[0].loRaConfig?.regionCode = Int32(config.region.rawValue) + fetchedNode[0].loRaConfig?.usePreset = config.usePreset + fetchedNode[0].loRaConfig?.modemPreset = Int32(config.modemPreset.rawValue) + fetchedNode[0].loRaConfig?.bandwidth = Int32(config.bandwidth) + fetchedNode[0].loRaConfig?.spreadFactor = Int32(config.spreadFactor) + fetchedNode[0].loRaConfig?.codingRate = Int32(config.codingRate) + fetchedNode[0].loRaConfig?.frequencyOffset = config.frequencyOffset + fetchedNode[0].loRaConfig?.hopLimit = Int32(config.hopLimit) + fetchedNode[0].loRaConfig?.txPower = Int32(config.txPower) + fetchedNode[0].loRaConfig?.txEnabled = config.txEnabled + fetchedNode[0].loRaConfig?.channelNum = Int32(config.channelNum) } do { try context.save() + context.refresh(fetchedNode[0], mergeChanges: true) print("πŸ’Ύ Updated LoRa Config for node number: \(String(nodeNum))") } catch { context.rollback() diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index 65e3587d..7dec26a2 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -168,6 +168,10 @@ struct Config { /// Defaults to PIN_BUZZER if defined. var buzzerGpio: UInt32 = 0 + /// + /// Sets the role of node + var rebroadcastMode: Config.DeviceConfig.RebroadcastMode = .all + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -198,13 +202,13 @@ struct Config { /// /// Repeater device role - /// Mesh packets will simply be rebroadcasted over this node. Nodes under this role node will not originate NodeInfo, Position, Telemetry - /// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factory, and coding rate. + /// Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry + /// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. case repeater // = 4 /// /// Tracker device role - /// Position Mesh packets for will be higher priority and sent more frequently by default. + /// Position Mesh packets will be prioritized higher and sent more frequently by default. case tracker // = 5 case UNRECOGNIZED(Int) @@ -238,6 +242,51 @@ struct Config { } + /// + /// Defines the device's behavior for how messages are rebroadcast + enum RebroadcastMode: SwiftProtobuf.Enum { + typealias RawValue = Int + + /// + /// Default behavior. + /// Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. + case all // = 0 + + /// + /// Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. + /// Only available in Repeater role. Setting this on any other roles will result in ALL behavior. + case allSkipDecoding // = 1 + + /// + /// Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. + /// Only rebroadcasts message on the nodes local primary / secondary channels. + case localOnly // = 2 + case UNRECOGNIZED(Int) + + init() { + self = .all + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .all + case 1: self = .allSkipDecoding + case 2: self = .localOnly + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .all: return 0 + case .allSkipDecoding: return 1 + case .localOnly: return 2 + case .UNRECOGNIZED(let i): return i + } + } + + } + init() {} } @@ -905,6 +954,14 @@ struct Config { /// If true, sets RX boosted gain mode on SX126X based radios var sx126XRxBoostedGain: Bool = false + /// + /// This parameter is for advanced users and licensed HAM radio operators. + /// Ignore Channel Calculation and use this frequency instead. The frequency_offset + /// will still be applied. This will allow you to use out-of-band frequencies. + /// Please respect your local laws and regulations. If you are a HAM, make sure you + /// enable HAM mode and turn off encryption. + var overrideFrequency: Float = 0 + /// /// For testing it is useful sometimes to force a node to never listen to /// particular other nodes (simulating radio out of range). All nodenums listed @@ -1186,6 +1243,15 @@ extension Config.DeviceConfig.Role: CaseIterable { ] } +extension Config.DeviceConfig.RebroadcastMode: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [Config.DeviceConfig.RebroadcastMode] = [ + .all, + .allSkipDecoding, + .localOnly, + ] +} + extension Config.PositionConfig.PositionFlags: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [Config.PositionConfig.PositionFlags] = [ @@ -1303,6 +1369,7 @@ extension Config: @unchecked Sendable {} extension Config.OneOf_PayloadVariant: @unchecked Sendable {} extension Config.DeviceConfig: @unchecked Sendable {} extension Config.DeviceConfig.Role: @unchecked Sendable {} +extension Config.DeviceConfig.RebroadcastMode: @unchecked Sendable {} extension Config.PositionConfig: @unchecked Sendable {} extension Config.PositionConfig.PositionFlags: @unchecked Sendable {} extension Config.PowerConfig: @unchecked Sendable {} @@ -1491,6 +1558,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl 3: .standard(proto: "debug_log_enabled"), 4: .standard(proto: "button_gpio"), 5: .standard(proto: "buzzer_gpio"), + 6: .standard(proto: "rebroadcast_mode"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1504,6 +1572,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl case 3: try { try decoder.decodeSingularBoolField(value: &self.debugLogEnabled) }() case 4: try { try decoder.decodeSingularUInt32Field(value: &self.buttonGpio) }() case 5: try { try decoder.decodeSingularUInt32Field(value: &self.buzzerGpio) }() + case 6: try { try decoder.decodeSingularEnumField(value: &self.rebroadcastMode) }() default: break } } @@ -1525,6 +1594,9 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl if self.buzzerGpio != 0 { try visitor.visitSingularUInt32Field(value: self.buzzerGpio, fieldNumber: 5) } + if self.rebroadcastMode != .all { + try visitor.visitSingularEnumField(value: self.rebroadcastMode, fieldNumber: 6) + } try unknownFields.traverse(visitor: &visitor) } @@ -1534,6 +1606,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl if lhs.debugLogEnabled != rhs.debugLogEnabled {return false} if lhs.buttonGpio != rhs.buttonGpio {return false} if lhs.buzzerGpio != rhs.buzzerGpio {return false} + if lhs.rebroadcastMode != rhs.rebroadcastMode {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -1550,6 +1623,14 @@ extension Config.DeviceConfig.Role: SwiftProtobuf._ProtoNameProviding { ] } +extension Config.DeviceConfig.RebroadcastMode: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "ALL"), + 1: .same(proto: "ALL_SKIP_DECODING"), + 2: .same(proto: "LOCAL_ONLY"), + ] +} + extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = Config.protoMessageName + ".PositionConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1987,6 +2068,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem 11: .standard(proto: "channel_num"), 12: .standard(proto: "override_duty_cycle"), 13: .standard(proto: "sx126x_rx_boosted_gain"), + 14: .standard(proto: "override_frequency"), 103: .standard(proto: "ignore_incoming"), ] @@ -2009,6 +2091,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem case 11: try { try decoder.decodeSingularUInt32Field(value: &self.channelNum) }() case 12: try { try decoder.decodeSingularBoolField(value: &self.overrideDutyCycle) }() case 13: try { try decoder.decodeSingularBoolField(value: &self.sx126XRxBoostedGain) }() + case 14: try { try decoder.decodeSingularFloatField(value: &self.overrideFrequency) }() case 103: try { try decoder.decodeRepeatedUInt32Field(value: &self.ignoreIncoming) }() default: break } @@ -2055,6 +2138,9 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if self.sx126XRxBoostedGain != false { try visitor.visitSingularBoolField(value: self.sx126XRxBoostedGain, fieldNumber: 13) } + if self.overrideFrequency != 0 { + try visitor.visitSingularFloatField(value: self.overrideFrequency, fieldNumber: 14) + } if !self.ignoreIncoming.isEmpty { try visitor.visitPackedUInt32Field(value: self.ignoreIncoming, fieldNumber: 103) } @@ -2075,6 +2161,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if lhs.channelNum != rhs.channelNum {return false} if lhs.overrideDutyCycle != rhs.overrideDutyCycle {return false} if lhs.sx126XRxBoostedGain != rhs.sx126XRxBoostedGain {return false} + if lhs.overrideFrequency != rhs.overrideFrequency {return false} if lhs.ignoreIncoming != rhs.ignoreIncoming {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true diff --git a/Meshtastic/Protobufs/deviceonly.pb.swift b/Meshtastic/Protobufs/deviceonly.pb.swift index 7e5e0d33..b75c793c 100644 --- a/Meshtastic/Protobufs/deviceonly.pb.swift +++ b/Meshtastic/Protobufs/deviceonly.pb.swift @@ -220,9 +220,34 @@ struct OEMStore { /// The default device encryption key, 16 or 32 byte var oemAesKey: Data = Data() + /// + /// A Preset LocalConfig to apply during factory reset + var oemLocalConfig: LocalConfig { + get {return _oemLocalConfig ?? LocalConfig()} + set {_oemLocalConfig = newValue} + } + /// Returns true if `oemLocalConfig` has been explicitly set. + var hasOemLocalConfig: Bool {return self._oemLocalConfig != nil} + /// Clears the value of `oemLocalConfig`. Subsequent reads from it will return its default value. + mutating func clearOemLocalConfig() {self._oemLocalConfig = nil} + + /// + /// A Preset LocalModuleConfig to apply during factory reset + var oemLocalModuleConfig: LocalModuleConfig { + get {return _oemLocalModuleConfig ?? LocalModuleConfig()} + set {_oemLocalModuleConfig = newValue} + } + /// Returns true if `oemLocalModuleConfig` has been explicitly set. + var hasOemLocalModuleConfig: Bool {return self._oemLocalModuleConfig != nil} + /// Clears the value of `oemLocalModuleConfig`. Subsequent reads from it will return its default value. + mutating func clearOemLocalModuleConfig() {self._oemLocalModuleConfig = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} + + fileprivate var _oemLocalConfig: LocalConfig? = nil + fileprivate var _oemLocalModuleConfig: LocalModuleConfig? = nil } #if swift(>=5.5) && canImport(_Concurrency) @@ -413,6 +438,8 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 4: .standard(proto: "oem_font"), 5: .standard(proto: "oem_text"), 6: .standard(proto: "oem_aes_key"), + 7: .standard(proto: "oem_local_config"), + 8: .standard(proto: "oem_local_module_config"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -427,12 +454,18 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB case 4: try { try decoder.decodeSingularEnumField(value: &self.oemFont) }() case 5: try { try decoder.decodeSingularStringField(value: &self.oemText) }() case 6: try { try decoder.decodeSingularBytesField(value: &self.oemAesKey) }() + case 7: try { try decoder.decodeSingularMessageField(value: &self._oemLocalConfig) }() + case 8: try { try decoder.decodeSingularMessageField(value: &self._oemLocalModuleConfig) }() default: break } } } func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if self.oemIconWidth != 0 { try visitor.visitSingularUInt32Field(value: self.oemIconWidth, fieldNumber: 1) } @@ -451,6 +484,12 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if !self.oemAesKey.isEmpty { try visitor.visitSingularBytesField(value: self.oemAesKey, fieldNumber: 6) } + try { if let v = self._oemLocalConfig { + try visitor.visitSingularMessageField(value: v, fieldNumber: 7) + } }() + try { if let v = self._oemLocalModuleConfig { + try visitor.visitSingularMessageField(value: v, fieldNumber: 8) + } }() try unknownFields.traverse(visitor: &visitor) } @@ -461,6 +500,8 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if lhs.oemFont != rhs.oemFont {return false} if lhs.oemText != rhs.oemText {return false} if lhs.oemAesKey != rhs.oemAesKey {return false} + if lhs._oemLocalConfig != rhs._oemLocalConfig {return false} + if lhs._oemLocalModuleConfig != rhs._oemLocalModuleConfig {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index fded4ec7..d9e29e1b 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -111,7 +111,7 @@ struct Connect: View { if isUnsetRegion { HStack { NavigationLink { - LoRaConfig(node: node, connectedNode: node) + LoRaConfig(node: node) } label: { Label("set.region", systemImage: "globe.americas.fill") .foregroundColor(.red) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index b57c4a59..f1f095a2 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -15,7 +15,7 @@ struct NodeMap: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings - @AppStorage("meshMapType") var type: String = "hybrid" + @AppStorage("meshMapCustomTileServer") var customTileServer: String = "" { didSet { if customTileServer == "" { @@ -39,7 +39,7 @@ struct NodeMap: View { ), animation: .easeIn) private var waypoints: FetchedResults - @State private var mapType: MKMapType = .standard + @State private var mapType: MKMapType? @State var waypointCoordinate: CLLocationCoordinate2D = LocationHelper.DefaultLocation @State var editingWaypoint: Int = 0 @State private var presentingWaypointForm = false @@ -68,7 +68,9 @@ struct NodeMap: View { editingWaypoint = wpId presentingWaypointForm = true } - }, positions: Array(positions), waypoints: Array(waypoints), mapViewType: mapType, + }, positions: Array(positions), + waypoints: Array(waypoints), + mapViewType: mapType ?? MKMapType.standard, centerOnPositionsOnly: false, customMapOverlay: self.customMapOverlay, overlays: self.overlays diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 8b8d9b2e..8efb83ef 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -14,7 +14,6 @@ struct LoRaConfig: View { @Environment(\.dismiss) private var goBack var node: NodeInfoEntity? - var connectedNode: NodeInfoEntity? @State var isPresentingSaveConfirm = false @State var hasChanges = false @@ -81,13 +80,14 @@ struct LoRaConfig: View { let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var lc = Config.LoRaConfig() lc.hopLimit = UInt32(hopLimit) lc.region = RegionCodes(rawValue: region)!.protoEnumValue() lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue() lc.usePreset = true lc.txEnabled = true - let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: node?.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 @@ -116,9 +116,12 @@ struct LoRaConfig: View { self.hasChanges = false // Need to request a LoRaConfig from the remote node before allowing changes - if connectedNode != nil && node?.loRaConfig == nil { + if bleManager.connectedPeripheral != nil && node?.loRaConfig == nil { print("empty lora config") - _ = bleManager.requestLoRaConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } } } .onChange(of: region) { newRegion in diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 6712dc04..fcbd2bcd 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -106,7 +106,7 @@ struct Settings: View { .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - UserConfig(node: nodes.first(where: { $0.num == selectedNode }), connectedNode: nodes.first(where: { $0.num == connectedNodeNum })) + UserConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "person.crop.rectangle.fill") @@ -116,14 +116,13 @@ struct Settings: View { .tag(SettingsSidebar.userConfig) NavigationLink() { - LoRaConfig(node: nodes.first(where: { $0.num == selectedNode }), connectedNode: nodes.first(where: { $0.num == connectedNodeNum })) + LoRaConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "dot.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical) Text("lora") } .tag(SettingsSidebar.loraConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink() { Channels(node: nodes.first(where: { $0.num == connectedNodeNum })) diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index 4b3dedc1..4af3240e 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -13,7 +13,6 @@ struct UserConfig: View { @Environment(\.dismiss) private var goBack var node: NodeInfoEntity? - var connectedNode: NodeInfoEntity? @State private var isPresentingFactoryResetConfirm: Bool = false @State private var isPresentingSaveConfirm: Bool = false @@ -86,10 +85,13 @@ struct UserConfig: View { titleVisibility: .visible ) { Button("Save User Config to \(node?.user?.longName ?? "Unknown")?") { + + let connectedUser = getUser(id: bleManager.connectedPeripheral.num, context: context) + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var u = User() u.shortName = shortName u.longName = longName - let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedUser, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { hasChanges = false goBack() From 5165548e05f9243c91a3e6ac17c2312ba8fc3757 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 31 Jan 2023 22:08:03 -0800 Subject: [PATCH 2/6] Admin messages, improved role picker --- Meshtastic/Enums/DeviceRoles.swift | 29 +++++ Meshtastic/Helpers/BLEManager.swift | 109 +++++++++++++++++- Meshtastic/Helpers/MeshPackets.swift | 42 ++++--- Meshtastic/Persistence/UpdateCoreData.swift | 66 +++++------ .../Settings/Config/BluetoothConfig.swift | 22 ++-- .../Views/Settings/Config/DeviceConfig.swift | 25 ++-- .../Views/Settings/Config/DisplayConfig.swift | 16 ++- .../Views/Settings/Config/LoRaConfig.swift | 2 +- .../Views/Settings/Config/NetworkConfig.swift | 15 ++- .../Settings/Config/PositionConfig.swift | 19 +-- Meshtastic/Views/Settings/Settings.swift | 7 +- 11 files changed, 253 insertions(+), 99 deletions(-) diff --git a/Meshtastic/Enums/DeviceRoles.swift b/Meshtastic/Enums/DeviceRoles.swift index c1e8d18c..f0c2876f 100644 --- a/Meshtastic/Enums/DeviceRoles.swift +++ b/Meshtastic/Enums/DeviceRoles.swift @@ -14,8 +14,29 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { case clientMute = 1 case router = 2 case routerClient = 3 + case repeater = 4 + case tracker = 5 var id: Int { self.rawValue } + var name: String { + get { + switch self { + + case .client: + return "Client" + case .clientMute: + return "Muted Client" + case .router: + return "Router" + case .routerClient: + return "Router & Client" + case .repeater: + return "Repeater" + case .tracker: + return "Tracker" + } + } + } var description: String { get { switch self { @@ -28,6 +49,10 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { return NSLocalizedString("device.role.router", comment: "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 NSLocalizedString("device.role.routerclient", comment: "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.") + case .repeater: + return "Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate." + case .tracker: + return "Position Mesh packets will be prioritized higher and sent more frequently by default." } } } @@ -43,6 +68,10 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { return Config.DeviceConfig.Role.router case .routerClient: return Config.DeviceConfig.Role.routerClient + case .repeater: + return Config.DeviceConfig.Role.repeater + case .tracker: + return Config.DeviceConfig.Role.tracker } } } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index f9335922..5370b12b 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1419,10 +1419,62 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let messageDescription = "πŸ›ŽοΈ Requested Bluetooth Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))" if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - return true } + return false + } + + public func requestDeviceConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + var adminPacket = AdminMessage() + adminPacket.getConfigRequest = AdminMessage.ConfigType.deviceConfig + + 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() + adminPacket.getConfigRequest = AdminMessage.ConfigType.displayConfig + + 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() + adminPacket.getConfigRequest = AdminMessage.ConfigType.networkConfig + + 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() + adminPacket.getConfigRequest = AdminMessage.ConfigType.positionConfig + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() @@ -1499,12 +1603,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { meshPacket.decoded = dataMessage let messageDescription = "Saved WiFi Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))" - if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) { - return Int64(meshPacket.id) } - return 0 } diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index e98df340..9c58e2f3 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -40,11 +40,11 @@ func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64 // We don't care about any of the Power settings, config is available for everything else if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { - upsertBluetoothConfigPacket(config: config, nodeNum: nodeNum, context: context) + upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { - upsertDeviceConfigPacket(config: config, nodeNum: nodeNum, context: context) + upsertDeviceConfigPacket(config: config.device, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) { - upsertDisplayConfigPacket(config: config, nodeNum: nodeNum, context: context) + upsertDisplayConfigPacket(config: config.display, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { upsertLoRaConfigPacket(config: config.lora, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { @@ -800,26 +800,24 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { deviceMetadataPacket(metadata: adminMessage.getDeviceMetadataResponse, fromNum: Int64(packet.from), context: context) } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getConfigResponse(adminMessage.getConfigResponse) { - if let config = try? Config(serializedData: packet.decoded.payload) { + + let config = adminMessage.getConfigResponse + + if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { + upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), context: context) - if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { - upsertBluetoothConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) - - } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { - upsertDeviceConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) - - } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { - - let lc = try? Config.LoRaConfig(serializedData: packet.decoded.payload) - upsertLoRaConfigPacket(config: lc!, nodeNum: Int64(packet.from), context: context) - - } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { - upsertNetworkConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) - - } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { - upsertPositionConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) - - } + } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { + upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), context: context) + + } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { + upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), context: context) + + } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { + upsertNetworkConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) + + } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { + upsertPositionConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) + } } else { MeshLogger.log("πŸ•ΈοΈ MESH PACKET received for Admin App \(try! packet.decoded.jsonString())") diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 77eda04e..24bf4868 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -159,7 +159,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) } } -func upsertBluetoothConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { +func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.bluetooth.config %@", comment: "Bluetooth config received: %@"), String(nodeNum)) MeshLogger.log("πŸ“Ά \(logString)") @@ -174,14 +174,14 @@ func upsertBluetoothConfigPacket(config: Config, nodeNum: Int64, context: NSMana if !fetchedNode.isEmpty { if fetchedNode[0].bluetoothConfig == nil { let newBluetoothConfig = BluetoothConfigEntity(context: context) - newBluetoothConfig.enabled = config.bluetooth.enabled - newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue) - newBluetoothConfig.fixedPin = Int32(config.bluetooth.fixedPin) + newBluetoothConfig.enabled = config.enabled + newBluetoothConfig.mode = Int32(config.mode.rawValue) + newBluetoothConfig.fixedPin = Int32(config.fixedPin) fetchedNode[0].bluetoothConfig = newBluetoothConfig } else { - fetchedNode[0].bluetoothConfig?.enabled = config.bluetooth.enabled - fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue) - fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.bluetooth.fixedPin) + fetchedNode[0].bluetoothConfig?.enabled = config.enabled + fetchedNode[0].bluetoothConfig?.mode = Int32(config.mode.rawValue) + fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.fixedPin) } do { try context.save() @@ -200,7 +200,7 @@ func upsertBluetoothConfigPacket(config: Config, nodeNum: Int64, context: NSMana } } -func upsertDeviceConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { +func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.config %@", comment: "Device config received: %@"), String(nodeNum)) MeshLogger.log("πŸ“Ÿ \(logString)") @@ -214,18 +214,18 @@ func upsertDeviceConfigPacket(config: Config, nodeNum: Int64, context: NSManaged if !fetchedNode.isEmpty { if fetchedNode[0].deviceConfig == nil { let newDeviceConfig = DeviceConfigEntity(context: context) - newDeviceConfig.role = Int32(config.device.role.rawValue) - newDeviceConfig.serialEnabled = config.device.serialEnabled - newDeviceConfig.debugLogEnabled = config.device.debugLogEnabled - newDeviceConfig.buttonGpio = Int32(config.device.buttonGpio) - newDeviceConfig.buzzerGpio = Int32(config.device.buzzerGpio) + newDeviceConfig.role = Int32(config.role.rawValue) + newDeviceConfig.serialEnabled = config.serialEnabled + newDeviceConfig.debugLogEnabled = config.debugLogEnabled + newDeviceConfig.buttonGpio = Int32(config.buttonGpio) + newDeviceConfig.buzzerGpio = Int32(config.buzzerGpio) fetchedNode[0].deviceConfig = newDeviceConfig } else { - fetchedNode[0].deviceConfig?.role = Int32(config.device.role.rawValue) - fetchedNode[0].deviceConfig?.serialEnabled = config.device.serialEnabled - fetchedNode[0].deviceConfig?.debugLogEnabled = config.device.debugLogEnabled - fetchedNode[0].deviceConfig?.buttonGpio = Int32(config.device.buttonGpio) - fetchedNode[0].deviceConfig?.buzzerGpio = Int32(config.device.buzzerGpio) + fetchedNode[0].deviceConfig?.role = Int32(config.role.rawValue) + fetchedNode[0].deviceConfig?.serialEnabled = config.serialEnabled + fetchedNode[0].deviceConfig?.debugLogEnabled = config.debugLogEnabled + fetchedNode[0].deviceConfig?.buttonGpio = Int32(config.buttonGpio) + fetchedNode[0].deviceConfig?.buzzerGpio = Int32(config.buzzerGpio) } do { try context.save() @@ -242,7 +242,7 @@ func upsertDeviceConfigPacket(config: Config, nodeNum: Int64, context: NSManaged } } -func upsertDisplayConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { +func upsertDisplayConfigPacket(config: Meshtastic.Config.DisplayConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.display.config %@", comment: "Display config received: %@"), String(nodeNum)) MeshLogger.log("πŸ–₯️ \(logString)") @@ -260,24 +260,24 @@ func upsertDisplayConfigPacket(config: Config, nodeNum: Int64, context: NSManage if fetchedNode[0].displayConfig == nil { let newDisplayConfig = DisplayConfigEntity(context: context) - newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue) - newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs) - newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) - newDisplayConfig.compassNorthTop = config.display.compassNorthTop - newDisplayConfig.flipScreen = config.display.flipScreen - newDisplayConfig.oledType = Int32(config.display.oled.rawValue) - newDisplayConfig.displayMode = Int32(config.display.displaymode.rawValue) + newDisplayConfig.gpsFormat = Int32(config.gpsFormat.rawValue) + newDisplayConfig.screenOnSeconds = Int32(config.screenOnSecs) + newDisplayConfig.screenCarouselInterval = Int32(config.autoScreenCarouselSecs) + newDisplayConfig.compassNorthTop = config.compassNorthTop + newDisplayConfig.flipScreen = config.flipScreen + newDisplayConfig.oledType = Int32(config.oled.rawValue) + newDisplayConfig.displayMode = Int32(config.displaymode.rawValue) fetchedNode[0].displayConfig = newDisplayConfig } 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) - fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop - fetchedNode[0].displayConfig?.flipScreen = config.display.flipScreen - fetchedNode[0].displayConfig?.oledType = Int32(config.display.oled.rawValue) - fetchedNode[0].displayConfig?.displayMode = Int32(config.display.displaymode.rawValue) + fetchedNode[0].displayConfig?.gpsFormat = Int32(config.gpsFormat.rawValue) + fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.screenOnSecs) + fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.autoScreenCarouselSecs) + fetchedNode[0].displayConfig?.compassNorthTop = config.compassNorthTop + fetchedNode[0].displayConfig?.flipScreen = config.flipScreen + fetchedNode[0].displayConfig?.oledType = Int32(config.oled.rawValue) + fetchedNode[0].displayConfig?.displayMode = Int32(config.displaymode.rawValue) } do { diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index ffeb291d..1d2f5ec3 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -14,7 +14,6 @@ struct BluetoothConfig: View { @Environment(\.dismiss) private var goBack var node: NodeInfoEntity? - var connectedNode: NodeInfoEntity? @State private var isPresentingSaveConfirm: Bool = false @State var hasChanges = false @@ -80,7 +79,7 @@ struct BluetoothConfig: View { } } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) Button { isPresentingSaveConfirm = true @@ -97,14 +96,15 @@ struct BluetoothConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var bc = Config.BluetoothConfig() bc.enabled = enabled bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin bc.fixedPin = UInt32(fixedPin) ?? 123456 - let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -127,15 +127,13 @@ struct BluetoothConfig: View { self.fixedPin = String(node?.bluetoothConfig?.fixedPin ?? 123456) self.hasChanges = false - // Need to request a LoRaConfig from the remote node before allowing changes - if node?.bluetoothConfig == nil { - print("empty bluetooth config") - - } - // Need to request a BluetoothConfig from the remote node before allowing changes - if connectedNode != nil && node?.bluetoothConfig == nil { - _ = bleManager.requestBluetoothConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if bleManager.connectedPeripheral != nil && node?.loRaConfig == nil { + print("empty bluetooth config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } } } .onChange(of: enabled) { newEnabled in diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index fcb55ad1..c58f9ce9 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -35,12 +35,14 @@ struct DeviceConfig: View { Picker("Device Role", selection: $deviceRole ) { ForEach(DeviceRoles.allCases) { dr in - Text(dr.description) + Text(dr.name) } } .pickerStyle(DefaultPickerStyle()) .padding(.top, 10) - .padding(.bottom, 10) + Text(DeviceRoles(rawValue: deviceRole)?.description ?? "") + .foregroundColor(.gray) + .font(.caption) } Section(header: Text("Debug")) { @@ -83,7 +85,7 @@ struct DeviceConfig: View { } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) HStack { @@ -138,11 +140,9 @@ struct DeviceConfig: View { HStack { Button { - isPresentingSaveConfirm = true } label: { - Label("save", systemImage: "square.and.arrow.down") } .disabled(bleManager.connectedPeripheral == nil || !hasChanges) @@ -156,10 +156,10 @@ struct DeviceConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { - + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var dc = Config.DeviceConfig() dc.role = DeviceRoles(rawValue: deviceRole)!.protoEnumValue() dc.serialEnabled = serialEnabled @@ -167,7 +167,7 @@ struct DeviceConfig: View { dc.buttonGpio = UInt32(buttonGPIO) dc.buzzerGpio = UInt32(buzzerGPIO) - let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -195,6 +195,15 @@ struct DeviceConfig: View { self.buttonGPIO = Int(node?.deviceConfig?.buttonGpio ?? 0) self.buzzerGPIO = Int(node?.deviceConfig?.buzzerGpio ?? 0) self.hasChanges = false + + // Need to request a LoRaConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.deviceConfig == nil { + print("empty device config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestDeviceConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: deviceRole) { newRole in diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 1554b536..dc595819 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -97,7 +97,7 @@ struct DisplayConfig: View { .listRowSeparator(.visible) } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) Button { @@ -116,9 +116,10 @@ struct DisplayConfig: View { "are.you.sure", isPresented: $isPresentingSaveConfirm ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var dc = Config.DisplayConfig() dc.gpsFormat = GpsFormats(rawValue: gpsFormat)!.protoEnumValue() dc.screenOnSecs = UInt32(screenOnSeconds) @@ -128,7 +129,7 @@ struct DisplayConfig: View { dc.oled = OledTypes(rawValue: oledType)!.protoEnumValue() dc.displaymode = DisplayModes(rawValue: displayMode)!.protoEnumValue() - let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: connectedNode.user!, toUser: node!.user!) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -156,6 +157,15 @@ struct DisplayConfig: View { self.oledType = Int(node?.displayConfig?.oledType ?? 0) self.displayMode = Int(node?.displayConfig?.displayMode ?? 0) self.hasChanges = false + + // Need to request a LoRaConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.displayConfig == nil { + print("empty display config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestDisplayConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: screenOnSeconds) { newScreenSecs in if node != nil && node!.displayConfig != nil { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 8efb83ef..40ef0ea0 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -61,7 +61,7 @@ struct LoRaConfig: View { .font(.caption) } } - .disabled(self.bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) Button { isPresentingSaveConfirm = true } label: { diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 58710789..26d8fa03 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -25,7 +25,6 @@ struct NetworkConfig: View { @State var ethEnabled = false @State var ethMode = 0 - var body: some View { VStack { @@ -108,9 +107,10 @@ struct NetworkConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var network = Config.NetworkConfig() network.wifiEnabled = self.wifiEnabled network.wifiSsid = self.wifiSsid @@ -118,7 +118,7 @@ struct NetworkConfig: View { network.ethEnabled = self.ethEnabled //network.addressMode = Config.NetworkConfig.AddressMode.dhcp - let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -143,6 +143,15 @@ struct NetworkConfig: View { self.wifiMode = Int(node?.networkConfig?.wifiMode ?? 0) self.ethEnabled = node?.networkConfig?.ethEnabled ?? false self.hasChanges = false + + // Need to request a NetworkConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.positionConfig == nil { + print("empty network config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestNetworkConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: wifiEnabled) { newEnabled in if node != nil && node!.networkConfig != nil { diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 8600dd53..4e2261a6 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -185,14 +185,11 @@ struct PositionConfig: View { } } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) Button { - isPresentingSaveConfirm = true - } label: { - Label("save", systemImage: "square.and.arrow.down") } .disabled(bleManager.connectedPeripheral == nil || !hasChanges) @@ -205,14 +202,14 @@ struct PositionConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { if fixedPosition { _ = bleManager.sendPosition(destNum: bleManager.connectedPeripheral.num, wantResponse: false) } - + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var pc = Config.PositionConfig() pc.positionBroadcastSmartEnabled = smartPositionEnabled pc.gpsEnabled = deviceGpsEnabled @@ -232,7 +229,7 @@ struct PositionConfig: View { if includeSpeed { pf.insert(.Speed) } if includeHeading { pf.insert(.Heading) } pc.positionFlags = UInt32(pf.rawValue) - let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -278,6 +275,14 @@ struct PositionConfig: View { self.hasChanges = false + // Need to request a PositionConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.positionConfig == nil { + print("empty position config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestPositionConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: deviceGpsEnabled) { newDeviceGps in if node != nil && node!.positionConfig != nil { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index fcbd2bcd..603202b2 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -135,14 +135,13 @@ struct Settings: View { .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink() { - BluetoothConfig(node: nodes.first(where: { $0.num == selectedNode }), connectedNode: nodes.first(where: { $0.num == connectedNodeNum })) + BluetoothConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "antenna.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical) Text("bluetooth") } .tag(SettingsSidebar.bluetoothConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { DeviceConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -152,7 +151,6 @@ struct Settings: View { Text("device") } .tag(SettingsSidebar.deviceConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { DisplayConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -162,7 +160,6 @@ struct Settings: View { Text("display") } .tag(SettingsSidebar.displayConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { NetworkConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -173,7 +170,6 @@ struct Settings: View { Text("network") } .tag(SettingsSidebar.networkConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { PositionConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -184,7 +180,6 @@ struct Settings: View { Text("position") } .tag(SettingsSidebar.positionConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) } Section("module.configuration") { From 24fedb7e2c0f83861834ec8f66adf283a00159a5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 31 Jan 2023 22:20:16 -0800 Subject: [PATCH 3/6] Fix remaining config views --- Meshtastic/Helpers/MeshPackets.swift | 8 ++-- Meshtastic/Persistence/UpdateCoreData.swift | 44 +++++++++++---------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 9c58e2f3..b1335c5d 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -48,9 +48,9 @@ func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64 } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { upsertLoRaConfigPacket(config: config.lora, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { - upsertNetworkConfigPacket(config: config, nodeNum: nodeNum, context: context) + upsertNetworkConfigPacket(config: config.network, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { - upsertPositionConfigPacket(config: config, nodeNum: nodeNum, context: context) + upsertPositionConfigPacket(config: config.position, nodeNum: nodeNum, context: context) } } @@ -813,10 +813,10 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { - upsertNetworkConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) + upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { - upsertPositionConfigPacket(config: config, nodeNum: Int64(packet.from), context: context) + upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), context: context) } } else { diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 24bf4868..13d56039 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -363,7 +363,7 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64 } } -func upsertNetworkConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { +func upsertNetworkConfigPacket(config: Meshtastic.Config.NetworkConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.network.config %@", comment: "Network config received: %@"), String(nodeNum)) MeshLogger.log("🌐 \(logString)") @@ -378,12 +378,16 @@ func upsertNetworkConfigPacket(config: Config, nodeNum: Int64, context: NSManage if !fetchedNode.isEmpty { if fetchedNode[0].networkConfig == nil { let newNetworkConfig = NetworkConfigEntity(context: context) - newNetworkConfig.wifiSsid = config.network.wifiSsid - newNetworkConfig.wifiPsk = config.network.wifiPsk + newNetworkConfig.wifiEnabled = config.wifiEnabled + newNetworkConfig.wifiSsid = config.wifiSsid + newNetworkConfig.wifiPsk = config.wifiPsk + newNetworkConfig.ethEnabled = config.ethEnabled fetchedNode[0].networkConfig = newNetworkConfig } else { - fetchedNode[0].networkConfig?.wifiSsid = config.network.wifiSsid - fetchedNode[0].networkConfig?.wifiPsk = config.network.wifiPsk + fetchedNode[0].networkConfig?.ethEnabled = config.ethEnabled + fetchedNode[0].networkConfig?.wifiEnabled = config.wifiEnabled + fetchedNode[0].networkConfig?.wifiSsid = config.wifiSsid + fetchedNode[0].networkConfig?.wifiPsk = config.wifiPsk } do { @@ -404,7 +408,7 @@ func upsertNetworkConfigPacket(config: Config, nodeNum: Int64, context: NSManage } } -func upsertPositionConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) { +func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNum: Int64, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.config %@", comment: "Positon config received: %@"), String(nodeNum)) MeshLogger.log("πŸ—ΊοΈ \(logString)") @@ -419,22 +423,22 @@ func upsertPositionConfigPacket(config: Config, nodeNum: Int64, context: NSManag if !fetchedNode.isEmpty { if fetchedNode[0].positionConfig == nil { let newPositionConfig = PositionConfigEntity(context: context) - newPositionConfig.smartPositionEnabled = config.position.positionBroadcastSmartEnabled - newPositionConfig.deviceGpsEnabled = config.position.gpsEnabled - newPositionConfig.fixedPosition = config.position.fixedPosition - newPositionConfig.gpsUpdateInterval = Int32(config.position.gpsUpdateInterval) - newPositionConfig.gpsAttemptTime = Int32(config.position.gpsAttemptTime) - newPositionConfig.positionBroadcastSeconds = Int32(config.position.positionBroadcastSecs) - newPositionConfig.positionFlags = Int32(config.position.positionFlags) + newPositionConfig.smartPositionEnabled = config.positionBroadcastSmartEnabled + newPositionConfig.deviceGpsEnabled = config.gpsEnabled + newPositionConfig.fixedPosition = config.fixedPosition + newPositionConfig.gpsUpdateInterval = Int32(config.gpsUpdateInterval) + newPositionConfig.gpsAttemptTime = Int32(config.gpsAttemptTime) + newPositionConfig.positionBroadcastSeconds = Int32(config.positionBroadcastSecs) + newPositionConfig.positionFlags = Int32(config.positionFlags) fetchedNode[0].positionConfig = newPositionConfig } else { - fetchedNode[0].positionConfig?.smartPositionEnabled = config.position.positionBroadcastSmartEnabled - fetchedNode[0].positionConfig?.deviceGpsEnabled = config.position.gpsEnabled - fetchedNode[0].positionConfig?.fixedPosition = config.position.fixedPosition - fetchedNode[0].positionConfig?.gpsUpdateInterval = Int32(config.position.gpsUpdateInterval) - fetchedNode[0].positionConfig?.gpsAttemptTime = Int32(config.position.gpsAttemptTime) - fetchedNode[0].positionConfig?.positionBroadcastSeconds = Int32(config.position.positionBroadcastSecs) - fetchedNode[0].positionConfig?.positionFlags = Int32(config.position.positionFlags) + fetchedNode[0].positionConfig?.smartPositionEnabled = config.positionBroadcastSmartEnabled + fetchedNode[0].positionConfig?.deviceGpsEnabled = config.gpsEnabled + fetchedNode[0].positionConfig?.fixedPosition = config.fixedPosition + fetchedNode[0].positionConfig?.gpsUpdateInterval = Int32(config.gpsUpdateInterval) + fetchedNode[0].positionConfig?.gpsAttemptTime = Int32(config.gpsAttemptTime) + fetchedNode[0].positionConfig?.positionBroadcastSeconds = Int32(config.positionBroadcastSecs) + fetchedNode[0].positionConfig?.positionFlags = Int32(config.positionFlags) } do { try context.save() From 9112e5cde269bc67a90e994a8c43a16b059d809c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 31 Jan 2023 22:59:43 -0800 Subject: [PATCH 4/6] Properly disable forms when there is not an associated config --- Meshtastic/Views/Settings/Config/BluetoothConfig.swift | 2 +- Meshtastic/Views/Settings/Config/DeviceConfig.swift | 2 +- Meshtastic/Views/Settings/Config/DisplayConfig.swift | 2 +- Meshtastic/Views/Settings/Config/NetworkConfig.swift | 2 +- Meshtastic/Views/Settings/Config/PositionConfig.swift | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 1d2f5ec3..b05d2190 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -79,7 +79,7 @@ struct BluetoothConfig: View { } } } - .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.bluetoothConfig == nil) Button { isPresentingSaveConfirm = true diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index c58f9ce9..8023fb4f 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -85,7 +85,7 @@ struct DeviceConfig: View { } } - .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.deviceConfig == nil) HStack { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index dc595819..cd589a4c 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -97,7 +97,7 @@ struct DisplayConfig: View { .listRowSeparator(.visible) } } - .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.displayConfig == nil) Button { diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 26d8fa03..2fafc701 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -91,7 +91,7 @@ struct NetworkConfig: View { } } .scrollDismissesKeyboard(.interactively) - .disabled(!(node != nil)) + .disabled(self.bleManager.connectedPeripheral == nil || node?.networkConfig == nil) Button { isPresentingSaveConfirm = true } label: { diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 4e2261a6..48301bdb 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -185,7 +185,7 @@ struct PositionConfig: View { } } } - .disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.positionConfig == nil) Button { isPresentingSaveConfirm = true From f89f5eddd04a18a6bd57fa46231d45b12c32eb27 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 31 Jan 2023 23:52:19 -0800 Subject: [PATCH 5/6] Add a couple more modules to the admin config --- Meshtastic/Helpers/BLEManager.swift | 52 +++++++++++++++++++ .../Config/Module/RangeTestConfig.swift | 17 ++++-- .../Config/Module/TelemetryConfig.swift | 16 ++++-- Meshtastic/Views/Settings/Settings.swift | 2 - 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 5370b12b..22628421 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1559,6 +1559,58 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { return false } + public func requestRangeTestModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.rangetestConfig + + 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() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.telemetryConfig + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 9fbd5ffd..26b47313 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -46,7 +46,7 @@ struct RangeTestConfig: View { .font(.caption) } } - .disabled(!(node != nil && node!.myInfo?.hasWifi ?? false)) + .disabled(self.bleManager.connectedPeripheral == nil || node?.positionConfig == nil || !(node != nil && node!.myInfo?.hasWifi ?? false)) Button { isPresentingSaveConfirm = true } label: { @@ -62,14 +62,16 @@ struct RangeTestConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var rtc = ModuleConfig.RangeTestConfig() rtc.enabled = enabled rtc.save = save rtc.sender = UInt32(sender) - let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -92,6 +94,15 @@ struct RangeTestConfig: View { self.save = node?.rangeTestConfig?.save ?? false self.sender = Int(node?.rangeTestConfig?.sender ?? 0) self.hasChanges = false + + // Need to request a RangeTestModule Config from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.rangeTestConfig == nil { + print("empty range test module config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: enabled) { newEnabled in if node != nil && node!.rangeTestConfig != nil { diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index c803add3..38b8bd05 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -61,7 +61,7 @@ struct TelemetryConfig: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.positionConfig == nil) Button { isPresentingSaveConfirm = true } label: { @@ -77,7 +77,8 @@ struct TelemetryConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { var tc = ModuleConfig.TelemetryConfig() @@ -86,7 +87,7 @@ struct TelemetryConfig: View { tc.environmentMeasurementEnabled = environmentMeasurementEnabled tc.environmentScreenEnabled = environmentScreenEnabled tc.environmentDisplayFahrenheit = environmentDisplayFahrenheit - let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -111,6 +112,15 @@ struct TelemetryConfig: View { self.environmentScreenEnabled = node?.telemetryConfig?.environmentScreenEnabled ?? false self.environmentDisplayFahrenheit = node?.telemetryConfig?.environmentDisplayFahrenheit ?? false self.hasChanges = false + + // Need to request a TelemetryModuleConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.telemetryConfig == nil { + print("empty telemetry module config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: deviceUpdateInterval) { newDeviceInterval in if node != nil && node!.telemetryConfig != nil { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 603202b2..cd11ca6f 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -224,7 +224,6 @@ struct Settings: View { Text("range.test") } .tag(SettingsSidebar.rangeTestConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { SerialConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -244,7 +243,6 @@ struct Settings: View { Text("telemetry") } .tag(SettingsSidebar.telemetryConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) } Section(header: Text("logging")) { NavigationLink { From 57dbc7b71f550ed577d425c96335e798b2e865f9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 1 Feb 2023 09:19:45 -0800 Subject: [PATCH 6/6] Remote Admin for Module Config --- Meshtastic/Helpers/BLEManager.swift | 104 +++++ Meshtastic/Helpers/MeshPackets.swift | 373 ++---------------- Meshtastic/Persistence/UpdateCoreData.swift | 372 ++++++++++++++++- .../Config/Module/CannedMessagesConfig.swift | 11 +- .../Module/ExternalNotificationConfig.swift | 16 +- .../Settings/Config/Module/MQTTConfig.swift | 16 +- .../Settings/Config/Module/SerialConfig.swift | 16 +- Meshtastic/Views/Settings/Settings.swift | 4 - 8 files changed, 544 insertions(+), 368 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 22628421..5e7d84e1 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1559,6 +1559,58 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { return false } + public func requestCannedMessagesModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.cannedmsgConfig + + 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() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.extnotifConfig + + 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() @@ -1585,6 +1637,58 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { return false } + public func requestMqttModuleConfig(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.mqttConfig + + 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() + adminPacket.getModuleConfigRequest = AdminMessage.ModuleConfigType.serialConfig + + 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 b1335c5d..9498af21 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -57,338 +57,17 @@ func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64 func moduleConfig (config: ModuleConfig, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) { - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessage.config %@", comment: "Canned Message module config received: %@"), String(nodeNum)) - MeshLogger.log("πŸ₯« \(logString)") - - 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 Canned Message Config - if !fetchedNode.isEmpty { - - if fetchedNode[0].cannedMessageConfig == nil { - - let newCannedMessageConfig = CannedMessageConfigEntity(context: context) - - newCannedMessageConfig.enabled = config.cannedMessage.enabled - newCannedMessageConfig.sendBell = config.cannedMessage.sendBell - newCannedMessageConfig.rotary1Enabled = config.cannedMessage.rotary1Enabled - newCannedMessageConfig.updown1Enabled = config.cannedMessage.updown1Enabled - newCannedMessageConfig.inputbrokerPinA = Int32(config.cannedMessage.inputbrokerPinA) - newCannedMessageConfig.inputbrokerPinB = Int32(config.cannedMessage.inputbrokerPinB) - newCannedMessageConfig.inputbrokerPinPress = Int32(config.cannedMessage.inputbrokerPinPress) - newCannedMessageConfig.inputbrokerEventCw = Int32(config.cannedMessage.inputbrokerEventCw.rawValue) - newCannedMessageConfig.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) - newCannedMessageConfig.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) - - fetchedNode[0].cannedMessageConfig = newCannedMessageConfig - - } else { - - fetchedNode[0].cannedMessageConfig?.enabled = config.cannedMessage.enabled - fetchedNode[0].cannedMessageConfig?.sendBell = config.cannedMessage.sendBell - fetchedNode[0].cannedMessageConfig?.rotary1Enabled = config.cannedMessage.rotary1Enabled - fetchedNode[0].cannedMessageConfig?.updown1Enabled = config.cannedMessage.updown1Enabled - fetchedNode[0].cannedMessageConfig?.inputbrokerPinA = Int32(config.cannedMessage.inputbrokerPinA) - fetchedNode[0].cannedMessageConfig?.inputbrokerPinB = Int32(config.cannedMessage.inputbrokerPinB) - fetchedNode[0].cannedMessageConfig?.inputbrokerPinPress = Int32(config.cannedMessage.inputbrokerPinPress) - fetchedNode[0].cannedMessageConfig?.inputbrokerEventCw = Int32(config.cannedMessage.inputbrokerEventCw.rawValue) - fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) - fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) - } - - do { - try context.save() - print("πŸ’Ύ Updated Canned Message Module Config for node number: \(String(nodeNum))") - } catch { - context.rollback() - let nsError = error as NSError - print("πŸ’₯ Error Updating Core Data CannedMessageConfigEntity: \(nsError)") - } - } else { - print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Canned Message Module Config") - } - } catch { - let nsError = error as NSError - print("πŸ’₯ Fetching node for core data CannedMessageConfigEntity failed: \(nsError)") - } - } - - if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) { - - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.externalnotification.config %@", comment: "External Notifiation module config received: %@"), String(nodeNum)) - MeshLogger.log("πŸ“£ \(logString)") - - 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 External Notificaitone Config - if !fetchedNode.isEmpty { - - if fetchedNode[0].externalNotificationConfig == nil { - let newExternalNotificationConfig = ExternalNotificationConfigEntity(context: context) - newExternalNotificationConfig.enabled = config.externalNotification.enabled - newExternalNotificationConfig.usePWM = config.externalNotification.usePwm - newExternalNotificationConfig.alertBell = config.externalNotification.alertBell - newExternalNotificationConfig.alertBellBuzzer = config.externalNotification.alertBellBuzzer - newExternalNotificationConfig.alertBellVibra = config.externalNotification.alertBellVibra - newExternalNotificationConfig.alertMessage = config.externalNotification.alertMessage - newExternalNotificationConfig.alertMessageBuzzer = config.externalNotification.alertMessageBuzzer - newExternalNotificationConfig.alertMessageVibra = config.externalNotification.alertMessageVibra - newExternalNotificationConfig.active = config.externalNotification.active - newExternalNotificationConfig.output = Int32(config.externalNotification.output) - newExternalNotificationConfig.outputBuzzer = Int32(config.externalNotification.outputBuzzer) - newExternalNotificationConfig.outputVibra = Int32(config.externalNotification.outputVibra) - newExternalNotificationConfig.outputMilliseconds = Int32(config.externalNotification.outputMs) - newExternalNotificationConfig.nagTimeout = Int32(config.externalNotification.nagTimeout) - fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig - - } else { - fetchedNode[0].externalNotificationConfig?.enabled = config.externalNotification.enabled - fetchedNode[0].externalNotificationConfig?.usePWM = config.externalNotification.usePwm - fetchedNode[0].externalNotificationConfig?.alertBell = config.externalNotification.alertBell - fetchedNode[0].externalNotificationConfig?.alertBellBuzzer = config.externalNotification.alertBellBuzzer - fetchedNode[0].externalNotificationConfig?.alertBellVibra = config.externalNotification.alertBellVibra - fetchedNode[0].externalNotificationConfig?.alertMessage = config.externalNotification.alertMessage - fetchedNode[0].externalNotificationConfig?.alertMessageBuzzer = config.externalNotification.alertMessageBuzzer - fetchedNode[0].externalNotificationConfig?.alertMessageVibra = config.externalNotification.alertMessageVibra - fetchedNode[0].externalNotificationConfig?.active = config.externalNotification.active - fetchedNode[0].externalNotificationConfig?.output = Int32(config.externalNotification.output) - fetchedNode[0].externalNotificationConfig?.outputBuzzer = Int32(config.externalNotification.outputBuzzer) - fetchedNode[0].externalNotificationConfig?.outputVibra = Int32(config.externalNotification.outputVibra) - fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.externalNotification.outputMs) - fetchedNode[0].externalNotificationConfig?.nagTimeout = Int32(config.externalNotification.nagTimeout) - } - - do { - try context.save() - print("πŸ’Ύ Updated External Notification Module Config for node number: \(String(nodeNum))") - } catch { - context.rollback() - let nsError = error as NSError - print("πŸ’₯ Error Updating Core Data ExternalNotificationConfigEntity: \(nsError)") - } - } else { - print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save External Notifiation Module Config") - } - } catch { - let nsError = error as NSError - print("πŸ’₯ Fetching node for core data ExternalNotificationConfigEntity failed: \(nsError)") - } - } - - if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(config.mqtt) { - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.mqtt.config %@", comment: "MQTT module config received: %@"), String(nodeNum)) - MeshLogger.log("πŸŒ‰ \(logString)") - - 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 MQTT Config - if !fetchedNode.isEmpty { - - if fetchedNode[0].mqttConfig == nil { - let newMQTTConfig = MQTTConfigEntity(context: context) - newMQTTConfig.enabled = config.mqtt.enabled - newMQTTConfig.address = config.mqtt.address - newMQTTConfig.address = config.mqtt.username - newMQTTConfig.password = config.mqtt.password - newMQTTConfig.encryptionEnabled = config.mqtt.encryptionEnabled - newMQTTConfig.jsonEnabled = config.mqtt.jsonEnabled - fetchedNode[0].mqttConfig = newMQTTConfig - } else { - fetchedNode[0].mqttConfig?.enabled = config.mqtt.enabled - fetchedNode[0].mqttConfig?.address = config.mqtt.address - fetchedNode[0].mqttConfig?.address = config.mqtt.username - fetchedNode[0].mqttConfig?.password = config.mqtt.password - fetchedNode[0].mqttConfig?.encryptionEnabled = config.mqtt.encryptionEnabled - fetchedNode[0].mqttConfig?.jsonEnabled = config.mqtt.jsonEnabled - } - do { - try context.save() - print("πŸ’Ύ Updated MQTT Config for node number: \(String(nodeNum))") - } catch { - context.rollback() - let nsError = error as NSError - print("πŸ’₯ Error Updating Core Data MQTTConfigEntity: \(nsError)") - } - } else { - print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save MQTT Module Config") - } - } catch { - let nsError = error as NSError - print("πŸ’₯ Fetching node for core data MQTTConfigEntity failed: \(nsError)") - } - } - - if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(config.rangeTest) { - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.rangetest.config %@", comment: "Range Test module config received: %@"), String(nodeNum)) - MeshLogger.log("⛰️ \(logString)") - - 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].rangeTestConfig == nil { - let newRangeTestConfig = RangeTestConfigEntity(context: context) - newRangeTestConfig.sender = Int32(config.rangeTest.sender) - newRangeTestConfig.enabled = config.rangeTest.enabled - newRangeTestConfig.save = config.rangeTest.save - fetchedNode[0].rangeTestConfig = newRangeTestConfig - } else { - fetchedNode[0].rangeTestConfig?.sender = Int32(config.rangeTest.sender) - fetchedNode[0].rangeTestConfig?.enabled = config.rangeTest.enabled - fetchedNode[0].rangeTestConfig?.save = config.rangeTest.save - } - do { - try context.save() - print("πŸ’Ύ Updated Range Test Config for node number: \(String(nodeNum))") - } catch { - context.rollback() - let nsError = error as NSError - print("πŸ’₯ Error Updating Core Data RangeTestConfigEntity: \(nsError)") - } - } else { - print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Range Test Module Config") - } - } catch { - let nsError = error as NSError - print("πŸ’₯ Fetching node for core data RangeTestConfigEntity failed: \(nsError)") - } - } - - if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(config.serial) { - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.serial.config %@", comment: "Serial module config received: %@"), String(nodeNum)) - MeshLogger.log("πŸ€– \(logString)") - - 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].serialConfig == nil { - - let newSerialConfig = SerialConfigEntity(context: context) - newSerialConfig.enabled = config.serial.enabled - newSerialConfig.echo = config.serial.echo - newSerialConfig.rxd = Int32(config.serial.rxd) - newSerialConfig.txd = Int32(config.serial.txd) - newSerialConfig.baudRate = Int32(config.serial.baud.rawValue) - newSerialConfig.timeout = Int32(config.serial.timeout) - newSerialConfig.mode = Int32(config.serial.mode.rawValue) - fetchedNode[0].serialConfig = newSerialConfig - - } else { - fetchedNode[0].serialConfig?.enabled = config.serial.enabled - fetchedNode[0].serialConfig?.echo = config.serial.echo - fetchedNode[0].serialConfig?.rxd = Int32(config.serial.rxd) - fetchedNode[0].serialConfig?.txd = Int32(config.serial.txd) - fetchedNode[0].serialConfig?.baudRate = Int32(config.serial.baud.rawValue) - fetchedNode[0].serialConfig?.timeout = Int32(config.serial.timeout) - fetchedNode[0].serialConfig?.mode = Int32(config.serial.mode.rawValue) - } - - do { - try context.save() - print("πŸ’Ύ Updated Serial Module Config for node number: \(String(nodeNum))") - - } catch { - - context.rollback() - - let nsError = error as NSError - print("πŸ’₯ Error Updating Core Data SerialConfigEntity: \(nsError)") - } - - } else { - - print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Serial Module Config") - } - - } catch { - - let nsError = error as NSError - print("πŸ’₯ Fetching node for core data SerialConfigEntity failed: \(nsError)") - } - } - - if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(config.telemetry) { - - let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.config %@", comment: "Telemetry module config received: %@"), String(nodeNum)) - MeshLogger.log("πŸ“ˆ \(logString)") - - 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 Telemetry Config - if !fetchedNode.isEmpty { - - if fetchedNode[0].telemetryConfig == nil { - - let newTelemetryConfig = TelemetryConfigEntity(context: context) - - newTelemetryConfig.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) - newTelemetryConfig.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) - newTelemetryConfig.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled - newTelemetryConfig.environmentScreenEnabled = config.telemetry.environmentScreenEnabled - newTelemetryConfig.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit - - fetchedNode[0].telemetryConfig = newTelemetryConfig - - } else { - - fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) - fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) - fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled - fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.telemetry.environmentScreenEnabled - fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit - } - - do { - try context.save() - print("πŸ’Ύ Updated Telemetry Module Config for node number: \(String(nodeNum))") - - } catch { - context.rollback() - let nsError = error as NSError - print("πŸ’₯ Error Updating Core Data TelemetryConfigEntity: \(nsError)") - } - - } else { - print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Telemetry Module Config") - } - - } catch { - let nsError = error as NSError - print("πŸ’₯ Fetching node for core data TelemetryConfigEntity failed: \(nsError)") - } + upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum, context: context) + } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) { + upsertExternalNotificationModuleConfigPacket(config: config.externalNotification, nodeNum: nodeNum, context: context) + } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(config.mqtt) { + upsertMqttModuleConfigPacket(config: config.mqtt, nodeNum: nodeNum, context: context) + } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(config.rangeTest) { + upsertRangeTestModuleConfigPacket(config: config.rangeTest, nodeNum: nodeNum, context: context) + } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(config.serial) { + upsertSerialModuleConfigPacket(config: config.serial, nodeNum: nodeNum, context: context) + } else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(config.telemetry) { + upsertTelemetryModuleConfigPacket(config: config.telemetry, nodeNum: nodeNum, context: context) } } @@ -800,9 +479,9 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { deviceMetadataPacket(metadata: adminMessage.getDeviceMetadataResponse, fromNum: Int64(packet.from), context: context) } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getConfigResponse(adminMessage.getConfigResponse) { - + let config = adminMessage.getConfigResponse - + if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), context: context) @@ -817,8 +496,32 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), context: context) - + } + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getModuleConfigResponse(adminMessage.getModuleConfigResponse) { + + let moduleConfig = adminMessage.getModuleConfigResponse + + 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.externalNotification(moduleConfig.externalNotification) { + upsertExternalNotificationModuleConfigPacket(config: moduleConfig.externalNotification, nodeNum: Int64(packet.from), context: context) + + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(moduleConfig.mqtt) { + upsertMqttModuleConfigPacket(config: moduleConfig.mqtt, nodeNum: Int64(packet.from), context: context) + + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(moduleConfig.rangeTest) { + upsertRangeTestModuleConfigPacket(config: moduleConfig.rangeTest, nodeNum: Int64(packet.from), context: context) + + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(moduleConfig.serial) { + upsertSerialModuleConfigPacket(config: moduleConfig.serial, nodeNum: Int64(packet.from), context: context) + + } else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(moduleConfig.telemetry) { + upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context) + + } + } else { MeshLogger.log("πŸ•ΈοΈ MESH PACKET received for Admin App \(try! packet.decoded.jsonString())") } diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 13d56039..b78dacd6 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -7,14 +7,14 @@ import CoreData public func clearPositions(destNum: Int64, context: NSManagedObjectContext) -> Bool { - + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(destNum)) - + do { - + let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] - + let newPostions = [PositionEntity]() fetchedNode[0].positions? = NSOrderedSet(array: newPostions) @@ -26,7 +26,7 @@ public func clearPositions(destNum: Int64, context: NSManagedObjectContext) -> B context.rollback() return false } - + } catch { print("πŸ’₯ Fetch NodeInfoEntity Error") return false @@ -37,11 +37,11 @@ public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManage let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(destNum)) - + do { - + let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] - + let emptyTelemetry = [TelemetryEntity]() fetchedNode[0].telemetries? = NSOrderedSet(array: emptyTelemetry) @@ -53,7 +53,7 @@ public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManage context.rollback() return false } - + } catch { print("πŸ’₯ Fetch NodeInfoEntity Error") return false @@ -63,9 +63,9 @@ public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManage public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObjectContext) { do { let objects = channel.allPrivateMessages - for object in objects { - context.delete(object) - } + for object in objects { + context.delete(object) + } try context.save() } catch let error as NSError { print("Error: \(error.localizedDescription)") @@ -73,12 +73,12 @@ public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObje } public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext) { - + do { let objects = user.messageList - for object in objects { - context.delete(object) - } + for object in objects { + context.delete(object) + } try context.save() } catch let error as NSError { print("Error: \(error.localizedDescription)") @@ -92,11 +92,11 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext) { let entity = persistenceController.managedObjectModel.entities[i] let query = NSFetchRequest(entityName: entity.name!) let deleteRequest = NSBatchDeleteRequest(fetchRequest: query) - + do { try context.executeAndMergeChanges(using: deleteRequest) } catch let error as NSError { - print(error) + print(error) } } } @@ -112,7 +112,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) do { if let positionMessage = try? Position(serializedData: packet.decoded.payload) { - + // Don't save empty position packets if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 && (positionMessage.latitudeI != 373346000 && positionMessage.longitudeI != -1220090000) { @@ -456,3 +456,337 @@ func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNu print("πŸ’₯ Fetching node for core data PositionConfigEntity failed: \(nsError)") } } + +func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.CannedMessageConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessage.config %@", comment: "Canned Message module config received: %@"), String(nodeNum)) + MeshLogger.log("πŸ₯« \(logString)") + + 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 Canned Message Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].cannedMessageConfig == nil { + + let newCannedMessageConfig = CannedMessageConfigEntity(context: context) + + newCannedMessageConfig.enabled = config.enabled + newCannedMessageConfig.sendBell = config.sendBell + newCannedMessageConfig.rotary1Enabled = config.rotary1Enabled + newCannedMessageConfig.updown1Enabled = config.updown1Enabled + newCannedMessageConfig.inputbrokerPinA = Int32(config.inputbrokerPinA) + newCannedMessageConfig.inputbrokerPinB = Int32(config.inputbrokerPinB) + newCannedMessageConfig.inputbrokerPinPress = Int32(config.inputbrokerPinPress) + newCannedMessageConfig.inputbrokerEventCw = Int32(config.inputbrokerEventCw.rawValue) + newCannedMessageConfig.inputbrokerEventCcw = Int32(config.inputbrokerEventCcw.rawValue) + newCannedMessageConfig.inputbrokerEventPress = Int32(config.inputbrokerEventPress.rawValue) + + fetchedNode[0].cannedMessageConfig = newCannedMessageConfig + + } else { + + fetchedNode[0].cannedMessageConfig?.enabled = config.enabled + fetchedNode[0].cannedMessageConfig?.sendBell = config.sendBell + fetchedNode[0].cannedMessageConfig?.rotary1Enabled = config.rotary1Enabled + fetchedNode[0].cannedMessageConfig?.updown1Enabled = config.updown1Enabled + fetchedNode[0].cannedMessageConfig?.inputbrokerPinA = Int32(config.inputbrokerPinA) + fetchedNode[0].cannedMessageConfig?.inputbrokerPinB = Int32(config.inputbrokerPinB) + fetchedNode[0].cannedMessageConfig?.inputbrokerPinPress = Int32(config.inputbrokerPinPress) + fetchedNode[0].cannedMessageConfig?.inputbrokerEventCw = Int32(config.inputbrokerEventCw.rawValue) + fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.inputbrokerEventCcw.rawValue) + fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.inputbrokerEventPress.rawValue) + } + + do { + try context.save() + print("πŸ’Ύ Updated Canned Message Module Config for node number: \(String(nodeNum))") + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Updating Core Data CannedMessageConfigEntity: \(nsError)") + } + } else { + print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Canned Message Module Config") + } + } catch { + let nsError = error as NSError + print("πŸ’₯ Fetching node for core data CannedMessageConfigEntity failed: \(nsError)") + } +} + +func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfig.ExternalNotificationConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.externalnotification.config %@", comment: "External Notifiation module config received: %@"), String(nodeNum)) + MeshLogger.log("πŸ“£ \(logString)") + + 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 External Notificaitone Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].externalNotificationConfig == nil { + let newExternalNotificationConfig = ExternalNotificationConfigEntity(context: context) + newExternalNotificationConfig.enabled = config.enabled + newExternalNotificationConfig.usePWM = config.usePwm + newExternalNotificationConfig.alertBell = config.alertBell + newExternalNotificationConfig.alertBellBuzzer = config.alertBellBuzzer + newExternalNotificationConfig.alertBellVibra = config.alertBellVibra + newExternalNotificationConfig.alertMessage = config.alertMessage + newExternalNotificationConfig.alertMessageBuzzer = config.alertMessageBuzzer + newExternalNotificationConfig.alertMessageVibra = config.alertMessageVibra + newExternalNotificationConfig.active = config.active + newExternalNotificationConfig.output = Int32(config.output) + newExternalNotificationConfig.outputBuzzer = Int32(config.outputBuzzer) + newExternalNotificationConfig.outputVibra = Int32(config.outputVibra) + newExternalNotificationConfig.outputMilliseconds = Int32(config.outputMs) + newExternalNotificationConfig.nagTimeout = Int32(config.nagTimeout) + fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig + + } else { + fetchedNode[0].externalNotificationConfig?.enabled = config.enabled + fetchedNode[0].externalNotificationConfig?.usePWM = config.usePwm + fetchedNode[0].externalNotificationConfig?.alertBell = config.alertBell + fetchedNode[0].externalNotificationConfig?.alertBellBuzzer = config.alertBellBuzzer + fetchedNode[0].externalNotificationConfig?.alertBellVibra = config.alertBellVibra + fetchedNode[0].externalNotificationConfig?.alertMessage = config.alertMessage + fetchedNode[0].externalNotificationConfig?.alertMessageBuzzer = config.alertMessageBuzzer + fetchedNode[0].externalNotificationConfig?.alertMessageVibra = config.alertMessageVibra + fetchedNode[0].externalNotificationConfig?.active = config.active + fetchedNode[0].externalNotificationConfig?.output = Int32(config.output) + fetchedNode[0].externalNotificationConfig?.outputBuzzer = Int32(config.outputBuzzer) + fetchedNode[0].externalNotificationConfig?.outputVibra = Int32(config.outputVibra) + fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.outputMs) + fetchedNode[0].externalNotificationConfig?.nagTimeout = Int32(config.nagTimeout) + } + + do { + try context.save() + print("πŸ’Ύ Updated External Notification Module Config for node number: \(String(nodeNum))") + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Updating Core Data ExternalNotificationConfigEntity: \(nsError)") + } + } else { + print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save External Notifiation Module Config") + } + } catch { + let nsError = error as NSError + print("πŸ’₯ Fetching node for core data ExternalNotificationConfigEntity failed: \(nsError)") + } +} + +func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.mqtt.config %@", comment: "MQTT module config received: %@"), String(nodeNum)) + MeshLogger.log("πŸŒ‰ \(logString)") + + 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 MQTT Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].mqttConfig == nil { + let newMQTTConfig = MQTTConfigEntity(context: context) + newMQTTConfig.enabled = config.enabled + newMQTTConfig.address = config.address + newMQTTConfig.address = config.username + newMQTTConfig.password = config.password + newMQTTConfig.encryptionEnabled = config.encryptionEnabled + newMQTTConfig.jsonEnabled = config.jsonEnabled + fetchedNode[0].mqttConfig = newMQTTConfig + } else { + fetchedNode[0].mqttConfig?.enabled = config.enabled + fetchedNode[0].mqttConfig?.address = config.address + fetchedNode[0].mqttConfig?.address = config.username + fetchedNode[0].mqttConfig?.password = config.password + fetchedNode[0].mqttConfig?.encryptionEnabled = config.encryptionEnabled + fetchedNode[0].mqttConfig?.jsonEnabled = config.jsonEnabled + } + do { + try context.save() + print("πŸ’Ύ Updated MQTT Config for node number: \(String(nodeNum))") + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Updating Core Data MQTTConfigEntity: \(nsError)") + } + } else { + print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save MQTT Module Config") + } + } catch { + let nsError = error as NSError + print("πŸ’₯ Fetching node for core data MQTTConfigEntity failed: \(nsError)") + } +} + +func upsertRangeTestModuleConfigPacket(config: Meshtastic.ModuleConfig.RangeTestConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.rangetest.config %@", comment: "Range Test module config received: %@"), String(nodeNum)) + MeshLogger.log("⛰️ \(logString)") + + 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].rangeTestConfig == nil { + let newRangeTestConfig = RangeTestConfigEntity(context: context) + newRangeTestConfig.sender = Int32(config.sender) + newRangeTestConfig.enabled = config.enabled + newRangeTestConfig.save = config.save + fetchedNode[0].rangeTestConfig = newRangeTestConfig + } else { + fetchedNode[0].rangeTestConfig?.sender = Int32(config.sender) + fetchedNode[0].rangeTestConfig?.enabled = config.enabled + fetchedNode[0].rangeTestConfig?.save = config.save + } + do { + try context.save() + print("πŸ’Ύ Updated Range Test Config for node number: \(String(nodeNum))") + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Updating Core Data RangeTestConfigEntity: \(nsError)") + } + } else { + print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Range Test Module Config") + } + } catch { + let nsError = error as NSError + print("πŸ’₯ Fetching node for core data RangeTestConfigEntity failed: \(nsError)") + } +} + +func upsertSerialModuleConfigPacket(config: Meshtastic.ModuleConfig.SerialConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.serial.config %@", comment: "Serial module config received: %@"), String(nodeNum)) + MeshLogger.log("πŸ€– \(logString)") + + 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].serialConfig == nil { + + let newSerialConfig = SerialConfigEntity(context: context) + newSerialConfig.enabled = config.enabled + newSerialConfig.echo = config.echo + newSerialConfig.rxd = Int32(config.rxd) + newSerialConfig.txd = Int32(config.txd) + newSerialConfig.baudRate = Int32(config.baud.rawValue) + newSerialConfig.timeout = Int32(config.timeout) + newSerialConfig.mode = Int32(config.mode.rawValue) + fetchedNode[0].serialConfig = newSerialConfig + + } else { + fetchedNode[0].serialConfig?.enabled = config.enabled + fetchedNode[0].serialConfig?.echo = config.echo + fetchedNode[0].serialConfig?.rxd = Int32(config.rxd) + fetchedNode[0].serialConfig?.txd = Int32(config.txd) + fetchedNode[0].serialConfig?.baudRate = Int32(config.baud.rawValue) + fetchedNode[0].serialConfig?.timeout = Int32(config.timeout) + fetchedNode[0].serialConfig?.mode = Int32(config.mode.rawValue) + } + + do { + try context.save() + print("πŸ’Ύ Updated Serial Module Config for node number: \(String(nodeNum))") + + } catch { + + context.rollback() + + let nsError = error as NSError + print("πŸ’₯ Error Updating Core Data SerialConfigEntity: \(nsError)") + } + + } else { + + print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Serial Module Config") + } + + } catch { + + let nsError = error as NSError + print("πŸ’₯ Fetching node for core data SerialConfigEntity failed: \(nsError)") + } +} + +func upsertTelemetryModuleConfigPacket(config: Meshtastic.ModuleConfig.TelemetryConfig, nodeNum: Int64, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.config %@", comment: "Telemetry module config received: %@"), String(nodeNum)) + MeshLogger.log("πŸ“ˆ \(logString)") + + 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 Telemetry Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].telemetryConfig == nil { + + let newTelemetryConfig = TelemetryConfigEntity(context: context) + + newTelemetryConfig.deviceUpdateInterval = Int32(config.deviceUpdateInterval) + newTelemetryConfig.environmentUpdateInterval = Int32(config.environmentUpdateInterval) + newTelemetryConfig.environmentMeasurementEnabled = config.environmentMeasurementEnabled + newTelemetryConfig.environmentScreenEnabled = config.environmentScreenEnabled + newTelemetryConfig.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit + + fetchedNode[0].telemetryConfig = newTelemetryConfig + + } else { + + fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.deviceUpdateInterval) + fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.environmentUpdateInterval) + fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.environmentMeasurementEnabled + fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.environmentScreenEnabled + fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit + } + + do { + try context.save() + print("πŸ’Ύ Updated Telemetry Module Config for node number: \(String(nodeNum))") + + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Updating Core Data TelemetryConfigEntity: \(nsError)") + } + + } else { + print("πŸ’₯ No Nodes found in local database matching node number \(nodeNum) unable to save Telemetry Module Config") + } + + } catch { + let nsError = error as NSError + print("πŸ’₯ Fetching node for core data TelemetryConfigEntity failed: \(nsError)") + } +} diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index c9c5374f..d4057971 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -200,7 +200,7 @@ struct CannedMessagesConfig: View { .disabled(configPreset > 0) } .scrollDismissesKeyboard(.immediately) - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.cannedMessageConfig == nil) Button { isPresentingSaveConfirm = true @@ -287,6 +287,15 @@ struct CannedMessagesConfig: View { self.messages = node?.cannedMessageConfig?.messages ?? "" self.hasChanges = false self.hasMessagesChanges = false + + // Need to request a CannedMessagesModuleConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.cannedMessageConfig == nil { + print("empty canned messages module config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: configPreset) { newPreset in diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 90b8e759..2a6e5222 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -138,7 +138,7 @@ struct ExternalNotificationConfig: View { } } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.externalNotificationConfig == nil) Button { isPresentingSaveConfirm = true } label: { @@ -154,7 +154,8 @@ struct ExternalNotificationConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { var enc = ModuleConfig.ExternalNotificationConfig() @@ -171,7 +172,7 @@ struct ExternalNotificationConfig: View { enc.outputVibra = UInt32(outputVibra) enc.outputMs = UInt32(outputMilliseconds) enc.usePwm = usePWM - let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -205,6 +206,15 @@ struct ExternalNotificationConfig: View { self.nagTimeout = Int(node?.externalNotificationConfig?.nagTimeout ?? 0) self.usePWM = node?.externalNotificationConfig?.usePWM ?? false self.hasChanges = false + + // Need to request a TelemetryModuleConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.externalNotificationConfig == nil { + print("empty external notification module config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: enabled) { newEnabled in if node != nil && node!.externalNotificationConfig != nil { diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 54f63e6d..7939a439 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -126,7 +126,7 @@ struct MQTTConfig: View { .font(.callout) } .scrollDismissesKeyboard(.interactively) - .disabled(!(node != nil)) + .disabled(self.bleManager.connectedPeripheral == nil || node?.mqttConfig == nil) Button { isPresentingSaveConfirm = true @@ -143,7 +143,8 @@ struct MQTTConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { var mqtt = ModuleConfig.MQTTConfig() @@ -153,7 +154,7 @@ struct MQTTConfig: View { mqtt.password = self.password mqtt.encryptionEnabled = self.encryptionEnabled mqtt.jsonEnabled = self.jsonEnabled - let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode.user!, toUser: node!.user!) 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 @@ -179,6 +180,15 @@ struct MQTTConfig: View { self.encryptionEnabled = (node?.mqttConfig?.encryptionEnabled ?? false) self.jsonEnabled = (node?.mqttConfig?.jsonEnabled ?? false) self.hasChanges = false + + // Need to request a TelemetryModuleConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.telemetryConfig == nil { + print("empty mqtt module config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestMqttModuleConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: enabled) { newEnabled in if node != nil && node?.mqttConfig != nil { diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index e515244c..5bd0409d 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -97,7 +97,7 @@ struct SerialConfig: View { .font(.caption) } } - .disabled(node == nil) + .disabled(self.bleManager.connectedPeripheral == nil || node?.serialConfig == nil) Button { @@ -118,9 +118,10 @@ struct SerialConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown") + let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown") let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName) Button(buttonText) { + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) var sc = ModuleConfig.SerialConfig() sc.enabled = enabled sc.echo = echo @@ -130,7 +131,7 @@ struct SerialConfig: View { sc.timeout = UInt32(timeout) sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue() - let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: connectedNode.user!, toUser: node!.user!) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -160,6 +161,15 @@ struct SerialConfig: View { self.timeout = Int(node?.serialConfig?.timeout ?? 0) self.mode = Int(node?.serialConfig?.mode ?? 0) self.hasChanges = false + + // Need to request a SerialModuleConfig from the remote node before allowing changes + if bleManager.connectedPeripheral != nil && node?.serialConfig == nil { + print("empty serial module config") + let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) + if connectedNode.id > 0 { + _ = bleManager.requestSerialModuleConfig(fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } .onChange(of: enabled) { newEnabled in diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index cd11ca6f..93bb597e 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -194,7 +194,6 @@ struct Settings: View { Text("canned.messages") } .tag(SettingsSidebar.cannedMessagesConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { ExternalNotificationConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -204,7 +203,6 @@ struct Settings: View { Text("external.notification") } .tag(SettingsSidebar.externalNotificationConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { MQTTConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -214,7 +212,6 @@ struct Settings: View { Text("mqtt") } .tag(SettingsSidebar.mqttConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { RangeTestConfig(node: nodes.first(where: { $0.num == selectedNode })) @@ -233,7 +230,6 @@ struct Settings: View { Text("serial") } .tag(SettingsSidebar.serialConfig) - .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { TelemetryConfig(node: nodes.first(where: { $0.num == selectedNode }))