diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
index 2e8b101d6..b8cf1ac5a 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(75, MAIN_PACKAGE + ".entities");
+ final Schema schema = new Schema(76, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@@ -97,7 +97,7 @@ public class GBDaoGenerator {
addWatchXPlusHealthActivityKindOverlay(schema, user, device);
addTLW64ActivitySample(schema, user, device);
addLefunActivitySample(schema, user, device);
- addLefunBiometricSample(schema,user,device);
+ addLefunBiometricSample(schema, user, device);
addLefunSleepSample(schema, user, device);
addSonySWR12Sample(schema, user, device);
addBangleJSActivitySample(schema, user, device);
@@ -110,6 +110,7 @@ public class GBDaoGenerator {
addGarminFitFile(schema, user, device);
addGarminActivitySample(schema, user, device);
addGarminStressSample(schema, user, device);
+ addGarminBodyEnergySample(schema, user, device);
addGarminSpo2Sample(schema, user, device);
addGarminSleepStageSample(schema, user, device);
addGarminEventSample(schema, user, device);
@@ -632,7 +633,7 @@ public class GBDaoGenerator {
return activitySample;
}
- private static Entity addCyclingSample(Schema schema, Entity user, Entity device){
+ private static Entity addCyclingSample(Schema schema, Entity user, Entity device) {
Entity cyclingSample = addEntity(schema, "CyclingSample");
addCommonTimeSampleProperties("AbstractTimeSample", cyclingSample, user, device);
@@ -704,6 +705,13 @@ public class GBDaoGenerator {
return stressSample;
}
+ private static Entity addGarminBodyEnergySample(Schema schema, Entity user, Entity device) {
+ Entity stressSample = addEntity(schema, "GarminBodyEnergySample");
+ addCommonTimeSampleProperties("AbstractBodyEnergySample", stressSample, user, device);
+ stressSample.addIntProperty("energy").notNull().codeBeforeGetter(OVERRIDE);
+ return stressSample;
+ }
+
private static Entity addGarminSpo2Sample(Schema schema, Entity user, Entity device) {
Entity spo2sample = addEntity(schema, "GarminSpo2Sample");
addCommonTimeSampleProperties("AbstractSpo2Sample", spo2sample, user, device);
@@ -798,7 +806,7 @@ public class GBDaoGenerator {
return activitySample;
}
- private static Entity addCasioGBX100Sample(Schema schema, Entity user, Entity device) {
+ private static Entity addCasioGBX100Sample(Schema schema, Entity user, Entity device) {
Entity activitySample = addEntity(schema, "CasioGBX100ActivitySample");
activitySample.implementsSerializable();
addCommonActivitySampleProperties("AbstractGBX100ActivitySample", activitySample, user, device);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java
index a229354b7..fe44123a3 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java
@@ -71,6 +71,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.AbstractNotificationPattern;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
+import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
@@ -210,6 +211,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return null;
}
+ @Override
+ public TimeSampleProvider extends BodyEnergySample> getBodyEnergySampleProvider(final GBDevice device, final DaoSession session) {
+ return null;
+ }
+
@Override
public TimeSampleProvider extends HrvSummarySample> getHrvSummarySampleProvider(GBDevice device, DaoSession session) {
return null;
@@ -454,6 +460,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return false;
}
+ @Override
+ public boolean supportsBodyEnergy() {
+ return false;
+ }
+
@Override
public boolean supportsHrvMeasurement() {
return false;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java
index fde5a3f18..e25dc7ab2 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java
@@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.AbstractNotificationPattern;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
+import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
@@ -216,6 +217,8 @@ public interface DeviceCoordinator {
*/
boolean supportsStressMeasurement();
+ boolean supportsBodyEnergy();
+
boolean supportsHrvMeasurement();
boolean supportsSleepMeasurement();
@@ -287,6 +290,11 @@ public interface DeviceCoordinator {
*/
TimeSampleProvider extends StressSample> getStressSampleProvider(GBDevice device, DaoSession session);
+ /**
+ * Returns the sample provider for body energy data, for the device being supported.
+ */
+ TimeSampleProvider extends BodyEnergySample> getBodyEnergySampleProvider(GBDevice device, DaoSession session);
+
/**
* Returns the sample provider for HRV summary, for the device being supported.
*/
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminBodyEnergySampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminBodyEnergySampleProvider.java
new file mode 100644
index 000000000..1fe89f0ca
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminBodyEnergySampleProvider.java
@@ -0,0 +1,56 @@
+/* Copyright (C) 2024 José Rebelo
+
+ 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.devices.garmin;
+
+import androidx.annotation.NonNull;
+
+import de.greenrobot.dao.AbstractDao;
+import de.greenrobot.dao.Property;
+import nodomain.freeyourgadget.gadgetbridge.devices.AbstractTimeSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.GarminBodyEnergySample;
+import nodomain.freeyourgadget.gadgetbridge.entities.GarminBodyEnergySampleDao;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+
+public class GarminBodyEnergySampleProvider extends AbstractTimeSampleProvider {
+ public GarminBodyEnergySampleProvider(final GBDevice device, final DaoSession session) {
+ super(device, session);
+ }
+
+ @NonNull
+ @Override
+ public AbstractDao getSampleDao() {
+ return getSession().getGarminBodyEnergySampleDao();
+ }
+
+ @NonNull
+ @Override
+ protected Property getTimestampSampleProperty() {
+ return GarminBodyEnergySampleDao.Properties.Timestamp;
+ }
+
+ @NonNull
+ @Override
+ protected Property getDeviceIdentifierSampleProperty() {
+ return GarminBodyEnergySampleDao.Properties.DeviceId;
+ }
+
+ @Override
+ public GarminBodyEnergySample createSample() {
+ return new GarminBodyEnergySample();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java
index 546ca95f6..d053daa08 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java
@@ -24,6 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2SampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
@@ -98,6 +99,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return new GarminStressSampleProvider(device, session);
}
+ @Override
+ public TimeSampleProvider extends BodyEnergySample> getBodyEnergySampleProvider(final GBDevice device, final DaoSession session) {
+ return new GarminBodyEnergySampleProvider(device, session);
+ }
+
@Override
public TimeSampleProvider extends HrvSummarySample> getHrvSummarySampleProvider(final GBDevice device, final DaoSession session) {
return new GarminHrvSummarySampleProvider(device, session);
@@ -174,6 +180,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return true;
}
+ @Override
+ public boolean supportsBodyEnergy() {
+ return true;
+ }
+
@Override
public boolean supportsHrvMeasurement() {
return true;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractBodyEnergySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractBodyEnergySample.java
new file mode 100644
index 000000000..6c0bcee7a
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractBodyEnergySample.java
@@ -0,0 +1,36 @@
+/* Copyright (C) 2024 José Rebelo
+
+ 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.entities;
+
+import androidx.annotation.NonNull;
+
+import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample;
+import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
+
+public abstract class AbstractBodyEnergySample extends AbstractTimeSample implements BodyEnergySample {
+
+ @NonNull
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" +
+ "timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimestampMillis(getTimestamp())) +
+ ", energy=" + getEnergy() +
+ ", userId=" + getUserId() +
+ ", deviceId=" + getDeviceId() +
+ "}";
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BodyEnergySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BodyEnergySample.java
new file mode 100644
index 000000000..50a412ae4
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BodyEnergySample.java
@@ -0,0 +1,24 @@
+/* Copyright (C) 2024 José Rebelo
+
+ 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.model;
+
+public interface BodyEnergySample extends TimeSample {
+ /**
+ * Body energy value, between 0 and 100.
+ */
+ int getEnergy();
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java
index be7096053..62eac383c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java
@@ -31,6 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminActivitySampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminBodyEnergySampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminEventSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminHrvSummarySampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminHrvValueSampleProvider;
@@ -41,6 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.entities.GarminBodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminEventSample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvValueSample;
@@ -79,6 +81,7 @@ public class FitImporter {
private final SortedMap> activitySamplesPerTimestamp = new TreeMap<>();
private final List stressSamples = new ArrayList<>();
+ private final List bodyEnergySamples = new ArrayList<>();
private final List spo2samples = new ArrayList<>();
private final List events = new ArrayList<>();
private final List sleepStageSamples = new ArrayList<>();
@@ -113,15 +116,24 @@ public class FitImporter {
}
fileId = newFileId;
} else if (record instanceof FitStressLevel) {
- final Integer stress = ((FitStressLevel) record).getStressLevelValue();
- if (stress == null || stress < 0) {
- continue;
+ final FitStressLevel stressRecord = (FitStressLevel) record;
+ final Integer stress = stressRecord.getStressLevelValue();
+ if (stress != null && stress >= 0) {
+ LOG.trace("Stress at {}: {}", ts, stress);
+ final GarminStressSample sample = new GarminStressSample();
+ sample.setTimestamp(ts * 1000L);
+ sample.setStress(stress);
+ stressSamples.add(sample);
+ }
+
+ final Integer energy = stressRecord.getBodyEnergy();
+ if (energy != null) {
+ LOG.trace("Body energy at {}: {}", ts, energy);
+ final GarminBodyEnergySample sample = new GarminBodyEnergySample();
+ sample.setTimestamp(ts * 1000L);
+ sample.setEnergy(energy);
+ bodyEnergySamples.add(sample);
}
- LOG.trace("Stress at {}: {}", ts, stress);
- final GarminStressSample sample = new GarminStressSample();
- sample.setTimestamp(ts * 1000L);
- sample.setStress(stress);
- stressSamples.add(sample);
} else if (record instanceof FitSleepStage) {
final FieldDefinitionSleepStage.SleepStage stage = ((FitSleepStage) record).getSleepStage();
if (stage == null) {
@@ -245,6 +257,7 @@ public class FitImporter {
persistActivitySamples();
persistSpo2Samples();
persistStressSamples();
+ persistBodyEnergySamples();
break;
case SLEEP:
persistEvents();
@@ -397,9 +410,12 @@ public class FitImporter {
private void reset() {
activitySamplesPerTimestamp.clear();
stressSamples.clear();
+ bodyEnergySamples.clear();
spo2samples.clear();
events.clear();
sleepStageSamples.clear();
+ hrvSummarySamples.clear();
+ hrvValueSamples.clear();
timesInZone.clear();
activityPoints.clear();
unknownRecords.clear();
@@ -655,4 +671,30 @@ public class FitImporter {
GB.toast(context, "Error saving stress samples", Toast.LENGTH_LONG, GB.ERROR, e);
}
}
+
+ private void persistBodyEnergySamples() {
+ if (bodyEnergySamples.isEmpty()) {
+ return;
+ }
+
+ LOG.debug("Will persist {} body energy samples", bodyEnergySamples.size());
+
+ try (DBHandler handler = GBApplication.acquireDB()) {
+ final DaoSession session = handler.getDaoSession();
+
+ final Device device = DBHelper.getDevice(gbDevice, session);
+ final User user = DBHelper.getUser(session);
+
+ final GarminBodyEnergySampleProvider sampleProvider = new GarminBodyEnergySampleProvider(gbDevice, session);
+
+ for (final GarminBodyEnergySample sample : bodyEnergySamples) {
+ sample.setDevice(device);
+ sample.setUser(user);
+ }
+
+ sampleProvider.addSamples(bodyEnergySamples);
+ } catch (final Exception e) {
+ GB.toast(context, "Error saving body energy samples", Toast.LENGTH_LONG, GB.ERROR, e);
+ }
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java
index 4399745ce..3b5916796 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java
@@ -230,7 +230,8 @@ public class GlobalFITMessage {
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)
+ new FieldDefinitionPrimitive(1, BaseType.UINT32, "stress_level_time", FieldDefinitionFactory.FIELD.TIMESTAMP),
+ new FieldDefinitionPrimitive(3, BaseType.SINT8, "body_energy")
));
public static GlobalFITMessage SPO2 = new GlobalFITMessage(269, "SPO2", Arrays.asList(
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitStressLevel.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitStressLevel.java
index 51f9986e4..a588da4d4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitStressLevel.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitStressLevel.java
@@ -46,6 +46,11 @@ public class FitStressLevel extends RecordData {
return (Long) getFieldByNumber(1);
}
+ @Nullable
+ public Integer getBodyEnergy() {
+ return (Integer) getFieldByNumber(3);
+ }
+
// manual changes below
@Override