@@ -35,7 +35,14 @@ final class BluetoothSerial : NSObject, ObservableObject {
|
||
| 35 | 35 |
var macAddress: MACAddress |
| 36 | 36 |
private var manager: CBCentralManager |
| 37 | 37 |
private var radio: BluetoothRadio |
| 38 |
- @Published var RSSI: Int |
|
| 38 |
+ @Published var RSSI: Int {
|
|
| 39 |
+ didSet {
|
|
| 40 |
+ minRSSI = Swift.min(minRSSI, RSSI) |
|
| 41 |
+ maxRSSI = Swift.max(maxRSSI, RSSI) |
|
| 42 |
+ } |
|
| 43 |
+ } |
|
| 44 |
+ @Published private(set) var minRSSI: Int |
|
| 45 |
+ @Published private(set) var maxRSSI: Int |
|
| 39 | 46 |
|
| 40 | 47 |
private var expectedResponseLength = 0 |
| 41 | 48 |
private var wdTimer: Timer? |
@@ -58,6 +65,8 @@ final class BluetoothSerial : NSObject, ObservableObject {
|
||
| 58 | 65 |
self.radio = radio |
| 59 | 66 |
self.manager = manager |
| 60 | 67 |
self.RSSI = RSSI |
| 68 |
+ self.minRSSI = RSSI |
|
| 69 |
+ self.maxRSSI = RSSI |
|
| 61 | 70 |
Timer.scheduledTimer(withTimeInterval: 3, repeats: true, block: {_ in
|
| 62 | 71 |
if peripheral.state == .connected {
|
| 63 | 72 |
peripheral.readRSSI() |
@@ -145,6 +154,8 @@ final class BluetoothSerial : NSObject, ObservableObject {
|
||
| 145 | 154 |
func connectionEstablished () {
|
| 146 | 155 |
resetCommunicationState(reason: "connectionEstablished()", clearCharacteristics: true) |
| 147 | 156 |
track("Connection established for '\(peripheral.identifier)'")
|
| 157 |
+ minRSSI = RSSI |
|
| 158 |
+ maxRSSI = RSSI |
|
| 148 | 159 |
operationalState = .peripheralConnected |
| 149 | 160 |
peripheral.discoverServices(BluetoothRadioServicesUUIDS[radio]) |
| 150 | 161 |
} |
@@ -152,6 +163,8 @@ final class BluetoothSerial : NSObject, ObservableObject {
|
||
| 152 | 163 |
func connectionClosed () {
|
| 153 | 164 |
track("Connection closed for '\(peripheral.identifier)' while peripheral state is \(peripheral.state.rawValue)")
|
| 154 | 165 |
resetCommunicationState(reason: "connectionClosed()", clearCharacteristics: true) |
| 166 |
+ minRSSI = RSSI |
|
| 167 |
+ maxRSSI = RSSI |
|
| 155 | 168 |
operationalState = .peripheralNotConnected |
| 156 | 169 |
} |
| 157 | 170 |
|
@@ -329,8 +329,12 @@ class Meter : NSObject, ObservableObject, Identifiable {
|
||
| 329 | 329 |
return "Screen \(currentScreen)" |
| 330 | 330 |
} |
| 331 | 331 |
|
| 332 |
+ var deviceModelName: String {
|
|
| 333 |
+ reportedModelName.isEmpty ? modelString : reportedModelName |
|
| 334 |
+ } |
|
| 335 |
+ |
|
| 332 | 336 |
var deviceModelSummary: String {
|
| 333 |
- let baseName = reportedModelName.isEmpty ? modelString : reportedModelName |
|
| 337 |
+ let baseName = deviceModelName |
|
| 334 | 338 |
if modelNumber != 0 {
|
| 335 | 339 |
return "\(baseName) (\(modelNumber))" |
| 336 | 340 |
} |
@@ -136,19 +136,19 @@ struct LiveView: View {
|
||
| 136 | 136 |
detailText: "Measured resistance" |
| 137 | 137 |
) |
| 138 | 138 |
|
| 139 |
- if shouldShowChargerTile {
|
|
| 140 |
- liveMetricCard( |
|
| 141 |
- title: "Charger", |
|
| 142 |
- symbol: "bolt.badge.checkmark", |
|
| 143 |
- color: .purple, |
|
| 144 |
- value: meter.chargerTypeDescription, |
|
| 145 |
- detailText: chargerTypeDetailText, |
|
| 146 |
- valueFont: .system(compactLayout ? .subheadline : .headline, design: .rounded).weight(.bold), |
|
| 147 |
- valueLineLimit: 2, |
|
| 148 |
- valueMonospacedDigits: false, |
|
| 149 |
- valueMinimumScaleFactor: 0.70 |
|
| 150 |
- ) |
|
| 151 |
- } |
|
| 139 |
+ liveMetricCard( |
|
| 140 |
+ title: "RSSI", |
|
| 141 |
+ symbol: "dot.radiowaves.left.and.right", |
|
| 142 |
+ color: .mint, |
|
| 143 |
+ value: "\(meter.btSerial.RSSI) dBm", |
|
| 144 |
+ range: MetricRange( |
|
| 145 |
+ minLabel: "Min", |
|
| 146 |
+ maxLabel: "Max", |
|
| 147 |
+ minValue: "\(meter.btSerial.minRSSI) dBm", |
|
| 148 |
+ maxValue: "\(meter.btSerial.maxRSSI) dBm" |
|
| 149 |
+ ), |
|
| 150 |
+ valueFont: .system(compactLayout ? .subheadline : .headline, design: .rounded).weight(.bold) |
|
| 151 |
+ ) |
|
| 152 | 152 |
} |
| 153 | 153 |
} |
| 154 | 154 |
.frame(maxWidth: .infinity, alignment: .topLeading) |
@@ -176,14 +176,6 @@ struct LiveView: View {
|
||
| 176 | 176 |
) |
| 177 | 177 |
} |
| 178 | 178 |
|
| 179 |
- private var shouldShowChargerTile: Bool {
|
|
| 180 |
- meter.supportsChargerDetection |
|
| 181 |
- } |
|
| 182 |
- |
|
| 183 |
- private var chargerTypeDetailText: String {
|
|
| 184 |
- meter.chargerTypeDescription == "Unknown" ? "No charging profile detected" : "Detected charging profile" |
|
| 185 |
- } |
|
| 186 |
- |
|
| 187 | 179 |
private var showsCompactMetricRange: Bool {
|
| 188 | 180 |
compactLayout && (availableSize?.height ?? 0) >= 380 |
| 189 | 181 |
} |
@@ -139,35 +139,9 @@ struct MeterView: View {
|
||
| 139 | 139 |
private func connectionCard(compact: Bool = false, showsActions: Bool = false) -> some View {
|
| 140 | 140 |
VStack(alignment: .leading, spacing: compact ? 12 : 18) {
|
| 141 | 141 |
HStack(alignment: .top) {
|
| 142 |
- VStack(alignment: .leading, spacing: 6) {
|
|
| 143 |
- Text(meter.name) |
|
| 144 |
- .font(.system(compact ? .title3 : .title2, design: .rounded).weight(.bold)) |
|
| 145 |
- Text(meter.deviceModelSummary) |
|
| 146 |
- .font(.subheadline.weight(.semibold)) |
|
| 147 |
- .foregroundColor(.secondary) |
|
| 148 |
- } |
|
| 142 |
+ meterIdentity(compact: compact) |
|
| 149 | 143 |
Spacer() |
| 150 |
- Group {
|
|
| 151 |
- if compact {
|
|
| 152 |
- HStack(spacing: 8) {
|
|
| 153 |
- statusBadge |
|
| 154 |
- if meter.operationalState > .notPresent {
|
|
| 155 |
- Text("RSSI \(meter.btSerial.RSSI)")
|
|
| 156 |
- .font(.caption) |
|
| 157 |
- .foregroundColor(.secondary) |
|
| 158 |
- } |
|
| 159 |
- } |
|
| 160 |
- } else {
|
|
| 161 |
- VStack(alignment: .trailing, spacing: 6) {
|
|
| 162 |
- statusBadge |
|
| 163 |
- if meter.operationalState > .notPresent {
|
|
| 164 |
- Text("RSSI \(meter.btSerial.RSSI)")
|
|
| 165 |
- .font(.caption) |
|
| 166 |
- .foregroundColor(.secondary) |
|
| 167 |
- } |
|
| 168 |
- } |
|
| 169 |
- } |
|
| 170 |
- } |
|
| 144 |
+ statusBadge |
|
| 171 | 145 |
} |
| 172 | 146 |
|
| 173 | 147 |
if compact {
|
@@ -191,6 +165,21 @@ struct MeterView: View {
|
||
| 191 | 165 |
.meterCard(tint: meter.color, fillOpacity: 0.22, strokeOpacity: 0.24) |
| 192 | 166 |
} |
| 193 | 167 |
|
| 168 |
+ private func meterIdentity(compact: Bool) -> some View {
|
|
| 169 |
+ HStack(alignment: .firstTextBaseline, spacing: 8) {
|
|
| 170 |
+ Text(meter.name) |
|
| 171 |
+ .font(.system(compact ? .title3 : .title2, design: .rounded).weight(.bold)) |
|
| 172 |
+ .lineLimit(1) |
|
| 173 |
+ .minimumScaleFactor(0.8) |
|
| 174 |
+ |
|
| 175 |
+ Text(meter.deviceModelName) |
|
| 176 |
+ .font((compact ? Font.caption : .subheadline).weight(.semibold)) |
|
| 177 |
+ .foregroundColor(.secondary) |
|
| 178 |
+ .lineLimit(1) |
|
| 179 |
+ .minimumScaleFactor(0.8) |
|
| 180 |
+ } |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 194 | 183 |
private func actionGrid(compact: Bool = false, embedded: Bool = false) -> some View {
|
| 195 | 184 |
let currentActionHeight = compact ? CGFloat(86) : actionButtonHeight |
| 196 | 185 |
|
@@ -377,7 +366,7 @@ private struct MeterInfoView: View {
|
||
| 377 | 366 |
VStack(spacing: 14) {
|
| 378 | 367 |
MeterInfoCard(title: "Overview", tint: meter.color) {
|
| 379 | 368 |
MeterInfoRow(label: "Name", value: meter.name) |
| 380 |
- MeterInfoRow(label: "Displayed Model", value: meter.deviceModelSummary) |
|
| 369 |
+ MeterInfoRow(label: "Device Model", value: meter.deviceModelName) |
|
| 381 | 370 |
MeterInfoRow(label: "Advertised Model", value: meter.modelString) |
| 382 | 371 |
MeterInfoRow(label: "Working Voltage", value: meter.documentedWorkingVoltage) |
| 383 | 372 |
MeterInfoRow(label: "Temperature Unit", value: meter.temperatureUnitDescription) |
@@ -386,7 +375,7 @@ private struct MeterInfoView: View {
|
||
| 386 | 375 |
MeterInfoCard(title: "Identifiers", tint: .blue) {
|
| 387 | 376 |
MeterInfoRow(label: "MAC", value: meter.btSerial.macAddress.description) |
| 388 | 377 |
if meter.modelNumber != 0 {
|
| 389 |
- MeterInfoRow(label: "Model Code", value: "\(meter.modelNumber)") |
|
| 378 |
+ MeterInfoRow(label: "Model Identifier", value: "\(meter.modelNumber)") |
|
| 390 | 379 |
} |
| 391 | 380 |
} |
| 392 | 381 |
|
@@ -409,6 +398,9 @@ private struct MeterInfoView: View {
|
||
| 409 | 398 |
if !meter.firmwareVersion.isEmpty {
|
| 410 | 399 |
MeterInfoRow(label: "Firmware", value: meter.firmwareVersion) |
| 411 | 400 |
} |
| 401 |
+ if meter.supportsChargerDetection {
|
|
| 402 |
+ MeterInfoRow(label: "Detected Charger", value: meter.chargerTypeDescription) |
|
| 403 |
+ } |
|
| 412 | 404 |
if meter.serialNumber != 0 {
|
| 413 | 405 |
MeterInfoRow(label: "Serial", value: "\(meter.serialNumber)") |
| 414 | 406 |
} |
@@ -434,7 +426,7 @@ private struct MeterInfoView: View {
|
||
| 434 | 426 |
) |
| 435 | 427 |
.ignoresSafeArea() |
| 436 | 428 |
) |
| 437 |
- .navigationBarTitle("Meter Info")
|
|
| 429 |
+ .navigationBarTitle("Device Info")
|
|
| 438 | 430 |
.navigationBarItems(trailing: RSSIView(RSSI: meter.btSerial.RSSI).frame(width: 18, height: 18)) |
| 439 | 431 |
} |
| 440 | 432 |
} |