mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-18 14:47:46 +01:00
[Huawei] Add support for workout calories and cycling power
This commit is contained in:
parent
013ffe5559
commit
1c2c1f710e
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||
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) {
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -1513,7 +1513,9 @@ public class HuaweiSupportProvider {
|
||||
data.eversionAngle,
|
||||
data.swolf,
|
||||
data.strokeRate,
|
||||
unknown
|
||||
unknown,
|
||||
data.calories,
|
||||
data.cyclingPower
|
||||
);
|
||||
dao.insertOrReplace(dataSample);
|
||||
}
|
||||
|
@ -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<HuaweiWorkoutPaceSample> it = qbPace.build().listIterator()) {
|
||||
|
@ -1907,6 +1907,9 @@
|
||||
<string name="fmtPaceCorrection">Pace %d correction</string>
|
||||
<string name="fmtPaceTypeAverage">Pace Type %d average</string>
|
||||
<string name="unknownDataEncountered">Unknown data encountered</string>
|
||||
<string name="cyclingPowerAverage">Average cycling power</string>
|
||||
<string name="cyclingPowerMin">Min cycling power</string>
|
||||
<string name="cyclingPowerMax">Max cycling power</string>
|
||||
<!-- activity summary units-->
|
||||
<string name="meters">m</string>
|
||||
<string name="cm">cm</string>
|
||||
|
Loading…
Reference in New Issue
Block a user