1 contributor
//
// MeterCapabilities.swift
// USB Meter
//
// Created by Codex on 23/03/2026.
//
import SwiftUI
struct MeterCapabilities {
let availableDataGroupIDs: [UInt8]
let supportsDataGroupCommands: Bool
let supportsRecordingView: Bool
let supportsScreenSettings: Bool
let supportsRecordingThreshold: Bool
let reportsCurrentScreenIndex: Bool
let showsDataGroupEnergy: Bool
let highlightsActiveDataGroup: Bool
let supportsFahrenheit: Bool
let supportsChargerDetection: Bool
let primaryTemperatureUnitSymbol: String?
let dataGroupsTitle: String
let documentedWorkingVoltage: String
let chargerTypeDescriptions: [UInt16: String]
let screenDescriptions: [UInt16: String]
let dataGroupsHint: String?
let recordingThresholdHint: String?
func chargerTypeDescription(for index: UInt16) -> String {
guard supportsChargerDetection else { return "-" }
if let label = chargerTypeDescriptions[index] {
return label
}
return index == 0 ? "Unknown" : "Unknown (\(index))"
}
func screenDescription(for index: UInt16) -> String? {
screenDescriptions[index]
}
}
extension MeterCapabilities {
static let umFamily = MeterCapabilities(
availableDataGroupIDs: Array(0...9),
supportsDataGroupCommands: true,
supportsRecordingView: true,
supportsScreenSettings: true,
supportsRecordingThreshold: true,
reportsCurrentScreenIndex: true,
showsDataGroupEnergy: true,
highlightsActiveDataGroup: true,
supportsFahrenheit: true,
supportsChargerDetection: true,
primaryTemperatureUnitSymbol: "℃",
dataGroupsTitle: "Data Groups",
documentedWorkingVoltage: "4-24 V",
chargerTypeDescriptions: [
1: "QC2",
2: "QC3",
3: "Apple 2.4A",
4: "Apple 2.1A",
5: "Apple 1.0A",
6: "Apple 0.5A",
7: "DCP 1.5A",
8: "Samsung"
],
screenDescriptions: [
0: "Main Measurement",
1: "Quick Charge",
2: "Charging Record",
3: "Cable Impedance",
4: "Graphing",
5: "System Settings"
],
dataGroupsHint: "The active group is reported by the meter. Group 0 is temporary. Groups 1-9 persist across power cycles.",
recordingThresholdHint: "The meter starts its built-in charge record when current rises above this threshold."
)
static let tc66c = MeterCapabilities(
availableDataGroupIDs: [0, 1],
supportsDataGroupCommands: false,
supportsRecordingView: true,
supportsScreenSettings: false,
supportsRecordingThreshold: false,
reportsCurrentScreenIndex: false,
showsDataGroupEnergy: true,
highlightsActiveDataGroup: false,
supportsFahrenheit: false,
supportsChargerDetection: false,
primaryTemperatureUnitSymbol: nil,
dataGroupsTitle: "Memory Totals",
documentedWorkingVoltage: "3.5-24 V",
chargerTypeDescriptions: [:],
screenDescriptions: [:],
dataGroupsHint: "The device exposes two read-only memories with charge and energy totals. The active memory is not reported.",
recordingThresholdHint: nil
)
}
extension Model {
private static let tc66Tint = Color(
uiColor: UIColor { traits in
traits.userInterfaceStyle == .dark ? .systemGray2 : .black
}
)
static let byPeripheralName = Dictionary(
uniqueKeysWithValues: allCases.flatMap { model in
model.peripheralNames.map { ($0, model) }
}
)
var radio: BluetoothRadio {
switch self {
case .UM25C, .UM34C:
return .BT18
case .TC66C:
return .PW0316
}
}
var peripheralNames: [String] {
switch self {
case .UM25C:
return ["UM25C"]
case .UM34C:
return ["UM34C"]
case .TC66C:
return ["TC66C", "PW0316"]
}
}
var color: Color {
switch self {
case .UM25C:
return .blue
case .UM34C:
return .yellow
case .TC66C:
return Self.tc66Tint
}
}
var capabilities: MeterCapabilities {
switch self {
case .UM25C, .UM34C:
return .umFamily
case .TC66C:
return .tc66c
}
}
}