Huawei: Collect and process more workout data: HRZones, RunPaceZones

This commit is contained in:
Me7c7 2024-11-26 17:37:19 +02:00 committed by José Rebelo
parent 48a6e1aa95
commit 7b8f02ec29
11 changed files with 344 additions and 51 deletions

View File

@ -54,7 +54,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
final Schema schema = new Schema(88, MAIN_PACKAGE + ".entities"); final Schema schema = new Schema(89, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema); Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes); Entity user = addUserInfo(schema, userAttributes);
@ -1411,6 +1411,26 @@ public class GBDaoGenerator {
workoutSummary.addByteProperty("swimType").notNull(); workoutSummary.addByteProperty("swimType").notNull();
workoutSummary.addIntProperty("maxMET").notNull();
workoutSummary.addByteProperty("hrZoneType").notNull();
workoutSummary.addShortProperty("runPaceZone1Min").notNull();
workoutSummary.addShortProperty("runPaceZone2Min").notNull();
workoutSummary.addShortProperty("runPaceZone3Min").notNull();
workoutSummary.addShortProperty("runPaceZone4Min").notNull();
workoutSummary.addShortProperty("runPaceZone5Min").notNull();
workoutSummary.addShortProperty("runPaceZone5Max").notNull();
workoutSummary.addShortProperty("runPaceZone1Time").notNull();
workoutSummary.addShortProperty("runPaceZone2Time").notNull();
workoutSummary.addShortProperty("runPaceZone3Time").notNull();
workoutSummary.addShortProperty("runPaceZone4Time").notNull();
workoutSummary.addShortProperty("runPaceZone5Time").notNull();
workoutSummary.addByteProperty("algType").notNull();
workoutSummary.addIntProperty("trainingPoints").notNull();
return workoutSummary; return workoutSummary;
} }

View File

@ -0,0 +1,93 @@
package nodomain.freeyourgadget.gadgetbridge.database.schema;
import android.database.sqlite.SQLiteDatabase;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao;
public class GadgetbridgeUpdate_89 implements DBUpdateScript {
@Override
public void upgradeSchema(final SQLiteDatabase db) {
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.MaxMET.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.MaxMET.columnName + "\" INTEGER NOT NULL DEFAULT 0;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.HrZoneType.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.HrZoneType.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone1Min.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone1Min.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone2Min.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone2Min.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone3Min.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone3Min.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone4Min.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone4Min.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone5Min.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone5Min.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone5Max.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone5Max.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone1Time.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone1Time.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone2Time.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone2Time.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone3Time.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone3Time.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone4Time.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone4Time.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone5Time.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.RunPaceZone5Time.columnName + "\" INTEGER NOT NULL DEFAULT -1;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.AlgType.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.AlgType.columnName + "\" INTEGER NOT NULL DEFAULT 0;";
db.execSQL(statement);
}
if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.TrainingPoints.columnName, db)) {
final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \""
+ HuaweiWorkoutSummarySampleDao.Properties.TrainingPoints.columnName + "\" INTEGER NOT NULL DEFAULT 0;";
db.execSQL(statement);
}
}
@Override
public void downgradeSchema(final SQLiteDatabase db) {
}
}

View File

