1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-23 18:36:50 +01:00

Garmin: Persist respiratory rate

This commit is contained in:
José Rebelo 2024-10-11 22:29:55 +01:00
parent 27a830fd13
commit 8c949ff6ab
16 changed files with 252 additions and 29 deletions

View File

@ -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(

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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 <https://www.gnu.org/licenses/>. */
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<GarminRespiratoryRateSample> {
public GarminRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
super(device, session);
}
@NonNull
@Override
public AbstractDao<GarminRespiratoryRateSample, ?> 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();
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -56,6 +56,7 @@ public enum TestFeature {
RGB_LED_COLOR,
SCREENSHOTS,
SLEEP_MEASUREMENT,
RESPIRATORY_RATE,
SLEEP_RESPIRATORY_RATE,
SMART_WAKEUP,
SMART_WAKEUP_INTERVAL,

View File

@ -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 <https://www.gnu.org/licenses/>. */
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<RespiratoryRateSample> {
@NonNull
@Override
public List<RespiratoryRateSample> getAllSamples(final long timestampFrom, final long timestampTo) {
final List<RespiratoryRateSample> 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<RespiratoryRateSample> 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);
}
}
}

View File

@ -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

View File

@ -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() +
"}";

View File

@ -16,9 +16,9 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
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();
}

View File

@ -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<GarminStressSample> stressSamples = new ArrayList<>();
private final List<GarminBodyEnergySample> bodyEnergySamples = new ArrayList<>();
private final List<GarminSpo2Sample> spo2samples = new ArrayList<>();
private final List<GarminRespiratoryRateSample> respiratoryRateSamples = new ArrayList<>();
private final List<GarminEventSample> events = new ArrayList<>();
private final List<GarminSleepStageSample> sleepStageSamples = new ArrayList<>();
private final List<GarminHrvSummarySample> 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;

View File

@ -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)
));

View File

@ -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

View File

@ -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);