feat(miniprogram): BLE 设备同步模块 — 扫描+连接+数据上传
- Task 18: BLE 类型定义(NormalizedReading/DeviceAdapter/BLEDevice)+ BLEManager 连接管理器 - Task 19: XiaomiBandAdapter 心率读取适配器(标准 HRS Service 0x180D) - Task 20: device-sync API 层 + 设备同步页面 + app.config 路由注册
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import type { DeviceAdapter, NormalizedReading } from '../types';
|
||||
|
||||
/**
|
||||
* 小米手环 BLE 适配器
|
||||
*
|
||||
* 支持 Mi Band 7/8 等型号,使用标准 Heart Rate Service 读取心率数据。
|
||||
* 未来可扩展步数(0xFEE1)、睡眠等 Mi Band 专有 Service。
|
||||
*/
|
||||
|
||||
// 标准 BLE Heart Rate Service
|
||||
const HRS_SERVICE = '0000180D-0000-1000-8000-00805F9B34FB';
|
||||
const HRM_CHARACTERISTIC = '00002A37-0000-1000-8000-00805F9B34FB';
|
||||
|
||||
// 小米 Mi Band 专有 Service(步数/活动量)
|
||||
// const MI_BAND_SERVICE = '0000FEE1-0000-1000-8000-8000-00805F9B34FB';
|
||||
|
||||
/** 解析心率测量值(Heart Rate Measurement 格式) */
|
||||
function parseHeartRate(data: ArrayBuffer): number | null {
|
||||
const view = new DataView(data);
|
||||
if (view.byteLength < 2) return null;
|
||||
|
||||
const flags = view.getUint8(0);
|
||||
// Bit 0: Heart Rate Format — 0 = UINT8, 1 = UINT16
|
||||
const isUINT16 = (flags & 0x01) !== 0;
|
||||
|
||||
if (isUINT16) {
|
||||
if (view.byteLength < 3) return null;
|
||||
return view.getUint16(1, true); // little-endian
|
||||
}
|
||||
return view.getUint8(1);
|
||||
}
|
||||
|
||||
export const XiaomiBandAdapter: DeviceAdapter = {
|
||||
name: 'Xiaomi Band',
|
||||
|
||||
supportedModels: [
|
||||
'Mi Band',
|
||||
'Mi Smart Band',
|
||||
'Xiaomi Band',
|
||||
'Xiaomi Smart Band',
|
||||
'MI BAND',
|
||||
'MiBand',
|
||||
],
|
||||
|
||||
serviceUUIDs: [HRS_SERVICE],
|
||||
|
||||
notifyCharacteristics: [
|
||||
{ service: HRS_SERVICE, characteristic: HRM_CHARACTERISTIC },
|
||||
],
|
||||
|
||||
readCharacteristics: [
|
||||
{ service: HRS_SERVICE, characteristic: HRM_CHARACTERISTIC },
|
||||
],
|
||||
|
||||
parseNotification(
|
||||
_serviceUUID: string,
|
||||
charUUID: string,
|
||||
data: ArrayBuffer,
|
||||
): NormalizedReading | null {
|
||||
if (charUUID.toUpperCase().includes('2A37')) {
|
||||
const hr = parseHeartRate(data);
|
||||
if (hr !== null && hr > 0 && hr < 300) {
|
||||
return {
|
||||
device_type: 'heart_rate',
|
||||
values: { heart_rate: hr },
|
||||
measured_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
parseReadResponse(
|
||||
_serviceUUID: string,
|
||||
_charUUID: string,
|
||||
_data: ArrayBuffer,
|
||||
): NormalizedReading | null {
|
||||
// 读取模式暂不支持,使用通知模式获取数据
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
export default XiaomiBandAdapter;
|
||||
Reference in New Issue
Block a user