mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-25 10:05:49 +01:00
Garmin: Parse HR time in zone, sweat loss, avg and max HR
This commit is contained in:
parent
880c85abcf
commit
10e27c88c6
@ -105,6 +105,7 @@ public class ActivitySummaryEntries {
|
||||
public static final String WORKOUT_LOAD = "currentWorkoutLoad";
|
||||
public static final String MAXIMUM_OXYGEN_UPTAKE = "maximumOxygenUptake";
|
||||
public static final String RECOVERY_TIME = "recoveryTime";
|
||||
public static final String ESTIMATED_SWEAT_LOSS = "estimatedSweatLoss";
|
||||
|
||||
public static final String CYCLING_POWER_AVERAGE = "cyclingPowerAverage";
|
||||
public static final String CYCLING_POWER_MIN = "cyclingPowerMin";
|
||||
@ -114,6 +115,7 @@ public class ActivitySummaryEntries {
|
||||
public static final String UNIT_CM = "cm";
|
||||
public static final String UNIT_UNIX_EPOCH_SECONDS = "unix_epoch_seconds";
|
||||
public static final String UNIT_KCAL = "calories_unit";
|
||||
public static final String UNIT_ML = "ml";
|
||||
public static final String UNIT_LAPS = "laps_unit";
|
||||
public static final String UNIT_KILOMETERS = "km";
|
||||
public static final String UNIT_METERS = "meters";
|
||||
|
@ -7,6 +7,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionFileType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrTimeInZone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrZoneHighBoundary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrvStatus;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
||||
@ -34,6 +36,10 @@ public class FieldDefinitionFactory {
|
||||
return new FieldDefinitionGoalType(localNumber, size, baseType, name);
|
||||
case HRV_STATUS:
|
||||
return new FieldDefinitionHrvStatus(localNumber, size, baseType, name);
|
||||
case HR_TIME_IN_ZONE:
|
||||
return new FieldDefinitionHrTimeInZone(localNumber, size, baseType, name);
|
||||
case HR_ZONE_HIGH_BOUNDARY:
|
||||
return new FieldDefinitionHrZoneHighBoundary(localNumber, size, baseType, name);
|
||||
case MEASUREMENT_SYSTEM:
|
||||
return new FieldDefinitionMeasurementSystem(localNumber, size, baseType, name);
|
||||
case TEMPERATURE:
|
||||
@ -62,6 +68,8 @@ public class FieldDefinitionFactory {
|
||||
GOAL_SOURCE,
|
||||
GOAL_TYPE,
|
||||
HRV_STATUS,
|
||||
HR_TIME_IN_ZONE,
|
||||
HR_ZONE_HIGH_BOUNDARY,
|
||||
MEASUREMENT_SYSTEM,
|
||||
TEMPERATURE,
|
||||
TIMESTAMP,
|
||||
|
@ -5,8 +5,19 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.CALORIES_BURNT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DESCENT_DISTANCE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DISTANCE_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.ESTIMATED_SWEAT_LOSS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_AVG;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_MAX;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_AEROBIC;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_ANAEROBIC;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_EXTREME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_FAT_BURN;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_NA;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_WARM_UP;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BPM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KCAL;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_ML;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS;
|
||||
|
||||
import android.content.Context;
|
||||
@ -336,6 +347,15 @@ public class FitImporter {
|
||||
if (session.getTotalCalories() != null) {
|
||||
summaryData.add(CALORIES_BURNT, session.getTotalCalories(), UNIT_KCAL);
|
||||
}
|
||||
if (session.getEstimatedSweatLoss() != null) {
|
||||
summaryData.add(ESTIMATED_SWEAT_LOSS, session.getEstimatedSweatLoss(), UNIT_ML);
|
||||
}
|
||||
if (session.getAverageHeartRate() != null) {
|
||||
summaryData.add(HR_AVG, session.getAverageHeartRate(), UNIT_BPM);
|
||||
}
|
||||
if (session.getMaxHeartRate() != null) {
|
||||
summaryData.add(HR_MAX, session.getMaxHeartRate(), UNIT_BPM);
|
||||
}
|
||||
if (session.getTotalAscent() != null) {
|
||||
summaryData.add(ASCENT_DISTANCE, session.getTotalAscent(), UNIT_METERS);
|
||||
}
|
||||
@ -343,16 +363,21 @@ public class FitImporter {
|
||||
summaryData.add(DESCENT_DISTANCE, session.getTotalDescent(), UNIT_METERS);
|
||||
}
|
||||
|
||||
//FitTimeInZone timeInZone = null;
|
||||
//for (final FitTimeInZone fitTimeInZone : timesInZone) {
|
||||
// // Find the firt time in zone for the session (assumes single-session)
|
||||
// if (fitTimeInZone.getReferenceMessage() != null && fitTimeInZone.getReferenceMessage() == 18) {
|
||||
// timeInZone = fitTimeInZone;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//if (timeInZone != null) {
|
||||
//}
|
||||
for (final FitTimeInZone fitTimeInZone : timesInZone) {
|
||||
// Find the firt time in zone for the session (assumes single-session)
|
||||
if (fitTimeInZone.getReferenceMessage() != null && fitTimeInZone.getReferenceMessage() == 18) {
|
||||
final Double[] timeInZone = fitTimeInZone.getTimeInZone();
|
||||
if (timeInZone != null && timeInZone.length == 6) {
|
||||
summaryData.add(HR_ZONE_NA, timeInZone[0].floatValue(), UNIT_SECONDS);
|
||||
summaryData.add(HR_ZONE_WARM_UP, timeInZone[1].floatValue(), UNIT_SECONDS);
|
||||
summaryData.add(HR_ZONE_FAT_BURN, timeInZone[2].floatValue(), UNIT_SECONDS);
|
||||
summaryData.add(HR_ZONE_AEROBIC, timeInZone[3].floatValue(), UNIT_SECONDS);
|
||||
summaryData.add(HR_ZONE_ANAEROBIC, timeInZone[4].floatValue(), UNIT_SECONDS);
|
||||
summaryData.add(HR_ZONE_EXTREME, timeInZone[5].floatValue(), UNIT_SECONDS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
summary.setSummaryData(summaryData.toString());
|
||||
if (file != null) {
|
||||
|
@ -99,9 +99,12 @@ public class GlobalFITMessage {
|
||||
new FieldDefinitionPrimitive(8, BaseType.UINT32, "total_timer_time"), // no pauses
|
||||
new FieldDefinitionPrimitive(9, BaseType.UINT32, "total_distance"),
|
||||
new FieldDefinitionPrimitive(11, BaseType.UINT16, "total_calories"),
|
||||
new FieldDefinitionPrimitive(16, BaseType.UINT8, "average_heart_rate"),
|
||||
new FieldDefinitionPrimitive(17, BaseType.UINT8, "max_heart_rate"),
|
||||
new FieldDefinitionPrimitive(22, BaseType.UINT16, "total_ascent"),
|
||||
new FieldDefinitionPrimitive(23, BaseType.UINT16, "total_descent"),
|
||||
new FieldDefinitionPrimitive(110, BaseType.STRING, 64, "sport_profile_name"),
|
||||
new FieldDefinitionPrimitive(178, BaseType.UINT16, "estimated_sweat_loss"),
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||
));
|
||||
|
||||
@ -231,8 +234,8 @@ public class GlobalFITMessage {
|
||||
public static GlobalFITMessage TIME_IN_ZONE = new GlobalFITMessage(216, "TIME_IN_ZONE", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.UINT16, "reference_message"),
|
||||
new FieldDefinitionPrimitive(1, BaseType.UINT16, "reference_index"),
|
||||
new FieldDefinitionPrimitive(2, BaseType.UINT32, "time_in_zone"), // seconds
|
||||
new FieldDefinitionPrimitive(6, BaseType.UINT8, "hr_zone_high_boundary"), // bpm
|
||||
new FieldDefinitionPrimitive(2, BaseType.UINT32, "time_in_zone", FieldDefinitionFactory.FIELD.HR_TIME_IN_ZONE), // seconds
|
||||
new FieldDefinitionPrimitive(6, BaseType.UINT8, "hr_zone_high_boundary", FieldDefinitionFactory.FIELD.HR_ZONE_HIGH_BOUNDARY), // bpm
|
||||
new FieldDefinitionPrimitive(10, BaseType.ENUM, "hr_calc_type"), // 1 percent max hr
|
||||
new FieldDefinitionPrimitive(11, BaseType.UINT8, "max_heart_rate"),
|
||||
new FieldDefinitionPrimitive(12, BaseType.UINT8, "resting_heart_rate"),
|
||||
@ -244,6 +247,14 @@ public class GlobalFITMessage {
|
||||
new FieldDefinitionPrimitive(0, BaseType.UINT16, "time", FieldDefinitionFactory.FIELD.ALARM)
|
||||
));
|
||||
|
||||
public static GlobalFITMessage SET = new GlobalFITMessage(225, "SET", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.UINT32, "duration"),
|
||||
new FieldDefinitionPrimitive(5, BaseType.UINT8, "set_type"), // 1 active 0 rest
|
||||
new FieldDefinitionPrimitive(6, BaseType.UINT32, "start_time", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
new FieldDefinitionPrimitive(10, BaseType.UINT16, "message_index"),
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||
));
|
||||
|
||||
public static GlobalFITMessage STRESS_LEVEL = new GlobalFITMessage(227, "STRESS_LEVEL", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.SINT16, "stress_level_value"),
|
||||
new FieldDefinitionPrimitive(1, BaseType.UINT32, "stress_level_time", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
@ -309,6 +320,7 @@ public class GlobalFITMessage {
|
||||
put(207, DEVELOPER_DATA);
|
||||
put(216, TIME_IN_ZONE);
|
||||
put(222, ALARM_SETTINGS);
|
||||
put(225, SET);
|
||||
put(227, STRESS_LEVEL);
|
||||
put(269, SPO2);
|
||||
put(275, SLEEP_STAGE);
|
||||
|
@ -190,7 +190,19 @@ public class FitCodeGen {
|
||||
sb.append("\n");
|
||||
sb.append(" @Nullable\n");
|
||||
sb.append(" public ").append(fieldTypeName).append(method(" get", primitive)).append("() {\n");
|
||||
sb.append(" return (").append(fieldTypeName).append(") getFieldByNumber(").append(primitive.getNumber()).append(");\n");
|
||||
if (fieldTypeName.endsWith("[]")) {
|
||||
// Special case for arrays, since these are decoded in RecordData and we can't easily decode them with the correct type
|
||||
// FIXME this should be refactored...
|
||||
final String simpleTypeName = fieldTypeName.replace("[]", "");
|
||||
sb.append(" final Object[] objectsArray = (Object[]) getFieldByNumber(").append(primitive.getNumber()).append(");\n");
|
||||
sb.append(" final ").append(fieldTypeName).append(" ret = new ").append(simpleTypeName).append("[objectsArray.length];\n");
|
||||
sb.append(" for (int i = 0; i < objectsArray.length; i++) {\n");
|
||||
sb.append(" ret[i] = (").append(simpleTypeName).append(") objectsArray[i];\n");
|
||||
sb.append(" }\n");
|
||||
sb.append(" return ret;\n");
|
||||
} else {
|
||||
sb.append(" return (").append(fieldTypeName).append(") getFieldByNumber(").append(primitive.getNumber()).append(");\n");
|
||||
}
|
||||
sb.append(" }\n");
|
||||
}
|
||||
|
||||
@ -238,6 +250,10 @@ public class FitCodeGen {
|
||||
return FieldDefinitionGoalType.Type.class;
|
||||
case HRV_STATUS:
|
||||
return FieldDefinitionHrvStatus.HrvStatus.class;
|
||||
case HR_TIME_IN_ZONE:
|
||||
return Double[].class;
|
||||
case HR_ZONE_HIGH_BOUNDARY:
|
||||
return Integer[].class;
|
||||
case MEASUREMENT_SYSTEM:
|
||||
return FieldDefinitionMeasurementSystem.Type.class;
|
||||
case TEMPERATURE:
|
||||
|
@ -0,0 +1,10 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||
|
||||
public class FieldDefinitionHrTimeInZone extends FieldDefinition {
|
||||
public FieldDefinitionHrTimeInZone(final int localNumber, final int size, final BaseType baseType, final String name) {
|
||||
super(localNumber, size, baseType, name, 1000, 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||
|
||||
public class FieldDefinitionHrZoneHighBoundary extends FieldDefinition {
|
||||
public FieldDefinitionHrZoneHighBoundary(final int localNumber, final int size, final BaseType baseType, final String name) {
|
||||
super(localNumber, size, baseType, name, 1, 0);
|
||||
}
|
||||
}
|
@ -59,6 +59,8 @@ public class FitRecordDataFactory {
|
||||
return new FitTimeInZone(recordDefinition, recordHeader);
|
||||
case 222:
|
||||
return new FitAlarmSettings(recordDefinition, recordHeader);
|
||||
case 225:
|
||||
return new FitSet(recordDefinition, recordHeader);
|
||||
case 227:
|
||||
return new FitStressLevel(recordDefinition, recordHeader);
|
||||
case 269:
|
||||
|
@ -75,6 +75,16 @@ public class FitSession extends RecordData {
|
||||
return (Integer) getFieldByNumber(11);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getAverageHeartRate() {
|
||||
return (Integer) getFieldByNumber(16);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getMaxHeartRate() {
|
||||
return (Integer) getFieldByNumber(17);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getTotalAscent() {
|
||||
return (Integer) getFieldByNumber(22);
|
||||
@ -90,6 +100,11 @@ public class FitSession extends RecordData {
|
||||
return (String) getFieldByNumber(110);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getEstimatedSweatLoss() {
|
||||
return (Integer) getFieldByNumber(178);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return (Long) getFieldByNumber(253);
|
||||
|
@ -0,0 +1,47 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
||||
|
||||
//
|
||||
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
||||
// See nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen
|
||||
//
|
||||
public class FitSet extends RecordData {
|
||||
public FitSet(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||
super(recordDefinition, recordHeader);
|
||||
|
||||
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
|
||||
if (globalNumber != 225) {
|
||||
throw new IllegalArgumentException("FitSet expects global messages of " + 225 + ", got " + globalNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getDuration() {
|
||||
return (Long) getFieldByNumber(0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getSetType() {
|
||||
return (Integer) getFieldByNumber(5);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getStartTime() {
|
||||
return (Long) getFieldByNumber(6);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getMessageIndex() {
|
||||
return (Integer) getFieldByNumber(10);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return (Long) getFieldByNumber(254);
|
||||
}
|
||||
}
|
@ -31,13 +31,23 @@ public class FitTimeInZone extends RecordData {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimeInZone() {
|
||||
return (Long) getFieldByNumber(2);
|
||||
public Double[] getTimeInZone() {
|
||||
final Object[] objectsArray = (Object[]) getFieldByNumber(2);
|
||||
final Double[] ret = new Double[objectsArray.length];
|
||||
for (int i = 0; i < objectsArray.length; i++) {
|
||||
ret[i] = (Double) objectsArray[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getHrZoneHighBoundary() {
|
||||
return (Integer) getFieldByNumber(6);
|
||||
public Integer[] getHrZoneHighBoundary() {
|
||||
final Object[] objectsArray = (Object[]) getFieldByNumber(6);
|
||||
final Integer[] ret = new Integer[objectsArray.length];
|
||||
for (int i = 0; i < objectsArray.length; i++) {
|
||||
ret[i] = (Integer) objectsArray[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -2092,6 +2092,7 @@
|
||||
<string name="anaerobicTrainingEffect">Anaerobic Effect</string>
|
||||
<string name="currentWorkoutLoad">Workout Load</string>
|
||||
<string name="maximumOxygenUptake">Maximum Oxygen Uptake</string>
|
||||
<string name="estimatedSweatLoss">Estimated Sweat Loss</string>
|
||||
<string name="averageStride">Average Stride</string>
|
||||
<string name="maxStride">Max Stride</string>
|
||||
<string name="minStride">Min Stride</string>
|
||||
|
Loading…
Reference in New Issue
Block a user