mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Add small weather widget back to node map
This commit is contained in:
parent
0dac9bc6a5
commit
b80b9e65b3
6 changed files with 149 additions and 267 deletions
|
|
@ -162,7 +162,6 @@
|
|||
DDDE5A1129AFE69700490C6C /* MeshActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDE5A0F29AFE69700490C6C /* MeshActivityAttributes.swift */; };
|
||||
DDDE5A1329AFEAB900490C6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDDE5A1229AFEAB900490C6C /* Assets.xcassets */; };
|
||||
DDDE5A1429AFEAB900490C6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDDE5A1229AFEAB900490C6C /* Assets.xcassets */; };
|
||||
DDDEE5E129DA3E1100A8E078 /* NodeInfoViewOld.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDEE5E029DA3E1100A8E078 /* NodeInfoViewOld.swift */; };
|
||||
DDE0F7C5295F77B700B8AAB3 /* AppSettingsEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE0F7C4295F77B700B8AAB3 /* AppSettingsEnums.swift */; };
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForward.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF6B2472A9AEBF500BA6931 /* StoreForward.swift */; };
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF924C926FBB953009FE055 /* ConnectedDevice.swift */; };
|
||||
|
|
@ -382,7 +381,6 @@
|
|||
DDDE5A0429AF163E00490C6C /* WidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetsExtension.entitlements; sourceTree = "<group>"; };
|
||||
DDDE5A0F29AFE69700490C6C /* MeshActivityAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshActivityAttributes.swift; sourceTree = "<group>"; };
|
||||
DDDE5A1229AFEAB900490C6C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
DDDEE5E029DA3E1100A8E078 /* NodeInfoViewOld.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoViewOld.swift; sourceTree = "<group>"; };
|
||||
DDDEE5E229DBE43E00A8E078 /* MeshtasticDataModelV11.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV11.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDE0F7C4295F77B700B8AAB3 /* AppSettingsEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsEnums.swift; sourceTree = "<group>"; };
|
||||
DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV4.xcdatamodel; sourceTree = "<group>"; };
|
||||
|
|
@ -474,7 +472,6 @@
|
|||
DD73FD1028750779000852D6 /* PositionLog.swift */,
|
||||
DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */,
|
||||
6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */,
|
||||
DDDEE5E029DA3E1100A8E078 /* NodeInfoViewOld.swift */,
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetailOld.swift */,
|
||||
DD47E3CD26F103C600029299 /* NodeListOld.swift */,
|
||||
);
|
||||
|
|
@ -1130,7 +1127,6 @@
|
|||
6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeListOld.swift in Sources */,
|
||||
DD5E520A298EE33B00D21B61 /* channel.pb.swift in Sources */,
|
||||
DDDEE5E129DA3E1100A8E078 /* NodeInfoViewOld.swift in Sources */,
|
||||
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
|
||||
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */,
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct ContentView: View {
|
|||
Label("bluetooth", systemImage: "antenna.radiowaves.left.and.right")
|
||||
}
|
||||
.tag(Tab.ble)
|
||||
NodeListSplit()
|
||||
NodeList()
|
||||
.tabItem {
|
||||
Label("nodes", systemImage: "flipphone")
|
||||
}
|
||||
|
|
|
|||
93
Meshtastic/Views/Nodes/Helpers/NodeInfo.swift
Normal file
93
Meshtastic/Views/Nodes/Helpers/NodeInfo.swift
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// NodeInfoItem.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 9/9/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreLocation
|
||||
import MapKit
|
||||
|
||||
struct NodeInfoItem: View {
|
||||
|
||||
@ObservedObject var node: NodeInfoEntity
|
||||
|
||||
var body: some View {
|
||||
|
||||
Divider()
|
||||
|
||||
HStack {
|
||||
|
||||
VStack(alignment: .center) {
|
||||
CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65)
|
||||
}
|
||||
if node.user != nil {
|
||||
Divider()
|
||||
VStack {
|
||||
Image(node.user!.hwModel ?? "unset".localized)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 75, height: 75)
|
||||
.cornerRadius(5)
|
||||
Text(String(node.user!.hwModel ?? "unset".localized))
|
||||
.font(.caption2).fixedSize()
|
||||
}
|
||||
}
|
||||
if node.snr != 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: ModemPresets.longModerate)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: node.snr, preset: ModemPresets.longModerate))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
if deviceMetrics?.count ?? 0 >= 1 {
|
||||
Divider()
|
||||
let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
VStack(alignment: .center) {
|
||||
BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0))
|
||||
if mostRecent?.voltage ?? 0 > 0 {
|
||||
|
||||
Text(String(format: "%.2f", mostRecent?.voltage ?? 0) + " V")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
HStack(alignment: .center) {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "number")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Node Number:").font(.title2)
|
||||
}
|
||||
Text(String(node.num)).font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "person")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("User Id:").font(.title2)
|
||||
}
|
||||
Text(node.user?.userId ?? "?").font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,21 @@
|
|||
import SwiftUI
|
||||
import CoreLocation
|
||||
import MapKit
|
||||
import WeatherKit
|
||||
|
||||
struct NodeMapControl: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
/// Weather
|
||||
/// The current weather condition for the city.
|
||||
@State private var condition: WeatherCondition?
|
||||
@State private var temperature: Measurement<UnitTemperature>?
|
||||
@State private var humidity: Int?
|
||||
@State private var symbolName: String = "cloud.fill"
|
||||
@State private var attributionLink: URL?
|
||||
@State private var attributionLogo: URL?
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
@AppStorage("meshMapType") private var meshMapType = 0
|
||||
@AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false
|
||||
|
|
@ -76,6 +86,50 @@ struct NodeMapControl: View {
|
|||
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
|
||||
.pickerStyle(.menu)
|
||||
.padding(5)
|
||||
VStack {
|
||||
Label(temperature?.formatted(.measurement(width: .narrow)) ?? "??", systemImage: symbolName)
|
||||
.font(.caption)
|
||||
|
||||
Label("\(humidity ?? 0)%", systemImage: "humidity")
|
||||
.font(.caption2)
|
||||
}
|
||||
.padding(10)
|
||||
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
|
||||
.padding(5)
|
||||
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)
|
||||
.padding(.bottom, 5)
|
||||
.task {
|
||||
do {
|
||||
if node.hasPositions {
|
||||
let mostRecent = node.positions?.lastObject as? PositionEntity
|
||||
let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude))
|
||||
condition = weather.currentWeather.condition
|
||||
temperature = weather.currentWeather.temperature
|
||||
humidity = Int(weather.currentWeather.humidity * 100)
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,261 +0,0 @@
|
|||
////
|
||||
//// NodeInfoView.swift
|
||||
//// Meshtastic
|
||||
////
|
||||
//// Created by Garth Vander Houwen on 4/2/23.
|
||||
////
|
||||
//
|
||||
////
|
||||
//// DistanceText.swift
|
||||
//// Meshtastic
|
||||
////
|
||||
//// Copyright(c) Garth Vander Houwen 8/19/22.
|
||||
////
|
||||
//
|
||||
//import SwiftUI
|
||||
//import CoreLocation
|
||||
//import MapKit
|
||||
//
|
||||
//struct NodeInfoView: View {
|
||||
//
|
||||
// var node: NodeInfoEntity
|
||||
//
|
||||
// var body: some View {
|
||||
// let hwModelString = node.user?.hwModel ?? "UNSET"
|
||||
//
|
||||
// Divider()
|
||||
// if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
// HStack {
|
||||
// VStack(alignment: .center) {
|
||||
// CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 150)
|
||||
// }
|
||||
// Divider()
|
||||
// VStack {
|
||||
// if node.user != nil {
|
||||
// Image(hwModelString)
|
||||
// .resizable()
|
||||
// .aspectRatio(contentMode: .fit)
|
||||
// .frame(width: 100, height: 100)
|
||||
// .cornerRadius(5)
|
||||
//
|
||||
// Text(String(hwModelString))
|
||||
// .foregroundColor(.gray)
|
||||
// .font(.title).fixedSize()
|
||||
// }
|
||||
// }
|
||||
// Divider()
|
||||
// if node.snr != 0 {
|
||||
// VStack(alignment: .center) {
|
||||
// let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: ModemPresets.longModerate)
|
||||
// LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
// Text("Signal \(signalStrength.description)").font(.title)
|
||||
// Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
// .foregroundColor(getSnrColor(snr: node.snr, preset: ModemPresets.longModerate))
|
||||
// .font(.title3)
|
||||
// Text("RSSI \(node.rssi)dB")
|
||||
// .foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
// .font(.title3)
|
||||
// }
|
||||
// Divider()
|
||||
// }
|
||||
//
|
||||
// if node.hasDeviceMetrics {
|
||||
// let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
// let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
// VStack(alignment: .center) {
|
||||
// BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0))
|
||||
// if mostRecent?.voltage ?? 0 > 0.0 {
|
||||
//
|
||||
// Text(String(format: "%.2f", mostRecent?.voltage ?? 0.0) + " V")
|
||||
// .font(.title)
|
||||
// .foregroundColor(.gray)
|
||||
// .fixedSize()
|
||||
// }
|
||||
// }
|
||||
// .padding()
|
||||
// }
|
||||
// }
|
||||
// .padding()
|
||||
//
|
||||
// Divider()
|
||||
// HStack(alignment: .center) {
|
||||
//
|
||||
// VStack {
|
||||
// HStack {
|
||||
// Image(systemName: "person")
|
||||
// .font(.title)
|
||||
// .foregroundColor(.accentColor)
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// Text("user").font(.title)+Text(":").font(.title)
|
||||
// }
|
||||
// Text("!\(String(format: "%02x", node.num))")
|
||||
// .font(.title).foregroundColor(.gray)
|
||||
// }
|
||||
// Divider()
|
||||
// VStack {
|
||||
// HStack {
|
||||
// Image(systemName: "number")
|
||||
// .font(.title2)
|
||||
// .foregroundColor(.accentColor)
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// Text("Node Number:").font(.title)
|
||||
// }
|
||||
// Text(String(node.num)).font(.title).foregroundColor(.gray)
|
||||
// }
|
||||
// Divider()
|
||||
// VStack {
|
||||
// HStack {
|
||||
// Image(systemName: "clock.badge.checkmark.fill")
|
||||
// .font(.title)
|
||||
// .foregroundColor(.accentColor)
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// Text("heard.last").font(.title)+Text(":").font(.title)
|
||||
//
|
||||
// }
|
||||
// DateTimeText(dateTime: node.lastHeard)
|
||||
// .font(.title3)
|
||||
// .foregroundColor(.gray)
|
||||
// }
|
||||
// }
|
||||
// Divider()
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// HStack {
|
||||
//
|
||||
// VStack(alignment: .center) {
|
||||
// CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65)
|
||||
// }
|
||||
// if node.user != nil {
|
||||
// Divider()
|
||||
// VStack {
|
||||
// Image(node.user!.hwModel ?? "unset".localized)
|
||||
// .resizable()
|
||||
// .aspectRatio(contentMode: .fit)
|
||||
// .frame(width: 75, height: 75)
|
||||
// .cornerRadius(5)
|
||||
// Text(String(node.user!.hwModel ?? "unset".localized))
|
||||
// .font(.caption2).fixedSize()
|
||||
// }
|
||||
// }
|
||||
// if node.snr != 0 {
|
||||
// Divider()
|
||||
// VStack(alignment: .center) {
|
||||
// let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: ModemPresets.longModerate)
|
||||
// LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
// Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
// Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
// .foregroundColor(getSnrColor(snr: node.snr, preset: ModemPresets.longModerate))
|
||||
// .font(.caption2)
|
||||
// Text("RSSI \(node.rssi)dB")
|
||||
// .foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
// .font(.caption2)
|
||||
// }
|
||||
// }
|
||||
// let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
// if deviceMetrics?.count ?? 0 >= 1 {
|
||||
// Divider()
|
||||
// let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
// VStack(alignment: .center) {
|
||||
// BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0))
|
||||
// if mostRecent?.voltage ?? 0 > 0 {
|
||||
//
|
||||
// Text(String(format: "%.2f", mostRecent?.voltage ?? 0) + " V")
|
||||
// .font(.callout)
|
||||
// .foregroundColor(.gray)
|
||||
// .fixedSize()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Divider()
|
||||
// HStack(alignment: .center) {
|
||||
// VStack {
|
||||
// HStack {
|
||||
// Image(systemName: "person")
|
||||
// .font(.title2)
|
||||
// .foregroundColor(.accentColor)
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// Text("User Id:").font(.title2)
|
||||
// }
|
||||
// Text(node.user?.userId ?? "?").font(.title3).foregroundColor(.gray)
|
||||
// }
|
||||
// Divider()
|
||||
// VStack {
|
||||
// HStack {
|
||||
// Image(systemName: "number")
|
||||
// .font(.title2)
|
||||
// .foregroundColor(.accentColor)
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// Text("Node Number:").font(.title2)
|
||||
// }
|
||||
// Text(String(node.num)).font(.title3).foregroundColor(.gray)
|
||||
// }
|
||||
// }
|
||||
// Divider()
|
||||
// }
|
||||
//
|
||||
// VStack {
|
||||
//
|
||||
// if node.hasPositions{
|
||||
//
|
||||
// NavigationLink {
|
||||
// PositionLog(node: node)
|
||||
// } label: {
|
||||
//
|
||||
// Image(systemName: "building.columns")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// .font(.title)
|
||||
//
|
||||
// Text("Position Log")
|
||||
// .font(.title3)
|
||||
// }
|
||||
// .fixedSize(horizontal: false, vertical: true)
|
||||
// Divider()
|
||||
// }
|
||||
//
|
||||
// if node.hasDeviceMetrics {
|
||||
//
|
||||
// NavigationLink {
|
||||
// DeviceMetricsLog(node: node)
|
||||
// } label: {
|
||||
//
|
||||
// Image(systemName: "flipphone")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// .font(.title)
|
||||
//
|
||||
// Text("Device Metrics Log")
|
||||
// .font(.title3)
|
||||
// }
|
||||
// Divider()
|
||||
// }
|
||||
// if node.hasEnvironmentMetrics {
|
||||
// NavigationLink {
|
||||
// EnvironmentMetricsLog(node: node)
|
||||
// } label: {
|
||||
//
|
||||
// Image(systemName: "chart.xyaxis.line")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// .font(.title)
|
||||
//
|
||||
// Text("Environment Metrics Log")
|
||||
// .font(.title3)
|
||||
// }
|
||||
// Divider()
|
||||
// }
|
||||
// NavigationLink {
|
||||
// DetectionSensorLog(node: node)
|
||||
// } label: {
|
||||
//
|
||||
// Image(systemName: "sensor")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// .font(.title)
|
||||
//
|
||||
// Text("Detection Sensor Log")
|
||||
// .font(.title3)
|
||||
// }
|
||||
// .fixedSize(horizontal: false, vertical: true)
|
||||
// Divider()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -17,7 +17,7 @@ enum SelectedDetail {
|
|||
|
||||
|
||||
|
||||
struct NodeListSplit: View {
|
||||
struct NodeList: View {
|
||||
|
||||
@State private var columnVisibility = NavigationSplitViewVisibility.all
|
||||
@State private var selectedNode: NodeInfoEntity?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue