diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
index d93ed745b..4b457d9e5 100644
--- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
+++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
@@ -45,7 +45,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
- final Schema schema = new Schema(63, MAIN_PACKAGE + ".entities");
+ final Schema schema = new Schema(64, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@@ -71,6 +71,7 @@ public class GBDaoGenerator {
addHuamiPaiSample(schema, user, device);
addHuamiSleepRespiratoryRateSample(schema, user, device);
addXiaomiActivitySample(schema, user, device);
+ addXiaomiSleepTimeSamples(schema, user, device);
addPebbleHealthActivitySample(schema, user, device);
addPebbleHealthActivityKindOverlay(schema, user, device);
addPebbleMisfitActivitySample(schema, user, device);
@@ -338,6 +339,14 @@ public class GBDaoGenerator {
return activitySample;
}
+ private static Entity addXiaomiSleepTimeSamples(Schema schema, Entity user, Entity device) {
+ Entity sample = addEntity(schema, "XiaomiSleepTimeSample");
+ addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device);
+ sample.addLongProperty("wakeupTime");
+ sample.addBooleanProperty("isAwake");
+ return sample;
+ }
+
private static void addHeartRateProperties(Entity activitySample) {
activitySample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetterAndSetter(OVERRIDE);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiSleepTimeSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiSleepTimeSampleProvider.java
new file mode 100644
index 000000000..0b031e8db
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiSleepTimeSampleProvider.java
@@ -0,0 +1,56 @@
+/* Copyright (C) 2023 José Rebelo
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices.xiaomi;
+
+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.XiaomiSleepTimeSample;
+import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSampleDao;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+
+public class XiaomiSleepTimeSampleProvider extends AbstractTimeSampleProvider {
+ public XiaomiSleepTimeSampleProvider(final GBDevice device, final DaoSession session) {
+ super(device, session);
+ }
+
+ @NonNull
+ @Override
+ public AbstractDao getSampleDao() {
+ return getSession().getXiaomiSleepTimeSampleDao();
+ }
+
+ @NonNull
+ @Override
+ protected Property getTimestampSampleProperty() {
+ return XiaomiSleepTimeSampleDao.Properties.Timestamp;
+ }
+
+ @NonNull
+ @Override
+ protected Property getDeviceIdentifierSampleProperty() {
+ return XiaomiSleepTimeSampleDao.Properties.DeviceId;
+ }
+
+ @Override
+ public XiaomiSleepTimeSample createSample() {
+ return new XiaomiSleepTimeSample();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/SleepDetailsParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/SleepDetailsParser.java
index 2164a69b3..3ae3f1804 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/SleepDetailsParser.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/SleepDetailsParser.java
@@ -16,15 +16,26 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
+import android.widget.Toast;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.List;
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
+import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSleepTimeSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSample;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class SleepDetailsParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(SleepDetailsParser.class);
@@ -39,14 +50,45 @@ public class SleepDetailsParser extends XiaomiActivityParser {
final ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
buf.get(); // header ? 0xF0
- buf.get(); // ?
+ final int isAwake = buf.get() & 0xff; // 0/1
final int bedTime = buf.getInt();
final int wakeupTime = buf.getInt();
- LOG.info("Bed time: {}, wake up time: {}", bedTime, wakeupTime);
+ LOG.debug("Sleep sample: bedTime: {}, wakeupTime: {}, isAwake: {}", bedTime, wakeupTime, isAwake);
- // TODO save timestamps and overlay on activity
- // TODO everything else...
+ final XiaomiSleepTimeSample sample = new XiaomiSleepTimeSample();
+ sample.setTimestamp(bedTime * 1000L);
+ sample.setWakeupTime(wakeupTime * 1000L);
+ sample.setIsAwake(isAwake == 1);
- return false;
+ // save all the samples that we got
+ try (DBHandler handler = GBApplication.acquireDB()) {
+ final DaoSession session = handler.getDaoSession();
+ final GBDevice gbDevice = support.getDevice();
+
+ sample.setDevice(DBHelper.getDevice(gbDevice, session));
+ sample.setUser(DBHelper.getUser(session));
+
+ final XiaomiSleepTimeSampleProvider sampleProvider = new XiaomiSleepTimeSampleProvider(gbDevice, session);
+
+ // Check if there is already a later sleep sample - if so, ignore this one
+ // Samples for the same sleep will always have the same bedtime (timestamp), but we might get
+ // multiple bedtimes until the user wakes up
+ final List existingSamples = sampleProvider.getAllSamples(sample.getTimestamp(), sample.getTimestamp());
+ if (!existingSamples.isEmpty()) {
+ final XiaomiSleepTimeSample existingSample = existingSamples.get(0);
+ if (existingSample.getWakeupTime() > sample.getWakeupTime()) {
+ LOG.warn("Ignoring sleep sample - existing sample is more recent ({})", existingSample.getWakeupTime());
+ return true;
+ }
+ }
+
+ sampleProvider.addSample(sample);
+
+ return true;
+ } catch (final Exception e) {
+ GB.toast(support.getContext(), "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
+ LOG.error("Error saving sleep sample", e);
+ return false;
+ }
}
}