|
|
const { validateCRC } = require('./crc');
|
|
|
const CommonUtil = require('./CommonUtil');
|
|
|
const parseDataPacket = {
|
|
|
|
|
|
/**
|
|
|
* 时间转时间戳
|
|
|
* @param {*} year
|
|
|
* @param {*} month
|
|
|
* @param {*} day
|
|
|
* @param {*} hours
|
|
|
* @param {*} minutes
|
|
|
* @param {*} seconds
|
|
|
*/
|
|
|
dateFormat(year,month,day,hours,minutes,seconds){
|
|
|
|
|
|
year = parseInt(year);
|
|
|
month = parseInt(month);
|
|
|
day = parseInt(day);
|
|
|
hours = parseInt(hours);
|
|
|
minutes = parseInt(minutes);
|
|
|
seconds = parseInt(seconds);
|
|
|
// 创建 Date 对象
|
|
|
const date = new Date(2000 + year,month-1, day, hours, minutes, seconds);
|
|
|
// 获取时间戳(单位:秒)
|
|
|
const timestamp = Math.floor(date.getTime() / 1000); // 转换为秒
|
|
|
return timestamp;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* level<=1:深睡
|
|
|
* level==2:浅睡
|
|
|
* level==3:REM
|
|
|
* level>=4:清醒
|
|
|
* @param {*} status
|
|
|
*/
|
|
|
mapSleepStatus(status) {
|
|
|
switch (status) {
|
|
|
case 1:
|
|
|
case 0:
|
|
|
return 1; // 浅睡
|
|
|
case 2:
|
|
|
case 3:
|
|
|
return 2; // 深睡
|
|
|
// case 3:
|
|
|
// return 4; // REM
|
|
|
case 4:
|
|
|
default:
|
|
|
return 3; // 清醒
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 将字节数组按照小端模式(Little Endian)解析为整数
|
|
|
* @param {DataView} dataView - DataView 对象
|
|
|
* @param {number} offset - 解析起始位置
|
|
|
* @returns {number} 解析后的整数值
|
|
|
*/
|
|
|
convertToDecimal(dataView, offset) {
|
|
|
// 读取 4 个字节,并按照小端模式进行转换
|
|
|
return dataView.getUint32(offset, true); // true 代表小端模式
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析步数总数包(27字节),包括头部和数据部分
|
|
|
*/
|
|
|
parseStepItem(dataView, offset) {
|
|
|
// 解析头部信息
|
|
|
const id = dataView.getUint8(offset + 1); // ID: 倒数第X天的总数据,0 表示当天
|
|
|
const year = dataView.getUint8(offset + 2).toString(16); // 年份(BCD格式)
|
|
|
const month = dataView.getUint8(offset + 3).toString(16); // 月份(BCD格式)
|
|
|
const day = dataView.getUint8(offset + 4).toString(16); // 日期(BCD格式)
|
|
|
|
|
|
// 解析数据部分
|
|
|
const steps = this.convertToDecimal(dataView, offset + 5); // 步数(高位在后),单位:步数
|
|
|
const exerciseTime = this.convertToDecimal(dataView, offset + 9); // 运动时间(高位在后),单位:秒
|
|
|
const distance = this.convertToDecimal(dataView, offset + 13) * 0.01; // 距离(高位在后),单位:KM
|
|
|
const calories = this.convertToDecimal(dataView, offset + 17) * 0.01; // 卡路里(高位在后),单位:KCAL
|
|
|
const target = dataView.getUint16(offset + 21, true); // 目标(高位在后),单位:目标数,使用小端模式
|
|
|
const fastExerciseTime = this.convertToDecimal(dataView, offset + 23); // 快速运动时间(高位在后),单位:分钟
|
|
|
|
|
|
// 输出解析结果
|
|
|
console.log(`ID: ${id}, Date: ${year}-${month}-${day}, Steps: ${steps}, Exercise Time: ${exerciseTime} secs, Distance: ${distance} KM, Calories: ${calories} KCAL, Target: ${target}, Fast Exercise Time: ${fastExerciseTime} mins`);
|
|
|
|
|
|
return {
|
|
|
id,
|
|
|
date: {
|
|
|
year,
|
|
|
month,
|
|
|
day
|
|
|
},
|
|
|
steps,
|
|
|
exerciseTime,
|
|
|
distance,
|
|
|
calories,
|
|
|
target,
|
|
|
fastExerciseTime
|
|
|
};
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析整个数据包,逐个处理每个 27 字节的记录
|
|
|
*/
|
|
|
parseStepList(dataView, startOffset) {
|
|
|
let results = [];
|
|
|
const totalLength = dataView.byteLength;
|
|
|
|
|
|
// 每个数据包 27 字节,因此以 27 字节为单位拆分
|
|
|
for (let offset = startOffset; offset + 26 <= totalLength; offset += 27) {
|
|
|
const dataPacket = this.parseStepItem(dataView, offset); // 解析每个 27 字节的数据包
|
|
|
results.push(dataPacket);
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析步数详情包(25字节)
|
|
|
*/
|
|
|
parseStepDetailItem(dataView, offset) {
|
|
|
// 解析头部信息
|
|
|
const id = dataView.getUint16(offset + 1,true); // ID: 倒数第X天的总数据,0 表示当天
|
|
|
// 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串
|
|
|
const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式)
|
|
|
const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式)
|
|
|
const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式)
|
|
|
const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式)
|
|
|
const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式)
|
|
|
const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式)
|
|
|
|
|
|
let time = this.dateFormat(year,month,day,hour,minute,second);
|
|
|
// 解析数据部分
|
|
|
const steps = dataView.getUint16(offset + 9,true);; // 步数(高位在后)单位:步数
|
|
|
const calories = dataView.getUint16(offset + 11,true); // 卡路里(高位在后),单位:KCAL
|
|
|
const distance = dataView.getUint16(offset + 13,true); // 距离(高位在后),单位:KM
|
|
|
|
|
|
return {
|
|
|
calories:calories,
|
|
|
steps:steps,
|
|
|
distance:distance,
|
|
|
startTime:time,
|
|
|
endTime:time + (10 * 60),
|
|
|
stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}`
|
|
|
};
|
|
|
},
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 解析整个数据包,逐个处理每个 25 字节的记录
|
|
|
*/
|
|
|
parseStepDetailList(dataView, startOffset) {
|
|
|
let results = [];
|
|
|
const totalLength = dataView.byteLength;
|
|
|
for (let offset = startOffset; offset + 24 <= totalLength; offset += 25) {
|
|
|
const dataPacket = this.parseStepDetailItem(dataView, offset);
|
|
|
results.push(dataPacket);
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
},
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 解析心率、血氧数据包,每个是10字节
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseDataItem(dataView, offset) {
|
|
|
|
|
|
let type = 1;
|
|
|
const cmdType = dataView.getUint8(offset); // 指令
|
|
|
switch (cmdType) {
|
|
|
case 0x55:
|
|
|
type = 1;
|
|
|
break;
|
|
|
case 0x66:
|
|
|
type = 3;
|
|
|
break;
|
|
|
default:
|
|
|
console.warn('未知数据类型', cmdType);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// 解析 ID1 和 ID2(数据编号,高位在后)
|
|
|
const id1 = dataView.getUint8(offset + 1); // ID1
|
|
|
const id2 = dataView.getUint8(offset + 2); // ID2
|
|
|
const id = (id2 << 8) | id1; // 组合成一个16位的ID(小端模式)
|
|
|
|
|
|
// 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串
|
|
|
const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式)
|
|
|
const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式)
|
|
|
const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式)
|
|
|
const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式)
|
|
|
const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式)
|
|
|
const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式)
|
|
|
|
|
|
let time = this.dateFormat(year,month,day,hour,minute,second);
|
|
|
// 解析心率值(HH)
|
|
|
const value = dataView.getUint8(offset + 9); // 心率值(单位:BPM),血氧,体温
|
|
|
|
|
|
return {
|
|
|
type:type,//1.心率,2,血压,3,血氧,4.体温,5.呼吸率,6,步数
|
|
|
oneValue:value,
|
|
|
time:time,
|
|
|
stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}`
|
|
|
};
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析整个数据包,逐个处理每个数据项
|
|
|
*/
|
|
|
parseDataList(dataView, startOffset) {
|
|
|
let results = [];
|
|
|
const totalLength = dataView.byteLength;
|
|
|
|
|
|
// 每个数据包 10 字节,因此以 10 字节为单位拆分
|
|
|
for (let offset = startOffset; offset + 9 <= totalLength; offset += 10) {
|
|
|
const dataPacket = this.parseDataItem(dataView, offset); // 解析每个 10 字节的数据包
|
|
|
results.push(dataPacket);
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析体温数据包,每个是11字节
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseTempItem(dataView, offset) {
|
|
|
// 解析 ID1 和 ID2(数据编号,高位在后)
|
|
|
const id = dataView.getUint16(offset + 1, true); // id
|
|
|
|
|
|
// 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串
|
|
|
const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式)
|
|
|
const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式)
|
|
|
const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式)
|
|
|
const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式)
|
|
|
const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式)
|
|
|
const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式)
|
|
|
|
|
|
let time = this.dateFormat(year,month,day,hour,minute,second);
|
|
|
// 解析体温值(T1 和 T2)
|
|
|
const t1 = dataView.getUint16(offset + 9, true); // 小端数据,保留一位小数
|
|
|
|
|
|
// 计算温度值,T1T2组合成一个16位数字,并转为浮动的值
|
|
|
const temperature = t1 / 10.0; // 保留1位小数
|
|
|
|
|
|
return {
|
|
|
type: 4,//4.体温
|
|
|
oneValue:temperature,
|
|
|
time:time,
|
|
|
stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}`
|
|
|
};
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析体温整个数据包,逐个处理每个数据项
|
|
|
*/
|
|
|
parseTempList(dataView, startOffset) {
|
|
|
let results = [];
|
|
|
const totalLength = dataView.byteLength;
|
|
|
|
|
|
// 每个数据包 11 字节,因此以 11 字节为单位拆分
|
|
|
for (let offset = startOffset; offset + 10 <= totalLength; offset += 11) {
|
|
|
const dataPacket = this.parseTempItem(dataView, offset); // 解析每个 10 字节的数据包
|
|
|
results.push(dataPacket);
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析血压数据包,每个是15字节
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseBloodPressureItem(dataView, offset) {
|
|
|
// 解析 ID1 和 ID2(数据编号,高位在后)
|
|
|
const id = dataView.getUint16(offset + 1,true);
|
|
|
|
|
|
// 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串
|
|
|
const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式)
|
|
|
const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式)
|
|
|
const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式)
|
|
|
const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式)
|
|
|
const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式)
|
|
|
const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式)
|
|
|
|
|
|
let time = this.dateFormat(year,month,day,hour,minute,second);
|
|
|
// 解析血压->高压
|
|
|
const high = dataView.getUint8(offset + 13);
|
|
|
// 解析血压->高压
|
|
|
const low = dataView.getUint8(offset + 14);
|
|
|
|
|
|
return {
|
|
|
type: 2,//2.血压
|
|
|
oneValue:high,
|
|
|
twoValue:low,
|
|
|
time:time,
|
|
|
stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}`
|
|
|
};
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析体温整个数据包,逐个处理每个数据项
|
|
|
*/
|
|
|
parseBloodPressureList(dataView, startOffset) {
|
|
|
let results = [];
|
|
|
const totalLength = dataView.byteLength;
|
|
|
|
|
|
// 每个数据包 15 字节,因此以 15 字节为单位拆分
|
|
|
for (let offset = startOffset; offset + 14 <= totalLength; offset += 15) {
|
|
|
const dataPacket = this.parseBloodPressureItem(dataView, offset); // 解析每个 15 字节的数据包
|
|
|
results.push(dataPacket);
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
},
|
|
|
/**
|
|
|
* 解析睡眠数据包,每个是11字节
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseSleepItem(dataView, offset) {
|
|
|
// 解析 ID1 和 ID2(数据编号,高位在后)
|
|
|
const id1 = dataView.getUint8(offset + 1); // ID1
|
|
|
|
|
|
// 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串
|
|
|
const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式)
|
|
|
const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式)
|
|
|
const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式)
|
|
|
const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式)
|
|
|
const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式)
|
|
|
const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式)
|
|
|
|
|
|
// 解析睡眠长度
|
|
|
const length = dataView.getUint8(offset + 9, true);
|
|
|
|
|
|
// 解析每一分钟的睡眠质量数据(SD1 到 SD120),最多 120 个数据
|
|
|
const sleepData = [];
|
|
|
for (let i = 0; i < length; i++) {
|
|
|
sleepData.push(dataView.getUint8(offset + 10 + i)); // 每分钟的睡眠质量
|
|
|
}
|
|
|
|
|
|
let time = this.dateFormat(year,month,day,hour,minute,second);
|
|
|
// 获取当天00:00:00的时间戳
|
|
|
const midnight = this.dateFormat(year,month,day,0,0,0);
|
|
|
let startMinutesOfDay = Math.floor((time - midnight) / 60); // 计算从当天00:00:00开始到当前时间的分钟数
|
|
|
console.log("midnight:",midnight)
|
|
|
console.log("startMinutesOfDay:",startMinutesOfDay)
|
|
|
|
|
|
// 1天最后1分钟是1440
|
|
|
let maxLastminute = 1440;
|
|
|
let deff = maxLastminute - startMinutesOfDay;
|
|
|
|
|
|
// 用于存储转换后的结果
|
|
|
let result = [];
|
|
|
let currentMinute = startMinutesOfDay; // 起始分钟(418分钟)
|
|
|
if(deff < sleepData.length){
|
|
|
// 将sleepData拆分为两个数组
|
|
|
let firstPart = sleepData.slice(0, deff); // 截取前deff个数据
|
|
|
let secondPart = sleepData.slice(deff); // 从deff开始到数组结尾的数据
|
|
|
// 开始循环遍历 sleepData 数据
|
|
|
for (let i = 0; i < firstPart.length; i++) {
|
|
|
const status = firstPart[i];
|
|
|
// 处理每分钟的睡眠数据
|
|
|
result.push({
|
|
|
minute: currentMinute + 1, // 分钟递增
|
|
|
status: this.mapSleepStatus(status), // 映射睡眠状态
|
|
|
yyyyMMdd: `${parseInt(year) + 2000}${month}${day}`, // 格式化为yyyyMMdd
|
|
|
});
|
|
|
// 递增分钟数
|
|
|
currentMinute++;
|
|
|
}
|
|
|
|
|
|
currentMinute = 0;
|
|
|
// 开始循环遍历 sleepData 数据
|
|
|
for (let i = 0; i < secondPart.length; i++) {
|
|
|
const status = secondPart[i];
|
|
|
// 处理每分钟的睡眠数据
|
|
|
result.push({
|
|
|
minute: currentMinute + 1, // 分钟递增
|
|
|
status: this.mapSleepStatus(status), // 映射睡眠状态
|
|
|
yyyyMMdd: `${parseInt(year) + 2000}${month}${(parseInt(day) + 1).toString().padStart(2, '0')}`, // 格式化为yyyyMMdd
|
|
|
|
|
|
});
|
|
|
// 递增分钟数
|
|
|
currentMinute++;
|
|
|
}
|
|
|
return result;
|
|
|
} else {
|
|
|
|
|
|
// 开始循环遍历 sleepData 数据
|
|
|
for (let i = 0; i < sleepData.length; i++) {
|
|
|
const status = sleepData[i];
|
|
|
let m = currentMinute + 1
|
|
|
if(m > 660 && m < 1200){
|
|
|
break;
|
|
|
}
|
|
|
// 处理每分钟的睡眠数据
|
|
|
result.push({
|
|
|
minute: currentMinute + 1, // 分钟递增
|
|
|
status: this.mapSleepStatus(status), // 映射睡眠状态
|
|
|
yyyyMMdd: `${parseInt(year) + 2000}${month}${day}`, // 格式化为yyyyMMdd
|
|
|
// dateTime: `${parseInt(year)}-${month}-${day} ${hour}:${minute}:${second}`, // 格式化为yyyyMMdd
|
|
|
// time: time
|
|
|
});
|
|
|
|
|
|
// 递增分钟数
|
|
|
currentMinute++;
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析睡眠整个数据包,逐个处理每个数据项
|
|
|
*/
|
|
|
parseSleepList(dataView, startOffset) {
|
|
|
|
|
|
const totalLength = dataView.byteLength;
|
|
|
|
|
|
// 每个数据包 130 字节,因此以 130 字节为单位拆分
|
|
|
for (let offset = startOffset; offset + 129 <= totalLength; offset += 130) {
|
|
|
const dataPacket = this.parseSleepItem(dataView, offset); // 解析每个 130 字节的数据包
|
|
|
return dataPacket;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 解析手环基本参数
|
|
|
*/
|
|
|
parseWatchBasic(dataView, offset){
|
|
|
|
|
|
// AA:距离单位:0x01:MILE 0x00:KM
|
|
|
// BB:12小时24小时显示:0x01: 12小时制,0x00:24小时制度。
|
|
|
// CC:抬手检查使能标志, 0x01:使能, 0x00:关闭
|
|
|
// DD:温度单位切换: 0x01:华摄氏度 ,0x00:摄氏度 默认摄氏度
|
|
|
// EE:夜间模式: 0x01开启 , 0x00:关闭(夜间模式开启后晚上六点到凌晨7点,自动调节显示亮度到最暗)
|
|
|
// FF:ANCS使能开关: 0x01:使能,0x00:关闭
|
|
|
// II:基础心率设置
|
|
|
// JJ:保留
|
|
|
// KK:屏幕亮度调节,调节范围80-8f(0为最亮,f为最暗)(0~15:值越大则越暗)
|
|
|
// LL:表盘界面
|
|
|
// MM: 社交距离开关 0x01 开 0x00 关
|
|
|
// NN:中英文切换, 0代表英文(默认), 1代表中文
|
|
|
const distanceFlag = dataView.getUint8(offset + 1);
|
|
|
const timeFlag = dataView.getUint8(offset + 2);
|
|
|
const WRSFlag = dataView.getUint8(offset + 3);
|
|
|
const tempFlag = dataView.getUint8(offset + 4);
|
|
|
const nightModeFlag = dataView.getUint8(offset + 5);
|
|
|
const ANCSFlag = dataView.getUint8(offset + 6);
|
|
|
const heartRateWarningFlag = dataView.getUint8(offset + 7);
|
|
|
const JJ = dataView.getUint8(offset + 10);
|
|
|
const KK = dataView.getUint8(offset + 11);
|
|
|
const LL = dataView.getUint8(offset + 12);
|
|
|
const MM = dataView.getUint8(offset + 13);
|
|
|
const NN = dataView.getUint8(offset + 14);
|
|
|
|
|
|
return {
|
|
|
distanceFlag:distanceFlag,timeFlag:timeFlag,WRSFlag:WRSFlag,tempFlag:tempFlag,
|
|
|
nightModeFlag:nightModeFlag,ANCSFlag:ANCSFlag,heartRateWarningFlag:heartRateWarningFlag,
|
|
|
JJ:JJ,SBLevel:CommonUtil.respSBLevelConv(KK),LL:LL,MM:MM,NN:NN
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析闹钟数据包,每个是41字节
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseAlarmClockItem(dataView, offset) {
|
|
|
const id = dataView.getUint16(offset + 1, true); // id
|
|
|
if (id === 0x57FF) {
|
|
|
return;
|
|
|
}
|
|
|
const count = dataView.getUint8(offset + 3); // 闹钟个数
|
|
|
const number = dataView.getUint8(offset + 4); // 闹钟编号
|
|
|
const enable = dataView.getUint8(offset + 5); // 闹钟状态 0:关闭,1:开启
|
|
|
const type = dataView.getUint8(offset + 6); // 闹钟类型 1:闹钟,2:吃药提示,3:喝水提示 4:吃饭闹钟 5:洗手闹钟
|
|
|
const hour = dataView.getUint8(offset + 7).toString(16); // 闹钟:时
|
|
|
const minute = dataView.getUint8(offset + 8).toString(16);// 闹钟:分
|
|
|
const week = dataView.getUint8(offset + 9);// 周,8bits,表示周一到周天
|
|
|
const length = 30; // 长度,30个字节是闹钟内容,目前发送了手表端不会显示,暂不处理
|
|
|
// 解析星期使能位
|
|
|
const weekDays = [
|
|
|
(week & 0x01) !== 0 ? 1 : 0, // Sunday
|
|
|
(week & 0x02) !== 0 ? 1 : 0, // Monday
|
|
|
(week & 0x04) !== 0 ? 1 : 0, // Tuesday
|
|
|
(week & 0x08) !== 0 ? 1 : 0, // Wednesday
|
|
|
(week & 0x10) !== 0 ? 1 : 0, // Thursday
|
|
|
(week & 0x20) !== 0 ? 1 : 0, // Friday
|
|
|
(week & 0x40) !== 0 ? 1 : 0 // Saturday
|
|
|
];
|
|
|
return {
|
|
|
count: count,
|
|
|
number:number,
|
|
|
enable:enable,
|
|
|
type:type,
|
|
|
hour:hour,
|
|
|
minute:minute,
|
|
|
weekDays:weekDays,
|
|
|
// textContent:textContent
|
|
|
};
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析闹钟整个数据包,逐个处理每个数据项
|
|
|
*/
|
|
|
parseAlarmClockList(dataView, startOffset) {
|
|
|
let results = [];
|
|
|
const totalLength = dataView.byteLength;
|
|
|
|
|
|
// 每个数据包 41 字节,因此以 41 字节为单位拆分
|
|
|
for (let offset = startOffset; offset + 40 <= totalLength; offset += 41) {
|
|
|
const dataPacket = this.parseAlarmClockItem(dataView, offset); // 解析每个 41 字节的数据包
|
|
|
results.push(dataPacket);
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 解析久坐数据
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseSedentary(dataView, offset){
|
|
|
const startHour = dataView.getUint8(offset + 1); // 开始运动时间 小时
|
|
|
const startMinute = dataView.getUint8(offset + 2);
|
|
|
const endHour =dataView.getUint8(offset + 3);
|
|
|
const endMinute = dataView.getUint8(offset + 4);
|
|
|
const week = dataView.getUint8(offset + 5);
|
|
|
const reminderInterval = dataView.getUint8(offset + 6);
|
|
|
const minSteps = dataView.getUint8(offset + 7);
|
|
|
const sportSwitch = dataView.getUint8(offset + 8);
|
|
|
|
|
|
// 解析星期使能位
|
|
|
const weekDays = [
|
|
|
(week & 0x01) !== 0 ? 1 : 0, // Sunday
|
|
|
(week & 0x02) !== 0 ? 1 : 0, // Monday
|
|
|
(week & 0x04) !== 0 ? 1 : 0, // Tuesday
|
|
|
(week & 0x08) !== 0 ? 1 : 0, // Wednesday
|
|
|
(week & 0x10) !== 0 ? 1 : 0, // Thursday
|
|
|
(week & 0x20) !== 0 ? 1 : 0, // Friday
|
|
|
(week & 0x40) !== 0 ? 1 : 0 // Saturday
|
|
|
];
|
|
|
return {
|
|
|
startHour: startHour,
|
|
|
startMinute:startMinute,
|
|
|
endHour:endHour,
|
|
|
endMinute:endMinute,
|
|
|
weekDays:weekDays,
|
|
|
reminderInterval:reminderInterval,
|
|
|
minSteps:minSteps,
|
|
|
sportSwitch:sportSwitch
|
|
|
};
|
|
|
},
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 运动目标解析
|
|
|
* @param {*} dataView
|
|
|
* @param {*} offset
|
|
|
*/
|
|
|
parseStepTarget(dataView, offset){
|
|
|
const targetSteps = dataView.getUint32(offset + 1,true);
|
|
|
const targetTime = dataView.getUint16(offset + 5,true);
|
|
|
const targetDistance = dataView.getUint16(offset + 7,true);
|
|
|
const targetCalories = dataView.getUint16(offset + 9,true);
|
|
|
const targetSleep = dataView.getUint16(offset + 11,true);
|
|
|
return {
|
|
|
targetSteps:targetSteps,
|
|
|
targetTime:targetTime,
|
|
|
targetDistance:targetDistance,
|
|
|
targetCalories:targetCalories,
|
|
|
targetSleep:targetSleep
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 导出模块
|
|
|
module.exports = parseDataPacket; |