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

../../_images/obation_health_statistics_data_flow.png

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

When the data format is sleep content,
If the time is 0xFFFF, then the data format is analysis data content, should use the following method to parse:

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:

  1. 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

  2. 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:

  1. 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