@ -11,12 +11,16 @@ public class HeartRateZonesConfig {
public static final int TYPE_SWIMMING = 3; public static final int TYPE_SWIMMING = 3;
public static final int TYPE_OTHER = 4; public static final int TYPE_OTHER = 4;
public static final int CALCULATE_METHOD_MHR = 0;
public static final int CALCULATE_METHOD_HRR = 1;
public static final int CALCULATE_METHOD_LTHR = 3;
private static final int DEFAULT_REST_HEART_RATE = 60; private static final int DEFAULT_REST_HEART_RATE = 60;
public static final int MAXIMUM_HEART_RATE = 220; public static final int MAXIMUM_HEART_RATE = 220;
private final int configType; private final int configType;
private int calculateMethod = 0; // 0 - MHR, 1 - HRR, 3 - LTHR private int calculateMethod = CALCULATE_METHOD_MHR; // 0 - MHR, 1 - HRR, 3 - LTHR
private int maxHRThreshold; private int maxHRThreshold;
private int restHeartRate = DEFAULT_REST_HEART_RATE; private int restHeartRate = DEFAULT_REST_HEART_RATE;
@ -262,4 +266,22 @@ public class HeartRateZonesConfig {
return getZoneForHR(heartRate, LTHRAnaerobic, LTHRLactate, LTHRAdvancedAerobic, LTHRBasicAerobic, LTHRWarmUp); return getZoneForHR(heartRate, LTHRAnaerobic, LTHRLactate, LTHRAdvancedAerobic, LTHRBasicAerobic, LTHRWarmUp);
} }
public int getZoneByMethod(int heartRate, int method) {
if(method == CALCULATE_METHOD_LTHR) {
return getLTHRZone(heartRate);
} else if(method == CALCULATE_METHOD_MHR) {
return getMHRZone(heartRate);
}
return getHHRZone(heartRate);
}
public static boolean isCalculateMethodValidFroType(int type, int method) {
if(method == CALCULATE_METHOD_LTHR && type == TYPE_UPRIGHT) {
return true;
}
return (method == CALCULATE_METHOD_MHR) || (method == CALCULATE_METHOD_HRR);
}
} }

View File

@ -0,0 +1,62 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
// TODO: make this configurable
// NOTE: algorithms used in this class are generic. So this data can be used with other devices.
// We can move this class to global scope.
public class HuaweiRunPaceConfig {
private int zone5HIITRunMax = 300;
private int zone5HIITRunMin = 330;
private int zone4AnaerobicMin = 360;
private int zone3LactateThresholdMin = 390;
private int zone2MarathonMin = 420;
private int zone1JogMin = 450;
public int getZone5HIITRunMax() {
return zone5HIITRunMax;
}
public void setZone5HIITRunMax(int zone5HIITRunMax) {
this.zone5HIITRunMax = zone5HIITRunMax;
}
public int getZone5HIITRunMin() {
return zone5HIITRunMin;
}
public void setZone5HIITRunMin(int zone5HIITRunMin) {
this.zone5HIITRunMin = zone5HIITRunMin;
}
public int getZone4AnaerobicMin() {
return zone4AnaerobicMin;
}
public void setZone4AnaerobicMin(int zone4AnaerobicMin) {
this.zone4AnaerobicMin = zone4AnaerobicMin;
}
public int getZone3LactateThresholdMin() {
return zone3LactateThresholdMin;
}
public void setZone3LactateThresholdMin(int zone3LactateThresholdMin) {
this.zone3LactateThresholdMin = zone3LactateThresholdMin;
}
public int getZone2MarathonMin() {
return zone2MarathonMin;
}
public void setZone2MarathonMin(int zone2MarathonMin) {
this.zone2MarathonMin = zone2MarathonMin;
}
public int getZone1JogMin() {
return zone1JogMin;
}
public void setZone1JogMin(int zone1JogMin) {
this.zone1JogMin = zone1JogMin;
}
}

View File

