diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
index 52e34c2eb..a8b381019 100644
--- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
+++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
@@ -45,7 +45,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
- final Schema schema = new Schema(71, MAIN_PACKAGE + ".entities");
+ final Schema schema = new Schema(72, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@@ -1167,6 +1167,9 @@ public class GBDaoGenerator {
workoutDataSample.addByteArrayProperty("dataErrorHex");
+ workoutDataSample.addShortProperty("calories").notNull();
+ workoutDataSample.addShortProperty("cyclingPower").notNull();
+
return workoutDataSample;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_72.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_72.java
new file mode 100644
index 000000000..21bc21dde
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_72.java
@@ -0,0 +1,43 @@
+/* Copyright (C) 2024 Martin.JM
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+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.HuaweiWorkoutDataSampleDao;
+
+public class GadgetbridgeUpdate_72 implements DBUpdateScript {
+ @Override
+ public void upgradeSchema(final SQLiteDatabase db) {
+ if (!DBHelper.existsColumn(HuaweiWorkoutDataSampleDao.TABLENAME, HuaweiWorkoutDataSampleDao.Properties.Calories.columnName, db)) {
+ final String statement = "ALTER TABLE " + HuaweiWorkoutDataSampleDao.TABLENAME + " ADD COLUMN \""
+ + HuaweiWorkoutDataSampleDao.Properties.Calories.columnName + "\" INTEGER NOT NULL DEFAULT -1";
+ db.execSQL(statement);
+ }
+ if (!DBHelper.existsColumn(HuaweiWorkoutDataSampleDao.TABLENAME, HuaweiWorkoutDataSampleDao.Properties.CyclingPower.columnName, db)) {
+ final String statement = "ALTER TABLE " + HuaweiWorkoutDataSampleDao.TABLENAME + " ADD COLUMN \""
+ + HuaweiWorkoutDataSampleDao.Properties.CyclingPower.columnName + "\" INTEGER NOT NULL DEFAULT -1";
+ db.execSQL(statement);
+ }
+ }
+
+ @Override
+ public void downgradeSchema(final SQLiteDatabase db) {
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java
index 74bf0dfdf..e725eb20e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java
@@ -239,12 +239,15 @@ public class Workout {
public byte swolf = -1;
public short strokeRate = -1;
+ public short calories = -1;
+ public short cyclingPower = -1;
+
public int timestamp = -1; // Calculated timestamp for this data point
@Override
public String toString() {
return "Data{" +
- "unknownData=" + unknownData +
+ "unknownData=" + Arrays.toString(unknownData) +
", heartRate=" + heartRate +
", speed=" + speed +
", stepRate=" + stepRate +
@@ -259,13 +262,15 @@ public class Workout {
", eversionAngle=" + eversionAngle +
", swolf=" + swolf +
", strokeRate=" + strokeRate +
+ ", calories=" + calories +
+ ", cyclingPower=" + cyclingPower +
", timestamp=" + timestamp +
'}';
}
}
// I'm not sure about the lengths, but we haven't gotten any complaints so they probably are fine
- private final byte[] bitmapLengths = {1, 2, 1, 2, 2, 4, -1, 2, 2, 1, 1, 1, 1, 1, 1, 1};
+ private final byte[] bitmapLengths = {1, 2, 1, 2, 2, 4, -1, 2, 2, 2, 1, 1, 1, 1, 1, 1};
private final byte[] innerBitmapLengths = {2, 2, 2, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1};
public short workoutNumber;
@@ -410,6 +415,12 @@ public class Workout {
}
}
break;
+ case 7:
+ data.calories = buf.getShort();
+ break;
+ case 8:
+ data.cyclingPower = buf.getShort();
+ break;
default:
data.unknownData = this.tlv.serialize();
// Fix alignment
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java
index bb062cc4d..a23847460 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java
@@ -106,6 +106,10 @@ public class ActivitySummaryEntries {
public static final String MAXIMUM_OXYGEN_UPTAKE = "maximumOxygenUptake";
public static final String RECOVERY_TIME = "recoveryTime";
+ public static final String CYCLING_POWER_AVERAGE = "cyclingPowerAverage";
+ public static final String CYCLING_POWER_MIN = "cyclingPowerMin";
+ public static final String CYCLING_POWER_MAX = "cyclingPowerMax";
+
public static final String UNIT_BPM = "bpm";
public static final String UNIT_CM = "cm";
public static final String UNIT_UNIX_EPOCH_SECONDS = "unix_epoch_seconds";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java
index 28ce5b4ee..7722676a4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java
@@ -1513,7 +1513,9 @@ public class HuaweiSupportProvider {
data.eversionAngle,
data.swolf,
data.strokeRate,
- unknown
+ unknown,
+ data.calories,
+ data.cyclingPower
);
dao.insertOrReplace(dataSample);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java
index 09d6634ed..ee552b264 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java
@@ -106,7 +106,9 @@ public class HuaweiWorkoutGbParser {
responseData.eversionAngle,
responseData.swolf,
responseData.strokeRate,
- dataErrorHex
+ dataErrorHex,
+ responseData.calories,
+ responseData.cyclingPower
);
dbHandler.getDaoSession().getHuaweiWorkoutDataSampleDao().insertOrReplace(dataSample);
@@ -301,6 +303,11 @@ public class HuaweiWorkoutGbParser {
int heartRateCount = 0;
int maxHeartRate = 0;
int minHeartRate = Integer.MAX_VALUE;
+ int sumCalories = 0;
+ int minCyclingPower = Integer.MAX_VALUE;
+ int maxCyclingPower = 0;
+ int cyclingPower = 0;
+ int cyclingPowerCount = 0;
for (HuaweiWorkoutDataSample dataSample : dataSamples) {
if (dataSample.getSpeed() != -1) {
speed += dataSample.getSpeed();
@@ -373,6 +380,17 @@ public class HuaweiWorkoutGbParser {
if (hr < minHeartRate)
minHeartRate = hr;
}
+ if (dataSample.getCalories() != -1)
+ sumCalories += dataSample.getCalories();
+ if (dataSample.getCyclingPower() != -1) {
+ int cp = dataSample.getCyclingPower();
+ cyclingPower += cp;
+ cyclingPowerCount += 1;
+ if (cp > maxCyclingPower)
+ maxCyclingPower = cp;
+ if (cp < minCyclingPower)
+ minCyclingPower = cp;
+ }
if (dataSample.getDataErrorHex() != null)
unknownData = true;
}
@@ -400,6 +418,8 @@ public class HuaweiWorkoutGbParser {
strokeRate = strokeRate / strokeRateCount;
if (heartRateCount > 0)
heartRate = heartRate / heartRateCount;
+ if (cyclingPowerCount > 0)
+ cyclingPower = cyclingPower / cyclingPowerCount;
if (speedCount > 0) {
JSONObject speedJson = new JSONObject();
@@ -534,6 +554,30 @@ public class HuaweiWorkoutGbParser {
minHeartRateJson.put("unit", ActivitySummaryEntries.UNIT_BPM);
jsonObject.put(ActivitySummaryEntries.HR_MIN, minHeartRateJson);
}
+
+ if (sumCalories > 0) {
+ JSONObject caloriesSumJson = new JSONObject();
+ caloriesSumJson.put("value", sumCalories);
+ caloriesSumJson.put("unit", ActivitySummaryEntries.UNIT_KCAL);
+ jsonObject.put(ActivitySummaryEntries.CALORIES_BURNT, caloriesSumJson);
+ }
+
+ if (cyclingPowerCount > 0) {
+ JSONObject cyclingPowerJson = new JSONObject();
+ cyclingPowerJson.put("value", cyclingPower);
+ cyclingPowerJson.put("unit", "");
+ jsonObject.put(ActivitySummaryEntries.CYCLING_POWER_AVERAGE, cyclingPowerJson);
+
+ JSONObject minCyclingPowerJson = new JSONObject();
+ minCyclingPowerJson.put("value", minCyclingPower);
+ minCyclingPowerJson.put("unit", "");
+ jsonObject.put(ActivitySummaryEntries.CYCLING_POWER_MIN, minCyclingPowerJson);
+
+ JSONObject maxCyclingPowerJson = new JSONObject();
+ maxCyclingPowerJson.put("value", maxCyclingPower);
+ maxCyclingPowerJson.put("unit", "");
+ jsonObject.put(ActivitySummaryEntries.CYCLING_POWER_MAX, maxCyclingPowerJson);
+ }
}
try (CloseableListIterator it = qbPace.build().listIterator()) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9a2717a8c..fa1ebc3c8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1907,6 +1907,9 @@
Pace %d correction
Pace Type %d average
Unknown data encountered
+ Average cycling power
+ Min cycling power
+ Max cycling power
m
cm