diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index ed32fff4b5..a56048cb01 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -49,22 +49,21 @@ public class GBDaoGenerator { private static final String SAMPLE_SPO2 = "spo2"; private static final String SAMPLE_STRESS = "stress"; private static final String SAMPLE_TEMPERATURE = "temperature"; - private static final String SAMPLE_TEMPERATURE_TYPE = "temperatureType"; private static final String SAMPLE_WEIGHT_KG = "weightKg"; private static final String SAMPLE_BLOOD_PRESSURE_SYSTOLIC = "bpSystolic"; private static final String SAMPLE_BLOOD_PRESSURE_DIASTOLIC = "bpDiastolic"; private static final String TIMESTAMP_FROM = "timestampFrom"; private static final String TIMESTAMP_TO = "timestampTo"; - public static void main(String[] args) throws Exception { - final Schema schema = new Schema(117, MAIN_PACKAGE + ".entities"); + final Schema schema = new Schema(118, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); Entity deviceAttributes = addDeviceAttributes(schema); Entity device = addDevice(schema, deviceAttributes); + addHealthConnectSyncState(schema, device); // yeah deep shit, has to be here (after device) for db upgrade and column order // because addDevice adds a property to deviceAttributes also.... @@ -644,7 +643,9 @@ public class GBDaoGenerator { private static Entity addColmiTemperatureSample(Schema schema, Entity user, Entity device) { Entity sample = addEntity(schema, "ColmiTemperatureSample"); addCommonTimeSampleProperties("AbstractTemperatureSample", sample, user, device); - addTemperatureProperties(sample); + sample.addFloatProperty(SAMPLE_TEMPERATURE).notNull(); + sample.addIntProperty("temperatureType"); + sample.addIntProperty("temperatureLocation"); return sample; } @@ -1223,6 +1224,14 @@ public class GBDaoGenerator { calendarSyncState.addIntProperty("hash").notNull(); } + private static void addHealthConnectSyncState(Schema schema, Entity device) { + Entity healthConnectSyncState = addEntity(schema, "HealthConnectSyncState"); + Property deviceId = healthConnectSyncState.addLongProperty("deviceId").primaryKey().notNull().getProperty(); + healthConnectSyncState.addStringProperty("dataType").primaryKey().notNull(); + healthConnectSyncState.addToOne(device, deviceId); + healthConnectSyncState.addLongProperty("lastSyncTimestamp").notNull(); + } + private static void addAlarms(Schema schema, Entity user, Entity device) { Entity alarm = addEntity(schema, "Alarm"); alarm.implementsInterface("nodomain.freeyourgadget.gadgetbridge.model.Alarm"); @@ -1598,9 +1607,9 @@ public class GBDaoGenerator { Entity sample = addEntity(schema, "HuaweiTemperatureSample"); addCommonTimeSampleProperties("AbstractTemperatureSample", sample, user, device); sample.addLongProperty("lastTimestamp").notNull().index(); - sample.addFloatProperty(SAMPLE_TEMPERATURE).notNull().codeBeforeGetter(OVERRIDE); - sample.addIntProperty(SAMPLE_TEMPERATURE_TYPE).notNull().primaryKey().codeBeforeGetter(OVERRIDE); - + sample.addFloatProperty(SAMPLE_TEMPERATURE).notNull(); + sample.addIntProperty("temperatureType").primaryKey(); + sample.addIntProperty("temperatureLocation"); return sample; } @@ -1891,23 +1900,22 @@ public class GBDaoGenerator { } - private static void addTemperatureProperties(Entity activitySample) { - activitySample.addFloatProperty(SAMPLE_TEMPERATURE).notNull().codeBeforeGetter(OVERRIDE); - activitySample.addIntProperty(SAMPLE_TEMPERATURE_TYPE).notNull().codeBeforeGetter(OVERRIDE); - } - private static Entity addFemometerVinca2TemperatureSample(Schema schema, Entity user, Entity device) { Entity sample = addEntity(schema, "FemometerVinca2TemperatureSample"); + sample.addFloatProperty(SAMPLE_TEMPERATURE).notNull(); + sample.addIntProperty("temperatureType"); + sample.addIntProperty("temperatureLocation"); addCommonTimeSampleProperties("AbstractTemperatureSample", sample, user, device); - addTemperatureProperties(sample); return sample; } private static Entity addMijiaLywsdRealtimeSample(Schema schema, Entity user, Entity device) { Entity sample = addEntity(schema, "MijiaLywsdRealtimeSample"); addCommonTimeSampleProperties("AbstractTemperatureSample", sample, user, device); - addTemperatureProperties(sample); + sample.addFloatProperty(SAMPLE_TEMPERATURE).notNull().codeBeforeGetter(OVERRIDE); sample.addIntProperty("humidity").notNull(); + sample.addIntProperty("temperatureType"); + sample.addIntProperty("temperatureLocation"); return sample; } @@ -1960,7 +1968,8 @@ public class GBDaoGenerator { Entity temperatureSample = addEntity(schema, "GenericTemperatureSample"); addCommonTimeSampleProperties("AbstractTemperatureSample", temperatureSample, user, device); temperatureSample.addFloatProperty(SAMPLE_TEMPERATURE).notNull(); - temperatureSample.addIntProperty(SAMPLE_TEMPERATURE_TYPE).notNull(); + temperatureSample.addIntProperty("temperatureType"); + temperatureSample.addIntProperty("temperatureLocation"); return temperatureSample; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_118.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_118.java new file mode 100644 index 0000000000..da5300ee96 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_118.java @@ -0,0 +1,38 @@ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import nodomain.freeyourgadget.gadgetbridge.entities.ColmiTemperatureSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.FemometerVinca2TemperatureSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.GenericTemperatureSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.MijiaLywsdRealtimeSampleDao; +import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample; + +public class GadgetbridgeUpdate_118 implements DBUpdateScript { + private void addTemperatureColumnsIfNotExists(SQLiteDatabase database, String tableName, int defaultTemperatureType, int defaultTemperatureLocation) { + // Add TemperatureType column if it doesn't exist + if (!DBHelper.existsColumn(tableName, GenericTemperatureSampleDao.Properties.TemperatureType.columnName, database)) { + database.execSQL("ALTER TABLE " + tableName + " ADD COLUMN " + GenericTemperatureSampleDao.Properties.TemperatureType.columnName + " INTEGER DEFAULT " + defaultTemperatureType); + } + // Add TemperatureLocation column if it doesn't exist + if (!DBHelper.existsColumn(tableName, GenericTemperatureSampleDao.Properties.TemperatureLocation.columnName, database)) { + database.execSQL("ALTER TABLE " + tableName + " ADD COLUMN " + GenericTemperatureSampleDao.Properties.TemperatureLocation.columnName + " INTEGER DEFAULT " + defaultTemperatureLocation); + } + } + + @Override + public void upgradeSchema(SQLiteDatabase database) { + addTemperatureColumnsIfNotExists(database, HuaweiTemperatureSampleDao.TABLENAME, TemperatureSample.TYPE_SKIN, TemperatureSample.LOCATION_WRIST); + addTemperatureColumnsIfNotExists(database, ColmiTemperatureSampleDao.TABLENAME, TemperatureSample.TYPE_SKIN, TemperatureSample.LOCATION_WRIST); + addTemperatureColumnsIfNotExists(database, FemometerVinca2TemperatureSampleDao.TABLENAME, TemperatureSample.TYPE_BODY, TemperatureSample.LOCATION_MOUTH); + addTemperatureColumnsIfNotExists(database, MijiaLywsdRealtimeSampleDao.TABLENAME, TemperatureSample.TYPE_AMBIENT, TemperatureSample.LOCATION_UNKNOWN); + addTemperatureColumnsIfNotExists(database, GenericTemperatureSampleDao.TABLENAME, TemperatureSample.TYPE_UNKNOWN, TemperatureSample.LOCATION_UNKNOWN); + } + + @Override + public void downgradeSchema(SQLiteDatabase db) { + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index d2ff0b3f4b..32ee895170 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -74,6 +74,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao; import nodomain.freeyourgadget.gadgetbridge.entities.GenericTrainingLoadAcuteSample; import nodomain.freeyourgadget.gadgetbridge.entities.GenericTrainingLoadChronicSample; +import nodomain.freeyourgadget.gadgetbridge.entities.HealthConnectSyncStateDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.AbstractNotificationPattern; @@ -225,6 +226,8 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { batteryLevelQueryBuilder.where(BatteryLevelDao.Properties.DeviceId.eq(device.getId())).buildDelete().executeDeleteWithoutDetachingEntities(); QueryBuilder alarmDeviceQueryBuilder = session.getAlarmDao().queryBuilder(); alarmDeviceQueryBuilder.where(AlarmDao.Properties.DeviceId.eq(device.getId())).buildDelete().executeDeleteWithoutDetachingEntities(); + QueryBuilder healthSyncStateQueryBuilder = session.getHealthConnectSyncStateDao().queryBuilder(); + healthSyncStateQueryBuilder.where(HealthConnectSyncStateDao.Properties.DeviceId.eq(device.getId())).buildDelete().executeDeleteWithoutDetachingEntities(); session.getDeviceDao().delete(device); } else { LOG.info("device to delete not found in db: {}", gbDevice); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/GenericTemperatureSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/GenericTemperatureSampleProvider.java index 3643514db9..9c2b47e38d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/GenericTemperatureSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/GenericTemperatureSampleProvider.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2025 Thomas Kuehne +/* Copyright (C) 2025 Thomas Kuehne, José Rebelo, Gideon Zenz + Copyright (C) 2023-2024 Alicia Hormann (Original AbstractTemperatureSample) This file is part of Gadgetbridge. @@ -18,6 +19,10 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; +import java.util.stream.Collectors; import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; @@ -25,10 +30,35 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.GenericTemperatureSample; import nodomain.freeyourgadget.gadgetbridge.entities.GenericTemperatureSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample; public class GenericTemperatureSampleProvider extends AbstractTimeSampleProvider { + private final Integer defaultType; + private final Integer defaultLocation; + public GenericTemperatureSampleProvider(final GBDevice device, final DaoSession session) { + this(device, session, TemperatureSample.TYPE_UNKNOWN, TemperatureSample.LOCATION_UNKNOWN); + } + + public GenericTemperatureSampleProvider(final GBDevice device, + final DaoSession session, + final Integer defaultType, + final Integer defaultLocation) { super(device, session); + this.defaultType = defaultType; + this.defaultLocation = defaultLocation; + } + + private GenericTemperatureSample applyDefaults(final GenericTemperatureSample sample) { + if (sample != null) { + if (defaultType != -1 && (sample.getTemperatureType() == null || sample.getTemperatureType() == 0)) { + sample.setTemperatureType(defaultType); + } + if (defaultLocation != -1 && (sample.getTemperatureLocation() == null || sample.getTemperatureLocation() == 0)) { + sample.setTemperatureLocation(defaultLocation); + } + } + return sample; } @NonNull @@ -53,4 +83,40 @@ public class GenericTemperatureSampleProvider extends AbstractTimeSampleProvider public GenericTemperatureSample createSample() { return new GenericTemperatureSample(); } -} + + @NonNull + @Override + public List getAllSamples(final long timestampFrom, final long timestampTo) { + return super.getAllSamples(timestampFrom, timestampTo).stream() + .map(this::applyDefaults) + .collect(Collectors.toList()); + } + + @Nullable + @Override + public GenericTemperatureSample getLatestSample() { + return applyDefaults(super.getLatestSample()); + } + + @Nullable + @Override + public GenericTemperatureSample getLatestSample(final long until) { + return applyDefaults(super.getLatestSample(until)); + } + + @Nullable + public GenericTemperatureSample getLastSampleBefore(final long timestampTo) { + return applyDefaults(super.getLastSampleBefore(timestampTo)); + } + + @Nullable + public GenericTemperatureSample getNextSampleAfter(final long timestampFrom) { + return applyDefaults(super.getNextSampleAfter(timestampFrom)); + } + + @Nullable + @Override + public GenericTemperatureSample getFirstSample() { + return applyDefaults(super.getFirstSample()); + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/femometer/FemometerVinca2SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/femometer/FemometerVinca2SampleProvider.java index c68bb3156a..4c6206d828 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/femometer/FemometerVinca2SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/femometer/FemometerVinca2SampleProvider.java @@ -25,6 +25,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.FemometerVinca2TemperatureSample; import nodomain.freeyourgadget.gadgetbridge.entities.FemometerVinca2TemperatureSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample; public class FemometerVinca2SampleProvider extends AbstractTimeSampleProvider { @@ -52,6 +53,9 @@ public class FemometerVinca2SampleProvider extends AbstractTimeSampleProvider= 20 && skinTemperature <= 42) { - res.add(new HuaweiTemperatureSample(vl.getHuaweiDictData().getStartTimestamp(), vl.getHuaweiDictData().getDeviceId(), vl.getHuaweiDictData().getUserId(), Math.max(vl.getHuaweiDictData().getModifyTimestamp(), vl.getHuaweiDictData().getEndTimestamp()), (float) skinTemperature, 0)); + res.add(new HuaweiTemperatureSample(vl.getHuaweiDictData().getStartTimestamp(), vl.getHuaweiDictData().getDeviceId(), vl.getHuaweiDictData().getUserId(), Math.max(vl.getHuaweiDictData().getModifyTimestamp(), vl.getHuaweiDictData().getEndTimestamp()), (float) skinTemperature, TYPE_SKIN, LOCATION_WRIST)); } } daoSession.getHuaweiTemperatureSampleDao().insertInTx(res); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiTemperatureSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiTemperatureSampleProvider.java index 2c4794ca9c..a40d57151a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiTemperatureSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiTemperatureSampleProvider.java @@ -30,6 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample; public class HuaweiTemperatureSampleProvider extends AbstractTimeSampleProvider { @@ -57,7 +58,10 @@ public class HuaweiTemperatureSampleProvider extends AbstractTimeSampleProvider< @Override public HuaweiTemperatureSample createSample() { - return new HuaweiTemperatureSample(); + HuaweiTemperatureSample sample = new HuaweiTemperatureSample(); + sample.setTemperatureType(TemperatureSample.TYPE_SKIN); + sample.setTemperatureLocation(TemperatureSample.LOCATION_WRIST); + return sample; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdRealtimeSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdRealtimeSampleProvider.java index 5cf0bf0534..44005d78c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdRealtimeSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdRealtimeSampleProvider.java @@ -25,6 +25,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.MijiaLywsdRealtimeSample; import nodomain.freeyourgadget.gadgetbridge.entities.MijiaLywsdRealtimeSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample; public class MijiaLywsdRealtimeSampleProvider extends AbstractTimeSampleProvider { public MijiaLywsdRealtimeSampleProvider(final GBDevice device, final DaoSession session) { @@ -51,6 +52,9 @@ public class MijiaLywsdRealtimeSampleProvider extends AbstractTimeSampleProvider @Override public MijiaLywsdRealtimeSample createSample() { - return new MijiaLywsdRealtimeSample(); + MijiaLywsdRealtimeSample sample=new MijiaLywsdRealtimeSample(); + sample.setTemperatureType(TemperatureSample.TYPE_AMBIENT); + sample.setTemperatureLocation(TemperatureSample.LOCATION_UNKNOWN); + return sample; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestTemperatureSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestTemperatureSampleProvider.java index 6e7005f0bc..a0dcf3a540 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestTemperatureSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/samples/TestTemperatureSampleProvider.java @@ -78,10 +78,5 @@ public class TestTemperatureSampleProvider extends AbstractTestSampleProvider getTemperatureSampleProvider(GBDevice device, DaoSession session) { - return new GenericTemperatureSampleProvider(device, session); + return new GenericTemperatureSampleProvider(device, session, TemperatureSample.TYPE_SKIN, TemperatureSample.LOCATION_FINGER); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiTemperatureSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiTemperatureSampleProvider.java index 78ce1cc1ef..6c1ecd0d2d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiTemperatureSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiTemperatureSampleProvider.java @@ -119,8 +119,13 @@ public class XiaomiTemperatureSampleProvider implements TimeSampleProvider