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()