import Foundation struct DeviceInfo { /// The unique ID generated by the device to distinguish between power cycles let uniqueIdOfPowerCycle: Int /// The number of bytes recorded by the tracker let numberOfRecordedBytes: Int /// The sum of all recorded bytes let dataChecksum: UInt16 /// The number of measurements already performed let numberOfStoredMeasurements: Int /// The interval between measurements (in seconds) let measurementInterval: Int let sensor0: TemperatureSensor? let sensor1: TemperatureSensor? // MARK: Device time let wakeupReason: DeviceWakeCause let time: DeviceTime // 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 currentMeasurementStartTime: Date { time.nextMeasurement.addingTimeInterval(-Double(numberOfStoredMeasurements * measurementInterval)) } } extension DeviceInfo { init(info: Data) throws { let date = Date() var data = info self.uniqueIdOfPowerCycle = try data.decodeFourByteInteger() self.numberOfRecordedBytes = try data.decodeTwoByteInteger() let secondsUntilNextMeasurement = try data.decodeTwoByteInteger() self.measurementInterval = try data.decodeTwoByteInteger() self.numberOfStoredMeasurements = try data.decodeTwoByteInteger() let totalNumberOfMeasurements = try data.decodeFourByteInteger() self.transferBlockSize = try data.decodeTwoByteInteger() self.storageSize = try data.decodeTwoByteInteger() let secondsSincePowerOn = try data.decodeFourByteInteger() let startSecondsOfCurrentRecording = try data.decodeFourByteInteger() self.dataChecksum = try data.decodeUInt16() self.wakeupReason = .init(rawValue: try data.getByte()) ?? .WAKEUP_UNDEFINED self.sensor0 = try data.decodeSensor() self.sensor1 = try data.decodeSensor() guard data.isEmpty else { log.error("\(data.count) bytes remaining in device info buffer") throw DeviceInfoError.missingData } self.time = .init( date: date, secondsSincePowerOn: secondsSincePowerOn, totalNumberOfMeasurements: totalNumberOfMeasurements, secondsUntilNextMeasurement: secondsUntilNextMeasurement, secondsOfFirstMeasurement: startSecondsOfCurrentRecording) } } extension DeviceInfo { static var mock: DeviceInfo { .init( uniqueIdOfPowerCycle: .random(in: 0...Int(UInt32.max)), numberOfRecordedBytes: 123, dataChecksum: .random(in: .min...UInt16.max), numberOfStoredMeasurements: 234, measurementInterval: 60, 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)), wakeupReason: .WAKEUP_EXT0, time: .mock, storageSize: 10000, transferBlockSize: 180) } } extension DeviceInfo: Codable { }