import type { DeviceAdapter, NormalizedReading } from '../types'; // Bluetooth SIG Blood Pressure Service const BPS_SERVICE = '00001810-0000-1000-8000-00805f9b34fb'; const BPM_CHARACTERISTIC = '00002A35-0000-1000-8000-00805f9b34fb'; interface BPMData { systolic: number; diastolic: number; map: number; unit: string; pulseRate?: number; } // SFLOAT (IEEE 11073 16-bit float): mantissa bits 0-11, exponent bits 12-15 function readSfloat(view: DataView, byteOffset: number): number { const raw = view.getUint16(byteOffset, true); const mantissa = raw & 0x0FFF; const exponent = (raw >> 12) & 0x0F; const signedMantissa = mantissa > 0x07FF ? mantissa - 0x1000 : mantissa; const signedExponent = exponent > 0x07 ? exponent - 0x10 : exponent; return signedMantissa * Math.pow(10, signedExponent); } function parseBloodPressureMeasurement(data: ArrayBuffer): BPMData | null { const view = new DataView(data); if (view.byteLength < 7) return null; let offset = 0; const flags = view.getUint8(offset); offset += 1; const systolic = readSfloat(view, offset); offset += 2; const diastolic = readSfloat(view, offset); offset += 2; const map = readSfloat(view, offset); offset += 2; let pulseRate: number | undefined; if (flags & 0x01) offset += 7; // timestamp if (flags & 0x02) { pulseRate = readSfloat(view, offset); } return { systolic: Math.round(systolic * 10) / 10, diastolic: Math.round(diastolic * 10) / 10, map: Math.round(map * 10) / 10, unit: 'mmHg', pulseRate, }; } export const BloodPressureAdapter: DeviceAdapter = { name: 'Blood Pressure Monitor', supportedModels: [ 'AND UA-651BLE', 'Omron HEM-7322', 'Omron BLE', 'BP Monitor', 'A&D BLE', 'iHealth BP', 'Beurer BM', 'Yuwell BLE', ], serviceUUIDs: [BPS_SERVICE], notifyCharacteristics: [ { service: BPS_SERVICE, characteristic: BPM_CHARACTERISTIC }, ], readCharacteristics: [ { service: BPS_SERVICE, characteristic: BPM_CHARACTERISTIC }, ], parseNotification( _serviceUUID: string, charUUID: string, data: ArrayBuffer, ): NormalizedReading[] { if (charUUID.toUpperCase() !== BPM_CHARACTERISTIC.toUpperCase()) return []; const parsed = parseBloodPressureMeasurement(data); if (!parsed) return []; const measuredAt = new Date().toISOString(); const readings: NormalizedReading[] = [ { device_type: 'blood_pressure', metric: 'systolic', values: { value: parsed.systolic, unit: parsed.unit }, measured_at: measuredAt, }, { device_type: 'blood_pressure', metric: 'diastolic', values: { value: parsed.diastolic, unit: parsed.unit }, measured_at: measuredAt, }, { device_type: 'blood_pressure', metric: 'map', values: { value: parsed.map, unit: parsed.unit }, measured_at: measuredAt, }, ]; if (parsed.pulseRate != null) { readings.push({ device_type: 'heart_rate', metric: 'pulse_rate', values: { value: parsed.pulseRate, unit: 'bpm' }, measured_at: measuredAt, }); } return readings; }, parseReadResponse( serviceUUID: string, charUUID: string, data: ArrayBuffer, ): NormalizedReading[] { return this.parseNotification(serviceUUID, charUUID, data); }, };