Merge pull request #914 from meshtastic/2.5-admin-updates

2.5 Administration with session passkey
This commit is contained in:
Garth Vander Houwen 2024-09-06 08:00:16 -07:00 committed by GitHub
commit 2788caea2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 413 additions and 224 deletions

View file

@ -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" : {

View file

@ -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 */,

View file

@ -95,7 +95,7 @@ enum RoutingError: Int, CaseIterable, Identifiable {
case .notAuthorized:
return true
case .pkiFailed:
return false
return true
case .pkiUnknownPubkey:
return false
}

View file

@ -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

View file

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

View file

@ -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)
}

View file

@ -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

View file

@ -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<UserEntity>

View file

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

View file

@ -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")

View file

@ -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

View file

@ -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
}
}

View file

@ -17,8 +17,9 @@ struct ConfigHeader<T>: 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 {

View file

@ -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

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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 }
}

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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
}

View file

@ -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 })