import Foundation struct TemperatureMeasurement: Identifiable { var sensor0: TemperatureValue var sensor1: TemperatureValue /// The seconds since 1970 var id: Int var date: Date { get { Date(seconds: id) } set { id = newValue.seconds } } var secondsToNow: Int { id - Date().seconds } var maximumValue: Double? { guard let s0 = sensor0.optionalValue else { return sensor1.optionalValue } guard let s1 = sensor1.optionalValue else { return s0 } return max(s0, s1) } var minimumValue: Double? { guard let s0 = sensor0.optionalValue else { return sensor1.optionalValue } guard let s1 = sensor1.optionalValue else { return s0 } return min(s0, s1) } var averageValue: Double? { guard let s0 = sensor0.optionalValue else { return sensor1.optionalValue } guard let s1 = sensor1.optionalValue else { return s0 } return (s0 + s1) / 2 } var displayText: String { guard let averageValue else { return "-" } return String(format: "%.1f °C", averageValue) } } extension TemperatureMeasurement { init(sensor0: TemperatureValue, sensor1: TemperatureValue, date: Date) { self.sensor0 = sensor0 self.sensor1 = sensor1 self.id = date.seconds } } extension TemperatureMeasurement: Codable { init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() self.sensor0 = .init(byte: try container.decode(UInt8.self)) self.sensor1 = .init(byte: try container.decode(UInt8.self)) self.id = try container.decode(Int.self) } func encode(to encoder: Encoder) throws { var container = encoder.unkeyedContainer() try container.encode(sensor0.byte) try container.encode(sensor1.byte) try container.encode(id) } } extension TemperatureMeasurement: Comparable { static func < (lhs: TemperatureMeasurement, rhs: TemperatureMeasurement) -> Bool { lhs.id < rhs.id } static func == (lhs: TemperatureMeasurement, rhs: TemperatureMeasurement) -> Bool { lhs.id == rhs.id } } extension Array where Element == TemperatureMeasurement { func maximumValue() -> Double? { compactMap { $0.maximumValue }.max() } func minimumValue() -> Double? { compactMap { $0.minimumValue }.min() } } private extension TemperatureValue { init(value: Double?) { if let value { self = .value(value) } else { self = .notFound } } } extension TemperatureMeasurement { static let mockData: [TemperatureMeasurement] = { let temps: [(Double?, Double?)] = [ (20, 14), (20, 13.5), (20.5, 13.5), (20.5, 13.5), (21, 14), (21, 14), (nil, 14.5), (nil, 14), (nil, 14.5), (nil, 14), (nil, 14), (nil, 14.5), (nil, 15), (5.0, 15), (4.5, 15.5), (4.5, 16), (4.0, 16.5), (3.0, 17), (3.0, 19), (2.5, 20), (2.5, 20.5), (2.0, 20.5), (1.0, 20.5), (0.5, 20.5), (0.0, 20), (0.0, 20), (-1.0, 21.0), (-0.5, 21.0), (-3.0, 21.0), (-3.5, 20.5), (-4.0, 20.5), (-5.0, 20.0), (-5.0, nil), (-5.5, nil), (-5.0, nil), (-5.5, nil), (-6.0, nil), (-5.0, nil), (nil, nil), (nil, nil), (nil, nil), (-5.0, nil), (-4.5, nil), (-4.0, 23.0), (5.0, 24.0), (7.0, 25.0), (8.0, 25.5), (8.5, 25.5), (10.0, 25.5), (10.5, 24.0), (10.5, 24.0), (10.5, 24.5), (12.0, 23.5), (12.5, 24.0), (12.0, 23.5), (14.0, 24.0), (15.0, 25.0), (15.0, 25.0), (15.5, 25.0), (15.0, 25.0), ] let seconds = Date().seconds return temps.enumerated().map { TemperatureMeasurement( sensor0: .init(value: $0.element.0), sensor1: .init(value: $0.element.1), id: seconds + ($0.offset - temps.count) * 60) } }() }