mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-09 03:37:03 +01:00
Garmin: Parse and persist HRV_STATUS
This commit is contained in:
parent
532c545093
commit
b33ab0b1c8
@ -45,7 +45,7 @@ public class GBDaoGenerator {
|
|||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
final Schema schema = new Schema(74, MAIN_PACKAGE + ".entities");
|
final Schema schema = new Schema(75, MAIN_PACKAGE + ".entities");
|
||||||
|
|
||||||
Entity userAttributes = addUserAttributes(schema);
|
Entity userAttributes = addUserAttributes(schema);
|
||||||
Entity user = addUserInfo(schema, userAttributes);
|
Entity user = addUserInfo(schema, userAttributes);
|
||||||
@ -113,6 +113,8 @@ public class GBDaoGenerator {
|
|||||||
addGarminSpo2Sample(schema, user, device);
|
addGarminSpo2Sample(schema, user, device);
|
||||||
addGarminSleepStageSample(schema, user, device);
|
addGarminSleepStageSample(schema, user, device);
|
||||||
addGarminEventSample(schema, user, device);
|
addGarminEventSample(schema, user, device);
|
||||||
|
addGarminHrvSummarySample(schema, user, device);
|
||||||
|
addGarminHrvValueSample(schema, user, device);
|
||||||
addWena3EnergySample(schema, user, device);
|
addWena3EnergySample(schema, user, device);
|
||||||
addWena3BehaviorSample(schema, user, device);
|
addWena3BehaviorSample(schema, user, device);
|
||||||
addWena3CaloriesSample(schema, user, device);
|
addWena3CaloriesSample(schema, user, device);
|
||||||
@ -725,6 +727,26 @@ public class GBDaoGenerator {
|
|||||||
return sleepStageSample;
|
return sleepStageSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Entity addGarminHrvSummarySample(Schema schema, Entity user, Entity device) {
|
||||||
|
Entity hrvSummarySample = addEntity(schema, "GarminHrvSummarySample");
|
||||||
|
addCommonTimeSampleProperties("AbstractHrvSummarySample", hrvSummarySample, user, device);
|
||||||
|
hrvSummarySample.addIntProperty("weeklyAverage").codeBeforeGetter(OVERRIDE);
|
||||||
|
hrvSummarySample.addIntProperty("lastNightAverage").codeBeforeGetter(OVERRIDE);
|
||||||
|
hrvSummarySample.addIntProperty("lastNight5MinHigh").codeBeforeGetter(OVERRIDE);
|
||||||
|
hrvSummarySample.addIntProperty("baselineLowUpper").codeBeforeGetter(OVERRIDE);
|
||||||
|
hrvSummarySample.addIntProperty("baselineBalancedLower").codeBeforeGetter(OVERRIDE);
|
||||||
|
hrvSummarySample.addIntProperty("baselineBalancedUpper").codeBeforeGetter(OVERRIDE);
|
||||||
|
hrvSummarySample.addIntProperty("statusNum").codeBeforeGetter(OVERRIDE);
|
||||||
|
return hrvSummarySample;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Entity addGarminHrvValueSample(Schema schema, Entity user, Entity device) {
|
||||||
|
Entity hrvValueSample = addEntity(schema, "GarminHrvValueSample");
|
||||||
|
addCommonTimeSampleProperties("AbstractHrvValueSample", hrvValueSample, user, device);
|
||||||
|
hrvValueSample.addIntProperty("value").notNull().codeBeforeGetter(OVERRIDE);
|
||||||
|
return hrvValueSample;
|
||||||
|
}
|
||||||
|
|
||||||
private static Entity addWatchXPlusHealthActivitySample(Schema schema, Entity user, Entity device) {
|
private static Entity addWatchXPlusHealthActivitySample(Schema schema, Entity user, Entity device) {
|
||||||
Entity activitySample = addEntity(schema, "WatchXPlusActivitySample");
|
Entity activitySample = addEntity(schema, "WatchXPlusActivitySample");
|
||||||
activitySample.implementsSerializable();
|
activitySample.implementsSerializable();
|
||||||
|
@ -73,6 +73,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
|
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.PaiSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
|
import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
||||||
@ -208,6 +210,16 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeSampleProvider<? extends HrvSummarySample> getHrvSummarySampleProvider(GBDevice device, DaoSession session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeSampleProvider<? extends HrvValueSample> getHrvValueSampleProvider(GBDevice device, DaoSession session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getStressRanges() {
|
public int[] getStressRanges() {
|
||||||
// 0-39 = relaxed
|
// 0-39 = relaxed
|
||||||
@ -442,6 +454,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsHrvMeasurement() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsActivityTabs() {
|
public boolean supportsActivityTabs() {
|
||||||
return supportsActivityTracking();
|
return supportsActivityTracking();
|
||||||
|
@ -52,6 +52,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
|
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.PaiSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
|
import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
||||||
@ -214,6 +216,8 @@ public interface DeviceCoordinator {
|
|||||||
*/
|
*/
|
||||||
boolean supportsStressMeasurement();
|
boolean supportsStressMeasurement();
|
||||||
|
|
||||||
|
boolean supportsHrvMeasurement();
|
||||||
|
|
||||||
boolean supportsSleepMeasurement();
|
boolean supportsSleepMeasurement();
|
||||||
boolean supportsStepCounter();
|
boolean supportsStepCounter();
|
||||||
boolean supportsSpeedzones();
|
boolean supportsSpeedzones();
|
||||||
@ -283,6 +287,16 @@ public interface DeviceCoordinator {
|
|||||||
*/
|
*/
|
||||||
TimeSampleProvider<? extends StressSample> getStressSampleProvider(GBDevice device, DaoSession session);
|
TimeSampleProvider<? extends StressSample> getStressSampleProvider(GBDevice device, DaoSession session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sample provider for HRV summary, for the device being supported.
|
||||||
|
*/
|
||||||
|
TimeSampleProvider<? extends HrvSummarySample> getHrvSummarySampleProvider(GBDevice device, DaoSession session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sample provider for HRV values, for the device being supported.
|
||||||
|
*/
|
||||||
|
TimeSampleProvider<? extends HrvValueSample> getHrvValueSampleProvider(GBDevice device, DaoSession session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the stress ranges (relaxed, mild, moderate, high), so that stress can be categorized.
|
* Returns the stress ranges (relaxed, mild, moderate, high), so that stress can be categorized.
|
||||||
*/
|
*/
|
||||||
|
@ -24,6 +24,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2SampleDao;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSampleDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSampleDao;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
|
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||||
@ -96,6 +98,16 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
return new GarminStressSampleProvider(device, session);
|
return new GarminStressSampleProvider(device, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeSampleProvider<? extends HrvSummarySample> getHrvSummarySampleProvider(final GBDevice device, final DaoSession session) {
|
||||||
|
return new GarminHrvSummarySampleProvider(device, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeSampleProvider<? extends HrvValueSample> getHrvValueSampleProvider(final GBDevice device, final DaoSession session) {
|
||||||
|
return new GarminHrvValueSampleProvider(device, session);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TimeSampleProvider<? extends Spo2Sample> getSpo2SampleProvider(final GBDevice device, final DaoSession session) {
|
public TimeSampleProvider<? extends Spo2Sample> getSpo2SampleProvider(final GBDevice device, final DaoSession session) {
|
||||||
return new GarminSpo2SampleProvider(device, session);
|
return new GarminSpo2SampleProvider(device, session);
|
||||||
@ -162,6 +174,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsHrvMeasurement() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getStressRanges() {
|
public int[] getStressRanges() {
|
||||||
// 1-25 = relaxed
|
// 1-25 = relaxed
|
||||||
|
@ -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.GarminHrvSummarySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvSummarySampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
public class GarminHrvSummarySampleProvider extends AbstractTimeSampleProvider<GarminHrvSummarySample> {
|
||||||
|
public GarminHrvSummarySampleProvider(final GBDevice device, final DaoSession session) {
|
||||||
|
super(device, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AbstractDao<GarminHrvSummarySample, ?> getSampleDao() {
|
||||||
|
return getSession().getGarminHrvSummarySampleDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getTimestampSampleProperty() {
|
||||||
|
return GarminHrvSummarySampleDao.Properties.Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getDeviceIdentifierSampleProperty() {
|
||||||
|
return GarminHrvSummarySampleDao.Properties.DeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GarminHrvSummarySample createSample() {
|
||||||
|
return new GarminHrvSummarySample();
|
||||||
|
}
|
||||||
|
}
|
@ -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.GarminHrvValueSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvValueSampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
public class GarminHrvValueSampleProvider extends AbstractTimeSampleProvider<GarminHrvValueSample> {
|
||||||
|
public GarminHrvValueSampleProvider(final GBDevice device, final DaoSession session) {
|
||||||
|
super(device, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AbstractDao<GarminHrvValueSample, ?> getSampleDao() {
|
||||||
|
return getSession().getGarminHrvValueSampleDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getTimestampSampleProperty() {
|
||||||
|
return GarminHrvValueSampleDao.Properties.Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getDeviceIdentifierSampleProperty() {
|
||||||
|
return GarminHrvValueSampleDao.Properties.DeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GarminHrvValueSample createSample() {
|
||||||
|
return new GarminHrvValueSample();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/* 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.entities;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.HrvSummarySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||||
|
|
||||||
|
public abstract class AbstractHrvSummarySample extends AbstractTimeSample implements HrvSummarySample {
|
||||||
|
public Integer getStatusNum() {
|
||||||
|
return Status.NONE.getNum();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status getStatus() {
|
||||||
|
return Status.fromNum(getStatusNum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "{" +
|
||||||
|
"timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimestampMillis(getTimestamp())) +
|
||||||
|
", weeklyAverage=" + getWeeklyAverage() +
|
||||||
|
", lastNightAverage=" + getLastNightAverage() +
|
||||||
|
", lastNight5MinHigh=" + getLastNight5MinHigh() +
|
||||||
|
", baselineLowUpper=" + getBaselineLowUpper() +
|
||||||
|
", baselineBalancedLower=" + getBaselineBalancedLower() +
|
||||||
|
", baselineBalancedUpper=" + getBaselineBalancedUpper() +
|
||||||
|
", status=" + getStatus() +
|
||||||
|
", userId=" + getUserId() +
|
||||||
|
", deviceId=" + getDeviceId() +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/* 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.entities;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||||
|
|
||||||
|
public abstract class AbstractHrvValueSample extends AbstractTimeSample implements HrvValueSample {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "{" +
|
||||||
|
"timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimestampMillis(getTimestamp())) +
|
||||||
|
", value=" + getValue() +
|
||||||
|
", userId=" + getUserId() +
|
||||||
|
", deviceId=" + getDeviceId() +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/* 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.model;
|
||||||
|
|
||||||
|
public interface HrvSummarySample extends TimeSample {
|
||||||
|
enum Status {
|
||||||
|
NONE(0),
|
||||||
|
POOR(1),
|
||||||
|
LOW(2),
|
||||||
|
UNBALANCED(3),
|
||||||
|
BALANCED(4),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final int num;
|
||||||
|
|
||||||
|
Status(final int num) {
|
||||||
|
this.num = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNum() {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Status fromNum(final int num) {
|
||||||
|
for (Status value : Status.values()) {
|
||||||
|
if (value.getNum() == num) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown hrv status num " + num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weekly average, in milliseconds.
|
||||||
|
*/
|
||||||
|
Integer getWeeklyAverage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last night average, in milliseconds.
|
||||||
|
*/
|
||||||
|
Integer getLastNightAverage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last night 5-min high, in milliseconds.
|
||||||
|
*/
|
||||||
|
Integer getLastNight5MinHigh();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline low upper, in milliseconds.
|
||||||
|
*/
|
||||||
|
Integer getBaselineLowUpper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline balanced lower, in milliseconds.
|
||||||
|
*/
|
||||||
|
Integer getBaselineBalancedLower();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline balanced upper, in milliseconds.
|
||||||
|
*/
|
||||||
|
Integer getBaselineBalancedUpper();
|
||||||
|
|
||||||
|
Status getStatus();
|
||||||
|
}
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||||
|
|
||||||
|
public interface HrvValueSample extends TimeSample {
|
||||||
|
/**
|
||||||
|
* HRV value, in milliseconds.
|
||||||
|
*/
|
||||||
|
int getValue();
|
||||||
|
}
|
@ -6,6 +6,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionFileType;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionFileType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrvStatus;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
||||||
@ -30,6 +31,8 @@ public class FieldDefinitionFactory {
|
|||||||
return new FieldDefinitionGoalSource(localNumber, size, baseType, name);
|
return new FieldDefinitionGoalSource(localNumber, size, baseType, name);
|
||||||
case GOAL_TYPE:
|
case GOAL_TYPE:
|
||||||
return new FieldDefinitionGoalType(localNumber, size, baseType, name);
|
return new FieldDefinitionGoalType(localNumber, size, baseType, name);
|
||||||
|
case HRV_STATUS:
|
||||||
|
return new FieldDefinitionHrvStatus(localNumber, size, baseType, name);
|
||||||
case MEASUREMENT_SYSTEM:
|
case MEASUREMENT_SYSTEM:
|
||||||
return new FieldDefinitionMeasurementSystem(localNumber, size, baseType, name);
|
return new FieldDefinitionMeasurementSystem(localNumber, size, baseType, name);
|
||||||
case TEMPERATURE:
|
case TEMPERATURE:
|
||||||
@ -55,6 +58,7 @@ public class FieldDefinitionFactory {
|
|||||||
FILE_TYPE,
|
FILE_TYPE,
|
||||||
GOAL_SOURCE,
|
GOAL_SOURCE,
|
||||||
GOAL_TYPE,
|
GOAL_TYPE,
|
||||||
|
HRV_STATUS,
|
||||||
MEASUREMENT_SYSTEM,
|
MEASUREMENT_SYSTEM,
|
||||||
TEMPERATURE,
|
TEMPERATURE,
|
||||||
TIMESTAMP,
|
TIMESTAMP,
|
||||||
|
@ -32,6 +32,8 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminActivitySampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminActivitySampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminEventSampleProvider;
|
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.GarminSleepStageSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminSleepStageSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminSpo2SampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminSpo2SampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminStressSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminStressSampleProvider;
|
||||||
@ -40,6 +42,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminEventSample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminEventSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvSummarySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminHrvValueSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSleepStageSample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSleepStageSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2Sample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2Sample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSample;
|
||||||
@ -51,9 +55,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminTimeUtils;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminTimeUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.enums.GarminSport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.enums.GarminSport;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrvStatus;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitEvent;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitEvent;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitFileId;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitFileId;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitHrvSummary;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitHrvValue;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitMonitoring;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitMonitoring;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
|
||||||
@ -75,6 +82,8 @@ public class FitImporter {
|
|||||||
private final List<GarminSpo2Sample> spo2samples = new ArrayList<>();
|
private final List<GarminSpo2Sample> spo2samples = new ArrayList<>();
|
||||||
private final List<GarminEventSample> events = new ArrayList<>();
|
private final List<GarminEventSample> events = new ArrayList<>();
|
||||||
private final List<GarminSleepStageSample> sleepStageSamples = new ArrayList<>();
|
private final List<GarminSleepStageSample> sleepStageSamples = new ArrayList<>();
|
||||||
|
private final List<GarminHrvSummarySample> hrvSummarySamples = new ArrayList<>();
|
||||||
|
private final List<GarminHrvValueSample> hrvValueSamples = new ArrayList<>();
|
||||||
private final List<FitTimeInZone> timesInZone = new ArrayList<>();
|
private final List<FitTimeInZone> timesInZone = new ArrayList<>();
|
||||||
private final List<ActivityPoint> activityPoints = new ArrayList<>();
|
private final List<ActivityPoint> activityPoints = new ArrayList<>();
|
||||||
private final Map<Integer, Integer> unknownRecords = new HashMap<>();
|
private final Map<Integer, Integer> unknownRecords = new HashMap<>();
|
||||||
@ -179,6 +188,34 @@ public class FitImporter {
|
|||||||
} else if (record instanceof FitTimeInZone) {
|
} else if (record instanceof FitTimeInZone) {
|
||||||
LOG.trace("Time in zone: {}", record);
|
LOG.trace("Time in zone: {}", record);
|
||||||
timesInZone.add((FitTimeInZone) record);
|
timesInZone.add((FitTimeInZone) record);
|
||||||
|
} else if (record instanceof FitHrvSummary) {
|
||||||
|
final FitHrvSummary hrvSummary = (FitHrvSummary) record;
|
||||||
|
final FieldDefinitionHrvStatus.HrvStatus status = hrvSummary.getStatus();
|
||||||
|
if (status == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LOG.trace("HRV summary at {}: {}", ts, record);
|
||||||
|
final GarminHrvSummarySample sample = new GarminHrvSummarySample( );
|
||||||
|
sample.setTimestamp(ts * 1000L);
|
||||||
|
sample.setWeeklyAverage(hrvSummary.getWeeklyAverage());
|
||||||
|
sample.setLastNightAverage(hrvSummary.getLastNightAverage());
|
||||||
|
sample.setLastNight5MinHigh(hrvSummary.getLastNight5MinHigh());
|
||||||
|
sample.setBaselineLowUpper(hrvSummary.getBaselineLowUpper());
|
||||||
|
sample.setBaselineBalancedLower(hrvSummary.getBaselineBalancedLower());
|
||||||
|
sample.setBaselineBalancedUpper(hrvSummary.getBaselineBalancedUpper());
|
||||||
|
sample.setStatusNum(status.getId());
|
||||||
|
hrvSummarySamples.add(sample);
|
||||||
|
} else if (record instanceof FitHrvValue) {
|
||||||
|
final FitHrvValue hrvValue = (FitHrvValue) record;
|
||||||
|
if (hrvValue.getValue() == null) {
|
||||||
|
LOG.warn("HRV value at {} is null", ts);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LOG.trace("HRV value at {}: {}", ts, hrvValue.getValue());
|
||||||
|
final GarminHrvValueSample sample = new GarminHrvValueSample();
|
||||||
|
sample.setTimestamp(ts * 1000L);
|
||||||
|
sample.setValue(hrvValue.getValue());
|
||||||
|
hrvValueSamples.add(sample);
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Unknown record: {}", record);
|
LOG.trace("Unknown record: {}", record);
|
||||||
|
|
||||||
@ -202,18 +239,22 @@ public class FitImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (fileId.getType()) {
|
switch (fileId.getType()) {
|
||||||
case activity:
|
case ACTIVITY:
|
||||||
persistWorkout(file);
|
persistWorkout(file);
|
||||||
break;
|
break;
|
||||||
case monitor:
|
case MONITOR:
|
||||||
persistActivitySamples();
|
persistActivitySamples();
|
||||||
persistSpo2Samples();
|
persistSpo2Samples();
|
||||||
persistStressSamples();
|
persistStressSamples();
|
||||||
break;
|
break;
|
||||||
case sleep:
|
case SLEEP:
|
||||||
persistEvents();
|
persistEvents();
|
||||||
persistSleepStageSamples();
|
persistSleepStageSamples();
|
||||||
break;
|
break;
|
||||||
|
case HRV_STATUS:
|
||||||
|
persistHrvSummarySamples();
|
||||||
|
persistHrvValueSamples();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG.warn("Unable to handle fit file of type {}", fileId.getType());
|
LOG.warn("Unable to handle fit file of type {}", fileId.getType());
|
||||||
}
|
}
|
||||||
@ -512,6 +553,58 @@ public class FitImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void persistHrvSummarySamples() {
|
||||||
|
if (hrvSummarySamples.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("Will persist {} HRV summary samples", hrvSummarySamples.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 GarminHrvSummarySampleProvider sampleProvider = new GarminHrvSummarySampleProvider(gbDevice, session);
|
||||||
|
|
||||||
|
for (final GarminHrvSummarySample sample : hrvSummarySamples) {
|
||||||
|
sample.setDevice(device);
|
||||||
|
sample.setUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleProvider.addSamples(hrvSummarySamples);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
GB.toast(context, "Error saving HRV summary samples", Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistHrvValueSamples() {
|
||||||
|
if (hrvValueSamples.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("Will persist {} HRV value samples", hrvValueSamples.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 GarminHrvValueSampleProvider sampleProvider = new GarminHrvValueSampleProvider(gbDevice, session);
|
||||||
|
|
||||||
|
for (final GarminHrvValueSample sample : hrvValueSamples) {
|
||||||
|
sample.setDevice(device);
|
||||||
|
sample.setUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleProvider.addSamples(hrvValueSamples);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
GB.toast(context, "Error saving HRV value samples", Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void persistSpo2Samples() {
|
private void persistSpo2Samples() {
|
||||||
if (spo2samples.isEmpty()) {
|
if (spo2samples.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -253,6 +253,22 @@ public class GlobalFITMessage {
|
|||||||
public static GlobalFITMessage SLEEP_STATS = new GlobalFITMessage(346, "SLEEP_STATS", Arrays.asList(
|
public static GlobalFITMessage SLEEP_STATS = new GlobalFITMessage(346, "SLEEP_STATS", Arrays.asList(
|
||||||
));
|
));
|
||||||
|
|
||||||
|
public static GlobalFITMessage HRV_SUMMARY = new GlobalFITMessage(370, "HRV_SUMMARY", Arrays.asList(
|
||||||
|
new FieldDefinitionPrimitive(0, BaseType.UINT16, "weekly_average", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(1, BaseType.UINT16, "last_night_average", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(2, BaseType.UINT16, "last_night_5_min_high", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(3, BaseType.UINT16, "baseline_low_upper", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(4, BaseType.UINT16, "baseline_balanced_lower", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(5, BaseType.UINT16, "baseline_balanced_upper", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(6, BaseType.ENUM, "status", FieldDefinitionFactory.FIELD.HRV_STATUS),
|
||||||
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
|
));
|
||||||
|
|
||||||
|
public static GlobalFITMessage HRV_VALUE = new GlobalFITMessage(371, "HRV_VALUE", Arrays.asList(
|
||||||
|
new FieldDefinitionPrimitive(0, BaseType.UINT16, "value", 128, 0), // milliseconds, scaled by 128
|
||||||
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
|
));
|
||||||
|
|
||||||
public static Map<Integer, GlobalFITMessage> KNOWN_MESSAGES = new HashMap<Integer, GlobalFITMessage>() {{
|
public static Map<Integer, GlobalFITMessage> KNOWN_MESSAGES = new HashMap<Integer, GlobalFITMessage>() {{
|
||||||
put(0, FILE_ID);
|
put(0, FILE_ID);
|
||||||
put(2, DEVICE_SETTINGS);
|
put(2, DEVICE_SETTINGS);
|
||||||
@ -280,6 +296,8 @@ public class GlobalFITMessage {
|
|||||||
put(275, SLEEP_STAGE);
|
put(275, SLEEP_STAGE);
|
||||||
put(297, RESPIRATION_RATE);
|
put(297, RESPIRATION_RATE);
|
||||||
put(346, SLEEP_STATS);
|
put(346, SLEEP_STATS);
|
||||||
|
put(370, HRV_SUMMARY);
|
||||||
|
put(371, HRV_VALUE);
|
||||||
}};
|
}};
|
||||||
private final int number;
|
private final int number;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
@ -22,13 +22,14 @@ import java.util.Set;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.FileType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalFITMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalFITMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionFileType;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrvStatus;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
||||||
@ -230,11 +231,13 @@ public class FitCodeGen {
|
|||||||
case DAY_OF_WEEK:
|
case DAY_OF_WEEK:
|
||||||
return DayOfWeek.class;
|
return DayOfWeek.class;
|
||||||
case FILE_TYPE:
|
case FILE_TYPE:
|
||||||
return FieldDefinitionFileType.Type.class;
|
return FileType.FILETYPE.class;
|
||||||
case GOAL_SOURCE:
|
case GOAL_SOURCE:
|
||||||
return FieldDefinitionGoalSource.Source.class;
|
return FieldDefinitionGoalSource.Source.class;
|
||||||
case GOAL_TYPE:
|
case GOAL_TYPE:
|
||||||
return FieldDefinitionGoalType.Type.class;
|
return FieldDefinitionGoalType.Type.class;
|
||||||
|
case HRV_STATUS:
|
||||||
|
return FieldDefinitionHrvStatus.HrvStatus.class;
|
||||||
case MEASUREMENT_SYSTEM:
|
case MEASUREMENT_SYSTEM:
|
||||||
return FieldDefinitionMeasurementSystem.Type.class;
|
return FieldDefinitionMeasurementSystem.Type.class;
|
||||||
case TEMPERATURE:
|
case TEMPERATURE:
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.FileType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||||
|
|
||||||
@ -16,47 +15,16 @@ public class FieldDefinitionFileType extends FieldDefinition {
|
|||||||
@Override
|
@Override
|
||||||
public Object decode(ByteBuffer byteBuffer) {
|
public Object decode(ByteBuffer byteBuffer) {
|
||||||
int raw = (int) baseType.decode(byteBuffer, scale, offset);
|
int raw = (int) baseType.decode(byteBuffer, scale, offset);
|
||||||
return Type.fromId(raw) == null ? raw : Type.fromId(raw);
|
final FileType.FILETYPE fileType = FileType.FILETYPE.fromDataTypeSubType(128, raw);
|
||||||
|
return fileType == null ? raw : fileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuffer byteBuffer, Object o) {
|
public void encode(ByteBuffer byteBuffer, Object o) {
|
||||||
if (o instanceof Type) {
|
if (o instanceof FileType.FILETYPE) {
|
||||||
baseType.encode(byteBuffer, (((Type) o).getId()), scale, offset);
|
baseType.encode(byteBuffer, (((FileType.FILETYPE) o).getSubType()), scale, offset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
baseType.encode(byteBuffer, o, scale, offset);
|
baseType.encode(byteBuffer, o, scale, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
settings(2),
|
|
||||||
activity(4), //FIT_TYPE_4 stands for activity directory
|
|
||||||
goals(11),
|
|
||||||
monitor(32), //FIT_TYPE_32
|
|
||||||
changelog(41), // FIT_TYPE_41 stands for changelog directory
|
|
||||||
metrics(44), //FIT_TYPE_41
|
|
||||||
sleep(49), //FIT_TYPE_49
|
|
||||||
;
|
|
||||||
|
|
||||||
private final int id;
|
|
||||||
|
|
||||||
Type(int i) {
|
|
||||||
this.id = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static Type fromId(int id) {
|
|
||||||
for (Type type :
|
|
||||||
Type.values()) {
|
|
||||||
if (id == type.getId()) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||||
|
|
||||||
|
public class FieldDefinitionHrvStatus extends FieldDefinition {
|
||||||
|
public FieldDefinitionHrvStatus(final int localNumber, final int size, final BaseType baseType, final String name) {
|
||||||
|
super(localNumber, size, baseType, name, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object decode(final ByteBuffer byteBuffer) {
|
||||||
|
final int raw = (int) baseType.decode(byteBuffer, scale, offset);
|
||||||
|
return HrvStatus.fromId(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(final ByteBuffer byteBuffer, final Object o) {
|
||||||
|
if (o instanceof HrvStatus) {
|
||||||
|
baseType.encode(byteBuffer, (((HrvStatus) o).getId()), scale, offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
baseType.encode(byteBuffer, o, scale, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HrvStatus {
|
||||||
|
NONE(0),
|
||||||
|
POOR(1),
|
||||||
|
LOW(2),
|
||||||
|
UNBALANCED(3),
|
||||||
|
BALANCED(4),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
HrvStatus(final int i) {
|
||||||
|
id = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static HrvStatus fromId(final int id) {
|
||||||
|
for (HrvStatus stage : HrvStatus.values()) {
|
||||||
|
if (id == stage.getId()) {
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,10 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.FileType.FILETYPE;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionFileType.Type;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
||||||
@ -22,8 +22,8 @@ public class FitFileId extends RecordData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Type getType() {
|
public FILETYPE getType() {
|
||||||
return (Type) getFieldByNumber(0);
|
return (FILETYPE) getFieldByNumber(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrvStatus.HrvStatus;
|
||||||
|
|
||||||
|
//
|
||||||
|
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
||||||
|
// See nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen
|
||||||
|
//
|
||||||
|
public class FitHrvSummary extends RecordData {
|
||||||
|
public FitHrvSummary(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||||
|
super(recordDefinition, recordHeader);
|
||||||
|
|
||||||
|
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
|
||||||
|
if (globalNumber != 370) {
|
||||||
|
throw new IllegalArgumentException("FitHrvSummary expects global messages of " + 370 + ", got " + globalNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getWeeklyAverage() {
|
||||||
|
return (Integer) getFieldByNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getLastNightAverage() {
|
||||||
|
return (Integer) getFieldByNumber(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getLastNight5MinHigh() {
|
||||||
|
return (Integer) getFieldByNumber(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getBaselineLowUpper() {
|
||||||
|
return (Integer) getFieldByNumber(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getBaselineBalancedLower() {
|
||||||
|
return (Integer) getFieldByNumber(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getBaselineBalancedUpper() {
|
||||||
|
return (Integer) getFieldByNumber(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public HrvStatus getStatus() {
|
||||||
|
return (HrvStatus) getFieldByNumber(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Long getTimestamp() {
|
||||||
|
return (Long) getFieldByNumber(253);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
||||||
|
|
||||||
|
//
|
||||||
|
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
||||||
|
// See nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen
|
||||||
|
//
|
||||||
|
public class FitHrvValue extends RecordData {
|
||||||
|
public FitHrvValue(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||||
|
super(recordDefinition, recordHeader);
|
||||||
|
|
||||||
|
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
|
||||||
|
if (globalNumber != 371) {
|
||||||
|
throw new IllegalArgumentException("FitHrvValue expects global messages of " + 371 + ", got " + globalNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getValue() {
|
||||||
|
return (Integer) getFieldByNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Long getTimestamp() {
|
||||||
|
return (Long) getFieldByNumber(253);
|
||||||
|
}
|
||||||
|
}
|
@ -67,6 +67,10 @@ public class FitRecordDataFactory {
|
|||||||
return new FitRespirationRate(recordDefinition, recordHeader);
|
return new FitRespirationRate(recordDefinition, recordHeader);
|
||||||
case 346:
|
case 346:
|
||||||
return new FitSleepStats(recordDefinition, recordHeader);
|
return new FitSleepStats(recordDefinition, recordHeader);
|
||||||
|
case 370:
|
||||||
|
return new FitHrvSummary(recordDefinition, recordHeader);
|
||||||
|
case 371:
|
||||||
|
return new FitHrvValue(recordDefinition, recordHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RecordData(recordDefinition, recordHeader);
|
return new RecordData(recordDefinition, recordHeader);
|
||||||
|
Loading…
Reference in New Issue
Block a user