- 新增 VeepooBridge API:精准睡眠读取(readPreciseSleepData)、B3自动测量配置 (readAutoTestConfig/setAutoTestConfig)、开关设置(setAutoHeartRate/BP/Temp)、 体温自动数据读取(readAutoTemperatureData),共 10 个新 API - 新增 SDK 事件类型:SDK_EVENT_SLEEP(4)、SDK_EVENT_AUTO_TEST(54) - VeepooPipeline 新增:readSleepData/readAllSleepData(enableAutoMeasurement 睡眠数据 Promise 化读取 + 自动测量一键开启 - VeepooHistoryReader 新增:uploadSleepReadings 睡眠数据上传 - stores/veepoo.ts 实装:注册 onSleepData 回调、syncHistory 实际读取+上传、 readSleepData 状态管理、enableAutoMeasurement、连接后自动触发三件事 - 原生页面(native/pkg-veepoo):_onReady 后自动读取 3 天睡眠 + 开启自动测量, 新增 _readSleepData/_handleSleepEvent/_enableAutoMeasurement - UI 重构:测量页药丸式选择器+SVG 圆环仪表盘+健康评估标签 - 数据上传页:2 列结果卡片网格+彩色条标识+睡眠数据卡片(★评分+总时长) - 修复上传按钮无响应 bug:patientId 增加 URL fallback + 错误提示不再静默 - 设计原型:docs/design/veepoo-measure-prototype.html(4 状态预览)
638 lines
21 KiB
JavaScript
638 lines
21 KiB
JavaScript
/**
|
||
* Veepoo M2 原生小程序页面 — 连接 + 测量
|
||
*
|
||
* 完全脱离 Taro 框架,直接使用微信原生 API + Veepoo SDK。
|
||
* 流程严格对齐官方 Demo:
|
||
* onLoad 注册全局监听器
|
||
* → scan → stopScan → connect(等待 connection:true)
|
||
* → delay 500ms → authenticate
|
||
* → SDK 事件(type=1) / Storage 轮询 → ready
|
||
*/
|
||
// eslint-disable-next-line no-undef
|
||
const { veepooBle, veepooFeature, veepooLogger } = require('./libs/veepoo-sdk');
|
||
|
||
// ── 常量 ──
|
||
|
||
var SDK_EVENT_AUTH = 1;
|
||
var SDK_EVENT_BATTERY = 2;
|
||
var SDK_EVENT_SLEEP = 4;
|
||
var SDK_EVENT_DAILY = 5;
|
||
var SDK_EVENT_TEMPERATURE = 6;
|
||
var SDK_EVENT_BLOOD_PRESSURE = 18;
|
||
var SDK_EVENT_BLOOD_OXYGEN = 31;
|
||
var SDK_EVENT_HEART_RATE = 51;
|
||
var SDK_EVENT_PRESSURE = 58;
|
||
var SDK_EVENT_AUTO_TEST = 54;
|
||
|
||
var MEASURE_TYPES = [
|
||
{ type: 'heart_rate', label: '心率', unit: 'bpm', icon: '♥', color: '#EF4444', sdkType: SDK_EVENT_HEART_RATE },
|
||
{ type: 'blood_oxygen', label: '血氧', unit: '%', icon: 'O₂', color: '#3B82F6', sdkType: SDK_EVENT_BLOOD_OXYGEN },
|
||
{ type: 'blood_pressure', label: '血压', unit: 'mmHg', icon: '↕', color: '#8B5CF6', sdkType: SDK_EVENT_BLOOD_PRESSURE },
|
||
{ type: 'temperature', label: '体温', unit: '°C', icon: 'T', color: '#F59E0B', sdkType: SDK_EVENT_TEMPERATURE },
|
||
{ type: 'pressure', label: '压力', unit: '', icon: '~', color: '#6366F1', sdkType: SDK_EVENT_PRESSURE },
|
||
];
|
||
|
||
var MEASURE_TIMEOUTS = {
|
||
heart_rate: 60000,
|
||
blood_oxygen: 60000,
|
||
blood_pressure: 120000,
|
||
temperature: 60000,
|
||
pressure: 90000,
|
||
};
|
||
|
||
var MEASURE_SETTLE_DELAY = 1500;
|
||
|
||
function _findConfig(type) {
|
||
for (var i = 0; i < MEASURE_TYPES.length; i++) {
|
||
if (MEASURE_TYPES[i].type === type) return MEASURE_TYPES[i];
|
||
}
|
||
return MEASURE_TYPES[0];
|
||
}
|
||
|
||
// ── Page ──
|
||
|
||
// eslint-disable-next-line no-undef
|
||
Page({
|
||
data: {
|
||
phase: 'idle',
|
||
deviceId: '',
|
||
deviceName: 'M2',
|
||
batteryLevel: null,
|
||
error: '',
|
||
selectedType: 'heart_rate',
|
||
selectedIcon: '♥',
|
||
selectedColor: '#EF4444',
|
||
selectedLabel: '心率',
|
||
selectedUnit: 'bpm',
|
||
measureTypes: MEASURE_TYPES,
|
||
measurePhase: 'idle',
|
||
measureProgress: 0,
|
||
measureDisplayValue: '',
|
||
measureError: '',
|
||
results: {},
|
||
hasResults: false,
|
||
},
|
||
|
||
_authTimer: null,
|
||
_authTimeout: null,
|
||
_scanTimer: null,
|
||
_scanFound: null,
|
||
_measureTimer: null,
|
||
_settleTimer: null,
|
||
_lastValues: null,
|
||
_connected: false,
|
||
_eventChannel: null,
|
||
_connecting: false,
|
||
_listenersRegistered: false,
|
||
|
||
// ── 生命周期 ──
|
||
|
||
onLoad: function () {
|
||
// eslint-disable-next-line no-undef
|
||
this._eventChannel = this.getOpenerEventChannel();
|
||
// eslint-disable-next-line no-undef
|
||
wx.setNavigationBarTitle({ title: 'M2 手环测量' });
|
||
this._updateSelectedDisplay('heart_rate');
|
||
|
||
// 注意:不在 onLoad 注册 veepooWeiXinSDKNotifyMonitorValueChange!
|
||
// 该函数内部会调用 wx.notifyBLECharacteristicValueChange,需要蓝牙适配器已初始化。
|
||
// onLoad 时适配器未初始化 → 返回 "notifyBLECharacteristicValueChange:fail:not init"
|
||
// 监听器改在 _doConnect 的 connection:true 回调中注册。
|
||
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 页面已加载');
|
||
},
|
||
|
||
onUnload: function () {
|
||
this._cleanup();
|
||
},
|
||
|
||
_cleanup: function () {
|
||
if (this._authTimer) { clearInterval(this._authTimer); this._authTimer = null; }
|
||
if (this._authTimeout) { clearTimeout(this._authTimeout); this._authTimeout = null; }
|
||
if (this._scanTimer) { clearTimeout(this._scanTimer); this._scanTimer = null; }
|
||
if (this._measureTimer) { clearTimeout(this._measureTimer); this._measureTimer = null; }
|
||
if (this._settleTimer) { clearTimeout(this._settleTimer); this._settleTimer = null; }
|
||
this._connecting = false;
|
||
if (this._connected) {
|
||
try { veepooBle.veepooWeiXinSDKloseBluetoothAdapterManager(function () {}); } catch (e) {
|
||
// eslint-disable-next-line no-undef
|
||
console.warn('[veepoo-native] 断开异常:', e);
|
||
}
|
||
this._connected = false;
|
||
}
|
||
},
|
||
|
||
// ── 全局监听器(onLoad 注册一次) ──
|
||
|
||
_registerGlobalListeners: function () {
|
||
if (this._listenersRegistered) return;
|
||
this._listenersRegistered = true;
|
||
var self = this;
|
||
|
||
// SDK 数据监听 — 接收所有解析后的事件(auth/measure/battery 等)
|
||
veepooBle.veepooWeiXinSDKNotifyMonitorValueChange(function (data) {
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] SDK 数据事件:', JSON.stringify(data).substring(0, 500));
|
||
self._handleSdkEvent(data);
|
||
});
|
||
|
||
// BLE 连接状态变化
|
||
veepooBle.veepooWeiXinSDKBLEConnectionStateChangeManager(function (res) {
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 连接状态变化:', JSON.stringify(res));
|
||
if (!res.connected) {
|
||
self._connected = false;
|
||
self._connecting = false;
|
||
self._cancelPendingMeasure();
|
||
self.setData({ phase: 'disconnected' });
|
||
}
|
||
});
|
||
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] SDK 函数类型:', {
|
||
scanFn: typeof veepooBle.veepooWeiXinSDKStartScanDeviceAndReceiveScanningDevice,
|
||
connectFn: typeof veepooBle.veepooWeiXinSDKBleConnectionServicesCharacteristicsNotifyManager,
|
||
dataFn: typeof veepooBle.veepooWeiXinSDKNotifyMonitorValueChange,
|
||
authFn: typeof veepooFeature.veepooBlePasswordCheckManager,
|
||
});
|
||
},
|
||
|
||
_updateSelectedDisplay: function (type) {
|
||
var cfg = _findConfig(type);
|
||
this.setData({
|
||
selectedType: type,
|
||
selectedIcon: cfg.icon,
|
||
selectedColor: cfg.color,
|
||
selectedLabel: cfg.label,
|
||
selectedUnit: cfg.unit,
|
||
});
|
||
},
|
||
|
||
// ── 连接流程 ──
|
||
|
||
handleConnect: function () {
|
||
if (this.data.phase !== 'idle' && this.data.phase !== 'error' && this.data.phase !== 'disconnected') return;
|
||
if (this._connecting) return;
|
||
this._connecting = true;
|
||
|
||
this.setData({ phase: 'scanning', error: '' });
|
||
veepooLogger.setLevel(0);
|
||
|
||
var self = this;
|
||
self._scanFound = null;
|
||
|
||
veepooBle.veepooWeiXinSDKStartScanDeviceAndReceiveScanningDevice(function (res) {
|
||
var device = Array.isArray(res) ? res[0] : res;
|
||
if (!device) return;
|
||
var name = (device.localName || device.name || '').toUpperCase();
|
||
var deviceId = device.deviceId || device.mac || '';
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 扫描到:', name, deviceId);
|
||
if (!self._scanFound && (name.indexOf('M2') !== -1 || name.indexOf('VPM') !== -1 || name.indexOf('VEEPOO') !== -1)) {
|
||
self._scanFound = device;
|
||
veepooBle.veepooWeiXinSDKStopSearchBleManager(function () {
|
||
self._doConnect(device);
|
||
});
|
||
}
|
||
});
|
||
|
||
this._scanTimer = setTimeout(function () {
|
||
if (!self._scanFound) {
|
||
veepooBle.veepooWeiXinSDKStopSearchBleManager(function () {});
|
||
self._connecting = false;
|
||
self.setData({ phase: 'error', error: '未找到 M2 设备,请确保手环已开机且蓝牙已开启' });
|
||
}
|
||
}, 15000);
|
||
},
|
||
|
||
_doConnect: function (device) {
|
||
this.setData({ phase: 'connecting' });
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 开始连接:', device.deviceId || device.mac);
|
||
var self = this;
|
||
|
||
veepooBle.veepooWeiXinSDKBleConnectionServicesCharacteristicsNotifyManager(device, function (result) {
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 连接阶段回调:', JSON.stringify(result).substring(0, 300));
|
||
|
||
// 只响应最终就绪回调(connection:true)
|
||
if (result.connection === true) {
|
||
self._connected = true;
|
||
self._connecting = false;
|
||
self.setData({
|
||
deviceId: device.deviceId || device.mac || '',
|
||
});
|
||
|
||
// 关键:在连接就绪后注册数据监听器
|
||
// veepooWeiXinSDKNotifyMonitorValueChange 内部会调用
|
||
// wx.notifyBLECharacteristicValueChange,需要蓝牙适配器已初始化+已连接
|
||
self._registerGlobalListeners();
|
||
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 连接就绪,监听器已注册,500ms 后发送认证');
|
||
|
||
setTimeout(function () {
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 调用 veepooBlePasswordCheckManager');
|
||
veepooFeature.veepooBlePasswordCheckManager();
|
||
self.setData({ phase: 'authenticating' });
|
||
}, 500);
|
||
|
||
// Storage 轮询兜底
|
||
self._authTimer = setInterval(function () {
|
||
try {
|
||
// eslint-disable-next-line no-undef
|
||
var status = wx.getStorageSync('deviceChipStatus');
|
||
// SDK 可能写入字符串或布尔值 true
|
||
if (status === 'successfulVerification' || status === 'passTheVerification' || status === true) {
|
||
clearInterval(self._authTimer);
|
||
self._authTimer = null;
|
||
self._onReady();
|
||
}
|
||
} catch (_) { /* ignore */ }
|
||
}, 500);
|
||
|
||
self._authTimeout = setTimeout(function () {
|
||
if (self._authTimer) {
|
||
clearInterval(self._authTimer);
|
||
self._authTimer = null;
|
||
self._connecting = false;
|
||
// eslint-disable-next-line no-undef
|
||
console.error('[veepoo-native] 认证超时 deviceChipStatus=', wx.getStorageSync('deviceChipStatus'));
|
||
self.setData({ phase: 'error', error: '设备认证超时,请重新连接' });
|
||
}
|
||
}, 8000);
|
||
}
|
||
});
|
||
},
|
||
|
||
_onReady: function () {
|
||
if (this._authTimeout) { clearTimeout(this._authTimeout); this._authTimeout = null; }
|
||
this._connecting = false;
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 认证成功,设备就绪');
|
||
this.setData({ phase: 'ready' });
|
||
veepooFeature.veepooReadElectricQuantityManager();
|
||
|
||
// 认证成功后自动读取 3 天睡眠数据 + 开启自动测量
|
||
this._readSleepData();
|
||
this._enableAutoMeasurement();
|
||
},
|
||
|
||
// ── SDK 事件路由 ──
|
||
|
||
_handleSdkEvent: function (data) {
|
||
if (!data || data.type === undefined) return;
|
||
var type = data.type;
|
||
|
||
if (type === SDK_EVENT_AUTH) {
|
||
var content = data.content || {};
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 认证事件: VPDeviceAck=' + content.VPDeviceAck + ' VPDevicepassword=' + content.VPDevicepassword);
|
||
// VPDeviceAck 是认证结果(successfulVerification/passTheVerification)
|
||
// VPDevicepassword 是设备密码原始值(如 "0000"),不是认证结果
|
||
if (content.VPDeviceAck === 'successfulVerification' || content.VPDeviceAck === 'passTheVerification') {
|
||
if (this._authTimer) { clearInterval(this._authTimer); this._authTimer = null; }
|
||
if (this._authTimeout) { clearTimeout(this._authTimeout); this._authTimeout = null; }
|
||
this._onReady();
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (type === SDK_EVENT_BATTERY) {
|
||
var pct = (data.content || {}).VPDeviceElectricPercent;
|
||
if (pct !== undefined && pct !== null) {
|
||
this.setData({ batteryLevel: Number(pct) });
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 睡眠数据回调(type=4)
|
||
if (type === SDK_EVENT_SLEEP) {
|
||
this._handleSleepEvent(data);
|
||
return;
|
||
}
|
||
|
||
// 日常数据回调(type=5)
|
||
if (type === SDK_EVENT_DAILY) {
|
||
// 日常数据用于历史同步,原生页面暂不处理
|
||
return;
|
||
}
|
||
|
||
// 自动测量配置回调(type=54)
|
||
if (type === SDK_EVENT_AUTO_TEST) {
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 自动测量配置回调');
|
||
return;
|
||
}
|
||
|
||
for (var i = 0; i < MEASURE_TYPES.length; i++) {
|
||
if (MEASURE_TYPES[i].sdkType === type) {
|
||
this._handleMeasureEvent(MEASURE_TYPES[i].type, data);
|
||
return;
|
||
}
|
||
}
|
||
},
|
||
|
||
// ── 测量流程 ──
|
||
|
||
handleSelectType: function (e) {
|
||
var type = e.currentTarget.dataset.type;
|
||
if (this.data.measurePhase === 'measuring') return;
|
||
this._updateSelectedDisplay(type);
|
||
this.setData({ measureError: '' });
|
||
},
|
||
|
||
handleStartMeasure: function () {
|
||
var type = this.data.selectedType;
|
||
if (this.data.measurePhase === 'measuring') return;
|
||
if (!this._connected) {
|
||
this.setData({ measureError: '设备未连接' });
|
||
return;
|
||
}
|
||
|
||
var self = this;
|
||
self._lastValues = null;
|
||
|
||
self.setData({
|
||
measurePhase: 'measuring',
|
||
measureProgress: 0,
|
||
measureDisplayValue: '',
|
||
measureError: '',
|
||
});
|
||
|
||
self._sendMeasureCommand(type, true);
|
||
|
||
self._measureTimer = setTimeout(function () {
|
||
self._onMeasureError('测量超时,请重试');
|
||
}, MEASURE_TIMEOUTS[type] || 60000);
|
||
},
|
||
|
||
handleCancelMeasure: function () {
|
||
this._cancelPendingMeasure();
|
||
this.setData({
|
||
measurePhase: 'idle',
|
||
measureProgress: 0,
|
||
measureDisplayValue: '',
|
||
});
|
||
},
|
||
|
||
handleDisconnect: function () {
|
||
this._cleanup();
|
||
this.setData({ phase: 'idle', deviceId: '', batteryLevel: null, error: '' });
|
||
},
|
||
|
||
handleBack: function () {
|
||
var results = this.data.results;
|
||
if (Object.keys(results).length > 0) {
|
||
try {
|
||
// eslint-disable-next-line no-undef
|
||
wx.setStorageSync('hms:veepoo_measure_results', JSON.stringify(results));
|
||
} catch (_) { /* ignore */ }
|
||
}
|
||
if (this._eventChannel) {
|
||
this._eventChannel.emit('measureComplete', { results: results, count: Object.keys(results).length });
|
||
}
|
||
// eslint-disable-next-line no-undef
|
||
wx.navigateBack({ delta: 1 });
|
||
},
|
||
|
||
handleResetResult: function () {
|
||
var type = this.data.selectedType;
|
||
var newResults = Object.assign({}, this.data.results);
|
||
delete newResults[type];
|
||
this.setData({
|
||
measurePhase: 'idle',
|
||
measureProgress: 0,
|
||
measureDisplayValue: '',
|
||
measureError: '',
|
||
results: newResults,
|
||
hasResults: Object.keys(newResults).length > 0,
|
||
});
|
||
},
|
||
|
||
// ── 测量事件处理 ──
|
||
|
||
_handleMeasureEvent: function (type, data) {
|
||
if (this.data.selectedType !== type || this.data.measurePhase !== 'measuring') return;
|
||
|
||
var content = data.content || {};
|
||
var self = this;
|
||
|
||
if (content.deviceBusy === true) { self._onMeasureError('设备正忙,请稍后重试'); return; }
|
||
if (content.notWear === true || data.state === 6) { self._onMeasureError('请将手环佩戴到手腕上'); return; }
|
||
if (data.state === 7) { self._onMeasureError('设备正在充电'); return; }
|
||
if (data.state === 8) { self._onMeasureError('设备电量不足'); return; }
|
||
if (type === 'pressure' && data.ack === 2) { self._onMeasureError('设备电量不足'); return; }
|
||
if (type === 'pressure' && data.ack === 3) { self._onMeasureError('设备正在测量其他数据'); return; }
|
||
if (type === 'pressure' && data.ack === 4) { self._onMeasureError('佩戴检测未通过'); return; }
|
||
|
||
var values = self._extractValues(type, content);
|
||
if (!values) return;
|
||
|
||
self._lastValues = values;
|
||
|
||
var displayVal = self._formatValues(type, values);
|
||
var progress = data.Progress !== undefined ? data.Progress : 0;
|
||
self.setData({
|
||
measureDisplayValue: displayVal,
|
||
measureProgress: Math.max(progress, 0),
|
||
});
|
||
|
||
if (progress >= 100) {
|
||
self._onMeasureSuccess(type, values);
|
||
return;
|
||
}
|
||
|
||
if (!self._settleTimer) {
|
||
self._settleTimer = setTimeout(function () {
|
||
if (self._lastValues && self.data.measurePhase === 'measuring') {
|
||
self._onMeasureSuccess(type, self._lastValues);
|
||
}
|
||
}, MEASURE_SETTLE_DELAY);
|
||
}
|
||
},
|
||
|
||
_extractValues: function (type, content) {
|
||
switch (type) {
|
||
case 'heart_rate': {
|
||
var hr = Number(content.heartRate);
|
||
return (hr >= 30 && hr <= 250) ? { heart_rate: hr } : null;
|
||
}
|
||
case 'blood_oxygen': {
|
||
var bo = Number(content.bloodOxygen);
|
||
return (bo >= 70 && bo <= 100) ? { blood_oxygen: bo } : null;
|
||
}
|
||
case 'blood_pressure': {
|
||
var high = Number(content.bloodPressureHigh);
|
||
var low = Number(content.bloodPressureLow);
|
||
return (high > 0 && low > 0) ? { systolic: high, diastolic: low } : null;
|
||
}
|
||
case 'temperature': {
|
||
var temp = Number(content.bodyTemperature);
|
||
return (temp > 30 && temp < 45) ? { temperature: temp } : null;
|
||
}
|
||
case 'pressure': {
|
||
var p = Number(content.pressure);
|
||
return (p >= 0 && p <= 100) ? { pressure: p } : null;
|
||
}
|
||
default: return null;
|
||
}
|
||
},
|
||
|
||
_formatValues: function (type, values) {
|
||
if (type === 'blood_pressure') {
|
||
return (values.systolic != null ? values.systolic : '--') + '/' + (values.diastolic != null ? values.diastolic : '--');
|
||
}
|
||
var v = Object.values(values)[0];
|
||
return (v !== undefined && v !== null) ? String(v) : '--';
|
||
},
|
||
|
||
_onMeasureSuccess: function (type, values) {
|
||
this._cancelPendingMeasure();
|
||
|
||
var result = { type: type, values: values, measuredAt: Date.now() };
|
||
var newResults = Object.assign({}, this.data.results);
|
||
newResults[type] = result;
|
||
|
||
this.setData({
|
||
measurePhase: 'success',
|
||
measureProgress: 100,
|
||
measureDisplayValue: this._formatValues(type, values),
|
||
results: newResults,
|
||
hasResults: true,
|
||
});
|
||
|
||
if (this._eventChannel) {
|
||
this._eventChannel.emit('measureResult', result);
|
||
}
|
||
},
|
||
|
||
_onMeasureError: function (msg) {
|
||
this._cancelPendingMeasure();
|
||
this.setData({ measurePhase: 'error', measureError: msg });
|
||
},
|
||
|
||
_cancelPendingMeasure: function () {
|
||
if (this._measureTimer) { clearTimeout(this._measureTimer); this._measureTimer = null; }
|
||
if (this._settleTimer) { clearTimeout(this._settleTimer); this._settleTimer = null; }
|
||
this._lastValues = null;
|
||
|
||
var type = this.data.selectedType;
|
||
if (type) this._sendMeasureCommand(type, false);
|
||
},
|
||
|
||
_sendMeasureCommand: function (type, on) {
|
||
switch (type) {
|
||
case 'heart_rate':
|
||
veepooFeature.veepooSendHeartRateTestSwitchManager({ switch: !!on });
|
||
break;
|
||
case 'blood_oxygen':
|
||
veepooFeature.veepooSendBloodOxygenControlDataManager({ switch: on ? 'start' : 'stop' });
|
||
break;
|
||
case 'blood_pressure':
|
||
veepooFeature.veepooSendReadUniversalBloodPressureDataManager({ switch: on ? 'start' : 'stop' });
|
||
break;
|
||
case 'temperature':
|
||
veepooFeature.veepooSendTemperatureMeasurementSwitchManager({ switch: !!on });
|
||
break;
|
||
case 'pressure':
|
||
veepooFeature.veepooSendPressureTestManager({ switch: !!on });
|
||
break;
|
||
}
|
||
},
|
||
|
||
// ── 睡眠数据读取 ──
|
||
|
||
_sleepResults: null,
|
||
_sleepDay: 0,
|
||
|
||
_readSleepData: function () {
|
||
this._sleepResults = [];
|
||
this._sleepDay = 0;
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 开始读取睡眠数据(3天)');
|
||
|
||
// 依次读取 3 天睡眠
|
||
var self = this;
|
||
veepooFeature.veepooSendReadPreciseSleepManager({ day: 0 });
|
||
|
||
// 延迟读取后续天(避免并发冲突)
|
||
// eslint-disable-next-line no-undef
|
||
setTimeout(function () { veepooFeature.veepooSendReadPreciseSleepManager({ day: 1 }); }, 3000);
|
||
// eslint-disable-next-line no-undef
|
||
setTimeout(function () { veepooFeature.veepooSendReadPreciseSleepManager({ day: 2 }); }, 6000);
|
||
},
|
||
|
||
_handleSleepEvent: function (data) {
|
||
var progress = data.Progress || 0;
|
||
if (progress < 100) return;
|
||
|
||
var content = data.content || {};
|
||
var readDay = data.readDay || 0;
|
||
var totalTime = Number(content.sleepTotalTime || 0);
|
||
|
||
if (totalTime <= 0) return;
|
||
|
||
var sleepResult = {
|
||
day: readDay,
|
||
deepSleepMinutes: Number(content.deepSleepTime || 0),
|
||
lightSleepMinutes: Number(content.lightSleepTime || 0),
|
||
totalSleepMinutes: totalTime,
|
||
qualityScore: Number(content.sleepQuality || 0),
|
||
fallAsleepTime: String(content.fallAsleepTime || ''),
|
||
exitSleepTime: String(content.exitSleepTime || ''),
|
||
};
|
||
|
||
if (!this._sleepResults) this._sleepResults = [];
|
||
this._sleepResults.push(sleepResult);
|
||
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 睡眠数据 day=' + readDay + ' 总时长=' + totalTime + '分钟 质量=' + sleepResult.qualityScore + '星');
|
||
|
||
// 保存到 Storage 供 Taro 页面读取
|
||
try {
|
||
// eslint-disable-next-line no-undef
|
||
wx.setStorageSync('hms:veepoo_sleep_results', JSON.stringify(this._sleepResults));
|
||
} catch (_) { /* ignore */ }
|
||
},
|
||
|
||
// ── 自动测量 ──
|
||
|
||
_enableAutoMeasurement: function () {
|
||
// eslint-disable-next-line no-undef
|
||
console.log('[veepoo-native] 开启自动测量功能');
|
||
|
||
// 开启心率自动监测
|
||
try {
|
||
veepooFeature.veepooSendSwitchSettingDataManager({
|
||
VPSettingAutomaticHRTest: 'open',
|
||
});
|
||
} catch (e) {
|
||
// eslint-disable-next-line no-undef
|
||
console.warn('[veepoo-native] 开启心率自动监测失败:', e);
|
||
}
|
||
|
||
// 开启血压自动监测
|
||
try {
|
||
veepooFeature.veepooSendSwitchSettingDataManager({
|
||
VPSettingAutomaticBPTest: 'open',
|
||
});
|
||
} catch (e) {
|
||
// eslint-disable-next-line no-undef
|
||
console.warn('[veepoo-native] 开启血压自动监测失败:', e);
|
||
}
|
||
|
||
// 开启体温自动监测
|
||
try {
|
||
veepooFeature.veepooSendSwitchSettingDataManager({
|
||
VPSettingAutomaticTemperatureTest: 'open',
|
||
});
|
||
} catch (e) {
|
||
// eslint-disable-next-line no-undef
|
||
console.warn('[veepoo-native] 开启体温自动监测失败:', e);
|
||
}
|
||
},
|
||
});
|