Mecanismul de descoperire şi conectare a dispozitivelor Bluetooth.
User opens app
↓
SceneDelegate calls appData.activateCloudDeviceSync()
↓
BluetoothManager starts CBCentralManager
↓
CBCentralManager begins scanning
↓
didDiscoverPeripheral → filter by service UUIDs
↓
Check device profile (class, capabilities)
↓
Add to discoveredMeters list (UI refresh)
let targetServices = [
CBUUID(string: "FFF0"), // UM25C, UM34C
CBUUID(string: "180D"), // TC66C (generic service)
]
Meter advertise-ază: - Device name: de ex "UM25C-XXXX" sau "TC66-XXXX" - Services: FFF0 (UM series) sau 180D (TC66) - Manufacturer data: RDTech identifier
if let manufacturerData = advertisement[CBAdvertisementDataManufacturerDataKey] as? Data {
let manufacturerId = manufacturerData.withUnsafeBytes { $0.load(as: UInt16.self) }
if manufacturerId == 0x5449 { // RDTech
// Possible UM meter
}
}
func identifyMeterType(name: String, services: [CBUUID]) -> Model {
if name.contains("UM25C") || services.contains(FFF0) {
return .UM25C
} else if name.contains("UM34C") || services.contains(FFF0) {
return .UM34C
} else if name.contains("TC66") || services.contains(180D) {
return .TC66C
}
return .unknown
}
.unknown dacă uncertainUser taps meter in list
↓
AppData calls meter.connect()
↓
BluetoothManager calls cbCentralManager.connect(peripheral)
↓
didConnect: state → peripheralConnected
↓
Discover services & characteristics
// For UM series (FFF0)
let targetService = CBUUID(string: "FFF0")
let readCharacteristic = CBUUID(string: "FFF1") // read measurements
let writeCharacteristic = CBUUID(string: "FFF2") // send commands
// For TC66 (180D)
let heartRate = CBUUID(string: "2A37") // uses standard HRM characteristic
peripheralConnected
↓
discoveringServices (discovering FFF0 / 180D)
↓
discoveringCharacteristics (discovering FFF1, FFF2 / 2A37)
↓
peripheralReady (services + characteristics found)
↓
comunicating ↔ dataIsAvailable (steady state)
UM series (UM25C, UM34C): ```swift // Send command to FFF2 (write characteristic) let command = UMProtocol.buildMeasurementRequest() peripheral.writeValue(command, for: writeCharacteristic, type: .withResponse)
// Receive on FFF1 (read characteristic) // Parse payload (voltage, current, power, temperature, etc.) let measurement = UMProtocol.parseMeasurement(data) ```
TC66C:
swift
// Uses standard HRM (Heart Rate Measurement) characteristic
// Value format: flags byte + heart_rate_value (2 bytes)
// Repurposed for power data: MSB = watts, LSB = amps (approximation)
let measurement = TC66Protocol.parseMeasurement(data)
UM series supports commands:
- Request measurement: 0x00 0xF0 0xA0 0x1B (+ checksum)
- Set time: 0x02 ... (timestamp)
- Set calibration: (advanced)
peripheral.writeValue(
command,
for: writeCharacteristic,
type: .withResponse // MUST: wait for ACK
)
.withResponse pentru command-uri critice.withoutResponse pentru bulk writesUser taps "Disconnect"
↓
meter.disconnect()
↓
BluetoothManager calls cbCentralManager.cancelPeripheralConnection()
↓
didDisconnect: state → peripheralNotConnected
BT device disconnects (out of range / powered off / interference)
↓
didDisconnect event (didDisconnect reason: optional)
↓
state → peripheralNotConnected
↓
Auto-reconnect logic starts (with backoff)
Attempt 1: 1s delay
Attempt 2: 2s delay
Attempt 3: 4s delay
Attempt 4: 8s delay
Attempt 5: 16s delay
Attempt 6: 32s delay
Attempt 7+: 60s delay (capped)
Max attempts: 3
test_scanFiltersByServiceUUIDs()
test_deviceIdentification_UM25C()
test_deviceIdentification_UM34C()
test_deviceIdentification_TC66C()
test_connectionStateTransitions()
test_serviceDiscovery_UM25C()
test_characteristicDiscovery_UM25C()
test_measurementParsing_ValidPayload()
test_measurementParsing_InvalidPayload()
test_disconnectCleansUp()
test_unintentionalDropDetected()
test_autoReconnectBackoff_Exponential()
test_autoReconnectStops_OnManualDisconnect()
Error: CBError.unknown
Error: CBError.managerStatePoweredOff
Error: CBError.invalidParameters
Handling: - Retry scan periodically - Notify UI: "Bluetooth unavailable"
Error: peripheral not found
→ Retry with backoff
Error: timeout (no services found)
→ Disconnect + retry
Error: security/pairing required
→ Notify user: "Pair device in Settings"
Error: write failed
→ Retry measurement request
Error: invalid payload
→ Log error, skip measurement
Error: characteristic not found
→ Disconnect + mark incompatible
CoreBluetooth: CBCentralManager, CBPeripheralUMProtocol: payload parsing for UM seriesTC66Protocol: payload parsing for TC66CMeterCapabilities: device type detectionAppData: orchestration