diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 6f8a654a..7eeb0ddf 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -291,7 +291,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } } - func requestDeviceMetadata(fromUser: UserEntity, toUser: UserEntity, context: NSManagedObjectContext) -> Int64 { + func requestDeviceMetadata(fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32, context: NSManagedObjectContext) -> Int64 { guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return 0 } @@ -303,14 +303,16 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { adminPacket.getDeviceMetadataRequest = true var meshPacket: MeshPacket = MeshPacket() meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 { var adminPacket = AdminMessage() adminPacket.setOwner = config var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(connectedPeripheral.num) - meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.channel = UInt32(adminIndex) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchedNodeRequest.predicate = NSPredicate(format: "num == %lld", fromNum) + + do { + + let fetchedNode = try context.fetch(fetchedNodeRequest) as! [NodeInfoEntity] + if fetchedNode.count == 1 { + let newMetadata = DeviceMetadataEntity(context: context) + newMetadata.firmwareVersion = metadata.firmwareVersion + newMetadata.deviceStateVersion = Int32(metadata.deviceStateVersion) + newMetadata.canShutdown = metadata.canShutdown + newMetadata.hasWifi = metadata.hasWifi_p + newMetadata.hasBluetooth = metadata.hasBluetooth_p + newMetadata.hasEthernet = metadata.hasEthernet_p + newMetadata.role = Int32(metadata.role.rawValue) + newMetadata.positionFlags = Int32(metadata.positionFlags) + + fetchedNode[0].metadata = newMetadata + + do { + try context.save() + } catch { + print("Failed to save device metadata") + } + print("💾 Updated Device Metadata from Admin App Packet For: \(fromNum)") + } + } catch { + context.rollback() + let nsError = error as NSError + print("💥 Error Saving MyInfo Channel from ADMIN_APP \(nsError)") + } + } +} + func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? { let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(nodeInfo.num)) @@ -986,8 +1030,12 @@ func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { + MeshLogger.log("🕸️ MESH PACKET received for Admin App \(try! packet.decoded.jsonString())") + if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) { + MeshLogger.log("🕸️ MESH PACKET received for Admin App \(adminMessage.getDeviceMetadataResponse)") + if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getCannedMessageModuleMessagesResponse(adminMessage.getCannedMessageModuleMessagesResponse) { if let cmmc = try? CannedMessageModuleConfig(serializedData: packet.decoded.payload) { @@ -1022,14 +1070,16 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { } } } - } - - else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getChannelResponse(adminMessage.getChannelResponse) { - + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getChannelResponse(adminMessage.getChannelResponse) { channelPacket(channel: adminMessage.getChannelResponse, fromNum: Int64(packet.from), context: context) + } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getDeviceMetadataResponse(adminMessage.getDeviceMetadataResponse) { + deviceMetadataPacket(metadata: adminMessage.getDeviceMetadataResponse, fromNum: Int64(packet.from), context: context) + + print(try! adminMessage.getDeviceMetadataResponse.jsonString()) + + } } - } func positionPacket (packet: MeshPacket, context: NSManagedObjectContext) { diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents index f658ef98..cabe58ef 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents @@ -135,6 +135,7 @@ + diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 19e219b4..649b5064 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -98,7 +98,9 @@ struct MapViewSwiftUI: UIViewRepresentable { self.parent = parent super.init() self.longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressHandler)) - self.longPressRecognizer.minimumPressDuration = 0.3 + self.longPressRecognizer.minimumPressDuration = 0.4 + //self.longPressRecognizer.numberOfTouchesRequired = 1 + //self.longPressRecognizer.cancelsTouchesInView = true self.longPressRecognizer.delegate = self self.parent.mapView.addGestureRecognizer(longPressRecognizer) self.overlays = [] @@ -163,13 +165,15 @@ struct MapViewSwiftUI: UIViewRepresentable { let location = longPressRecognizer.location(in: self.parent.mapView) // Map Coordinate - CLLocationCoordinate2D let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView) - parent.onLongPress(coordinate, 0) // Add annotation: - let annotation = MKPointAnnotation() - annotation.title = "Dropped Pin" - annotation.coordinate = coordinate - parent.mapView.addAnnotation(annotation) - UINotificationFeedbackGenerator().notificationOccurred(.success) + if coordinate.longitude != LocationHelper.DefaultLocation.longitude && coordinate.latitude != LocationHelper.DefaultLocation.latitude { + let annotation = MKPointAnnotation() + annotation.title = "Dropped Pin" + annotation.coordinate = coordinate + parent.mapView.addAnnotation(annotation) + UINotificationFeedbackGenerator().notificationOccurred(.success) + parent.onLongPress(coordinate, 0) + } } public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index abd8ed10..ccd84b5e 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -397,13 +397,12 @@ struct NodeDetail: View { } .navigationBarTitle(String(node.user?.longName ?? NSLocalizedString("unknown", comment: "")), displayMode: .inline) .navigationBarItems(trailing: - ZStack { + ZStack { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") - } - ) + }) .onAppear { self.bleManager.context = context } diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index d4728fc1..e3b953ca 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -12,22 +12,59 @@ struct Settings: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings - @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], animation: .default) + @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "user.longName", ascending: true)], animation: .default) private var nodes: FetchedResults + @State private var selectedNode: Int = 0 + @State private var connectedNodeNum: Int = 0 + @State private var initialLoad: Bool = true var body: some View { NavigationSplitView { List { - let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0 - NavigationLink() { AppSettings() } label: { - Image(systemName: "gearshape") .symbolRenderingMode(.hierarchical) Text("app.settings") } + Section("Configure") { + Picker("Configuring Node", selection: $selectedNode) { + if connectedNodeNum == 0 { + Text("Connect to a Node").tag(0) + } + ForEach(nodes) { node in + if node.num == bleManager.connectedPeripheral?.num ?? 0 { + Text("BLE Config: \(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))") + .tag(Int(node.num)) + } else if node.metadata != nil { + Text("Remote Config: \(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))") + .tag(Int(node.num)) + } else { + Text("Request Admin: \(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))") + .tag(Int(node.num)) + } + } + } + .pickerStyle(.menu) + .labelsHidden() + .onChange(of: selectedNode) { newValue in + if selectedNode > 0 { + let node = nodes.first(where: { $0.num == newValue }) + let connectedNode = nodes.first(where: { $0.num == connectedNodeNum }) + connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0) + + if node?.metadata == nil && node!.num != connectedNodeNum { + let adminMessageId = bleManager.requestDeviceMetadata(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode!.myInfo!.adminIndex, context: context) + + if adminMessageId > 0 { + print("Saved node metadata") + } + } + } + } + } + Section("radio.configuration") { NavigationLink { @@ -37,85 +74,87 @@ struct Settings: View { .symbolRenderingMode(.hierarchical) Text("share.channels") } - + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - UserConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + UserConfig(node: nodes.first(where: { $0.num == selectedNode }), connectedNode: nodes.first(where: { $0.num == connectedNodeNum })) } label: { Image(systemName: "person.crop.rectangle.fill") .symbolRenderingMode(.hierarchical) Text("user") } + .disabled(selectedNode == 0) NavigationLink() { - - LoRaConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + LoRaConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { - Image(systemName: "dot.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical) Text("lora") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink() { - Channels(node: nodes.first(where: { $0.num == connectedNodeNum })) } label: { - Image(systemName: "fibrechannel") .symbolRenderingMode(.hierarchical) - Text("channels") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink() { - - BluetoothConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + BluetoothConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "antenna.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical) Text("bluetooth") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + DeviceConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "flipphone") .symbolRenderingMode(.hierarchical) Text("device") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - DisplayConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + DisplayConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "display") .symbolRenderingMode(.hierarchical) Text("display") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - NetworkConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + NetworkConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "network") .symbolRenderingMode(.hierarchical) Text("network") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + PositionConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "location") .symbolRenderingMode(.hierarchical) Text("position") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) } Section("module.configuration") { NavigationLink { - CannedMessagesConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + CannedMessagesConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "list.bullet.rectangle.fill") @@ -123,42 +162,52 @@ struct Settings: View { Text("canned.messages") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) NavigationLink { - ExternalNotificationConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + ExternalNotificationConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "megaphone") .symbolRenderingMode(.hierarchical) Text("external.notification") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) + NavigationLink { - MQTTConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + MQTTConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "dot.radiowaves.right") .symbolRenderingMode(.hierarchical) Text("mqtt") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) + NavigationLink { - RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + RangeTestConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "point.3.connected.trianglepath.dotted") .symbolRenderingMode(.hierarchical) Text("range.test") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) + NavigationLink { - SerialConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + SerialConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "terminal") .symbolRenderingMode(.hierarchical) Text("serial") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) + NavigationLink { - TelemetryConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + TelemetryConfig(node: nodes.first(where: { $0.num == selectedNode })) } label: { Image(systemName: "chart.xyaxis.line") .symbolRenderingMode(.hierarchical) Text("telemetry") } + .disabled(selectedNode > 0 && selectedNode != connectedNodeNum) } Section(header: Text("logging")) { NavigationLink { @@ -169,7 +218,7 @@ struct Settings: View { Text("mesh.log") } NavigationLink { - let connectedNode = nodes.first(where: { $0.num == connectedNodeNum }) + let connectedNode = nodes.first(where: { $0.num == selectedNode }) AdminMessageList(user: connectedNode?.user) } label: { Image(systemName: "building.columns") @@ -191,6 +240,13 @@ struct Settings: View { .onAppear { self.bleManager.context = context self.bleManager.userSettings = userSettings + if initialLoad { + selectedNode = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0) + initialLoad = false + } + connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0) + //let node = nodes.first(where: { $0.num == connectedNodeNum }) + //selectedNode = node?.num ?? 0 } .listStyle(GroupedListStyle()) .navigationTitle("settings") diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index f372499e..4b3dedc1 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -13,6 +13,7 @@ struct UserConfig: View { @Environment(\.dismiss) private var goBack var node: NodeInfoEntity? + var connectedNode: NodeInfoEntity? @State private var isPresentingFactoryResetConfirm: Bool = false @State private var isPresentingSaveConfirm: Bool = false @@ -84,11 +85,11 @@ struct UserConfig: View { isPresented: $isPresentingSaveConfirm, titleVisibility: .visible ) { - Button("Save User Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + Button("Save User Config to \(node?.user?.longName ?? "Unknown")?") { var u = User() u.shortName = shortName u.longName = longName - let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) if adminMessageId > 0 { hasChanges = false goBack()