mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Weatherkit widgets
This commit is contained in:
parent
321a41d3f8
commit
796c1abb31
7 changed files with 143 additions and 1 deletions
|
|
@ -6953,6 +6953,9 @@
|
|||
},
|
||||
"Enter DFU Mode" : {
|
||||
|
||||
},
|
||||
"Environment" : {
|
||||
|
||||
},
|
||||
"Environment Metrics Log" : {
|
||||
|
||||
|
|
@ -8265,6 +8268,9 @@
|
|||
},
|
||||
"Humidity" : {
|
||||
|
||||
},
|
||||
"HUMIDITY" : {
|
||||
|
||||
},
|
||||
"hybrid" : {
|
||||
"extractionState" : "migrated",
|
||||
|
|
@ -20839,6 +20845,9 @@
|
|||
},
|
||||
"The compass heading on the screen outside of the circle will always point north." : {
|
||||
|
||||
},
|
||||
"The dew point is %@ right now." : {
|
||||
|
||||
},
|
||||
"The fastest that position updates will be sent if the minimum distance has been satisfied" : {
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
DD3CC6BE28E4CD9800FA9159 /* BatteryGauge.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */; };
|
||||
DD3CC6C028E7A60700FA9159 /* MessagingEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */; };
|
||||
DD3CC6C228EB9D4900FA9159 /* UpdateCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */; };
|
||||
DD3D17E02C3FB67200561584 /* LocalWeatherConditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3D17DF2C3FB67200561584 /* LocalWeatherConditions.swift */; };
|
||||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582528582E9B009B0E59 /* DeviceConfig.swift */; };
|
||||
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; };
|
||||
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582928585C32009B0E59 /* RangeTestConfig.swift */; };
|
||||
|
|
@ -294,6 +295,7 @@
|
|||
DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingEnums.swift; sourceTree = "<group>"; };
|
||||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCoreData.swift; sourceTree = "<group>"; };
|
||||
DD3D17DC2C3D7B1400561584 /* MeshtasticDataModelV 39.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 39.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD3D17DF2C3FB67200561584 /* LocalWeatherConditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalWeatherConditions.swift; sourceTree = "<group>"; };
|
||||
DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = "<group>"; };
|
||||
DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = "<group>"; };
|
||||
DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -605,6 +607,7 @@
|
|||
DDA9515D2BC6F56F00CEA535 /* IndoorAirQuality.swift */,
|
||||
DD354FD82BD96A0B0061A25F /* IAQScale.swift */,
|
||||
DD41A61429AB0035003C5A37 /* NodeWeatherForecast.swift */,
|
||||
DD3D17DF2C3FB67200561584 /* LocalWeatherConditions.swift */,
|
||||
);
|
||||
path = Weather;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1134,6 +1137,7 @@
|
|||
DDDB444C29F8AAA600EE2349 /* Color.swift in Sources */,
|
||||
DDB8F4122A9EE5DD00230ECE /* UserList.swift in Sources */,
|
||||
DDB75A0F2A05920E006ED576 /* FileManager.swift in Sources */,
|
||||
DD3D17E02C3FB67200561584 /* LocalWeatherConditions.swift in Sources */,
|
||||
DD1933782B084F4200771CD5 /* Measurement.swift in Sources */,
|
||||
DD4F23CD28779A3C001D37CB /* EnvironmentMetricsLog.swift in Sources */,
|
||||
DD93800E2BA74D0C008BEC06 /* ChannelForm.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"originHash" : "c5be9820b6e5add3da0e3bd134c3826b3eece5f926d667cb3800a26572f9e63c",
|
||||
"originHash" : "74b3ad6215f078d89f4436b6ce0e318f145842efa3453bbe055ab76057de7d6b",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "cocoamqtt",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@ import CoreData
|
|||
|
||||
extension NodeInfoEntity {
|
||||
|
||||
var latestPosition: PositionEntity? {
|
||||
return self.positions?.lastObject as? PositionEntity
|
||||
}
|
||||
|
||||
var latestEnvironmentMetrics: TelemetryEntity? {
|
||||
return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")).lastObject as? TelemetryEntity
|
||||
}
|
||||
|
||||
var hasPositions: Bool {
|
||||
return positions?.count ?? 0 > 0
|
||||
}
|
||||
|
|
|
|||
114
Meshtastic/Views/Helpers/Weather/LocalWeatherConditions.swift
Normal file
114
Meshtastic/Views/Helpers/Weather/LocalWeatherConditions.swift
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// LocalWeatherConditions.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 7/9/24.
|
||||
//
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
import WeatherKit
|
||||
import OSLog
|
||||
|
||||
struct LocalWeatherConditions: View {
|
||||
@State var location: CLLocation?
|
||||
/// Weather
|
||||
/// The current weather condition for the city.
|
||||
@State private var condition: WeatherCondition?
|
||||
@State private var temperature: Measurement<UnitTemperature>?
|
||||
@State private var temperatureCompact: String?
|
||||
@State private var dewPoint: Measurement<UnitTemperature>?
|
||||
@State private var dewPointString: String?
|
||||
@State private var humidity: Int?
|
||||
@State private var pressure: Measurement<UnitPressure>?
|
||||
@State private var symbolName: String = "cloud.fill"
|
||||
@State private var attributionLink: URL?
|
||||
@State private var attributionLogo: URL?
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
var body: some View {
|
||||
if location != nil {
|
||||
ZStack {
|
||||
VStack {
|
||||
HStack {
|
||||
VStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text(temperatureCompact ?? "??")
|
||||
.font(.largeTitle)
|
||||
Text(condition?.description ?? "??")
|
||||
.font(.title3)
|
||||
Image(systemName: symbolName).resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40, height: 40)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||
.frame(maxWidth: 225, maxHeight: 225)
|
||||
|
||||
VStack {
|
||||
VStack(alignment: .leading) {
|
||||
Label { Text("HUMIDITY") } icon: { Image(systemName: "humidity").symbolRenderingMode(.multicolor) }
|
||||
.font(.caption)
|
||||
VStack(alignment: .leading) {
|
||||
Text("\(humidity ?? 0)%")
|
||||
.font(.largeTitle)
|
||||
.padding(.bottom)
|
||||
Text("The dew point is \(temperatureCompact ?? "?") right now.")
|
||||
.lineLimit(3)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||
.frame(maxWidth: 225, maxHeight: 225)
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack {
|
||||
HStack {
|
||||
AsyncImage(url: attributionLogo) { image in
|
||||
image
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
} placeholder: {
|
||||
ProgressView()
|
||||
.controlSize(.mini)
|
||||
}
|
||||
.frame(height: 10)
|
||||
Link("Other data sources", destination: attributionLink ?? URL(string: "https://weather-data.apple.com/legal-attribution.html")!)
|
||||
.font(.caption2)
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
.task {
|
||||
do {
|
||||
if location != nil {
|
||||
let weather = try await WeatherService.shared.weather(for: location!)
|
||||
let numFormatter = NumberFormatter()
|
||||
let measurementFormatter = MeasurementFormatter()
|
||||
numFormatter.maximumFractionDigits = 0
|
||||
measurementFormatter.numberFormatter = numFormatter
|
||||
measurementFormatter.unitStyle = .short
|
||||
condition = weather.currentWeather.condition
|
||||
temperature = weather.currentWeather.temperature
|
||||
temperatureCompact = measurementFormatter.string(from: dewPoint ?? Measurement<UnitTemperature>(value: 0, unit: .celsius))
|
||||
dewPoint = weather.currentWeather.dewPoint
|
||||
humidity = Int(weather.currentWeather.humidity * 100)
|
||||
pressure = weather.currentWeather.pressure
|
||||
symbolName = weather.currentWeather.symbolName
|
||||
let attribution = try await WeatherService.shared.attribution
|
||||
attributionLink = attribution.legalPageURL
|
||||
attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL
|
||||
}
|
||||
} catch {
|
||||
Logger.services.error("Could not gather weather information: \(error.localizedDescription)")
|
||||
condition = .clear
|
||||
symbolName = "cloud.fill"
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +157,12 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if node.hasEnvironmentMetrics {
|
||||
Section("Environment") {
|
||||
LocalWeatherConditions(location: node.latestPosition?.nodeLocation)
|
||||
}
|
||||
// }
|
||||
|
||||
Section("Logs") {
|
||||
// Metrics
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue