import Foundation struct DeviceInfo { let receivedDate: Date /// The number of bytes recorded by the tracker let numberOfRecordedBytes: Int /// The number of measurements already performed let numberOfStoredMeasurements: Int /// The measurements since device start let totalNumberOfMeasurements: Int /// The interval between measurements (in seconds) let measurementInterval: Int let nextMeasurement: Date let sensor0: TemperatureSensor? let sensor1: TemperatureSensor? // MARK: Device time /** The number of seconds the device has been powered on */ let numberOfSecondsRunning: Int let deviceStartTime: Date let hasDeviceStartTimeSet: Bool let wakeupReason: DeviceWakeCause // MARK: Storage let storageSize: Int /// The maximum number of bytes which can be requested let transferBlockSize: Int var storageFillRatio: Double { Double(numberOfRecordedBytes) / Double(storageSize) } var storageFillPercentage: Int { Int((storageFillRatio * 100).rounded()) } var clockOffset: TimeInterval { // Measurements are performed on device start (-1) and also count next measurement (+1) let nextMeasurementTime = deviceStartTime.adding(seconds: totalNumberOfMeasurements * measurementInterval) return nextMeasurement.timeIntervalSince(nextMeasurementTime) } var calculatedDeviceStartTime: Date { let runtime = totalNumberOfMeasurements * measurementInterval return nextMeasurement.adding(seconds: -runtime) } } extension DeviceInfo { init(info: Data) throws { var data = info let date = Date().nearestSecond self.receivedDate = date self.numberOfRecordedBytes = try data.decodeTwoByteInteger() self.nextMeasurement = date.adding(seconds: try data.decodeTwoByteInteger()) self.measurementInterval = try data.decodeTwoByteInteger() self.numberOfStoredMeasurements = try data.decodeTwoByteInteger() self.totalNumberOfMeasurements = try data.decodeFourByteInteger() self.transferBlockSize = try data.decodeTwoByteInteger() self.storageSize = try data.decodeTwoByteInteger() let secondsSincePowerOn = try data.decodeFourByteInteger() self.numberOfSecondsRunning = secondsSincePowerOn let deviceStartTimeSeconds = try data.decodeFourByteInteger() self.sensor0 = try data.decodeSensor() self.sensor1 = try data.decodeSensor() self.wakeupReason = .init(rawValue: try data.getByte()) ?? .WAKEUP_UNDEFINED if deviceStartTimeSeconds != 0 { self.hasDeviceStartTimeSet = true self.deviceStartTime = Date(seconds: deviceStartTimeSeconds) } else { self.hasDeviceStartTimeSet = false self.deviceStartTime = Date(seconds: date.seconds - secondsSincePowerOn) // Round to nearest second } } } extension DeviceInfo { static var mock: DeviceInfo { .init( receivedDate: Date(), numberOfRecordedBytes: 123, numberOfStoredMeasurements: 234, totalNumberOfMeasurements: 345, measurementInterval: 60, nextMeasurement: .now.addingTimeInterval(5), sensor0: .init(address: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], value: .value(21.0), date: .now.addingTimeInterval(-2)), sensor1: .init(address: [0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09], value: .value(19.0), date: .now.addingTimeInterval(-4)), numberOfSecondsRunning: 20, deviceStartTime: .now.addingTimeInterval(-20755), hasDeviceStartTimeSet: true, wakeupReason: .WAKEUP_EXT0, storageSize: 10000, transferBlockSize: 180) } }