diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
index d09b20f1d..fb6d20ad2 100644
--- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
+++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
@@ -46,7 +46,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
- final Schema schema = new Schema(80, MAIN_PACKAGE + ".entities");
+ final Schema schema = new Schema(81, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@@ -117,6 +117,7 @@ public class GBDaoGenerator {
addGarminEventSample(schema, user, device);
addGarminHrvSummarySample(schema, user, device);
addGarminHrvValueSample(schema, user, device);
+ addGarminRespiratoryRateSample(schema, user, device);
addPendingFile(schema, user, device);
addWena3EnergySample(schema, user, device);
addWena3BehaviorSample(schema, user, device);
@@ -354,9 +355,14 @@ public class GBDaoGenerator {
private static Entity addHuamiSleepRespiratoryRateSample(Schema schema, Entity user, Entity device) {
Entity sleepRespiratoryRateSample = addEntity(schema, "HuamiSleepRespiratoryRateSample");
- addCommonTimeSampleProperties("AbstractSleepRespiratoryRateSample", sleepRespiratoryRateSample, user, device);
+ addCommonTimeSampleProperties("AbstractRespiratoryRateSample", sleepRespiratoryRateSample, user, device);
sleepRespiratoryRateSample.addIntProperty("utcOffset").notNull();
- sleepRespiratoryRateSample.addIntProperty("rate").notNull().codeBeforeGetter(OVERRIDE);
+ sleepRespiratoryRateSample.addIntProperty("rate").notNull().codeBeforeGetter(
+ "@Override\n" +
+ " public float getRespiratoryRate() {\n" +
+ " return (float) getRate();\n" +
+ " }\n\n"
+ );
return sleepRespiratoryRateSample;
}
@@ -815,6 +821,13 @@ public class GBDaoGenerator {
return hrvValueSample;
}
+ private static Entity addGarminRespiratoryRateSample(Schema schema, Entity user, Entity device) {
+ Entity garminRespiratoryRateSample = addEntity(schema, "GarminRespiratoryRateSample");
+ addCommonTimeSampleProperties("AbstractRespiratoryRateSample", garminRespiratoryRateSample, user, device);
+ garminRespiratoryRateSample.addFloatProperty("respiratoryRate").notNull().codeBeforeGetter(OVERRIDE);
+ return garminRespiratoryRateSample;
+ }
+
private static Entity addPendingFile(Schema schema, Entity user, Entity device) {
Entity pendingFile = addEntity(schema, "PendingFile");
pendingFile.setJavaDoc(
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 935877260..9ac9cffc5 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java
@@ -76,7 +76,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
-import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
@@ -271,7 +271,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
}
@Override
- public TimeSampleProvider extends SleepRespiratoryRateSample> getSleepRespiratoryRateSampleProvider(GBDevice device, DaoSession session) {
+ public TimeSampleProvider extends RespiratoryRateSample> getRespiratoryRateSampleProvider(GBDevice device, DaoSession session) {
return null;
}
@@ -538,10 +538,15 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
}
@Override
- public boolean supportsSleepRespiratoryRate() {
+ public boolean supportsRespiratoryRate() {
return false;
}
+ @Override
+ public boolean supportsSleepRespiratoryRate() {
+ return supportsRespiratoryRate();
+ }
+
@Override
public boolean supportsWeightMeasurement() {
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 b58ab0903..76f90cf85 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java
@@ -56,7 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
-import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
@@ -263,6 +263,11 @@ public interface DeviceCoordinator {
*/
boolean supportsPaiTime();
+ /**
+ * Indicates whether the device supports respiratory rate tracking.
+ */
+ boolean supportsRespiratoryRate();
+
/**
* Returns true if sleep respiratory rate measurement and fetching is supported by
* the device (with this coordinator).
@@ -360,7 +365,7 @@ public interface DeviceCoordinator {
/**
* Returns the sample provider for sleep respiratory rate data, for the device being supported.
*/
- TimeSampleProvider extends SleepRespiratoryRateSample> getSleepRespiratoryRateSampleProvider(GBDevice device, DaoSession session);
+ TimeSampleProvider extends RespiratoryRateSample> getRespiratoryRateSampleProvider(GBDevice device, DaoSession session);
/**
* Returns the sample provider for weight data, for the device being supported.
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 e156c418e..53a4d4866 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
@@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.GarminBodyEnergySampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminEventSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvSummarySampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvValueSampleDao;
+import nodomain.freeyourgadget.gadgetbridge.entities.GarminRespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSleepStageSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2SampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSampleDao;
@@ -44,6 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
import nodomain.freeyourgadget.gadgetbridge.model.Vo2MaxSample;
@@ -147,6 +149,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return new GarminSpo2SampleProvider(device, session);
}
+ @Override
+ public TimeSampleProvider extends RespiratoryRateSample> getRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
+ return new GarminRespiratoryRateSampleProvider(device, session);
+ }
+
@Override
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
@@ -267,6 +274,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return true;
}
+ @Override
+ public boolean supportsRespiratoryRate() {
+ return true;
+ }
+
@Override
public boolean supportsFindDevice() {
return true;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminRespiratoryRateSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminRespiratoryRateSampleProvider.java
new file mode 100644
index 000000000..eca393f0f
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminRespiratoryRateSampleProvider.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.GarminRespiratoryRateSample;
+import nodomain.freeyourgadget.gadgetbridge.entities.GarminRespiratoryRateSampleDao;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+
+public class GarminRespiratoryRateSampleProvider extends AbstractTimeSampleProvider {
+ public GarminRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
+ super(device, session);
+ }
+
+ @NonNull
+ @Override
+ public AbstractDao getSampleDao() {
+ return getSession().getGarminRespiratoryRateSampleDao();
+ }
+
+ @NonNull
+ @Override
+ protected Property getTimestampSampleProperty() {
+ return GarminRespiratoryRateSampleDao.Properties.Timestamp;
+ }
+
+ @NonNull
+ @Override
+ protected Property getDeviceIdentifierSampleProperty() {
+ return GarminRespiratoryRateSampleDao.Properties.DeviceId;
+ }
+
+ @Override
+ public GarminRespiratoryRateSample createSample() {
+ return new GarminRespiratoryRateSample();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java
index b32c4dde9..acb3e8069 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java
@@ -161,7 +161,7 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
}
@Override
- public HuamiSleepRespiratoryRateSampleProvider getSleepRespiratoryRateSampleProvider(GBDevice device, DaoSession session) {
+ public HuamiSleepRespiratoryRateSampleProvider getRespiratoryRateSampleProvider(GBDevice device, DaoSession session) {
return new HuamiSleepRespiratoryRateSampleProvider(device, session);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java
index e8106c691..94a6df1ff 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java
@@ -52,6 +52,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestBodyEnergyS
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestHrvSummarySampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestHrvValueSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestPaiSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestRespiratoryRateSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestSpo2SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestStressSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestSampleProvider;
@@ -70,7 +71,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
-import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
@@ -175,9 +176,8 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator {
}
@Override
- public TimeSampleProvider extends SleepRespiratoryRateSample> getSleepRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
- // TODO getHeartRateManualSampleProvider
- return super.getSleepRespiratoryRateSampleProvider(device, session);
+ public TimeSampleProvider extends RespiratoryRateSample> getRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
+ return supportsRespiratoryRate() ? new TestRespiratoryRateSampleProvider() : super.getRespiratoryRateSampleProvider(device, session);
}
@Nullable
@@ -373,6 +373,11 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator {
return supports(getTestDevice(), TestFeature.PAI_TIME);
}
+ @Override
+ public boolean supportsRespiratoryRate() {
+ return supports(getTestDevice(), TestFeature.RESPIRATORY_RATE);
+ }
+
@Override
public boolean supportsSleepRespiratoryRate() {
return supports(getTestDevice(), TestFeature.SLEEP_RESPIRATORY_RATE);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java
index ed5a485f4..5ee6d41b1 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java
@@ -56,6 +56,7 @@ public enum TestFeature {
RGB_LED_COLOR,
SCREENSHOTS,
SLEEP_MEASUREMENT,
+ RESPIRATORY_RATE,
SLEEP_RESPIRATORY_RATE,
SMART_WAKEUP,
SMART_WAKEUP_INTERVAL,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestRespiratoryRateSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestRespiratoryRateSampleProvider.java
new file mode 100644
index 000000000..bf880777b
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestRespiratoryRateSampleProvider.java
@@ -0,0 +1,87 @@
+/* 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.test.samples;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceRand;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
+
+public class TestRespiratoryRateSampleProvider implements TimeSampleProvider {
+ @NonNull
+ @Override
+ public List getAllSamples(final long timestampFrom, final long timestampTo) {
+ final List samples = new ArrayList<>();
+
+ for (long ts = timestampFrom; ts < timestampTo; ts += 15 * 60 * 1000L) {
+ samples.add(new TestRespiratoryRateSample(ts));
+ }
+
+ return samples;
+ }
+
+ @Override
+ public void addSample(final RespiratoryRateSample timeSample) {
+ throw new UnsupportedOperationException("read-only sample provider");
+ }
+
+ @Override
+ public void addSamples(final List timeSamples) {
+ throw new UnsupportedOperationException("read-only sample provider");
+ }
+
+ @Override
+ public RespiratoryRateSample createSample() {
+ throw new UnsupportedOperationException("read-only sample provider");
+ }
+
+ @Nullable
+ @Override
+ public RespiratoryRateSample getLatestSample() {
+ final long ts = System.currentTimeMillis();
+ return new TestRespiratoryRateSample(ts - TestDeviceRand.randLong(ts, 10 * 1000L, 2 * 60 * 60 * 1000L));
+ }
+
+ @Nullable
+ @Override
+ public RespiratoryRateSample getFirstSample() {
+ return new TestRespiratoryRateSample(TestDeviceRand.BASE_TIMESTAMP);
+ }
+
+ protected static class TestRespiratoryRateSample implements RespiratoryRateSample {
+ private final long timestamp;
+
+ public TestRespiratoryRateSample(final long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ @Override
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ @Override
+ public float getRespiratoryRate() {
+ return TestDeviceRand.randFloat(timestamp, 10, 15);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java
index d39aa98fd..bcfb1be55 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java
@@ -58,7 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
-import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
@@ -170,9 +170,9 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator {
}
@Override
- public TimeSampleProvider extends SleepRespiratoryRateSample> getSleepRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
+ public TimeSampleProvider extends RespiratoryRateSample> getRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
// TODO XiaomiSleepRespiratoryRateSampleProvider
- return super.getSleepRespiratoryRateSampleProvider(device, session);
+ return super.getRespiratoryRateSampleProvider(device, session);
}
@Nullable
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractSleepRespiratoryRateSample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractRespiratoryRateSample.java
similarity index 83%
rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractSleepRespiratoryRateSample.java
rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractRespiratoryRateSample.java
index ca0e020f8..fb51f89b0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractSleepRespiratoryRateSample.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractRespiratoryRateSample.java
@@ -18,16 +18,16 @@ package nodomain.freeyourgadget.gadgetbridge.entities;
import androidx.annotation.NonNull;
-import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
+import nodomain.freeyourgadget.gadgetbridge.model.RespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
-public abstract class AbstractSleepRespiratoryRateSample extends AbstractTimeSample implements SleepRespiratoryRateSample {
+public abstract class AbstractRespiratoryRateSample extends AbstractTimeSample implements RespiratoryRateSample {
@NonNull
@Override
public String toString() {
return getClass().getSimpleName() + "{" +
"timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimestampMillis(getTimestamp())) +
- ", rate=" + getRate() +
+ ", respiratoryRate=" + getRespiratoryRate() +
", userId=" + getUserId() +
", deviceId=" + getDeviceId() +
"}";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SleepRespiratoryRateSample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/RespiratoryRateSample.java
similarity index 90%
rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SleepRespiratoryRateSample.java
rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/RespiratoryRateSample.java
index 80294486e..fa6a97505 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SleepRespiratoryRateSample.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/RespiratoryRateSample.java
@@ -16,9 +16,9 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.model;
-public interface SleepRespiratoryRateSample extends TimeSample {
+public interface RespiratoryRateSample extends TimeSample {
/**
* Returns the respiratory rate value, in breaths per minute.
*/
- int getRate();
+ float getRespiratoryRate();
}
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 c9d9148e4..734c6844b 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
@@ -9,7 +9,6 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -17,7 +16,6 @@ import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
-import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
@@ -26,12 +24,12 @@ import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminBodyEnergySampl
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminEventSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminHrvSummarySampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminHrvValueSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminRespiratoryRateSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminSleepStageSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminSpo2SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminStressSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminWorkoutParser;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
-import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySample;
@@ -39,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.GarminBodyEnergySample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminEventSample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvSummarySample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvValueSample;
+import nodomain.freeyourgadget.gadgetbridge.entities.GarminRespiratoryRateSample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSleepStageSample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2Sample;
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSample;
@@ -57,6 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitMonitoring;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitPhysiologicalMetrics;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRespirationRate;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSleepDataInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSleepDataRaw;
@@ -77,6 +77,7 @@ public class FitImporter {
private final List stressSamples = new ArrayList<>();
private final List bodyEnergySamples = new ArrayList<>();
private final List spo2samples = new ArrayList<>();
+ private final List respiratoryRateSamples = new ArrayList<>();
private final List events = new ArrayList<>();
private final List sleepStageSamples = new ArrayList<>();
private final List hrvSummarySamples = new ArrayList<>();
@@ -179,6 +180,16 @@ public class FitImporter {
sample.setTimestamp(ts * 1000L);
sample.setSpo2(spo2);
spo2samples.add(sample);
+ } else if (record instanceof FitRespirationRate) {
+ final Float respiratoryRate = ((FitRespirationRate) record).getRespirationRate();
+ if (respiratoryRate == null || respiratoryRate <= 0) {
+ continue;
+ }
+ LOG.trace("Respiratory rate at {}: {}", ts, respiratoryRate);
+ final GarminRespiratoryRateSample sample = new GarminRespiratoryRateSample();
+ sample.setTimestamp(ts * 1000L);
+ sample.setRespiratoryRate(respiratoryRate);
+ respiratoryRateSamples.add(sample);
} else if (record instanceof FitEvent) {
final FitEvent event = (FitEvent) record;
if (event.getEvent() == null) {
@@ -276,6 +287,7 @@ public class FitImporter {
case MONITOR:
persistActivitySamples();
persistSpo2Samples();
+ persistRespiratoryRateSamples();
persistStressSamples();
persistBodyEnergySamples();
break;
@@ -338,6 +350,7 @@ public class FitImporter {
stressSamples.clear();
bodyEnergySamples.clear();
spo2samples.clear();
+ respiratoryRateSamples.clear();
events.clear();
sleepStageSamples.clear();
hrvSummarySamples.clear();
@@ -645,7 +658,7 @@ public class FitImporter {
return;
}
- LOG.debug("Will persist {} spo2 samples", stressSamples.size());
+ LOG.debug("Will persist {} spo2 samples", spo2samples.size());
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
@@ -666,6 +679,32 @@ public class FitImporter {
}
}
+ private void persistRespiratoryRateSamples() {
+ if (respiratoryRateSamples.isEmpty()) {
+ return;
+ }
+
+ LOG.debug("Will persist {} respiratory rate samples", stressSamples.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 GarminRespiratoryRateSampleProvider sampleProvider = new GarminRespiratoryRateSampleProvider(gbDevice, session);
+
+ for (final GarminRespiratoryRateSample sample : respiratoryRateSamples) {
+ sample.setDevice(device);
+ sample.setUser(user);
+ }
+
+ sampleProvider.addSamples(respiratoryRateSamples);
+ } catch (final Exception e) {
+ GB.toast(context, "Error saving respiratory rate samples", Toast.LENGTH_LONG, GB.ERROR, e);
+ }
+ }
+
private void persistStressSamples() {
if (stressSamples.isEmpty()) {
return;
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 432a5a834..93dd50f97 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
@@ -323,7 +323,7 @@ public class GlobalFITMessage {
));
public static GlobalFITMessage RESPIRATION_RATE = new GlobalFITMessage(297, "RESPIRATION_RATE", Arrays.asList(
- new FieldDefinitionPrimitive(0, BaseType.SINT16, "respiration_rate"), // breaths / min, scaled by 100
+ new FieldDefinitionPrimitive(0, BaseType.SINT16, "respiration_rate", 100, 0), // breaths / min
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
));
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRespirationRate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRespirationRate.java
index a7cf47d20..6d7fe0eea 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRespirationRate.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRespirationRate.java
@@ -21,8 +21,8 @@ public class FitRespirationRate extends RecordData {
}
@Nullable
- public Integer getRespirationRate() {
- return (Integer) getFieldByNumber(0);
+ public Float getRespirationRate() {
+ return (Float) getFieldByNumber(0);
}
@Nullable
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSleepRespiratoryRateOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSleepRespiratoryRateOperation.java
index 3019b4e93..6c0ff201a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSleepRespiratoryRateOperation.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSleepRespiratoryRateOperation.java
@@ -94,7 +94,7 @@ public class FetchSleepRespiratoryRateOperation extends AbstractRepeatingFetchOp
final User user = DBHelper.getUser(session);
final HuamiCoordinator coordinator = (HuamiCoordinator) getDevice().getDeviceCoordinator();
- final HuamiSleepRespiratoryRateSampleProvider sampleProvider = coordinator.getSleepRespiratoryRateSampleProvider(getDevice(), session);
+ final HuamiSleepRespiratoryRateSampleProvider sampleProvider = coordinator.getRespiratoryRateSampleProvider(getDevice(), session);
for (final HuamiSleepRespiratoryRateSample sample : samples) {
sample.setDevice(device);