@@ -153,6 +153,14 @@ class Meter : NSObject, ObservableObject, Identifiable {
|
||
| 153 | 153 |
capabilities.chargerTypeDescription(for: chargerTypeIndex) |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
+ var deviceModelSummary: String {
|
|
| 157 |
+ let baseName = reportedModelName.isEmpty ? modelString : reportedModelName |
|
| 158 |
+ if modelNumber != 0 {
|
|
| 159 |
+ return "\(baseName) (\(modelNumber))" |
|
| 160 |
+ } |
|
| 161 |
+ return baseName |
|
| 162 |
+ } |
|
| 163 |
+ |
|
| 156 | 164 |
@Published var btSerial: BluetoothSerial |
| 157 | 165 |
|
| 158 | 166 |
@Published var measurements = Measurements() |
@@ -217,6 +225,10 @@ class Meter : NSObject, ObservableObject, Identifiable {
|
||
| 217 | 225 |
@Published var loadResistance: Double = 0 |
| 218 | 226 |
@Published var modelNumber: UInt16 = 0 |
| 219 | 227 |
@Published var chargerTypeIndex: UInt16 = 0 |
| 228 |
+ @Published var reportedModelName: String = "" |
|
| 229 |
+ @Published var firmwareVersion: String = "" |
|
| 230 |
+ @Published var serialNumber: UInt32 = 0 |
|
| 231 |
+ @Published var bootCount: UInt32 = 0 |
|
| 220 | 232 |
private var enableAutoConnect: Bool = false |
| 221 | 233 |
|
| 222 | 234 |
init ( model: Model, with serialPort: BluetoothSerial ) {
|
@@ -332,6 +344,10 @@ class Meter : NSObject, ObservableObject, Identifiable {
|
||
| 332 | 344 |
} |
| 333 | 345 |
|
| 334 | 346 |
private func apply(tc66Snapshot snapshot: TC66Snapshot) {
|
| 347 |
+ reportedModelName = snapshot.modelName |
|
| 348 |
+ firmwareVersion = snapshot.firmwareVersion |
|
| 349 |
+ serialNumber = snapshot.serialNumber |
|
| 350 |
+ bootCount = snapshot.bootCount |
|
| 335 | 351 |
voltage = snapshot.voltage |
| 336 | 352 |
current = snapshot.current |
| 337 | 353 |
power = snapshot.power |
@@ -47,6 +47,12 @@ enum TC66Protocol {
|
||
| 47 | 47 |
0xa7, 0xf1, 0x06, 0x61, 0x9a, 0xb8, 0x72, 0x88 |
| 48 | 48 |
] |
| 49 | 49 |
|
| 50 |
+ private static func sanitizedASCII(from data: Data) -> String {
|
|
| 51 |
+ data.asciiString |
|
| 52 |
+ .replacingOccurrences(of: "\0", with: "") |
|
| 53 |
+ .trimmingCharacters(in: .whitespacesAndNewlines.union(.controlCharacters)) |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 50 | 56 |
private static func validate(packetId: UInt8, packet: Data) -> Bool {
|
| 51 | 57 |
let expectedHeader = "pac\(packetId)".data(using: .ascii) |
| 52 | 58 |
let packetHeader = packet.subdata(from: 0, length: 4) |
@@ -91,8 +97,8 @@ enum TC66Protocol {
|
||
| 91 | 97 |
let temperatureSign = UInt32(littleEndian: pac2.value(from: 24)) == 1 ? -1.0 : 1.0 |
| 92 | 98 |
|
| 93 | 99 |
return TC66Snapshot( |
| 94 |
- modelName: pac1.subdata(from: 4, length: 4).asciiString, |
|
| 95 |
- firmwareVersion: pac1.subdata(from: 8, length: 4).asciiString, |
|
| 100 |
+ modelName: sanitizedASCII(from: pac1.subdata(from: 4, length: 4)), |
|
| 101 |
+ firmwareVersion: sanitizedASCII(from: pac1.subdata(from: 8, length: 4)), |
|
| 96 | 102 |
serialNumber: UInt32(littleEndian: pac1.value(from: 12)), |
| 97 | 103 |
bootCount: UInt32(littleEndian: pac1.value(from: 44)), |
| 98 | 104 |
voltage: Double(UInt32(littleEndian: pac1.value(from: 48))) / 10000, |
@@ -35,6 +35,27 @@ struct MeterSettingsView: View {
|
||
| 35 | 35 |
} |
| 36 | 36 |
.padding() |
| 37 | 37 |
.background(RoundedRectangle(cornerRadius: 15).foregroundColor(.secondary).opacity(0.1)) |
| 38 |
+ if meter.operationalState == .dataIsAvailable {
|
|
| 39 |
+ VStack(alignment: .leading, spacing: 8) {
|
|
| 40 |
+ Text("Device Info").fontWeight(.semibold)
|
|
| 41 |
+ DeviceInfoRow(label: "Advertised Model", value: meter.modelString) |
|
| 42 |
+ DeviceInfoRow(label: "Displayed Model", value: meter.deviceModelSummary) |
|
| 43 |
+ if meter.modelNumber != 0 {
|
|
| 44 |
+ DeviceInfoRow(label: "Model Code", value: "\(meter.modelNumber)") |
|
| 45 |
+ } |
|
| 46 |
+ if !meter.firmwareVersion.isEmpty {
|
|
| 47 |
+ DeviceInfoRow(label: "Firmware", value: meter.firmwareVersion) |
|
| 48 |
+ } |
|
| 49 |
+ if meter.serialNumber != 0 {
|
|
| 50 |
+ DeviceInfoRow(label: "Serial", value: "\(meter.serialNumber)") |
|
| 51 |
+ } |
|
| 52 |
+ if meter.bootCount != 0 {
|
|
| 53 |
+ DeviceInfoRow(label: "Boot Count", value: "\(meter.bootCount)") |
|
| 54 |
+ } |
|
| 55 |
+ } |
|
| 56 |
+ .padding() |
|
| 57 |
+ .background(RoundedRectangle(cornerRadius: 15).foregroundColor(.secondary).opacity(0.1)) |
|
| 58 |
+ } |
|
| 38 | 59 |
if meter.operationalState == .dataIsAvailable && meter.supportsUMSettings {
|
| 39 | 60 |
// MARK: Screen Timeout |
| 40 | 61 |
// Ar trebui separat enabled/disabled de valorile in minute eventual stocata valoarea in iCloud la dezactivare pentru restaurare |
@@ -78,6 +99,22 @@ struct MeterSettingsView: View {
|
||
| 78 | 99 |
} |
| 79 | 100 |
} |
| 80 | 101 |
|
| 102 |
+private struct DeviceInfoRow: View {
|
|
| 103 |
+ let label: String |
|
| 104 |
+ let value: String |
|
| 105 |
+ |
|
| 106 |
+ var body: some View {
|
|
| 107 |
+ HStack {
|
|
| 108 |
+ Text(label) |
|
| 109 |
+ Spacer() |
|
| 110 |
+ Text(value) |
|
| 111 |
+ .foregroundColor(.secondary) |
|
| 112 |
+ .multilineTextAlignment(.trailing) |
|
| 113 |
+ } |
|
| 114 |
+ .font(.footnote) |
|
| 115 |
+ } |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 81 | 118 |
struct EditNameView: View {
|
| 82 | 119 |
|
| 83 | 120 |
@EnvironmentObject private var meter: Meter |
@@ -42,7 +42,7 @@ struct MeterView: View {
|
||
| 42 | 42 |
connectionControlButton() |
| 43 | 43 |
// MARK: Show Data |
| 44 | 44 |
if ( meter.operationalState == .dataIsAvailable) {
|
| 45 |
- Text("Model: \(meter.modelString) - (\(meter.modelNumber))")
|
|
| 45 |
+ Text("Model: \(meter.deviceModelSummary)")
|
|
| 46 | 46 |
HStack {
|
| 47 | 47 |
Button(action: {self.dataGroupsViewVisibility.toggle()}) {
|
| 48 | 48 |
VStack {
|