mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-26 02:25:50 +01:00
Garmin: Infer sleep time for devices that do not send sleep stages
See #4048 for more information
This commit is contained in:
parent
bd5b54c3b4
commit
e51b55a38a
@ -134,7 +134,12 @@ public class GarminActivitySampleProvider extends AbstractSampleProvider<GarminA
|
|||||||
case 0: // start
|
case 0: // start
|
||||||
// We only need the start event as an upper-bound timestamp (anything before it is unknown)
|
// We only need the start event as an upper-bound timestamp (anything before it is unknown)
|
||||||
stagesMap.put(event.getTimestamp(), ActivityKind.UNKNOWN);
|
stagesMap.put(event.getTimestamp(), ActivityKind.UNKNOWN);
|
||||||
|
break;
|
||||||
case 1: // stop
|
case 1: // stop
|
||||||
|
// See FitImporter#processRawSleepSamples / #4048
|
||||||
|
if (event.getData() != null && event.getData() == -1) {
|
||||||
|
stagesMap.put(event.getTimestamp(), ActivityKind.LIGHT_SLEEP);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,12 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import de.greenrobot.dao.AbstractDao;
|
||||||
|
import de.greenrobot.dao.Property;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
@ -24,6 +28,10 @@ import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySampleDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySampleDao;
|
||||||
|
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.GarminSleepStageSampleDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSleepStageSampleDao;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2SampleDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminSpo2SampleDao;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSampleDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminStressSampleDao;
|
||||||
@ -54,30 +62,25 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
public void deleteAllActivityData(@NonNull final Device device, @NonNull final DaoSession session) throws GBException {
|
public void deleteAllActivityData(@NonNull final Device device, @NonNull final DaoSession session) throws GBException {
|
||||||
final Long deviceId = device.getId();
|
final Long deviceId = device.getId();
|
||||||
|
|
||||||
session.getGarminActivitySampleDao().queryBuilder()
|
final Map<AbstractDao<?, ?>, Property> daoMap = new HashMap<AbstractDao<?, ?>, Property>() {{
|
||||||
.where(GarminActivitySampleDao.Properties.DeviceId.eq(deviceId))
|
put(session.getGarminActivitySampleDao(), GarminActivitySampleDao.Properties.DeviceId);
|
||||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
put(session.getGarminStressSampleDao(), GarminStressSampleDao.Properties.DeviceId);
|
||||||
|
put(session.getGarminBodyEnergySampleDao(), GarminBodyEnergySampleDao.Properties.DeviceId);
|
||||||
|
put(session.getGarminSpo2SampleDao(), GarminSpo2SampleDao.Properties.DeviceId);
|
||||||
|
put(session.getGarminSleepStageSampleDao(), GarminSleepStageSampleDao.Properties.DeviceId);
|
||||||
|
put(session.getGarminEventSampleDao(), GarminEventSampleDao.Properties.DeviceId);
|
||||||
|
put(session.getGarminHrvSummarySampleDao(), GarminHrvSummarySampleDao.Properties.DeviceId);
|
||||||
|
put(session.getGarminHrvValueSampleDao(), GarminHrvValueSampleDao.Properties.DeviceId);
|
||||||
|
put(session.getBaseActivitySummaryDao(), BaseActivitySummaryDao.Properties.DeviceId);
|
||||||
|
put(session.getPendingFileDao(), PendingFileDao.Properties.DeviceId);
|
||||||
|
}};
|
||||||
|
|
||||||
session.getGarminStressSampleDao().queryBuilder()
|
for (final Map.Entry<AbstractDao<?, ?>, Property> e : daoMap.entrySet()) {
|
||||||
.where(GarminStressSampleDao.Properties.DeviceId.eq(deviceId))
|
e.getKey().queryBuilder()
|
||||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
.where(e.getValue().eq(deviceId))
|
||||||
|
|
||||||
session.getGarminSleepStageSampleDao().queryBuilder()
|
|
||||||
.where(GarminSleepStageSampleDao.Properties.DeviceId.eq(deviceId))
|
|
||||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
|
||||||
|
|
||||||
session.getGarminSpo2SampleDao().queryBuilder()
|
|
||||||
.where(GarminSpo2SampleDao.Properties.DeviceId.eq(deviceId))
|
|
||||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
|
||||||
|
|
||||||
session.getBaseActivitySummaryDao().queryBuilder()
|
|
||||||
.where(BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId))
|
|
||||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
|
||||||
|
|
||||||
session.getPendingFileDao().queryBuilder()
|
|
||||||
.where(PendingFileDao.Properties.DeviceId.eq(deviceId))
|
|
||||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
.buildDelete().executeDeleteWithoutDetachingEntities();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getManufacturer() {
|
public String getManufacturer() {
|
||||||
|
@ -57,6 +57,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitPhysiologicalMetrics;
|
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.FitRecord;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
|
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;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSleepStage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSleepStage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSpo2;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSpo2;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSport;
|
||||||
@ -79,6 +81,8 @@ public class FitImporter {
|
|||||||
private final List<GarminHrvSummarySample> hrvSummarySamples = new ArrayList<>();
|
private final List<GarminHrvSummarySample> hrvSummarySamples = new ArrayList<>();
|
||||||
private final List<GarminHrvValueSample> hrvValueSamples = new ArrayList<>();
|
private final List<GarminHrvValueSample> hrvValueSamples = new ArrayList<>();
|
||||||
private final Map<Integer, Integer> unknownRecords = new HashMap<>();
|
private final Map<Integer, Integer> unknownRecords = new HashMap<>();
|
||||||
|
private FitSleepDataInfo fitSleepDataInfo = null;
|
||||||
|
private final List<FitSleepDataRaw> fitSleepDataRawSamples = new ArrayList<>();
|
||||||
private FitFileId fileId = null;
|
private FitFileId fileId = null;
|
||||||
|
|
||||||
private final GarminWorkoutParser workoutParser;
|
private final GarminWorkoutParser workoutParser;
|
||||||
@ -131,6 +135,18 @@ public class FitImporter {
|
|||||||
sample.setEnergy(energy);
|
sample.setEnergy(energy);
|
||||||
bodyEnergySamples.add(sample);
|
bodyEnergySamples.add(sample);
|
||||||
}
|
}
|
||||||
|
} else if (record instanceof FitSleepDataInfo) {
|
||||||
|
final FitSleepDataInfo newFitSleepDataInfo = (FitSleepDataInfo) record;
|
||||||
|
LOG.debug("Sleep Data Info: {}", newFitSleepDataInfo);
|
||||||
|
if (fitSleepDataInfo != null) {
|
||||||
|
// Should not happen
|
||||||
|
LOG.warn("Already had sleep data info: {}", fitSleepDataInfo);
|
||||||
|
}
|
||||||
|
fitSleepDataInfo = newFitSleepDataInfo;
|
||||||
|
} else if (record instanceof FitSleepDataRaw) {
|
||||||
|
final FitSleepDataRaw fitSleepDataRaw = (FitSleepDataRaw) record;
|
||||||
|
//LOG.debug("Sleep Data Raw: {}", fitSleepDataRaw);
|
||||||
|
fitSleepDataRawSamples.add(fitSleepDataRaw);
|
||||||
} else if (record instanceof FitSleepStage) {
|
} else if (record instanceof FitSleepStage) {
|
||||||
final FieldDefinitionSleepStage.SleepStage stage = ((FitSleepStage) record).getSleepStage();
|
final FieldDefinitionSleepStage.SleepStage stage = ((FitSleepStage) record).getSleepStage();
|
||||||
if (stage == null) {
|
if (stage == null) {
|
||||||
@ -260,6 +276,7 @@ public class FitImporter {
|
|||||||
case SLEEP:
|
case SLEEP:
|
||||||
persistEvents();
|
persistEvents();
|
||||||
persistSleepStageSamples();
|
persistSleepStageSamples();
|
||||||
|
processRawSleepSamples();
|
||||||
break;
|
break;
|
||||||
case HRV_STATUS:
|
case HRV_STATUS:
|
||||||
persistHrvSummarySamples();
|
persistHrvSummarySamples();
|
||||||
@ -346,6 +363,8 @@ public class FitImporter {
|
|||||||
hrvSummarySamples.clear();
|
hrvSummarySamples.clear();
|
||||||
hrvValueSamples.clear();
|
hrvValueSamples.clear();
|
||||||
unknownRecords.clear();
|
unknownRecords.clear();
|
||||||
|
fitSleepDataInfo = null;
|
||||||
|
fitSleepDataRawSamples.clear();
|
||||||
fileId = null;
|
fileId = null;
|
||||||
workoutParser.reset();
|
workoutParser.reset();
|
||||||
}
|
}
|
||||||
@ -469,7 +488,11 @@ public class FitImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void persistSleepStageSamples() {
|
private void persistSleepStageSamples() {
|
||||||
if (sleepStageSamples.isEmpty()) {
|
// We may have samples, but not sleep samples - #4048
|
||||||
|
// 0 unmeasurable, 1 awake
|
||||||
|
final boolean anySleepSample = sleepStageSamples.stream()
|
||||||
|
.anyMatch(s -> s.getStage() != 0 && s.getStage() != 1);
|
||||||
|
if (!anySleepSample) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,6 +517,61 @@ public class FitImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As per #4048, devices that do not have a sleep widget send raw sleep samples, which we do not
|
||||||
|
* know how to parse. Therefore, we don't persist the sleep stages they report (they're all awake),
|
||||||
|
* but we fake light sleep for the duration of the raw sleep samples, in order to have some data
|
||||||
|
* at all.
|
||||||
|
*/
|
||||||
|
private void processRawSleepSamples() {
|
||||||
|
if (fitSleepDataRawSamples.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean anySleepSample = sleepStageSamples.stream()
|
||||||
|
.anyMatch(s -> s.getStage() != 0 && s.getStage() != 1);
|
||||||
|
if (anySleepSample) {
|
||||||
|
// We have at least one real sleep sample - do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long asleepTimeMillis = Objects.requireNonNull(fileId.getTimeCreated()).intValue() * 1000L;
|
||||||
|
final long wakeTimeMillis = asleepTimeMillis + fitSleepDataRawSamples.size() * 60 * 1000L;
|
||||||
|
|
||||||
|
LOG.debug("Got {} raw sleep samples - faking sleep events from {} to {}", fitSleepDataRawSamples.size(), asleepTimeMillis, wakeTimeMillis);
|
||||||
|
|
||||||
|
// We only need to fake sleep start and end times, the sample provider will take care of the rest
|
||||||
|
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||||
|
final DaoSession session = handler.getDaoSession();
|
||||||
|
|
||||||
|
final Device device = DBHelper.getDevice(gbDevice, session);
|
||||||
|
final User user = DBHelper.getUser(session);
|
||||||
|
|
||||||
|
final GarminEventSampleProvider sampleProvider = new GarminEventSampleProvider(gbDevice, session);
|
||||||
|
|
||||||
|
final GarminEventSample sampleFallAsleep = new GarminEventSample();
|
||||||
|
sampleFallAsleep.setTimestamp(asleepTimeMillis);
|
||||||
|
sampleFallAsleep.setEvent(74); // sleep
|
||||||
|
sampleFallAsleep.setEventType(0); // sleep start
|
||||||
|
sampleFallAsleep.setData(-1L); // in actual samples they're a garmin epoch, this way we can identify them
|
||||||
|
sampleFallAsleep.setDevice(device);
|
||||||
|
sampleFallAsleep.setUser(user);
|
||||||
|
|
||||||
|
final GarminEventSample sampleWakeUp = new GarminEventSample();
|
||||||
|
sampleWakeUp.setTimestamp(wakeTimeMillis);
|
||||||
|
sampleWakeUp.setEvent(74); // sleep
|
||||||
|
sampleWakeUp.setEventType(1); // sleep end
|
||||||
|
sampleWakeUp.setData(-1L); // in actual samples they're a garmin epoch, this way we can identify them
|
||||||
|
sampleWakeUp.setDevice(device);
|
||||||
|
sampleWakeUp.setUser(user);
|
||||||
|
|
||||||
|
sampleProvider.addSample(sampleFallAsleep);
|
||||||
|
sampleProvider.addSample(sampleWakeUp);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
GB.toast(context, "Error faking event samples", Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void persistHrvSummarySamples() {
|
private void persistHrvSummarySamples() {
|
||||||
if (hrvSummarySamples.isEmpty()) {
|
if (hrvSummarySamples.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -282,6 +282,19 @@ public class GlobalFITMessage {
|
|||||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
public static GlobalFITMessage SLEEP_DATA_INFO = new GlobalFITMessage(273, "SLEEP_DATA_INFO", Arrays.asList(
|
||||||
|
new FieldDefinitionPrimitive(0, BaseType.UINT8, "unk0"), // 2
|
||||||
|
new FieldDefinitionPrimitive(1, BaseType.UINT16, "sample_length"), // 60, sample time?
|
||||||
|
new FieldDefinitionPrimitive(2, BaseType.UINT32, "timestamp_in_tz"), // garmin timestamp, but in user timezone
|
||||||
|
new FieldDefinitionPrimitive(3, BaseType.ENUM, "unk3"), // 1
|
||||||
|
new FieldDefinitionPrimitive(4, BaseType.STRING, "version"), // matches ETE in settings
|
||||||
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
|
));
|
||||||
|
|
||||||
|
public static GlobalFITMessage SLEEP_DATA_RAW = new GlobalFITMessage(274, "SLEEP_DATA_RAW", Arrays.asList(
|
||||||
|
new FieldDefinitionPrimitive(0, BaseType.BASE_TYPE_BYTE, "bytes") // arr of 20 bytes per sample
|
||||||
|
));
|
||||||
|
|
||||||
public static GlobalFITMessage SLEEP_STAGE = new GlobalFITMessage(275, "SLEEP_STAGE", Arrays.asList(
|
public static GlobalFITMessage SLEEP_STAGE = new GlobalFITMessage(275, "SLEEP_STAGE", Arrays.asList(
|
||||||
new FieldDefinitionPrimitive(0, BaseType.ENUM, "sleep_stage", FieldDefinitionFactory.FIELD.SLEEP_STAGE),
|
new FieldDefinitionPrimitive(0, BaseType.ENUM, "sleep_stage", FieldDefinitionFactory.FIELD.SLEEP_STAGE),
|
||||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
@ -338,6 +351,8 @@ public class GlobalFITMessage {
|
|||||||
put(225, SET);
|
put(225, SET);
|
||||||
put(227, STRESS_LEVEL);
|
put(227, STRESS_LEVEL);
|
||||||
put(269, SPO2);
|
put(269, SPO2);
|
||||||
|
put(273, SLEEP_DATA_INFO);
|
||||||
|
put(274, SLEEP_DATA_RAW);
|
||||||
put(275, SLEEP_STAGE);
|
put(275, SLEEP_STAGE);
|
||||||
put(297, RESPIRATION_RATE);
|
put(297, RESPIRATION_RATE);
|
||||||
put(346, SLEEP_STATS);
|
put(346, SLEEP_STATS);
|
||||||
|
@ -28,6 +28,7 @@ public class FieldDefinitionSleepStage extends FieldDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum SleepStage {
|
public enum SleepStage {
|
||||||
|
UNMEASURABLE(0),
|
||||||
AWAKE(1),
|
AWAKE(1),
|
||||||
LIGHT(2),
|
LIGHT(2),
|
||||||
DEEP(3),
|
DEEP(3),
|
||||||
|
@ -67,6 +67,10 @@ public class FitRecordDataFactory {
|
|||||||
return new FitStressLevel(recordDefinition, recordHeader);
|
return new FitStressLevel(recordDefinition, recordHeader);
|
||||||
case 269:
|
case 269:
|
||||||
return new FitSpo2(recordDefinition, recordHeader);
|
return new FitSpo2(recordDefinition, recordHeader);
|
||||||
|
case 273:
|
||||||
|
return new FitSleepDataInfo(recordDefinition, recordHeader);
|
||||||
|
case 274:
|
||||||
|
return new FitSleepDataRaw(recordDefinition, recordHeader);
|
||||||
case 275:
|
case 275:
|
||||||
return new FitSleepStage(recordDefinition, recordHeader);
|
return new FitSleepStage(recordDefinition, recordHeader);
|
||||||
case 297:
|
case 297:
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
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 FitSleepDataInfo extends RecordData {
|
||||||
|
public FitSleepDataInfo(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||||
|
super(recordDefinition, recordHeader);
|
||||||
|
|
||||||
|
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
|
||||||
|
if (globalNumber != 273) {
|
||||||
|
throw new IllegalArgumentException("FitSleepDataInfo expects global messages of " + 273 + ", got " + globalNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getUnk0() {
|
||||||
|
return (Integer) getFieldByNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getSampleLength() {
|
||||||
|
return (Integer) getFieldByNumber(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Long getTimestampInTz() {
|
||||||
|
return (Long) getFieldByNumber(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getUnk3() {
|
||||||
|
return (Integer) getFieldByNumber(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getVersion() {
|
||||||
|
return (String) getFieldByNumber(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Long getTimestamp() {
|
||||||
|
return (Long) getFieldByNumber(253);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
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 FitSleepDataRaw extends RecordData {
|
||||||
|
public FitSleepDataRaw(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||||
|
super(recordDefinition, recordHeader);
|
||||||
|
|
||||||
|
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
|
||||||
|
if (globalNumber != 274) {
|
||||||
|
throw new IllegalArgumentException("FitSleepDataRaw expects global messages of " + 274 + ", got " + globalNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getBytes() {
|
||||||
|
return (Integer) getFieldByNumber(0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user