diff --git a/Localizable.xcstrings b/Localizable.xcstrings index df568dc1..05c2bbb8 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -109,7 +109,7 @@ "%@ Channels?" : { }, - "%@ config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log." : { + "%@ config data was requested over the admin channel but no response has been returned from the remote node." : { }, "%@ dB" : { @@ -1865,9 +1865,6 @@ } } } - }, - "Bluetooth Logs" : { - }, "bluetooth.config" : { "localizations" : { @@ -4584,9 +4581,6 @@ }, "Configure" : { - }, - "Configuring Node" : { - }, "Connect to a Node" : { @@ -5070,6 +5064,9 @@ }, "Debug Log" : { + }, + "Debug Logs" : { + }, "Debug Logs%@" : { @@ -5361,9 +5358,6 @@ } } } - }, - "Developer" : { - }, "Developers" : { @@ -5429,10 +5423,7 @@ "Device GPS" : { }, - "Device is managed by a mesh administrator." : { - - }, - "Device Logging Enabled" : { + "Device is managed by a mesh administrator, the user is unable to access any of the device settings." : { }, "Device Metrics" : { @@ -6929,9 +6920,6 @@ }, "Enabling Ethernet will disable the bluetooth connection to the app." : { - }, - "Enabling Managed mode will restrict access to all radio configurations, such as short/long names, regions, channels, modules, etc. and will only be accessible through the Admin channel. To avoid being locked out, make sure the Admin channel is working properly before enabling it." : { - }, "Enabling WiFi will disable the bluetooth connection to the app." : { @@ -16076,7 +16064,7 @@ "Other data sources" : { }, - "Output live debug logging over serial." : { + "Output live debug logging over serial, view and export position-redacted device logs over Bluetooth." : { }, "Output pin buzzer GPIO " : { @@ -19276,9 +19264,6 @@ }, "Serial Console over the Stream API." : { - }, - "Serial Debug Logs" : { - }, "serial.config" : { "localizations" : { @@ -22511,12 +22496,6 @@ }, "Via Mqtt" : { - }, - "View and export position-redacted device logs over Bluetooth" : { - - }, - "View Logs" : { - }, "voltage" : { "localizations" : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 83fac71b..43f27393 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -719,8 +719,8 @@ DD41582528582E9B009B0E59 /* DeviceConfig.swift */, DD8EBF42285058FA00426DCA /* DisplayConfig.swift */, DD2553562855B02500E55709 /* LoRaConfig.swift */, - DD2553582855B52700E55709 /* PositionConfig.swift */, DD8ED9C42898D51F00B3B0AB /* NetworkConfig.swift */, + DD2553582855B52700E55709 /* PositionConfig.swift */, D93068DA2B81C85E0066FBC8 /* PowerConfig.swift */, DD1BD0F22C63C65E008C0C70 /* SecurityConfig.swift */, DD61937B2863877A00E59241 /* Module */, diff --git a/Meshtastic/Enums/RoutingError.swift b/Meshtastic/Enums/RoutingError.swift index 108fedd2..8b50de89 100644 --- a/Meshtastic/Enums/RoutingError.swift +++ b/Meshtastic/Enums/RoutingError.swift @@ -95,7 +95,7 @@ enum RoutingError: Int, CaseIterable, Identifiable { case .notAuthorized: return true case .pkiFailed: - return false + return true case .pkiUnknownPubkey: return false } diff --git a/Meshtastic/Extensions/View.swift b/Meshtastic/Extensions/View.swift index 7349f36d..cec5b003 100644 --- a/Meshtastic/Extensions/View.swift +++ b/Meshtastic/Extensions/View.swift @@ -8,13 +8,13 @@ import SwiftUI public extension View { - func onFirstAppear(_ action: @escaping () -> ()) -> some View { + func onFirstAppear(_ action: @escaping () -> Void) -> some View { modifier(FirstAppear(action: action)) } } private struct FirstAppear: ViewModifier { - let action: () -> () + let action: () -> Void // Use this to only fire your block one time @State private var hasAppeared = false diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 08b53801..87551c4f 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -2564,6 +2564,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate var adminPacket = AdminMessage() adminPacket.getConfigRequest = AdminMessage.ConfigType.bluetoothConfig + if UserDefaults.enableAdministration { + adminPacket.sessionPasskey = toUser.userNode?.sessionPasskey ?? Data() + } var meshPacket: MeshPacket = MeshPacket() meshPacket.to = UInt32(toUser.num) meshPacket.from = UInt32(fromUser.num) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index efd80d72..3b6b2b65 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -50,6 +50,7 @@ func generateMessageMarkdown (message: String) -> String { func localConfig (config: Config, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { + let remote = nodeNum != UserDefaults.preferredPeripheralNum if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: nodeNum, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { @@ -365,7 +366,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].user = UserEntity(context: context) } // Set the public key for a user if it is empty, don't update - if fetchedNode[0].user?.publicKey?.isEmpty == nil && !nodeInfo.user.publicKey.isEmpty { + if fetchedNode[0].user?.publicKey == nil && !nodeInfo.user.publicKey.isEmpty { fetchedNode[0].user?.pkiEncrypted = true fetchedNode[0].user?.publicKey = nodeInfo.user.publicKey } @@ -496,19 +497,19 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } 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) + upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) { - upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), context: context) + upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) { - upsertDisplayConfigPacket(config: config.display, nodeNum: Int64(packet.from), context: context) + upsertDisplayConfigPacket(config: config.display, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) { - upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), context: context) + upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) { - upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), context: context) + upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) { - upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), context: context) + upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.power(config.power) { - upsertPowerConfigPacket(config: config.power, nodeNum: Int64(packet.from), context: context) + upsertPowerConfigPacket(config: config.power, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } else if config.payloadVariant == Config.OneOf_PayloadVariant.security(config.security) { upsertSecurityConfigPacket(config: config.security, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context) } diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 7c43448b..624dbb20 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -209,6 +209,8 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) } else { if packet.from > Constants.minimumNodeNum { let newUser = createUser(num: Int64(packet.from), context: context) + newNode.user?.pkiEncrypted = packet.pkiEncrypted + newNode.user?.publicKey = packet.publicKey newNode.user = newUser } } @@ -269,6 +271,11 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) fetchedNode[0].user!.role = Int32(nodeInfoMessage.user.role.rawValue) fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased() fetchedNode[0].user!.hwModelId = Int32(nodeInfoMessage.user.hwModel.rawValue) + + if !packet.publicKey.isEmpty { + fetchedNode[0].user!.pkiEncrypted = packet.pkiEncrypted + fetchedNode[0].user!.publicKey = packet.publicKey + } Task { Api().loadDeviceHardwareData { (hw) in let dh = hw.first(where: { $0.hwModel == fetchedNode[0].user?.hwModelId ?? 0 }) @@ -409,13 +416,11 @@ func upsertBluetoothConfigPacket(config: Config.BluetoothConfig, nodeNum: Int64, newBluetoothConfig.enabled = config.enabled newBluetoothConfig.mode = Int32(config.mode.rawValue) newBluetoothConfig.fixedPin = Int32(config.fixedPin) - newBluetoothConfig.deviceLoggingEnabled = config.deviceLoggingEnabled fetchedNode[0].bluetoothConfig = newBluetoothConfig } else { fetchedNode[0].bluetoothConfig?.enabled = config.enabled fetchedNode[0].bluetoothConfig?.mode = Int32(config.mode.rawValue) fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.fixedPin) - fetchedNode[0].bluetoothConfig?.deviceLoggingEnabled = config.deviceLoggingEnabled } if sessionPasskey != nil { fetchedNode[0].sessionPasskey = sessionPasskey diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 3a2c4a47..4e09859f 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -46,6 +46,7 @@ struct UserList: View { NSSortDescriptor(key: "userNode.favorite", ascending: false), NSSortDescriptor(key: "pkiEncrypted", ascending: false), NSSortDescriptor(key: "longName", ascending: true)], + predicate: NSPredicate(format: "hwModelId != nil"), animation: .default ) private var users: FetchedResults diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index 63aff338..481666a1 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -30,8 +30,22 @@ struct NodeListItem: View { } VStack(alignment: .leading) { HStack { + if node.user?.pkiEncrypted ?? false { + if !(node.user?.keyMatch ?? false) { + /// Public Key on the User and the Public Key on the Last Message don't match + Image(systemName: "key.slash") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") + .foregroundColor(.green) + } + } else { + Image(systemName: "lock.open.fill") + .foregroundColor(.yellow) + } Text(node.user?.longName ?? "unknown".localized) .font(.headline) + .fontWeight(.regular) .allowsTightening(true) if node.favorite { Spacer() diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 61cddcea..f039534c 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -95,7 +95,6 @@ struct Channels: View { if channelKey == "AQ==" { positionPrecision = 13 preciseLocation = false - positionsEnabled = true } } else if !supportedVersion && channelRole == 2 { positionPrecision = 0 @@ -104,7 +103,7 @@ struct Channels: View { } else { if channelKey == "AQ==" { preciseLocation = false - if positionPrecision < 10 || positionPrecision > 16 { + if (positionPrecision > 0 && positionPrecision < 10) || positionPrecision > 16 { positionPrecision = 13 } } else if positionPrecision == 32 { @@ -226,7 +225,7 @@ struct Channels: View { } label: { Label("save", systemImage: "square.and.arrow.down") } - .disabled(bleManager.connectedPeripheral == nil || !hasChanges || !hasValidKey) + .disabled(bleManager.connectedPeripheral == nil)// || !hasChanges)// !hasValidKey) .buttonStyle(.bordered) .buttonBorderShape(.capsule) .controlSize(.large) @@ -259,10 +258,9 @@ struct Channels: View { channelKey = key positionsEnabled = false preciseLocation = false - positionPrecision = 13 + positionPrecision = 0 uplink = false downlink = false - hasChanges = true let newChannel = ChannelEntity(context: context) newChannel.id = channelIndex @@ -274,6 +272,7 @@ struct Channels: View { newChannel.psk = Data(base64Encoded: channelKey) ?? Data() newChannel.positionPrecision = Int32(positionPrecision) selectedChannel = newChannel + hasChanges = true } label: { Label("Add Channel", systemImage: "plus.square") diff --git a/Meshtastic/Views/Settings/Channels/ChannelForm.swift b/Meshtastic/Views/Settings/Channels/ChannelForm.swift index d0a111e5..d21e0f42 100644 --- a/Meshtastic/Views/Settings/Channels/ChannelForm.swift +++ b/Meshtastic/Views/Settings/Channels/ChannelForm.swift @@ -130,7 +130,6 @@ struct ChannelForm: View { } Section(header: Text("position")) { - VStack(alignment: .leading) { Toggle(isOn: $positionsEnabled) { Label(channelRole == 1 ? "Positions Enabled" : "Allow Position Requests", systemImage: positionsEnabled ? "mappin" : "mappin.slash") @@ -140,20 +139,21 @@ struct ChannelForm: View { } if positionsEnabled { - VStack(alignment: .leading) { - Toggle(isOn: $preciseLocation) { - Label("Precise Location", systemImage: "scope") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .disabled(!supportedVersion) - .listRowSeparator(.visible) - .onChange(of: preciseLocation) { pl in - if pl == false { - positionPrecision = 13 + if channelKey != "AQ==" && channelRole > 0 { + VStack(alignment: .leading) { + Toggle(isOn: $preciseLocation) { + Label("Precise Location", systemImage: "scope") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .disabled(!supportedVersion) + .listRowSeparator(.visible) + .onChange(of: preciseLocation) { pl in + if pl == false { + positionPrecision = 13 + } } } } - if !preciseLocation { VStack(alignment: .leading) { Label("Approximate Location", systemImage: "location.slash.circle.fill") @@ -213,7 +213,7 @@ struct ChannelForm: View { } .onChange(of: preciseLocation) { loc in if loc == true { - if channelKey == "AQ==" && channelRole == 0 { + if channelKey == "AQ==" { preciseLocation = false } else { positionPrecision = 32 diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 75132df1..46211dac 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -19,7 +19,6 @@ struct BluetoothConfig: View { @State var mode = 0 @State var fixedPin = "123456" @State var shortPin = false - @State var deviceLoggingEnabled = false var pinLength: Int = 6 let numberFormatter: NumberFormatter = { let formatter = NumberFormatter() @@ -70,10 +69,6 @@ struct BluetoothConfig: View { .foregroundColor(.red) } } - Toggle(isOn: $deviceLoggingEnabled) { - Label("Device Logging Enabled", systemImage: "ladybug") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } .disabled(self.bleManager.connectedPeripheral == nil || node?.bluetoothConfig == nil) @@ -85,7 +80,6 @@ struct BluetoothConfig: View { bc.enabled = enabled bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin bc.fixedPin = UInt32(fixedPin) ?? 123456 - bc.deviceLoggingEnabled = deviceLoggingEnabled let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -106,13 +100,24 @@ struct BluetoothConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a BluetoothConfig from the remote node before allowing changes - if let connectedPeripheral = bleManager.connectedPeripheral, let node, node.bluetoothConfig == nil { + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty bluetooth config") let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) if let connectedNode { - _ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.bluetoothConfig == nil { + _ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } @@ -125,15 +130,11 @@ struct BluetoothConfig: View { .onChange(of: fixedPin) { newFixedPin in if newFixedPin != String(node?.bluetoothConfig?.fixedPin ?? -1) { hasChanges = true } } - .onChange(of: deviceLoggingEnabled) { - if $0 != node?.bluetoothConfig?.deviceLoggingEnabled { hasChanges = true } - } } func setBluetoothValues() { self.enabled = node?.bluetoothConfig?.enabled ?? true self.mode = Int(node?.bluetoothConfig?.mode ?? 0) self.fixedPin = String(node?.bluetoothConfig?.fixedPin ?? 123456) - self.deviceLoggingEnabled = node?.bluetoothConfig?.deviceLoggingEnabled ?? false self.hasChanges = false } } diff --git a/Meshtastic/Views/Settings/Config/ConfigHeader.swift b/Meshtastic/Views/Settings/Config/ConfigHeader.swift index 3f59a01a..d1af3a6a 100644 --- a/Meshtastic/Views/Settings/Config/ConfigHeader.swift +++ b/Meshtastic/Views/Settings/Config/ConfigHeader.swift @@ -17,8 +17,9 @@ struct ConfigHeader: View { } else if node != nil && node?.num ?? 0 != bleManager.connectedPeripheral?.num ?? 0 { // Let users know what is going on if they are using remote admin and don't have the config yet - if node?[keyPath: config] == nil { - Text("\(title) config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log.") + let expiration = node?.sessionExpiration ?? Date() + if node?[keyPath: config] == nil || expiration < node?.sessionExpiration ?? Date() { + Text("\(title) config data was requested over the admin channel but no response has been returned from the remote node.") .font(.callout) .foregroundColor(.orange) } else { diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 13d3aa83..dc17446e 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -29,7 +29,6 @@ struct DeviceConfig: View { @State var nodeInfoBroadcastSecs = 10800 @State var doubleTapAsButtonPress = false @State var ledHeartbeatEnabled = true - @State var isManaged = false @State var tzdef = "" var body: some View { @@ -62,12 +61,6 @@ struct DeviceConfig: View { } .pickerStyle(DefaultPickerStyle()) - Toggle(isOn: $isManaged) { - Label("Managed Device", systemImage: "gearshape.arrow.triangle.2.circlepath") - Text("Enabling Managed mode will restrict access to all radio configurations, such as short/long names, regions, channels, modules, etc. and will only be accessible through the Admin channel. To avoid being locked out, make sure the Admin channel is working properly before enabling it.") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Picker("Node Info Broadcast Interval", selection: $nodeInfoBroadcastSecs ) { ForEach(UpdateIntervals.allCases) { ui in if ui.rawValue >= 3600 { @@ -213,13 +206,8 @@ struct DeviceConfig: View { dc.rebroadcastMode = RebroadcastModes(rawValue: rebroadcastMode)?.protoEnumValue() ?? RebroadcastModes.all.protoEnumValue() dc.nodeInfoBroadcastSecs = UInt32(nodeInfoBroadcastSecs) dc.doubleTapAsButtonPress = doubleTapAsButtonPress - dc.isManaged = isManaged dc.tzdef = tzdef dc.ledHeartbeatDisabled = !ledHeartbeatEnabled - if isManaged { - serialEnabled = false - debugLogEnabled = false - } let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -242,13 +230,26 @@ struct DeviceConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a DeviceConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.deviceConfig == nil { + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty device config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context) - if node != nil && connectedNode != nil && connectedNode?.user != nil { - _ = bleManager.requestDeviceConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.deviceConfig == nil { + _ = bleManager.requestDeviceConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + if node.deviceConfig == nil { + /// Legacy Administration + _ = bleManager.requestDeviceConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } + } } } } @@ -276,9 +277,6 @@ struct DeviceConfig: View { .onChange(of: doubleTapAsButtonPress) { if $0 != node?.deviceConfig?.doubleTapAsButtonPress { hasChanges = true } } - .onChange(of: isManaged) { - if $0 != node?.deviceConfig?.isManaged { hasChanges = true } - } .onChange(of: tzdef) { newTzdef in if newTzdef != node?.deviceConfig?.tzdef { hasChanges = true } } @@ -301,7 +299,6 @@ struct DeviceConfig: View { } self.doubleTapAsButtonPress = node?.deviceConfig?.doubleTapAsButtonPress ?? false self.ledHeartbeatEnabled = node?.deviceConfig?.ledHeartbeatEnabled ?? true - self.isManaged = node?.deviceConfig?.isManaged ?? false self.tzdef = node?.deviceConfig?.tzdef ?? "" if self.tzdef.isEmpty { self.tzdef = TimeZone.current.posixDescription diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 1f3f6de4..1e9781a6 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -163,13 +163,24 @@ struct DisplayConfig: View { ) } ) - .onAppear { - // Need to request a LoRaConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.displayConfig == nil { + .onFirstAppear { + // Need to request a DisplayConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty display config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestDisplayConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.displayConfig == nil { + _ = bleManager.requestDisplayConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestDisplayConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index dad7c1a4..43729f86 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -232,13 +232,24 @@ struct LoRaConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a LoRaConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.loRaConfig == nil { + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty lora config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestLoRaConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.loRaConfig == nil { + _ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift b/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift index 76c98752..fe3faa51 100644 --- a/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift @@ -6,6 +6,7 @@ // import MeshtasticProtobufs import SwiftUI +import OSLog @available(iOS 17.0, macOS 14.0, *) struct AmbientLightingConfig: View { @@ -85,12 +86,24 @@ struct AmbientLightingConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a Ambient Lighting Config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.ambientLightingConfig == nil { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty ambient lighting config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.ambientLightingConfig == nil { + _ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index b4ec0b63..5b94e8c3 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -233,13 +233,24 @@ struct CannedMessagesConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a CannedMessagesModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.cannedMessageConfig == nil { - Logger.mesh.info("empty canned messages module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty canned message config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.cannedMessageConfig == nil { + _ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift index 1ff1ed86..e67898eb 100644 --- a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift @@ -189,13 +189,24 @@ struct DetectionSensorConfig: View { ) } ) - .onAppear { - // Need to request a Detection Sensor Module Config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.detectionSensorConfig == nil { - Logger.mesh.info("empty detection sensor module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + .onFirstAppear { + // Need to request a DetectionSensorModuleConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty detection sensor config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.detectionSensorConfig == nil { + _ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 32cacbd3..57c3a672 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -199,13 +199,24 @@ struct ExternalNotificationConfig: View { ) } ) - .onAppear { - // Need to request a TelemetryModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.externalNotificationConfig == nil { - Logger.mesh.info("empty external notification module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + .onFirstAppear { + // Need to request a ExternalNotificationModuleConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty external notificaiton module config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.externalNotificationConfig == nil { + _ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } @@ -279,7 +290,7 @@ struct ExternalNotificationConfig: View { if newNagTimeout != node!.externalNotificationConfig!.nagTimeout { hasChanges = true } } } - .onChange(of: useI2SAsBuzzer) { + .onChange(of: useI2SAsBuzzer) { if let val = node?.externalNotificationConfig?.useI2SAsBuzzer { hasChanges = $0 != val } diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 8ef411ea..d6cbbf2b 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -347,13 +347,24 @@ struct MQTTConfig: View { if newMapPublishIntervalSecs != node!.mqttConfig!.mapPublishIntervalSecs { hasChanges = true } } } - .onAppear { - // Need to request a TelemetryModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.mqttConfig == nil { + .onFirstAppear { + // Need to request a MqttModuleConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty mqtt module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestMqttModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.mqttConfig == nil { + _ = bleManager.requestMqttModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestMqttModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/PaxCounterConfig.swift b/Meshtastic/Views/Settings/Config/Module/PaxCounterConfig.swift index 6e7bef08..1141bc7d 100644 --- a/Meshtastic/Views/Settings/Config/Module/PaxCounterConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/PaxCounterConfig.swift @@ -7,6 +7,7 @@ import MeshtasticProtobufs import SwiftUI +import OSLog struct PaxCounterConfig: View { @Environment(\.managedObjectContext) private var context @@ -57,12 +58,24 @@ struct PaxCounterConfig: View { name: "\(bleManager.connectedPeripheral?.shortName ?? "?")" ) }) - .onAppear { - // Need to request a PAX Counter module config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.paxCounterConfig == nil { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestPaxCounterModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + .onFirstAppear { + // Need to request a PaxCounterModuleConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty pax counter module config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.paxCounterConfig == nil { + _ = bleManager.requestPaxCounterModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestPaxCounterModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 001ad2b1..872294d8 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -81,13 +81,24 @@ struct RangeTestConfig: View { ) } ) - .onAppear { - // Need to request a RangeTestModule Config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.rangeTestConfig == nil { - Logger.mesh.debug("empty range test module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + .onFirstAppear { + // Need to request a RangeTestModuleConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty range test module config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.rangeTestConfig == nil { + _ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift b/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift index 95f237a3..46b02848 100644 --- a/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift @@ -6,6 +6,7 @@ // import SwiftUI +import OSLog struct RtttlConfig: View { @Environment(\.managedObjectContext) var context @@ -71,12 +72,24 @@ struct RtttlConfig: View { ) } ) - .onAppear { - // Need to request a Rtttl Config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && (node?.rtttlConfig == nil || node?.rtttlConfig?.ringtone?.count ?? 0 == 0) { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestRtttlConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + .onFirstAppear { + // Need to request a RtttlConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty range test module config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.rtttlConfig == nil { + _ = bleManager.requestRtttlConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestRtttlConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index c7243a87..893ddca1 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -136,20 +136,31 @@ struct SerialConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a SerialModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.serialConfig == nil { - Logger.mesh.debug("empty serial module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestSerialModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty serial module config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.serialConfig == nil { + _ = bleManager.requestSerialModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestSerialModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } .onChange(of: enabled) { if $0 != node?.serialConfig?.enabled { hasChanges = true } } - .onChange(of: echo) { + .onChange(of: echo) { if $0 != node?.serialConfig?.echo { hasChanges = true } } .onChange(of: rxd) { newRxd in diff --git a/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift b/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift index 4829a5fa..8fff8979 100644 --- a/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift @@ -146,13 +146,24 @@ struct StoreForwardConfig: View { ) } ) - .onAppear { - // Need to request a Detection Sensor Module Config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.storeForwardConfig == nil { - Logger.mesh.debug("empty store and forward module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestStoreAndForwardModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + .onFirstAppear { + // Need to request a StoreForwardModuleConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty store & forward module config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.storeForwardConfig == nil { + _ = bleManager.requestStoreAndForwardModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestStoreAndForwardModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index df811677..afef5727 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -134,13 +134,24 @@ struct TelemetryConfig: View { ) } ) - .onAppear { + .onFirstAppear { // Need to request a TelemetryModuleConfig from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.telemetryConfig == nil { + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty telemetry module config") - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration && node.num != connectedNode.num { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.telemetryConfig == nil { + _ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 0554c766..928bad57 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -128,6 +128,27 @@ struct NetworkConfig: View { } } } + .onFirstAppear { + // Need to request a NetworkConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty network config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.networkConfig == nil { + _ = bleManager.requestNetworkConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestNetworkConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } + } + } + } .onChange(of: wifiEnabled) { if $0 != node?.networkConfig?.wifiEnabled { hasChanges = true } } diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 0f71b379..4497c1f7 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -376,18 +376,25 @@ struct PositionConfig: View { ) } ) - .onAppear { + .onFirstAppear { supportedVersion = bleManager.connectedVersion == "0.0.0" || self.minimumVersion.compare(bleManager.connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(bleManager.connectedVersion, options: .numeric) == .orderedSame - // Need to request a PositionConfig from the remote node before allowing changes - if let connectedPeripheral = bleManager.connectedPeripheral, node?.positionConfig == nil { + // Need to request a NetworkConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { Logger.mesh.info("empty position config") let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) - if let node, let connectedNode { - _ = bleManager.requestPositionConfig( - fromUser: connectedNode.user!, - toUser: node.user!, - adminIndex: connectedNode.myInfo?.adminIndex ?? 0 - ) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.positionConfig == nil { + _ = bleManager.requestPositionConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestPositionConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/PowerConfig.swift b/Meshtastic/Views/Settings/Config/PowerConfig.swift index 42b6a534..9ba0fe0a 100644 --- a/Meshtastic/Views/Settings/Config/PowerConfig.swift +++ b/Meshtastic/Views/Settings/Config/PowerConfig.swift @@ -1,5 +1,6 @@ import SwiftUI import MeshtasticProtobufs +import OSLog struct PowerConfig: View { @Environment(\.managedObjectContext) private var context @@ -117,7 +118,7 @@ struct PowerConfig: View { .font(.subheadline) } } - .onAppear { + .onFirstAppear { Api().loadDeviceHardwareData { (hw) in for device in hw { let currentHardware = node?.user?.hwModel ?? "UNSET" @@ -127,11 +128,23 @@ struct PowerConfig: View { } } } - // Need to request a Power config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.powerConfig == nil { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestPowerConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + // Need to request a NetworkConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty power config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.powerConfig == nil { + _ = bleManager.requestPowerConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + /// Legacy Administration + _ = bleManager.requestPowerConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } } } } diff --git a/Meshtastic/Views/Settings/Config/SecurityConfig.swift b/Meshtastic/Views/Settings/Config/SecurityConfig.swift index e07ed945..e58de058 100644 --- a/Meshtastic/Views/Settings/Config/SecurityConfig.swift +++ b/Meshtastic/Views/Settings/Config/SecurityConfig.swift @@ -30,7 +30,6 @@ struct SecurityConfig: View { @State var isManaged = false @State var serialEnabled = false @State var debugLogApiEnabled = false - @State var bluetoothLoggingEnabled = false @State var adminChannelEnabled = false var body: some View { @@ -71,10 +70,14 @@ struct SecurityConfig: View { } } Section(header: Text("Logs")) { - Toggle(isOn: $bluetoothLoggingEnabled) { - Label("Bluetooth Logs", systemImage: "dot.radiowaves.right") - Text("View and export position-redacted device logs over Bluetooth") - Link("View Logs", destination: URL(string: "meshtastic:///settings/debugLogs")!) + Toggle(isOn: $serialEnabled) { + Label("Serial Console", systemImage: "terminal") + Text("Serial Console over the Stream API.") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Toggle(isOn: $debugLogApiEnabled) { + Label("Debug Logs", systemImage: "ant.fill") + Text("Output live debug logging over serial, view and export position-redacted device logs over Bluetooth.") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } @@ -82,7 +85,7 @@ struct SecurityConfig: View { if adminKey.length > 0 || adminChannelEnabled { Toggle(isOn: $isManaged) { Label("Managed Device", systemImage: "gearshape.arrow.triangle.2.circlepath") - Text("Device is managed by a mesh administrator.") + Text("Device is managed by a mesh administrator, the user is unable to access any of the device settings.") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } @@ -92,20 +95,6 @@ struct SecurityConfig: View { } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } - Section(header: Text("Developer")) { - Toggle(isOn: $serialEnabled) { - Label("Serial Console", systemImage: "terminal") - Text("Serial Console over the Stream API.") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - if serialEnabled { - Toggle(isOn: $debugLogApiEnabled) { - Label("Serial Debug Logs", systemImage: "ant.fill") - Text("Output live debug logging over serial.") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - } - } } } .scrollDismissesKeyboard(.immediately) @@ -126,9 +115,6 @@ struct SecurityConfig: View { .onChange(of: debugLogApiEnabled) { if $0 != node?.securityConfig?.debugLogApiEnabled { hasChanges = true } } - .onChange(of: bluetoothLoggingEnabled) { - if $0 != node?.securityConfig?.bluetoothLoggingEnabled { hasChanges = true } - } .onChange(of: adminChannelEnabled) { if $0 != node?.securityConfig?.adminChannelEnabled { hasChanges = true } } @@ -162,11 +148,25 @@ struct SecurityConfig: View { hasChanges = true } .onFirstAppear { - // Need to request a Power config from the remote node before allowing changes - if bleManager.connectedPeripheral != nil && node?.securityConfig == nil { - let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) - if node != nil && connectedNode != nil { - _ = bleManager.requestSecurityConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + // Need to request a DeviceConfig from the remote node before allowing changes + if let connectedPeripheral = bleManager.connectedPeripheral, let node { + Logger.mesh.info("empty security config") + let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context) + if let connectedNode { + if node.num != connectedNode.num { + if UserDefaults.enableAdministration { + /// 2.5 Administration with session passkey + let expiration = node.sessionExpiration ?? Date() + if expiration < Date() || node.securityConfig == nil { + _ = bleManager.requestSecurityConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } else { + if node.deviceConfig == nil { + /// Legacy Administration + _ = bleManager.requestSecurityConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0) + } + } + } } } } @@ -190,7 +190,6 @@ struct SecurityConfig: View { config.isManaged = isManaged config.serialEnabled = serialEnabled config.debugLogApiEnabled = debugLogApiEnabled - config.bluetoothLoggingEnabled = bluetoothLoggingEnabled config.adminChannelEnabled = adminChannelEnabled let adminMessageId = bleManager.saveSecurityConfig( @@ -215,7 +214,6 @@ struct SecurityConfig: View { self.isManaged = node?.securityConfig?.isManaged ?? false self.serialEnabled = node?.securityConfig?.serialEnabled ?? false self.debugLogApiEnabled = node?.securityConfig?.debugLogApiEnabled ?? false - self.bluetoothLoggingEnabled = node?.securityConfig?.bluetoothLoggingEnabled ?? false self.adminChannelEnabled = node?.securityConfig?.adminChannelEnabled ?? false self.hasChanges = false } diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index e88816ab..481fb29d 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -346,7 +346,7 @@ struct Settings: View { if bleManager.connectedPeripheral != nil { Section("Configure") { if node?.canRemoteAdmin ?? false { - Picker("Configuring Node", selection: $selectedNode) { + Picker("Node", selection: $selectedNode) { if selectedNode == 0 { Text("Connect to a Node").tag(0) } @@ -365,6 +365,7 @@ struct Settings: View { } icon: { Image(systemName: "av.remote") } + .font(.caption2) .tag(Int(node.num)) } else if !UserDefaults.enableAdministration && node.metadata != nil { /// Nodes using the old admin system Label { @@ -390,8 +391,7 @@ struct Settings: View { } } } - .pickerStyle(.automatic) - .labelsHidden() + .pickerStyle(.navigationLink) .onChange(of: selectedNode) { newValue in if selectedNode > 0 { let node = nodes.first(where: { $0.num == newValue })