From 35ce87c6682aed91c20a9d466ba84c6a547abb01 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 27 Jan 2023 20:22:17 -0800 Subject: [PATCH 1/4] Weather kit for the node details map --- Meshtastic/Meshtastic.entitlements | 2 + .../Persistence/PositionEntityExtension.swift | 9 +++ Meshtastic/Views/Nodes/NodeDetail.swift | 78 +++++++++++++++++-- Meshtastic/Views/Nodes/NodeMap.swift | 2 + 4 files changed, 83 insertions(+), 8 deletions(-) diff --git a/Meshtastic/Meshtastic.entitlements b/Meshtastic/Meshtastic.entitlements index 5c8b6ee4..26f4ce01 100644 --- a/Meshtastic/Meshtastic.entitlements +++ b/Meshtastic/Meshtastic.entitlements @@ -6,6 +6,8 @@ applinks:meshtastic.org/e/* + com.apple.developer.weatherkit + com.apple.security.app-sandbox com.apple.security.device.bluetooth diff --git a/Meshtastic/Persistence/PositionEntityExtension.swift b/Meshtastic/Persistence/PositionEntityExtension.swift index ca7bd3e9..23ec54b2 100644 --- a/Meshtastic/Persistence/PositionEntityExtension.swift +++ b/Meshtastic/Persistence/PositionEntityExtension.swift @@ -32,6 +32,15 @@ extension PositionEntity { } } + var nodeLocation: CLLocation? { + if latitudeI != 0 && longitudeI != 0 { + let location = CLLocation(latitude: latitude!, longitude: longitude!) + return location + } else { + return nil + } + } + var annotaton: MKPointAnnotation { let pointAnn = MKPointAnnotation() if nodeCoordinate != nil { diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 032c72be..f19b57ca 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -4,6 +4,7 @@ */ import SwiftUI +import WeatherKit import MapKit import CoreLocation @@ -11,6 +12,7 @@ struct NodeDetail: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + @Environment(\.colorScheme) var colorScheme: ColorScheme @State var satsInView = 0 @State private var mapType: MKMapType = .standard @State var waypointCoordinate: CLLocationCoordinate2D? @@ -35,6 +37,14 @@ struct NodeDetail: View { ), animation: .easeIn) private var waypoints: FetchedResults + /// The current weather condition for the city. + @State private var condition: WeatherCondition? + @State private var temperature: Measurement? + @State private var symbolName: String = "cloud.fill" + + @State private var attributionLink: URL? + @State private var attributionLogo: URL? + var body: some View { let hwModelString = node.user?.hwModel ?? "UNSET" @@ -63,17 +73,31 @@ struct NodeDetail: View { overlays: self.overlays ) - VStack { + VStack (alignment: .leading) { Spacer() - Text(mostRecent.satsInView > 0 ? "Sats: \(mostRecent.satsInView)" : " ") - .font(.caption) - .offset(y: 20) - Picker("Map Type", selection: $mapType) { - ForEach(MeshMapType.allCases) { map in - Text(map.description).tag(map.MKMapTypeValue()) + HStack (alignment: .bottom, spacing: 1) { + + Picker("Map Type", selection: $mapType) { + ForEach(MeshMapType.allCases) { map in + Text(map.description).tag(map.MKMapTypeValue()) + } } + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .pickerStyle(.menu) + .padding(5) + VStack { + if mostRecent.satsInView > 0 { + Text("Sats: \(mostRecent.satsInView)") + .font(.caption) + .padding(.bottom, 1) + } + Label(temperature?.formatted() ?? "??", systemImage: symbolName) + .font(.caption) + } + .padding(10) + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .padding(5) } - .pickerStyle(.menu) } } .ignoresSafeArea(.all, edges: [.top, .leading, .trailing]) @@ -332,6 +356,20 @@ struct NodeDetail: View { } Divider() } + VStack { + AsyncImage(url: attributionLogo) { image in + image + .resizable() + .scaledToFit() + } placeholder: { + ProgressView() + .controlSize(.mini) + } + .frame(height: 15) + + Link("Other data sources", destination: attributionLink ?? URL(string: "https://weather-data.apple.com/legal-attribution.html")!) + } + .font(.footnote) } if self.bleManager.connectedPeripheral != nil && self.bleManager.connectedPeripheral.num == node.num && self.bleManager.connectedPeripheral.num == node.num { @@ -403,6 +441,30 @@ struct NodeDetail: View { .onAppear { self.bleManager.context = context } + .task(id: node.num) { + do { + + if node.positions?.count ?? 0 > 0 { + + let mostRecent = node.positions?.lastObject as! PositionEntity + + let weather = try await WeatherService.shared.weather(for: mostRecent.nodeLocation!) + condition = weather.currentWeather.condition + temperature = weather.currentWeather.temperature + symbolName = weather.currentWeather.symbolName + + let attribution = try await WeatherService.shared.attribution + attributionLink = attribution.legalPageURL + attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL + + } + + } catch { + print("Could not gather weather information...", error.localizedDescription) + condition = .clear + symbolName = "cloud.fill" + } + } } } } diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 99f21bca..b57c4a59 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -80,7 +80,9 @@ struct NodeMap: View { Text(map.description).tag(map.MKMapTypeValue()) } } + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) .pickerStyle(.menu) + .padding(.bottom, 5) } } .ignoresSafeArea(.all, edges: [.top, .leading, .trailing]) From dd6da2727e386b3c935a7e09694e666dabac5a9b Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 27 Jan 2023 20:23:20 -0800 Subject: [PATCH 2/4] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f3915cf1..09fdab3e 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1038,7 +1038,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.11; + MARKETING_VERSION = 2.0.12; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1071,7 +1071,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.11; + MARKETING_VERSION = 2.0.12; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From 5fdf97f7c531b64a29c9379c8a589af98e91739b Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 29 Jan 2023 00:16:17 -0800 Subject: [PATCH 3/4] Fix mac crashes --- Meshtastic.xcodeproj/project.pbxproj | 4 +-- Meshtastic/Helpers/BLEManager.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 32 ++++++++++--------- .../Settings/Config/BluetoothConfig.swift | 6 +++- .../Views/Settings/Config/LoRaConfig.swift | 2 +- Meshtastic/Views/Settings/Settings.swift | 27 ++++++++-------- 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 09fdab3e..f3915cf1 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1038,7 +1038,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.12; + MARKETING_VERSION = 2.0.11; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1071,7 +1071,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.12; + MARKETING_VERSION = 2.0.11; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 1ad90961..6f28a49c 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -862,7 +862,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { adminPacket.rebootSeconds = 5 var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.to = UInt32(toUser.num) meshPacket.from = UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. 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") -// } -// } -// } + 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") + } + } + } } } } @@ -114,7 +114,6 @@ struct Settings: View { Text("user") } .tag(SettingsSidebar.userConfig) - .disabled(selectedNode == 0) NavigationLink() { LoRaConfig(node: nodes.first(where: { $0.num == selectedNode }), connectedNode: nodes.first(where: { $0.num == connectedNodeNum })) From 0108080c8731a4af8ab050b5f46633616c263309 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 29 Jan 2023 08:28:44 -0800 Subject: [PATCH 4/4] update protos clean up config warnings --- Meshtastic.xcodeproj/project.pbxproj | 8 +- Meshtastic/Helpers/BLEManager.swift | 8 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 2 + .../contents | 294 ++++++++++++++++++ Meshtastic/Protobufs/config.pb.swift | 28 ++ Meshtastic/Views/Nodes/NodeDetail.swift | 4 +- .../Settings/Config/BluetoothConfig.swift | 2 +- .../Views/Settings/Config/LoRaConfig.swift | 2 +- 9 files changed, 336 insertions(+), 14 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f3915cf1..60e70e47 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -176,6 +176,7 @@ DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelEntityExtension.swift; sourceTree = ""; }; DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV2.xcdatamodel; sourceTree = ""; }; DD5D0A9B2931B9F200F7EA61 /* EthernetModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthernetModes.swift; sourceTree = ""; }; + DD5E51CC2986643400D21B61 /* MeshtasticDataModelV7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV7.xcdatamodel; sourceTree = ""; }; DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalNotificationConfig.swift; sourceTree = ""; }; DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = ""; }; DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = ""; }; @@ -1038,7 +1039,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.11; + MARKETING_VERSION = 2.0.12; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1071,7 +1072,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.11; + MARKETING_VERSION = 2.0.12; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1247,6 +1248,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD5E51CC2986643400D21B61 /* MeshtasticDataModelV7.xcdatamodel */, DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */, DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */, DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */, @@ -1254,7 +1256,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */; + currentVersion = DD5E51CC2986643400D21B61 /* MeshtasticDataModelV7.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 6f28a49c..f9335922 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -596,8 +596,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { if positionTimer != nil { RunLoop.current.add(positionTimer!, forMode: .common) } - } - + } return } @@ -736,13 +735,12 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { var success = false let fromNodeNum = UInt32(connectedPeripheral.num) - var waypointPacket = waypoint var meshPacket = MeshPacket() meshPacket.to = emptyNodeNum meshPacket.from = fromNodeNum meshPacket.wantAck = true var dataMessage = DataMessage() - dataMessage.payload = try! waypointPacket.serializedData() + dataMessage.payload = try! waypoint.serializedData() dataMessage.portnum = PortNum.waypointApp meshPacket.decoded = dataMessage var toRadio: ToRadio! @@ -758,7 +756,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { let wayPointEntity = getWaypoint(id: Int64(waypoint.id), context: context!) wayPointEntity.id = Int64(waypoint.id) - wayPointEntity.name = waypoint.name.count >= 1 ? waypointPacket.name : "Dropped Pin" + wayPointEntity.name = waypoint.name.count >= 1 ? waypoint.name : "Dropped Pin" wayPointEntity.longDescription = waypoint.description_p wayPointEntity.icon = Int64(waypoint.icon) wayPointEntity.latitudeI = waypoint.latitudeI diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index a13f6b2e..aab08b69 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV6.xcdatamodel + MeshtasticDataModelV7.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents index 6f82e5d0..48da2809 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents @@ -282,9 +282,11 @@ + + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents new file mode 100644 index 00000000..6f82e5d0 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index 29ec3be8..65e3587d 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -188,12 +188,24 @@ struct Config { /// Router device role. /// Mesh packets will prefer to be routed over this node. This node will not be used by client apps. /// The wifi/ble radios and the oled screen will be put to sleep. + /// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. case router // = 2 /// /// Router Client device role /// Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client. case routerClient // = 3 + + /// + /// Repeater device role + /// Mesh packets will simply be rebroadcasted over this node. Nodes under this role node will not originate NodeInfo, Position, Telemetry + /// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factory, and coding rate. + case repeater // = 4 + + /// + /// Tracker device role + /// Position Mesh packets for will be higher priority and sent more frequently by default. + case tracker // = 5 case UNRECOGNIZED(Int) init() { @@ -206,6 +218,8 @@ struct Config { case 1: self = .clientMute case 2: self = .router case 3: self = .routerClient + case 4: self = .repeater + case 5: self = .tracker default: self = .UNRECOGNIZED(rawValue) } } @@ -216,6 +230,8 @@ struct Config { case .clientMute: return 1 case .router: return 2 case .routerClient: return 3 + case .repeater: return 4 + case .tracker: return 5 case .UNRECOGNIZED(let i): return i } } @@ -1048,6 +1064,10 @@ struct Config { /// /// Short Range - Fast case shortFast // = 6 + + /// + /// Long Range - Moderately Fast + case longModerate // = 7 case UNRECOGNIZED(Int) init() { @@ -1063,6 +1083,7 @@ struct Config { case 4: self = .mediumFast case 5: self = .shortSlow case 6: self = .shortFast + case 7: self = .longModerate default: self = .UNRECOGNIZED(rawValue) } } @@ -1076,6 +1097,7 @@ struct Config { case .mediumFast: return 4 case .shortSlow: return 5 case .shortFast: return 6 + case .longModerate: return 7 case .UNRECOGNIZED(let i): return i } } @@ -1159,6 +1181,8 @@ extension Config.DeviceConfig.Role: CaseIterable { .clientMute, .router, .routerClient, + .repeater, + .tracker, ] } @@ -1259,6 +1283,7 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable { .mediumFast, .shortSlow, .shortFast, + .longModerate, ] } @@ -1520,6 +1545,8 @@ extension Config.DeviceConfig.Role: SwiftProtobuf._ProtoNameProviding { 1: .same(proto: "CLIENT_MUTE"), 2: .same(proto: "ROUTER"), 3: .same(proto: "ROUTER_CLIENT"), + 4: .same(proto: "REPEATER"), + 5: .same(proto: "TRACKER"), ] } @@ -2084,6 +2111,7 @@ extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding { 4: .same(proto: "MEDIUM_FAST"), 5: .same(proto: "SHORT_SLOW"), 6: .same(proto: "SHORT_FAST"), + 7: .same(proto: "LONG_MODERATE"), ] } diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 715ae558..c00d475b 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -54,7 +54,6 @@ struct NodeDetail: View { VStack { if node.positions?.count ?? 0 > 0 { let mostRecent = node.positions?.lastObject as! PositionEntity - let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: mostRecent.latitude!, longitude: mostRecent.longitude!) ZStack { let annotations = node.positions?.array as! [PositionEntity] ZStack { @@ -362,10 +361,9 @@ struct NodeDetail: View { HStack { - if hwModelString == "TBEAM" || hwModelString == "TECHO" || hwModelString.contains("4631") { + if node.metadata != nil && node.metadata?.canShutdown ?? false { Button(action: { - showingShutdownConfirm = true }) { diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index f6a14e41..ffeb291d 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -135,7 +135,7 @@ struct BluetoothConfig: View { // Need to request a BluetoothConfig from the remote node before allowing changes if connectedNode != nil && node?.bluetoothConfig == nil { - let adminMessageId = bleManager.requestBluetoothConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + _ = bleManager.requestBluetoothConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) } } .onChange(of: enabled) { newEnabled in diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 2c5cc383..8b8d9b2e 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -118,7 +118,7 @@ struct LoRaConfig: View { // Need to request a LoRaConfig from the remote node before allowing changes if connectedNode != nil && node?.loRaConfig == nil { print("empty lora config") - let adminMessageId = bleManager.requestLoRaConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) + _ = bleManager.requestLoRaConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) } } .onChange(of: region) { newRegion in