@ -2,26 +2,26 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
public class HuaweiSportHRZones { public class HuaweiSportHRZones {
private final HeartRateZonesConfig otherHRZonesConfig; private final HeartRateZonesConfig otherHRZonesConfig;
private final HeartRateZonesConfig SittingHRZonesConfig; private final HeartRateZonesConfig sittingHRZonesConfig;
private final HeartRateZonesConfig UprightHRZonesConfig; private final HeartRateZonesConfig uprightHRZonesConfig;
private final HeartRateZonesConfig SwimmingHRZonesConfig; private final HeartRateZonesConfig swimmingHRZonesConfig;
public HuaweiSportHRZones(int age) { public HuaweiSportHRZones(int age) {
this.UprightHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_UPRIGHT, age); this.uprightHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_UPRIGHT, age);
this.SittingHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_SITTING, age); this.sittingHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_SITTING, age);
this.SwimmingHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_SWIMMING, age); this.swimmingHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_SWIMMING, age);
this.otherHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_OTHER, age); this.otherHRZonesConfig = new HeartRateZonesConfig(HeartRateZonesConfig.TYPE_OTHER, age);
} }
public HeartRateZonesConfig getHRZonesConfigByType(int type) { public HeartRateZonesConfig getHRZonesConfigByType(int type) {
if (type == HeartRateZonesConfig.TYPE_SITTING) { if (type == HeartRateZonesConfig.TYPE_SITTING) {
return this.SittingHRZonesConfig; return this.sittingHRZonesConfig;
} else if (type == HeartRateZonesConfig.TYPE_SWIMMING) { } else if (type == HeartRateZonesConfig.TYPE_SWIMMING) {
return this.SwimmingHRZonesConfig; return this.swimmingHRZonesConfig;
} else if (type == HeartRateZonesConfig.TYPE_OTHER) { } else if (type == HeartRateZonesConfig.TYPE_OTHER) {
return this.otherHRZonesConfig; return this.otherHRZonesConfig;
} }
return this.UprightHRZonesConfig; return this.uprightHRZonesConfig;
} }
public byte[] getHRZonesData() { public byte[] getHRZonesData() {

View File

@ -23,6 +23,7 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HeartRateZonesConfig; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HeartRateZonesConfig;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiReportThreshold; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiReportThreshold;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiRunPaceConfig;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
public class FitnessData { public class FitnessData {
@ -741,25 +742,19 @@ public class FitnessData {
public static final byte id = 0x28; public static final byte id = 0x28;
public static class Request extends HuaweiPacket { public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, public Request(ParamsProvider paramsProvider, final HuaweiRunPaceConfig runPaceConfig) {
int easyPaceZoneMinValue,
int marathonPaceZoneMinValue,
int lactatePaceZoneMinValue,
int anaerobicPaceZoneMinValue,
int maxOxygenPaceZoneMinValue,
int maxOxygenPaceZoneMaxValue) {
super(paramsProvider); super(paramsProvider);
this.serviceId = FitnessData.id; this.serviceId = FitnessData.id;
this.commandId = id; this.commandId = id;
this.tlv = new HuaweiTLV() this.tlv = new HuaweiTLV()
.put(0x01, (short) easyPaceZoneMinValue) .put(0x01, (short) runPaceConfig.getZone1JogMin())
.put(0x02, (short) marathonPaceZoneMinValue) .put(0x02, (short) runPaceConfig.getZone2MarathonMin())
.put(0x03, (short) lactatePaceZoneMinValue) .put(0x03, (short) runPaceConfig.getZone3LactateThresholdMin())
.put(0x04, (short) anaerobicPaceZoneMinValue) .put(0x04, (short) runPaceConfig.getZone4AnaerobicMin())
.put(0x05, (short) maxOxygenPaceZoneMinValue) .put(0x05, (short) runPaceConfig.getZone5HIITRunMin())
.put(0x06, (short) maxOxygenPaceZoneMaxValue); .put(0x06, (short) runPaceConfig.getZone5HIITRunMax());
this.complete = true; this.complete = true;
} }
} }

View File

@ -154,6 +154,26 @@ public class Workout {
public byte swimType = -1; public byte swimType = -1;
public int maxMET = 0;
public byte hrZoneType = -1;
public short runPaceZone1Min = -1;
public short runPaceZone2Min = -1;
public short runPaceZone3Min = -1;
public short runPaceZone4Min = -1;
public short runPaceZone5Min = -1;
public short runPaceZone5Max = -1;
public short runPaceZone1Time = -1;
public short runPaceZone2Time = -1;
public short runPaceZone3Time = -1;
public short runPaceZone4Time = -1;
public short runPaceZone5Time = -1;
public byte algType = 0;
public int trainingPoints = -1;
public Response(ParamsProvider paramsProvider) { public Response(ParamsProvider paramsProvider) {
super(paramsProvider); super(paramsProvider);
@ -189,6 +209,8 @@ public class Workout {
this.workoutLoad = container.getInteger(0x0d); this.workoutLoad = container.getInteger(0x0d);
if (container.contains(0x0e)) if (container.contains(0x0e))
this.workoutAerobicEffect = container.getInteger(0x0e); this.workoutAerobicEffect = container.getInteger(0x0e);
if (container.contains(0x10))
this.maxMET = container.getInteger(0x10);
if (container.contains(0x11)) if (container.contains(0x11))
this.recoveryTime = container.getShort(0x11); this.recoveryTime = container.getShort(0x11);
if (container.contains(0x12)) if (container.contains(0x12))
@ -215,9 +237,36 @@ public class Workout {
this.minAltitude = container.getInteger(0x1d); this.minAltitude = container.getInteger(0x1d);
if (container.contains(0x20)) if (container.contains(0x20))
this.workoutAnaerobicEffect = container.getByte(0x20); this.workoutAnaerobicEffect = container.getByte(0x20);
if (container.contains(0x24))
this.hrZoneType = container.getByte(0x24);
if (container.contains(0x50))
this.runPaceZone1Min = container.getShort(0x50);
if (container.contains(0x51))
this.runPaceZone2Min = container.getShort(0x51);
if (container.contains(0x52))
this.runPaceZone3Min = container.getShort(0x52);
if (container.contains(0x53))
this.runPaceZone4Min = container.getShort(0x53);
if (container.contains(0x54))
this.runPaceZone5Min = container.getShort(0x54);
if (container.contains(0x55))
this.runPaceZone5Max = container.getShort(0x55);
if (container.contains(0x56))
this.runPaceZone1Time = container.getShort(0x56);
if (container.contains(0x57))
this.runPaceZone2Time = container.getShort(0x57);
if (container.contains(0x58))
this.runPaceZone3Time = container.getShort(0x58);
if (container.contains(0x59))
this.runPaceZone4Time = container.getShort(0x59);
if (container.contains(0x5a))
this.runPaceZone5Time = container.getShort(0x5a);
if (container.contains(0x5d))
this.algType = container.getByte(0x5d);
if (container.contains(0x63))
this.trainingPoints = container.getShort(0x63);
if (container.contains(0x66)) if (container.contains(0x66))
this.recoveryHeartRates = container.getBytes(0x66); this.recoveryHeartRates = container.getBytes(0x66);
} }
} }
} }

View File

@ -1649,8 +1649,25 @@ public class HuaweiSupportProvider {
packet.minHeartRatePeak, packet.minHeartRatePeak,
packet.maxHeartRatePeak, packet.maxHeartRatePeak,
recoveryHeartRates, recoveryHeartRates,
packet.swimType packet.swimType,
packet.maxMET,
packet.hrZoneType,
packet.runPaceZone1Min,
packet.runPaceZone2Min,
packet.runPaceZone3Min,
packet.runPaceZone4Min,
packet.runPaceZone5Min,
packet.runPaceZone5Max,
packet.runPaceZone1Time,
packet.runPaceZone2Time,
packet.runPaceZone3Time,
packet.runPaceZone4Time,
packet.runPaceZone5Time,
packet.algType,
packet.trainingPoints
); );
db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample); db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample);
return summarySample.getWorkoutId(); return summarySample.getWorkoutId();

