12. Health Function
Synchronize real-time health data and health data files
12.1. Get Real-time Health Data
//Actively get real-time health data
//WatchManager is a subclass of WatchOpImpl, must configure sdk in 1.3
WatchManager watchManager = WatchManager.getInstance();
//Initialize health function implementation
HealthOpImpl healthOp = new HealthOpImpl(watchManager);
//Register RCSP event listener
healthOp.getRcspOp().registerOnRcspEventListener(new OnRcspEventListener() {
@Override
public void onHealthDataChange(BluetoothDevice device, HealthData data) {
//Health data will be called back here
switch (data.type) {//Classified by different types
case AttrAndFunCode.HEALTH_DATA_TYPE_HEART_RATE: //Heart rate
HeartRate heartRate = (HeartRate) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_AIR_PRESSURE: //Air pressure
AirPressure airPressure = (AirPressure) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_ALTITUDE: //Altitude
Altitude altitude = (Altitude) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_STEP: //Sports steps
SportsSteps sportsSteps = (SportsSteps) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_PRESSUER: //Pressure detection
PressureDetection pressureDetection = (PressureDetection) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_BLOOD_OXYGEN: //Blood oxygen saturation
OxygenSaturation oxygenSaturation = (OxygenSaturation) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_TRAINING_LOAD: //Training load
TrainingLoad trainingLoad = (TrainingLoad) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_MAX_OXYGEN_UPTAKE: //Maximum oxygen uptake
MaxOxygenUptake maxOxygenUptake = (MaxOxygenUptake) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_SPORT_RECOVERY_TIME://Exercise recovery time
ExerciseRecoveryTime recoveryTime = (ExerciseRecoveryTime) data;
break;
}
}
});
//Get real-time data through mask, related masks: AttrAndFunCode.HEALTH_DATA_TYPE_XXX
//Example: Get real-time heart rate, steps, distance, calories and blood oxygen saturation
int mask = 0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_HEART_RATE | (0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_STEP) | (0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_BLOOD_OXYGEN);
//Sort from low bit to high bit
byte[] subMask = new byte[]{0x01, 0x07, 0x01};
//Current data version: 0
byte version = 0;
//Execute read health data function and wait for result callback
healthOp.readHealthData(healthOp.getConnectedDevice(), new HealthDataQuery(version, mask, subMask), new OnOperationCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
//Success callback
//Result will be called back in OnRcspEventListener#onHealthDataChange
}
@Override
public void onFailed(BaseError error) {
//Failure callback
//error - Error message
}
});
12.2. Listen to Real-time Health Data
//Listen to real-time health data
//WatchManager is a subclass of WatchOpImpl, must configure sdk in 1.3
WatchManager watchManager = WatchManager.getInstance();
//Register RCSP event listener
watchManager.registerOnRcspEventListener(new RcspEventListenerManager() {
@Override
public void onHealthDataChange(BluetoothDevice device, HealthData data) {
//Health data will be called back here
switch (data.type) {//Classified by different types
case AttrAndFunCode.HEALTH_DATA_TYPE_HEART_RATE: //Heart rate
HeartRate heartRate = (HeartRate) data;
// heartRate.getRealTimeValue(); //Real-time heart rate
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_AIR_PRESSURE: //Air pressure
AirPressure airPressure = (AirPressure) data;
// airPressure.getRealTimeValue(); //Real-time air pressure
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_ALTITUDE: //Altitude
Altitude altitude = (Altitude) data;
// altitude.getRealTimeValue(); //Real-time height
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_STEP: //Sports steps
SportsSteps sportsSteps = (SportsSteps) data;
// sportsSteps.getStepNum(); //Steps
// sportsSteps.getDistance(); //Distance
// sportsSteps.getCalorie(); //Calories
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_PRESSUER: //Pressure detection
PressureDetection pressureDetection = (PressureDetection) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_BLOOD_OXYGEN: //Blood oxygen saturation
OxygenSaturation oxygenSaturation = (OxygenSaturation) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_TRAINING_LOAD: //Training load
TrainingLoad trainingLoad = (TrainingLoad) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_MAX_OXYGEN_UPTAKE: //Maximum oxygen uptake
MaxOxygenUptake maxOxygenUptake = (MaxOxygenUptake) data;
break;
case AttrAndFunCode.HEALTH_DATA_TYPE_SPORT_RECOVERY_TIME://Exercise recovery time
ExerciseRecoveryTime recoveryTime = (ExerciseRecoveryTime) data;
// recoveryTime.getHour(); //Hour
// recoveryTime.getMin(); //Minute
break;
}
}
});
12.3. Health Data Comparison Table
Name |
Type |
Data Model |
Options |
|---|---|---|---|
Heart Rate |
0x00 |
HeartRate |
Heart rate range: 0~220
Bit0 => realTimeValue: Real-time heart rate
Bit1 => restingValue: Resting heart rate
Bit2 => maxValue: Maximum heart rate
|
Air Pressure |
0x01 |
AirPressure |
Air pressure range: 300~1100 hPa
Bit0 => realTimeValue: Real-time air pressure
Bit1 => minValue: Minimum air pressure
Bit2 => maxValue: Maximum air pressure
|
Altitude |
0x02 |
Altitude |
Altitude range: 0~65536 m
Bit0 => realTimeValue: Real-time altitude
Bit1 => minValue: Minimum altitude
Bit2 => maxValue: Maximum altitude
|
Sports Steps |
0x03 |
SportsSteps |
Bit0 => stepNum: Sports steps, range: 0~200000 steps
Bit1 => distance: Sports distance, unit: 0.01 km
Bit2 => calorie: Calories, unit: kcal
|
Pressure Detection |
0x04 |
PressureDetection |
Bit0 => detectionValue: Pressure test value, range: 0%~100% |
Blood Oxygen Saturation |
0x05 |
OxygenSaturation |
Bit0 => percent: Blood oxygen saturation, range: 0%~100% |
Training Load |
0x06 |
TrainingLoad |
Bit0 => value: Load value |
Maximum Oxygen Uptake |
0x07 |
MaxOxygenUptake |
Bit0 => value: Oxygen uptake |
Exercise Recovery Time |
0x08 |
ExerciseRecoveryTime |
Bit0 => time: Time, hour, min |
Important
Base class of health data: HealthData
12.3.1. HealthData
Base class of health data
public class HealthData {
public int type; //Function type
public int version; //Version
public byte subMask; //Function mask
public byte[] data; //Valid data
...
}
12.3.2. Mask Generation Rules
Generation of subMask, subMask structure is 1Byte
//Example - Query real-time heart rate, sports steps, sports distance, calories
int mask = 0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_HEART_RATE | (0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_STEP);
//Sort from low bit to high bit
byte[] subMask = new byte[]{0x01, 0x07};
//type : 0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_HEART_RATE, subMask : 0x01 => 0000 0001 => Query real-time heart rate
//type : 0x01 << AttrAndFunCode.HEALTH_DATA_TYPE_STEP, subMask : 0x07 => 0000 0111 => Query sports steps, sports distance, calories
public class AttrAndFunCode {
...
//Health data type
public final static int HEALTH_DATA_TYPE_HEART_RATE = 0x00;//Heart rate
public final static int HEALTH_DATA_TYPE_AIR_PRESSURE = 0x01;//Air pressure
public final static int HEALTH_DATA_TYPE_ALTITUDE = 0x02;//Altitude
public final static int HEALTH_DATA_TYPE_STEP = 0x03;//Sports steps
public final static int HEALTH_DATA_TYPE_PRESSURE = 0x04;//Pressure detection
public final static int HEALTH_DATA_TYPE_BLOOD_OXYGEN = 0x05;//Blood oxygen saturation
public final static int HEALTH_DATA_TYPE_TRAINING_LOAD = 0x06;//Training load
public final static int HEALTH_DATA_TYPE_MAX_OXYGEN_UPTAKE = 0x07;//Maximum oxygen uptake
/**
* Exercise recovery time
* Deprecated Return from exercise end data, no longer obtained separately
*/
@Deprecated
public final static int HEALTH_DATA_TYPE_SPORT_RECOVERY_TIME = 0x08;//Exercise recovery time
...
}
12.4. Get Health Statistical Data
12.4.1. Get Process
12.4.2. General Implementation Class for Acquisition
public abstract class HealthFileSyncDemo {
final byte type;
final WatchManager mWatchManager;
/**
*
* @param type File type:
* QueryFileTask.TYPE_HEART_RATE;
* QueryFileTask.TYPE_BLOOD_OXYGEN;
* QueryFileTask.TYPE_SLEEP;
* QueryFileTask.TYPE_STEP;
*/
public HealthFileSyncDemo(byte type) {
this.type = type;
mWatchManager = WatchManager.getInstance();
}
public void start() {
//Get small file list
QueryFileTask task = new QueryFileTask(mWatchManager, new QueryFileTask.Param(type));
task.setListener(new SimpleTaskListener() {
@Override
public void onFinish() {
super.onFinish();
List<QueryFileTask.File> list = task.getFiles();
Collections.reverse(list);//Reverse order, read from back
readFileInfoRecursion(list);
}
@Override
public void onError(int code, String msg) {
super.onError(code, msg);
//Failed to get file list, end
}
});
task.start();
}
/**
* Recursively read small files
* @param list
*/
private void readFileInfoRecursion(List<QueryFileTask.File> list) {
if (list.size() < 1) {
//No files, end
return;
}
readFileHeader(list.get(0), new SimpleTaskListener() {
@Override
public void onFinish() {
list.remove(0);
readFileInfoRecursion(list);
}
@Override
public void onError(int code, String msg) {
//Failed to get file content, should not continue reading, end
}
});
}
/**
*Read small file header content
*/
private void readFileHeader(QueryFileTask.File file, final SimpleTaskListener simpleTaskListener) {
int size = 30;
ReadFileTask readFileTask = new ReadFileTask(mWatchManager, new ReadFileTask.Param(file.type, file.id, size, 0));
readFileTask.setListener(new SimpleTaskListener() {
@Override
public void onError(int code, String msg) {
super.onError(code, msg);
//Failed to get file header
simpleTaskListener.onError(code, msg);
}
@Override
public void onFinish() {
//todo Compare with local database
byte[] data = readFileTask.getReadData();
if (isInLocal(data)) {
//One file read ends
simpleTaskListener.onFinish();
} else {
readFileData(file, simpleTaskListener);
}
}
});
readFileTask.start();
}
/**
*Read small file content
*/
private void readFileData(QueryFileTask.File file, SimpleTaskListener simpleTaskListener) {
ReadFileTask readFileTask = new ReadFileTask(mWatchManager, new ReadFileTask.Param(file.type, file.id, file.size, 0));
readFileTask.setListener(new SimpleTaskListener() {
@Override
public void onError(int code, String msg) {
simpleTaskListener.onError(code, msg);
}
@Override
public void onFinish() {
byte[] data = readFileTask.getReadData();
// todo Save to database
saveToDb(data);
simpleTaskListener.onFinish();//One file read ends
}
});
readFileTask.start();
}
/**
* Compare with local database
*/
protected boolean isInLocal(byte[] data) {
//todo Determine whether the file has been read
//1. Get the file crc in data
//2. Look up in local whether there is a file with the same crc, if so, it has been read
return false;
}
/**
* Save to database
*/
protected void saveToDb(byte[] data) {
//todo Save file to local
}
}
12.4.3. Usage of General Implementation Class for Acquisition
HealthFileSyncDemo healthFileSyncDemo = new HealthFileSyncDemo(QueryFileTask.TYPE_SLEEP);
healthFileSyncDemo.start();
12.5. Statistical Data File Structure
12.5.1. Small File Format
File Type(1Byte) |
Date(year month day,4Bytes) |
File Check Code(2Bytes) |
Version Number(1Byte) |
Storage Interval(1Byte) |
Reserved Bit(2Bytes) |
N * (Data Format) |
|---|---|---|---|---|---|---|
Refer Type Comparison Table |
yyyyMMdd
Year: 2Bytes
Month: 1Byte
Day: 1Byte
|
CRC check code |
Current version number:0 |
Time interval of data storage
Range: 1~60min
When this value is 0xFF, it is sleep type, do not read
|
Reserved bit |
Refer Data Format |
12.5.2. Type Comparison Table
Type |
Type Value |
Remarks |
|---|---|---|
Contact |
0x01 |
SmallFileTransferCmd.TYPE_CONTACTS |
Sports Record |
0x02 |
SmallFileTransferCmd.TYPE_SPORTS_RECORD
(Not in small file format, refer to Sports Record File Structure )
|
All-day Heart Rate Data |
0x03 |
SmallFileTransferCmd.TYPE_HEART_RATE |
All-day Blood Oxygen Data |
0x04 |
SmallFileTransferCmd.TYPE_BLOOD_OXYGEN |
All-day Sleep Data |
0x05 |
SmallFileTransferCmd.TYPE_SLEEP |
Message Data |
0x06 |
SmallFileTransferCmd.TYPE_MESSAGE_SYNC |
Weather Data |
0x07 |
SmallFileTransferCmd.TYPE_WEATHER |
Call Log File |
0x08 |
SmallFileTransferCmd.TYPE_CALL_LOG |
All-day Step Data |
0x09 |
SmallFileTransferCmd.TYPE_STEP (includes distance and calories) |
12.5.3. Data Format
Time(hour minute)(2Bytes) |
Length(2Bytes) |
Data Content(n bytes) |
|---|---|---|
HHmm (Hour: 1Byte,Minute: 1Byte) |
Length of data content |
Refer :Data Content |
12.5.4. Data Content
Data Content Type |
Data Content |
Notes |
|---|---|---|
Sleep |
n * (Sleep Type(1Byte), Duration(1Byte, unit is minute)) |
|
Heart Rate |
n * Value(1Byte, unit is times) |
Resting heart rate is in the low address of the reserved bit in the file header, for example: 0x4000 64 |
Blood Oxygen |
n * Value(1Byte, unit is times) |
|
All-day Steps |
n * (Steps(2Bytes, unit is steps),Distance(2Bytes, unit is 10 meters),Calories(2Bytes, unit is kcal)) * Column Increment |
Note
Sleep Score(1Byte) |
Deep Sleep Ratio(1Byte) |
Light Sleep Ratio(1Byte) |
REM Ratio(1Byte) |
Duration Evaluation(1Byte) |
Deep Sleep Continuity Score(1Byte) |
Night Waking Times(1Byte) |
|---|---|---|---|---|---|---|
0~100 |
0~100 |
0~100 |
0~100 |
Value range:0 – Not updated
1 – Low 2 – Normal
3 – High
Total duration evaluation: 0~1Bit
Deep sleep evaluation: 2~3Bit
Light sleep evaluation: 4~5Bit
REM evaluation: 6~7Bit
|
0~100 |
0~127 |
12.5.4.1. Sleep Type
- Prerequisites:
The date of sleep type is recorded based on the time of waking up
Sleep Type Judgment Logic:
When detecting that the start time or end time of the wearer’s sleep falls within the range of zero to six o’clock, it is considered as night sleep
When detecting that both the start time and end time of the wearer’s sleep fall within other sleep time (not zero to six o’clock), it is considered as nap
Sleep Date Hour Minute Distinction Strategy:
When receiving a piece of night sleep data parsing, 00:00 will serve as the boundary line to distinguish the data of the previous and next day. When the sleep type is nap, it only serves as the sleep data of the current day
Type |
Type Value |
Notes |
|---|---|---|
Awake |
0xFF |
|
Light Sleep |
0x01 |
|
Deep Sleep |
0x02 |
|
REM(Rapid Eye Movement) |
0x03 |
|
Nap |
0x04 |
Not counted in chart display |