2023-05-29 18:23:13 +02:00
|
|
|
import SwiftUI
|
2023-06-03 08:15:00 +02:00
|
|
|
import SFSafeSymbols
|
|
|
|
import BottomSheet
|
2023-05-29 18:23:13 +02:00
|
|
|
|
|
|
|
struct ContentView: View {
|
2023-06-05 13:05:57 +02:00
|
|
|
|
|
|
|
private let minTempColor = Color(hue: 0.624, saturation: 0.5, brightness: 1.0)
|
|
|
|
private let minTemperature = -20.0
|
|
|
|
|
|
|
|
private let maxTempColor = Color(hue: 1.0, saturation: 0.5, brightness: 1.0)
|
|
|
|
private let maxTemperature = 40.0
|
|
|
|
|
|
|
|
private let disconnectedColor = Color(white: 0.8)
|
|
|
|
|
2023-06-08 09:52:20 +02:00
|
|
|
@EnvironmentObject
|
|
|
|
var bluetoothClient: BluetoothClient
|
2023-06-05 13:05:57 +02:00
|
|
|
|
2023-06-08 09:52:20 +02:00
|
|
|
@EnvironmentObject
|
|
|
|
var storage: TemperatureStorage
|
2023-06-05 13:05:57 +02:00
|
|
|
|
|
|
|
@State
|
|
|
|
var showDeviceInfo = false
|
2023-06-08 09:52:20 +02:00
|
|
|
|
2023-06-05 13:05:57 +02:00
|
|
|
@State
|
2023-06-08 09:52:20 +02:00
|
|
|
var showHistory = false
|
2023-06-05 13:05:57 +02:00
|
|
|
|
2023-06-03 08:15:00 +02:00
|
|
|
init() {
|
2023-06-08 09:52:20 +02:00
|
|
|
|
2023-06-05 13:05:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var hasDeviceInfo: Bool {
|
2023-06-08 09:52:20 +02:00
|
|
|
bluetoothClient.deviceInfo != nil
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var averageTemperature: Double? {
|
2023-06-08 09:52:20 +02:00
|
|
|
let t1 = bluetoothClient.deviceInfo?.sensor1?.optionalValue
|
|
|
|
guard let t0 = bluetoothClient.deviceInfo?.sensor0?.optionalValue else {
|
2023-06-03 08:15:00 +02:00
|
|
|
return t1
|
|
|
|
}
|
|
|
|
guard let t1 else {
|
|
|
|
return t0
|
|
|
|
}
|
|
|
|
return (t0 + t1) / 2
|
|
|
|
}
|
|
|
|
|
|
|
|
var hasTemperature: Bool {
|
|
|
|
averageTemperature != nil
|
|
|
|
}
|
|
|
|
var temperatureString: String {
|
|
|
|
guard let temp = averageTemperature else {
|
|
|
|
return "?"
|
|
|
|
}
|
|
|
|
return String(format: "%.0f°", locale: .current, temp)
|
|
|
|
}
|
|
|
|
|
|
|
|
var temperatureIcon: SFSymbol {
|
|
|
|
guard let temp = averageTemperature else {
|
|
|
|
return .thermometerMediumSlash
|
|
|
|
}
|
|
|
|
guard temp > 0 else {
|
|
|
|
return .thermometerSnowflake
|
|
|
|
}
|
|
|
|
guard temp > 15 else {
|
|
|
|
return .thermometerLow
|
|
|
|
}
|
|
|
|
guard temp > 25 else {
|
|
|
|
return .thermometerMedium
|
|
|
|
}
|
|
|
|
return .thermometerHigh
|
|
|
|
}
|
2023-06-05 13:05:57 +02:00
|
|
|
|
|
|
|
var backgroundColor: Color {
|
|
|
|
guard let temp = averageTemperature else {
|
|
|
|
return disconnectedColor
|
|
|
|
}
|
|
|
|
guard temp > minTemperature else {
|
|
|
|
return minTempColor
|
|
|
|
}
|
|
|
|
guard temp < maxTemperature else {
|
|
|
|
return maxTempColor
|
|
|
|
}
|
|
|
|
let ratio = (temp - minTemperature) / (maxTemperature - minTemperature)
|
|
|
|
return minTempColor.blend(to: maxTempColor, intensity: ratio)
|
|
|
|
}
|
|
|
|
|
|
|
|
var backgroundGradient: Gradient {
|
|
|
|
let color = backgroundColor
|
|
|
|
let lighter = color.opacity(0.5)
|
|
|
|
return .init(colors: [lighter, color])
|
|
|
|
}
|
|
|
|
|
2023-05-29 18:23:13 +02:00
|
|
|
var body: some View {
|
|
|
|
VStack {
|
2023-06-03 08:15:00 +02:00
|
|
|
Spacer()
|
2023-06-05 13:05:57 +02:00
|
|
|
// Image(systemSymbol: temperatureIcon)
|
|
|
|
// .font(.system(size: 100, weight: .light))
|
2023-06-03 08:15:00 +02:00
|
|
|
if hasTemperature {
|
|
|
|
Text(temperatureString)
|
2023-06-05 13:05:57 +02:00
|
|
|
.font(.system(size: 150, weight: .light))
|
|
|
|
.foregroundColor(.white)
|
2023-06-03 08:15:00 +02:00
|
|
|
}
|
2023-06-05 13:05:57 +02:00
|
|
|
|
2023-06-03 08:15:00 +02:00
|
|
|
Spacer()
|
2023-06-08 09:52:20 +02:00
|
|
|
|
|
|
|
Button {
|
|
|
|
self.showHistory = true
|
|
|
|
} label: {
|
|
|
|
TemperatureHistoryChart(points: $storage.recentMeasurements)
|
|
|
|
.frame(height: 150)
|
|
|
|
.background(Color.white.opacity(0.1))
|
|
|
|
.cornerRadius(8)
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
HStack(alignment: .center) {
|
|
|
|
Button {
|
|
|
|
self.showDeviceInfo = true
|
|
|
|
} label: {
|
2023-06-05 13:05:57 +02:00
|
|
|
if hasDeviceInfo {
|
|
|
|
Image(systemSymbol: .iphone)
|
|
|
|
.font(.system(size: 30, weight: .regular))
|
|
|
|
}
|
2023-06-08 09:52:20 +02:00
|
|
|
Text(bluetoothClient.deviceState.text)
|
2023-06-05 13:05:57 +02:00
|
|
|
}
|
|
|
|
.disabled(!hasDeviceInfo)
|
|
|
|
.foregroundColor(.white)
|
|
|
|
|
2023-06-03 08:15:00 +02:00
|
|
|
}.padding()
|
2023-05-29 18:23:13 +02:00
|
|
|
}
|
|
|
|
.padding()
|
2023-06-05 13:05:57 +02:00
|
|
|
.bottomSheet(isPresented: $showDeviceInfo, height: 600) {
|
2023-06-08 09:52:20 +02:00
|
|
|
if let info = bluetoothClient.deviceInfo {
|
|
|
|
DeviceInfoView(info: info, isPresented: $showDeviceInfo)
|
2023-06-03 08:15:00 +02:00
|
|
|
} else {
|
|
|
|
EmptyView()
|
|
|
|
}
|
|
|
|
}
|
2023-06-08 09:52:20 +02:00
|
|
|
.sheet(isPresented: $showHistory) {
|
|
|
|
HistoryList()
|
|
|
|
.environmentObject(storage)
|
|
|
|
}
|
2023-06-05 13:05:57 +02:00
|
|
|
.background(backgroundGradient)
|
2023-05-29 18:23:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ContentView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
2023-06-08 09:52:20 +02:00
|
|
|
ContentView()
|
|
|
|
.environmentObject(TemperatureStorage(lastMeasurements: TemperatureMeasurement.mockData))
|
|
|
|
.environmentObject(BluetoothClient(deviceInfo: .mock))
|
2023-05-29 18:23:13 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-03 08:15:00 +02:00
|
|
|
|
|
|
|
extension HorizontalAlignment {
|
|
|
|
|
|
|
|
private struct InfoTextAlignment: AlignmentID {
|
|
|
|
static func defaultValue(in context: ViewDimensions) -> CGFloat {
|
|
|
|
context[HorizontalAlignment.leading]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static let infoTextAlignmentGuide = HorizontalAlignment(
|
|
|
|
InfoTextAlignment.self
|
|
|
|
)
|
|
|
|
}
|