蓝牙设备接入
家用健康设备通过晓葆检测插件(XB_HealthHub)以微信小程序插件形式接入。 厂家提供蓝牙协议文档或 SDK,由插件完成 BLE 连接、数据采集、结果上报全流程, 宿主小程序无需感知设备细节。
概述
检测插件以标准化架构分层管理设备接入:
- UI 层:只订阅检测状态,不直接操作蓝牙 API
- MeasureSession:管理一次完整检测的生命周期(状态机)
- BleManager:GATT 连接/断连/分包/心跳,不感知业务协议
- DeviceDriver:帧解析、指令生成、结果构建,不操作 UI,不发网络请求
- DeviceRegistry:广播名/SN → Driver 的静态映射表
新增设备不改现有层:只需新增 Driver 类 + 在 Registry 注册一行, UI 层、BleManager、上报链路均不变。
接入模式选择
| 模式 | 适用场景 | 用户体验 | 典型能力 |
|---|---|---|---|
| Pattern A | 有蓝牙协议文档,可自解析帧格式 | 即测即得,几十秒内出结果 | 血压、血糖、血氧等直出指标 |
| Pattern B1 | 厂商提供微信插件 SDK,需云端算法同步返回结果 | 稍等几秒,云端算完后出结果 | 体成分分析等需云算法的检测 |
| Pattern B2 | 厂商提供端侧 SDK 采集,云算法异步回调结果 | 采集结束后「报告生成中」,稍后 Webhook 补齐 | 心电等需异步算法与报告文件的检测 |
Pattern A — BLE 直连自解析
插件直接通过 BLE GATT 接收设备数据帧,由 Driver 自行解析,无需厂商 SDK 或云端算法。 适合有完整蓝牙协议文档的设备型号。
接入步骤:
- 厂商提供蓝牙协议文档(Service UUID、Characteristic UUID、帧格式、CRC 算法)
- 晓葆研发实现 Driver,核心方法:
getBleConfig():返回 serviceId / writeCharId / notifyCharIdgetPacketConfig():帧头字节 + 帧长度计算函数(用于分包重组)generateCmd(action):生成 start/stop 指令 ArrayBufferparseFrame(frame):解析完整帧,返回 metrics + liveData + _done 标志buildResult():构建最终 MeasureResult
- 在 DeviceRegistry 注册广播名与 Driver 的映射关系
- 厂商提供样机与联调环境,走接入 Checklist 验收
帧解析契约:
// parseFrame 返回值结构
{
metrics: {}, // 中间帧暂无最终指标时为 {}
liveData: { ... }, // 实时展示字段(与 devices.js liveFeedback.liveField 对应)
_done: true | false, // 测量完成标志
rawHex: '...', // 完整帧原始字节(可选,用于问题复现)
}
// 设备错误帧
{ _error: true, errorCode: 'E_DEV_XXX' }
// 非业务帧(忽略)
nullPattern B1 — 厂商插件 + 云算法(同步)
厂商提供官方微信小程序插件或 SDK,插件采集原始数据(如体重 + 阻抗)后, 由晓葆服务端调用厂商云算法同步计算结果,写入 MeasureResult。
- 厂商 API 凭证由晓葆服务端持有,不下发到插件
- 插件只上传原始采集值,不直连厂商云
- 算法 API 需报备晓葆服务端 IP 至厂商白名单
- 用户体验:采集完成后稍等几秒,指标一次性展示
Pattern B2 — 端 SDK + 云算法(异步回调)
厂商提供端侧采集 SDK,插件用 SDK 采集完整原始数据(如波形), 上传晓葆服务端后异步调厂商算法云,算法结果通过回调写入平台。
- 厂商 API 凭证由晓葆服务端持有
- 算法云通过
callBackPath(晓葆服务端提供)异步推送结果 - 会话状态机扩展:
measuring → device_storing → uploading_to_vendor → waiting_vendor_result → uploading → done - 宿主通过 Webhook
report.generated事件接收最终报告 - 可支持两阶段推送:初报(快)+ 报告就绪(含 PDF 等附件,稍慢)
用户身份协议
插件启动时通过 URL 参数获取用户身份,支持两种模式。 宿主服务端负责生成跳转签名,不在前端生成。
两种用户档案模式
| 模式 | 参数 | 适用场景 | 档案来源 |
|---|---|---|---|
resolve | thirdUserId | 第三方渠道(如远盟、新华保险) | 插件调 /open/v1/integrations/{channel}/resolve-user |
xiaobao_session | sessionToken | 健康站、晓葆自有品牌 App | 插件调 /open/v1/plugin/resolve-session |
跳转参数(完整)
wx.navigateTo({
url: `plugin://xiaobao-measure/index?${qs.stringify({
// 必填·鉴权(宿主服务端生成)
appKey,
timestamp, // Unix 秒级时间戳
nonce, // 随机字符串
sign, // HMAC-SHA256 签名
// 必填·用户身份(二选一)
thirdUserId, // resolve 模式
sessionToken, // xiaobao_session 模式
// 可选·检测配置
deviceKey, // 不传则进入设备选择页
measureMode, // 仅 HOME_MULTI_BIO_MONITOR:BP | BG
deviceSn, // 健康站扫码时指定设备 SN
stationId, // 健康站 ID
// 可选·UI(不纳入签名)
env, // prod | sandbox
title, // 自定义检测页标题
noSound, // 1=静音
metadata, // 自定义字符串,原样透传至 Webhook 与报告详情 API
})}`
})签名生成规则(宿主服务端)
// 待签字符串 = appKey + timestamp + nonce(按此顺序拼接,无分隔符)
const signStr = appKey + timestamp + nonce;
// 签名 = HMAC-SHA256(signStr, appSecret),结果取十六进制小写
const sign = hmacSha256(signStr, appSecret);appSecret 只能存在于宿主服务端,不得写入小程序前端代码。 UI 参数(env/title/noSound/scene/metadata)不纳入签名计算。检测结束回传(Storage)
插件检测结束后写入 wx.setStorageSync,宿主在 onShow 中读取并清除:
// 读取
const result = wx.getStorageSync('xiaobao_measure_result');
wx.removeStorageSync('xiaobao_measure_result');
// 结构
{
status: 'success' | 'cancel' | 'error',
measureId: 'xxx', // 本次检测唯一 ID,关联 Webhook 回调
deviceKey: 'HOME_MULTI_BIO_MONITOR',
summary: '收缩压 128 / 舒张压 82', // 供宿主 toast 展示
}数据规范
MeasureResult 数据契约
所有 Driver 输出统一结构,上层不感知设备差异:
interface MeasureResult {
deviceSn: string // 设备序列号(BLE 广播名作为展示 SN)
deviceKey: string // 对应平台 device_key
measureMode?: string // BP | BG | SPO2 | ECG | BCA;多模式设备必填
measuredAt: number // Unix 毫秒时间戳
metrics: Record<string, number> // 结构化指标,key 见下方命名约定
rawFile?: string // Pattern B:原始波形文件本地路径
waveData?: number[] // ECG 实时波形点(仅页面绘制,不上报)
rawHex?: string // 原始字节备份(用于问题复现)
}measureMode 枚举
| device_key | measureMode | 中文 |
|---|---|---|
HOME_MULTI_BIO_MONITOR | BP / BG | 血压 / 血糖 |
HOME_SPO2_RING | SPO2 | 血氧 |
HEARTLOG_ECG_RECORDER | ECG | 心电 |
BODY_COMPOSITION_ANALYSIS | BCA | 人体成分 |
metrics 命名约定
metrics 的 key 以平台设备指标标准码(standard_indicator_code)为准。 新设备接入前须在平台指标库完成映射并发布;具体字段由各设备型号独立配置,本规范不枚举品类指标清单。
liveData 实时字段
Driver 的 parseFrame 可在 liveData 中返回实时数据, 供检测过程中动态展示。通用保留字段:
| 字段 | 类型 | 含义 |
|---|---|---|
batteryPct | number | 电量百分比(0–100,Driver 内归一化) |
signalOk | boolean | 信号是否有效 |
probeOff | boolean | 探头 / 传感器是否脱落 |
报告回传
检测完成并上报开放平台后,平台通过 Webhook 向宿主服务端推送事件。 宿主需在控制台配置 Webhook 地址并完成验签。
核心事件:report.generated
{
"event": "report.generated",
"deliveryId": "evt_abc123", // Webhook 投递唯一 ID(幂等键)
"timestamp": 1722000000,
"data": {
"measureId": "msr_xxx",
"deviceKey": "YOUR_DEVICE_KEY",
"measureMode": "BP",
"measuredAt": 1722000000000, // Unix 毫秒
"metrics": {
"indicator_code_a": 128,
"indicator_code_b": 82
},
"thirdUserId": "user_from_channel", // resolve 模式时存在
"metadata": "your-custom-string" // 原样透传
}
}Pattern B2 两阶段推送
Pattern B2(云算法异步回调)报告推送分两个阶段:
| 阶段 | 事件 | 包含内容 | 时间 |
|---|---|---|---|
| 初报 | report.generated | 基础 metrics(部分指标) | 采集完成后数秒内 |
| 报告就绪 | report.ready | 全量指标 + 报告文件 URL(如 PDF)+ H5 报告页 URL | 算法回调后(数秒至数十秒) |
用户身份在 Webhook 中的体现
| 接入场景 | Webhook 中的用户标识字段 |
|---|---|
| 第三方渠道(resolve 模式) | thirdUserId |
| 健康站 / 晓葆品牌 App | 晓葆用户体系 ID |
接入 Checklist
厂商 / 商务(立项前)
- 明确设备型号与出货量,是否独家
- 确认能提供蓝牙协议文档(Pattern A)或 SDK + 样机(Pattern B)
- 判断接入模式(Pattern A / B1 / B2)
- Pattern B2 需评估厂商算法云稳定性与回调 SLA
运营(上线前)
- 在控制台为对应
appKey开通设备白名单(device_whitelist) - 确认微信插件版本与 request 合法域名
- Pattern B:确认厂商凭证已配置,晓葆服务端 IP 已加入厂商白名单
- 准备联调账号与沙箱 Webhook 地址
研发(实现与验收)
- 新增 Driver 文件,实现必要接口,写单元测试(≥ 90% 覆盖核心帧类型)
- 在 DeviceRegistry 注册广播名 + Driver 映射
- 在 devices.js 补充 UI 配置(
liveFeedback.liveField) - 确认
metricskey 与平台指标库standard_indicator_code一致 - Pattern B:服务端厂商代理接口 + 队列配置
联合验收(上线门槛)
- 沙箱走通:连接 → 测量 → 结果页 → Webhook 落库
- 覆盖:正常值、边界值、断连重试、低电量、用户中途退出
- Pattern B2:额外验收 PDF 生成与异步回调时延
- 合作方确认
metrics字段满足展示与落库需求