2023-06-03 08:15:00 +02:00
|
|
|
import SwiftUI
|
|
|
|
import SFSafeSymbols
|
|
|
|
|
|
|
|
private let df: DateFormatter = {
|
|
|
|
let df = DateFormatter()
|
|
|
|
df.locale = .current
|
|
|
|
df.dateStyle = .short
|
|
|
|
df.timeStyle = .short
|
|
|
|
return df
|
|
|
|
}()
|
|
|
|
|
|
|
|
struct DeviceInfoView: View {
|
|
|
|
|
|
|
|
private let storageWarnBytes = 500
|
|
|
|
|
|
|
|
let info: DeviceInfo
|
2023-06-05 13:05:57 +02:00
|
|
|
|
|
|
|
@Binding
|
|
|
|
var isPresented: Bool
|
2023-06-03 08:15:00 +02:00
|
|
|
|
|
|
|
private var runTimeString: String {
|
|
|
|
let number = info.numberOfSecondsRunning
|
|
|
|
guard number >= 60 else {
|
2023-06-13 17:14:57 +02:00
|
|
|
return "\(number) s"
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
let minutes = number / 60
|
|
|
|
guard minutes > 1 else {
|
2023-06-13 17:14:57 +02:00
|
|
|
return "1 min"
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
guard minutes >= 60 else {
|
2023-06-13 17:14:57 +02:00
|
|
|
return "\(minutes) min"
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
let hours = minutes / 60
|
|
|
|
guard hours > 1 else {
|
|
|
|
return "1 hour"
|
|
|
|
}
|
|
|
|
guard hours >= 60 else {
|
|
|
|
return "\(hours) hours"
|
|
|
|
}
|
|
|
|
let days = hours / 24
|
|
|
|
guard days > 1 else {
|
|
|
|
return "1 day"
|
|
|
|
}
|
|
|
|
return "\(days) days"
|
|
|
|
}
|
|
|
|
|
|
|
|
private var nextUpdateText: String {
|
|
|
|
let secs = Int(info.nextMeasurement.timeIntervalSinceNow.rounded())
|
|
|
|
guard secs > 1 else {
|
|
|
|
return "Now"
|
|
|
|
}
|
2023-06-13 17:14:57 +02:00
|
|
|
return "In \(secs) s"
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private var storageIcon: SFSymbol {
|
|
|
|
if info.storageSize - info.numberOfRecordedBytes < storageWarnBytes {
|
|
|
|
return .externaldriveTrianglebadgeExclamationmark
|
|
|
|
}
|
|
|
|
return .externaldrive
|
|
|
|
}
|
|
|
|
|
|
|
|
private var storageText: String {
|
|
|
|
if info.storageSize <= 0 {
|
|
|
|
return "\(info.numberOfRecordedBytes)"
|
|
|
|
}
|
|
|
|
return "\(info.numberOfRecordedBytes) / \(info.storageSize) Bytes (\(info.storageFillPercentage) %)"
|
|
|
|
}
|
|
|
|
|
|
|
|
func sensorView(_ sensor: TemperatureSensor?, id: Int) -> some View {
|
2023-06-08 14:57:40 +02:00
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
|
|
|
Text("Sensor \(id)")
|
|
|
|
.font(.headline)
|
|
|
|
if let sensor {
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: sensor.temperatureIcon)
|
|
|
|
.frame(width: 30)
|
2023-06-13 17:14:57 +02:00
|
|
|
Text("\(sensor.temperatureText) (\(sensor.updateText))")
|
2023-06-08 14:57:40 +02:00
|
|
|
}
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: .tag)
|
|
|
|
.frame(width: 30)
|
|
|
|
Text(sensor.hexAddress)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: .thermometerMediumSlash)
|
|
|
|
.frame(width: 30)
|
|
|
|
Text("Not connected")
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-08 09:52:20 +02:00
|
|
|
var updateText: String {
|
2023-06-08 14:57:40 +02:00
|
|
|
guard info.receivedDate.secondsToNow > 3 else {
|
|
|
|
return "Updated Now"
|
|
|
|
}
|
2023-06-08 09:52:20 +02:00
|
|
|
return "Updated \(info.receivedDate.timePassedText)"
|
|
|
|
}
|
|
|
|
|
2023-06-08 14:57:40 +02:00
|
|
|
var clockOffsetText: String {
|
|
|
|
guard info.hasDeviceStartTimeSet else {
|
|
|
|
return "Clock not synchronized"
|
|
|
|
}
|
|
|
|
let offset = info.clockOffset.roundedInt
|
|
|
|
guard abs(offset) > 1 else {
|
|
|
|
return "No clock offset"
|
|
|
|
}
|
2023-06-13 17:14:57 +02:00
|
|
|
return "Offset: \(offset) s"
|
2023-06-08 14:57:40 +02:00
|
|
|
}
|
|
|
|
|
2023-06-03 08:15:00 +02:00
|
|
|
var body: some View {
|
2023-06-08 14:57:40 +02:00
|
|
|
NavigationView {
|
2023-06-03 08:15:00 +02:00
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
2023-06-08 14:57:40 +02:00
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
2023-06-13 17:14:57 +02:00
|
|
|
Text("System")
|
2023-06-08 14:57:40 +02:00
|
|
|
.font(.headline)
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: .power)
|
|
|
|
.frame(width: 30)
|
2023-06-13 17:14:57 +02:00
|
|
|
Text("\(df.string(from: info.deviceStartTime)) (\(runTimeString))")
|
2023-06-08 14:57:40 +02:00
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: .clockBadgeExclamationmark)
|
|
|
|
.frame(width: 30)
|
|
|
|
Text(clockOffsetText)
|
|
|
|
}
|
|
|
|
HStack {
|
2023-06-13 17:14:57 +02:00
|
|
|
Image(systemSymbol: .autostartstop)
|
2023-06-08 14:57:40 +02:00
|
|
|
.frame(width: 30)
|
2023-06-13 17:14:57 +02:00
|
|
|
Text("Wakeup: \(info.wakeupReason.text)")
|
2023-06-08 14:57:40 +02:00
|
|
|
Spacer()
|
|
|
|
}
|
2023-06-13 17:14:57 +02:00
|
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
|
|
|
Text("Recording")
|
|
|
|
.font(.headline)
|
2023-06-08 14:57:40 +02:00
|
|
|
HStack {
|
2023-06-13 17:14:57 +02:00
|
|
|
Image(systemSymbol: .stopwatch)
|
2023-06-08 14:57:40 +02:00
|
|
|
.frame(width: 30)
|
2023-06-13 17:14:57 +02:00
|
|
|
Text("\(nextUpdateText) (Every \(info.measurementInterval) s)")
|
2023-06-08 14:57:40 +02:00
|
|
|
Spacer()
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
2023-06-08 14:57:40 +02:00
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
|
|
|
Text("Storage")
|
|
|
|
.font(.headline)
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: .speedometer)
|
|
|
|
.frame(width: 30)
|
|
|
|
Text("\(info.numberOfStoredMeasurements) Measurements (\(info.totalNumberOfMeasurements) total)")
|
|
|
|
}
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: storageIcon)
|
|
|
|
.frame(width: 30)
|
|
|
|
Text(storageText)
|
|
|
|
}
|
|
|
|
HStack {
|
|
|
|
Image(systemSymbol: .iphoneAndArrowForward)
|
|
|
|
.frame(width: 30)
|
|
|
|
Text("\(info.transferBlockSize) Byte Block Size")
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
2023-06-08 14:57:40 +02:00
|
|
|
sensorView(info.sensor0, id: 0)
|
|
|
|
sensorView(info.sensor1, id: 1)
|
|
|
|
Spacer()
|
2023-06-03 08:15:00 +02:00
|
|
|
HStack {
|
|
|
|
Spacer()
|
2023-06-08 14:57:40 +02:00
|
|
|
TimelineView(.periodic(from: Date(), by: 1)) { context in
|
|
|
|
Text(updateText)
|
|
|
|
.font(.footnote)
|
|
|
|
.textCase(.uppercase)
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
}
|
2023-06-08 14:57:40 +02:00
|
|
|
.padding()
|
|
|
|
.navigationTitle("Device Info")
|
|
|
|
.navigationBarTitleDisplayMode(.large)
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DeviceInfoView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
2023-06-08 09:52:20 +02:00
|
|
|
DeviceInfoView(info: .mock, isPresented: .constant(true))
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|