USB-Meter / USB Meter / Model / UMProtocol.swift
Newer Older
119 lines | 4.183kb
Bogdan Timofte authored 2 weeks ago
1
//
2
//  UMProtocol.swift
3
//  USB Meter
4
//
5
//  Created by Codex on 23/03/2026.
6
//
7

            
8
import Foundation
9

            
10
struct UMDataGroupTotals {
11
    let ah: Double
12
    let wh: Double
13
}
14

            
15
struct UMSnapshot {
16
    let modelNumber: UInt16
17
    let voltage: Double
18
    let current: Double
19
    let power: Double
20
    let temperatureCelsius: Double
21
    let temperatureFahrenheit: Double
22
    let selectedDataGroup: UInt8
23
    let dataGroupRecords: [Int: UMDataGroupTotals]
24
    let usbPlusVoltage: Double
25
    let usbMinusVoltage: Double
26
    let chargerTypeIndex: UInt16
27
    let recordedAH: Double
28
    let recordedWH: Double
29
    let recordingThreshold: Double
30
    let recordingDuration: UInt32
31
    let recording: Bool
32
    let screenTimeout: Int
33
    let screenBrightness: Int
34
    let loadResistance: Double
35
    let currentScreen: UInt16
36
}
37

            
38
enum UMProtocolError: Error {
39
    case invalidPayloadLength(Int)
40
}
41

            
42
enum UMProtocol {
43
    static let minimumSnapshotLength = 128
44
    static let snapshotRequest = Data([0xF0])
45
    static let nextScreen = Data([0xF1])
46
    static let rotateScreen = Data([0xF2])
47
    static let previousScreen = Data([0xF3])
48
    static let clearCurrentGroup = Data([0xF4])
49

            
50
    static func selectDataGroup(_ id: UInt8) -> Data {
51
        Data([0xA0 | id])
52
    }
53

            
54
    static func setScreenBrightness(_ value: UInt8) -> Data {
55
        Data([0xD0 | value])
56
    }
57

            
58
    static func setScreenSaverTimeout(_ value: UInt8) -> Data {
59
        Data([0xE0 | value])
60
    }
61

            
62
    static func setRecordingThreshold(_ value: UInt8) -> Data {
63
        Data([0xB0 + value])
64
    }
65

            
66
    static func parseSnapshot(from buffer: Data, model: Model) throws -> UMSnapshot {
67
        guard buffer.count >= minimumSnapshotLength else {
68
            throw UMProtocolError.invalidPayloadLength(buffer.count)
69
        }
70

            
71
        let modelNumber = UInt16(bigEndian: buffer.value(from: 0))
72

            
73
        let voltageScale: Double
74
        let currentScale: Double
75
        switch model {
76
        case .UM25C:
77
            voltageScale = 1000
78
            currentScale = 10000
79
        case .UM34C:
80
            voltageScale = 100
81
            currentScale = 1000
82
        case .TC66C:
83
            voltageScale = 1
84
            currentScale = 1
85
        }
86

            
87
        var dataGroupRecords: [Int: UMDataGroupTotals] = [:]
88
        for index in stride(from: 0, through: 9, by: 1) {
89
            let offset = 16 + index * 8
90
            dataGroupRecords[index] = UMDataGroupTotals(
91
                ah: Double(UInt32(bigEndian: buffer.value(from: offset))) / 1000,
92
                wh: Double(UInt32(bigEndian: buffer.value(from: offset + 4))) / 1000
93
            )
94
        }
95

            
96
        return UMSnapshot(
97
            modelNumber: modelNumber,
98
            voltage: Double(UInt16(bigEndian: buffer.value(from: 2))) / voltageScale,
99
            current: Double(UInt16(bigEndian: buffer.value(from: 4))) / currentScale,
100
            power: Double(UInt32(bigEndian: buffer.value(from: 6))) / 1000,
101
            temperatureCelsius: Double(UInt16(bigEndian: buffer.value(from: 10))),
102
            temperatureFahrenheit: Double(UInt16(bigEndian: buffer.value(from: 12))),
103
            selectedDataGroup: UInt8(UInt16(bigEndian: buffer.value(from: 14))),
104
            dataGroupRecords: dataGroupRecords,
105
            usbPlusVoltage: Double(UInt16(bigEndian: buffer.value(from: 96))) / 100,
106
            usbMinusVoltage: Double(UInt16(bigEndian: buffer.value(from: 98))) / 100,
107
            chargerTypeIndex: UInt16(bigEndian: buffer.value(from: 100)),
108
            recordedAH: Double(UInt32(bigEndian: buffer.value(from: 102))) / 1000,
109
            recordedWH: Double(UInt32(bigEndian: buffer.value(from: 106))) / 1000,
110
            recordingThreshold: Double(UInt16(bigEndian: buffer.value(from: 110))) / 100,
111
            recordingDuration: UInt32(bigEndian: buffer.value(from: 112)),
112
            recording: UInt16(bigEndian: buffer.value(from: 116)) == 1,
113
            screenTimeout: Int(UInt16(bigEndian: buffer.value(from: 118))),
114
            screenBrightness: Int(UInt16(bigEndian: buffer.value(from: 120))),
115
            loadResistance: Double(UInt32(bigEndian: buffer.value(from: 122))) / 10,
116
            currentScreen: UInt16(bigEndian: buffer.value(from: 126))
117
        )
118
    }
119
}