View File

@ -395,7 +395,7 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
summaryData.add(ActivitySummaryEntries.DISTANCE_METERS, summary.getDistance(), ActivitySummaryEntries.UNIT_METERS); summaryData.add(ActivitySummaryEntries.DISTANCE_METERS, summary.getDistance(), ActivitySummaryEntries.UNIT_METERS);
summaryData.add(ActivitySummaryEntries.STEPS, summary.getStepCount(), ActivitySummaryEntries.UNIT_STEPS); summaryData.add(ActivitySummaryEntries.STEPS, summary.getStepCount(), ActivitySummaryEntries.UNIT_STEPS);
summaryData.add(ActivitySummaryEntries.ACTIVE_SECONDS, summary.getDuration(), ActivitySummaryEntries.UNIT_SECONDS); summaryData.add(ActivitySummaryEntries.ACTIVE_SECONDS, summary.getDuration(), ActivitySummaryEntries.UNIT_SECONDS);
summaryData.add(ActivitySummaryEntries.STATUS, summary.getStatus() & 0xFF, ActivitySummaryEntries.UNIT_NONE); //summaryData.add(ActivitySummaryEntries.STATUS, summary.getStatus() & 0xFF, ActivitySummaryEntries.UNIT_NONE);
summaryData.add(ActivitySummaryEntries.TYPE, summary.getType() & 0xFF, ActivitySummaryEntries.UNIT_NONE); summaryData.add(ActivitySummaryEntries.TYPE, summary.getType() & 0xFF, ActivitySummaryEntries.UNIT_NONE);
if (summary.getStrokes() != -1) { if (summary.getStrokes() != -1) {
@ -492,15 +492,15 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
int sumAltitudeDown = 0; int sumAltitudeDown = 0;
//NOTE: The method of retrieving HR zones from the Huawei watch is not discovered. It may not return zones. //NOTE: The method of retrieving HR zones from the Huawei watch is not discovered. It may not return zones.
// So they are calculated based on config. Enabled only for running and walking activities for testing. // So they are calculated based on config.
// Currently only calculated zones based on MHR.
// TODO: Use other methods after the configuration will be implemented. Use calculateMethod on HeartRateZonesConfig class.
// TODO: Enable for other workout types
HeartRateZonesConfig HRZonesCfg = null; HeartRateZonesConfig HRZonesCfg = null;
if( type == ActivityKind.WALKING || type == ActivityKind.RUNNING) { Integer zoneType = HuaweiWorkoutUtils.getHRZoneTypeByActivity(type);
int zoneCalculateMethod = summary.getHrZoneType();
LOG.info("Workout HR Zone Calculate Type: {}", zoneCalculateMethod);
if(zoneType != null && HeartRateZonesConfig.isCalculateMethodValidFroType(zoneType, zoneCalculateMethod)) {
ActivityUser activityUser = new ActivityUser(); ActivityUser activityUser = new ActivityUser();
HuaweiSportHRZones hrSportZones = new HuaweiSportHRZones(activityUser.getAge()); HuaweiSportHRZones hrSportZones = new HuaweiSportHRZones(activityUser.getAge());
HRZonesCfg = hrSportZones.getHRZonesConfigByType(HeartRateZonesConfig.TYPE_UPRIGHT); HRZonesCfg = hrSportZones.getHRZonesConfigByType(zoneType);
} }
int dataDelta = 5; int dataDelta = 5;
@ -514,7 +514,7 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
for (HuaweiWorkoutDataSample dataSample : dataSamples) { for (HuaweiWorkoutDataSample dataSample : dataSamples) {
if(HRZonesCfg != null) { if(HRZonesCfg != null) {
int zoneIdx = HRZonesCfg.getMHRZone(dataSample.getHeartRate() & 0xFF); int zoneIdx = HRZonesCfg.getZoneByMethod(dataSample.getHeartRate() & 0xFF, zoneCalculateMethod);
if (zoneIdx != -1 && dataIdx < (dataSamples.size() - 1)) { if (zoneIdx != -1 && dataIdx < (dataSamples.size() - 1)) {
HRZones[zoneIdx] += dataDelta; HRZones[zoneIdx] += dataDelta;
} }
@ -627,7 +627,7 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
if(HRZonesCfg != null) { if(HRZonesCfg != null) {
final double totalTime = Arrays.stream(HRZones).sum(); final double totalTime = Arrays.stream(HRZones).sum();
final List<String> zoneOrder = Arrays.asList(ActivitySummaryEntries.HR_ZONE_WARM_UP, ActivitySummaryEntries.HR_ZONE_FAT_BURN, ActivitySummaryEntries.HR_ZONE_AEROBIC, ActivitySummaryEntries.HR_ZONE_ANAEROBIC, ActivitySummaryEntries.HR_ZONE_EXTREME); final List<String> zoneOrder = Arrays.asList(ActivitySummaryEntries.HR_ZONE_WARM_UP, ActivitySummaryEntries.HR_ZONE_FAT_BURN, ActivitySummaryEntries.HR_ZONE_AEROBIC, ActivitySummaryEntries.HR_ZONE_ANAEROBIC, ActivitySummaryEntries.HR_ZONE_EXTREME);
for (int i = 0; i < zoneOrder.size(); i++) { for (int i = zoneOrder.size() - 1; i >= 0; i--) {
double timeInZone = HRZones[i]; double timeInZone = HRZones[i];
LOG.info("Zone: {} {}", zoneOrder.get(i), timeInZone); LOG.info("Zone: {} {}", zoneOrder.get(i), timeInZone);
summaryData.add( summaryData.add(
@ -675,8 +675,6 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
} }
if (stepRatePresent) { if (stepRatePresent) {
summaryData.add(ActivitySummaryEntries.STEP_RATE_SUM, stepRate, ActivitySummaryEntries.UNIT_SPM);
summaryData.add(ActivitySummaryEntries.STEP_RATE_AVG, avgStepRate, ActivitySummaryEntries.UNIT_SPM); summaryData.add(ActivitySummaryEntries.STEP_RATE_AVG, avgStepRate, ActivitySummaryEntries.UNIT_SPM);
} }
@ -724,11 +722,7 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
if(avgStrokeRate == -1) { if(avgStrokeRate == -1) {
avgStrokeRate = strokeRate; avgStrokeRate = strokeRate;
} }
// TODO: find out unit? summaryData.add(ActivitySummaryEntries.STROKE_RATE_MAX, maxStrokeRate, ActivitySummaryEntries.UNIT_STROKES_PER_MINUTE);
summaryData.add(ActivitySummaryEntries.STROKE_RATE_AVG, strokeRate, ActivitySummaryEntries.UNIT_NONE);
// TODO: find out unit?
summaryData.add(ActivitySummaryEntries.STROKE_RATE_MAX, maxStrokeRate, ActivitySummaryEntries.UNIT_NONE);
} }
if (heartRateCount > 0) { if (heartRateCount > 0) {

View File

@ -0,0 +1,48 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HeartRateZonesConfig;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
public class HuaweiWorkoutUtils {
private static final Map<ActivityKind, Integer> activityHRZoneType = createActivityHRZoneType();
//TODO: discover and add more activity types. Should be same as in the watch.
private static Map<ActivityKind, Integer> createActivityHRZoneType() {
Map<ActivityKind, Integer> result = new HashMap<>();
result.put(ActivityKind.RUNNING, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.WALKING, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.CYCLING, HeartRateZonesConfig.TYPE_SITTING);
result.put(ActivityKind.MOUNTAIN_HIKE, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.INDOOR_RUNNING, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.POOL_SWIM, HeartRateZonesConfig.TYPE_SWIMMING);
result.put(ActivityKind.INDOOR_CYCLING, HeartRateZonesConfig.TYPE_SITTING);
result.put(ActivityKind.SWIMMING_OPENWATER, HeartRateZonesConfig.TYPE_SWIMMING);
result.put(ActivityKind.INDOOR_WALKING, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.HIKING, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.JUMP_ROPING, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.PINGPONG, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.BADMINTON, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.TENNIS, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.SOCCER, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.BASKETBALL, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.VOLLEYBALL, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.ELLIPTICAL_TRAINER, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.ROWING_MACHINE, HeartRateZonesConfig.TYPE_SITTING);
result.put(ActivityKind.STEPPER, HeartRateZonesConfig.TYPE_UPRIGHT);
result.put(ActivityKind.YOGA, HeartRateZonesConfig.TYPE_OTHER);
return Collections.unmodifiableMap(result);
}
public static Integer getHRZoneTypeByActivity(ActivityKind type) {
if(activityHRZoneType.containsKey(type)) {
return activityHRZoneType.get(type);
}
return null;
}
}

View File

@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiRunPaceConfig;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FitnessData; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FitnessData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
@ -26,15 +27,7 @@ public class SendRunPaceConfigRequest extends Request {
@Override @Override
protected List<byte[]> createRequest() throws Request.RequestCreationException { protected List<byte[]> createRequest() throws Request.RequestCreationException {
try { try {
//Hardcoded value till interface enable threshold values return new FitnessData.RunPaceConfig.Request(paramsProvider, new HuaweiRunPaceConfig()).serialize();
return new FitnessData.RunPaceConfig.Request(paramsProvider,
0x1C2,
0x1A4,
0x186,
0x168,
0x14A,
0x12C
).serialize();
} catch (HuaweiPacket.CryptoException e) { } catch (HuaweiPacket.CryptoException e) {
throw new Request.RequestCreationException(e); throw new Request.RequestCreationException(e);
} }