mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-02-04 04:54:10 +01:00
Xiaomi-protobuf: Persist heart pulse samples
This commit is contained in:
parent
95b9009318
commit
69847c19ff
@ -54,7 +54,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(90, MAIN_PACKAGE + ".entities");
|
final Schema schema = new Schema(91, MAIN_PACKAGE + ".entities");
|
||||||
|
|
||||||
Entity userAttributes = addUserAttributes(schema);
|
Entity userAttributes = addUserAttributes(schema);
|
||||||
Entity user = addUserInfo(schema, userAttributes);
|
Entity user = addUserInfo(schema, userAttributes);
|
||||||
@ -81,6 +81,7 @@ public class GBDaoGenerator {
|
|||||||
addHuamiSleepRespiratoryRateSample(schema, user, device);
|
addHuamiSleepRespiratoryRateSample(schema, user, device);
|
||||||
addXiaomiActivitySample(schema, user, device);
|
addXiaomiActivitySample(schema, user, device);
|
||||||
addXiaomiSleepTimeSamples(schema, user, device);
|
addXiaomiSleepTimeSamples(schema, user, device);
|
||||||
|
addHeartPulseSamples(schema, user, device);
|
||||||
addXiaomiSleepStageSamples(schema, user, device);
|
addXiaomiSleepStageSamples(schema, user, device);
|
||||||
addXiaomiManualSamples(schema, user, device);
|
addXiaomiManualSamples(schema, user, device);
|
||||||
addXiaomiDailySummarySamples(schema, user, device);
|
addXiaomiDailySummarySamples(schema, user, device);
|
||||||
@ -410,6 +411,12 @@ public class GBDaoGenerator {
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Entity addHeartPulseSamples(Schema schema, Entity user, Entity device) {
|
||||||
|
Entity sample = addEntity(schema, "HeartPulseSample");
|
||||||
|
addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device);
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
private static Entity addXiaomiSleepStageSamples(Schema schema, Entity user, Entity device) {
|
private static Entity addXiaomiSleepStageSamples(Schema schema, Entity user, Entity device) {
|
||||||
Entity sample = addEntity(schema, "XiaomiSleepStageSample");
|
Entity sample = addEntity(schema, "XiaomiSleepStageSample");
|
||||||
addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device);
|
addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device);
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/* 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;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import de.greenrobot.dao.AbstractDao;
|
||||||
|
import de.greenrobot.dao.Property;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.HeartPulseSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.HeartPulseSampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
public class HeartPulseSampleProvider extends AbstractTimeSampleProvider<HeartPulseSample> {
|
||||||
|
public HeartPulseSampleProvider(final GBDevice device, final DaoSession session) {
|
||||||
|
super(device, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AbstractDao<HeartPulseSample, ?> getSampleDao() {
|
||||||
|
return getSession().getHeartPulseSampleDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getTimestampSampleProperty() {
|
||||||
|
return HeartPulseSampleDao.Properties.Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getDeviceIdentifierSampleProperty() {
|
||||||
|
return HeartPulseSampleDao.Properties.DeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeartPulseSample createSample() {
|
||||||
|
return new HeartPulseSample();
|
||||||
|
}
|
||||||
|
}
|
@ -30,10 +30,12 @@ import java.util.List;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.HeartPulseSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSleepStageSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSleepStageSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSleepTimeSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSleepTimeSampleProvider;
|
||||||
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.HeartPulseSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepStageSample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepStageSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSample;
|
||||||
@ -140,6 +142,7 @@ public class SleepDetailsParser extends XiaomiActivityParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final List<XiaomiSleepStageSample> stages = new ArrayList<>();
|
final List<XiaomiSleepStageSample> stages = new ArrayList<>();
|
||||||
|
final List<HeartPulseSample> heartPulseSamples = new ArrayList<>();
|
||||||
LOG.debug("Sleep stage packets from offset {}", Integer.toHexString(buf.position()));
|
LOG.debug("Sleep stage packets from offset {}", Integer.toHexString(buf.position()));
|
||||||
|
|
||||||
// Do not crash if we face a buffer underflow, as the next parsing is not 100% fool-proof,
|
// Do not crash if we face a buffer underflow, as the next parsing is not 100% fool-proof,
|
||||||
@ -181,7 +184,16 @@ public class SleepDetailsParser extends XiaomiActivityParser {
|
|||||||
|
|
||||||
final ByteBuffer dataBuf = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
|
final ByteBuffer dataBuf = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
if (type == 16) {
|
if (type == 1) {
|
||||||
|
long timestampAcc = ts;
|
||||||
|
while (dataBuf.position() < dataBuf.limit()) {
|
||||||
|
final int delta = dataBuf.get() & 0xff;
|
||||||
|
final HeartPulseSample heartPulseSample = new HeartPulseSample();
|
||||||
|
heartPulseSample.setTimestamp(timestampAcc + delta);
|
||||||
|
heartPulseSamples.add(heartPulseSample);
|
||||||
|
timestampAcc += delta;
|
||||||
|
}
|
||||||
|
} else if (type == 16) {
|
||||||
final int data_0 = dataBuf.get() & 0xFF;
|
final int data_0 = dataBuf.get() & 0xFF;
|
||||||
final int sleep_index = data_0 >> 4;
|
final int sleep_index = data_0 >> 4;
|
||||||
final int wake_count = data_0 & 0x0F;
|
final int wake_count = data_0 & 0x0F;
|
||||||
@ -243,6 +255,8 @@ public class SleepDetailsParser extends XiaomiActivityParser {
|
|||||||
summaries.add(sample);
|
summaries.add(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean persistSuccess = !stagesParseFailed;
|
||||||
|
|
||||||
// save all the samples that we got
|
// save all the samples that we got
|
||||||
try (DBHandler handler = GBApplication.acquireDB()) {
|
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||||
final DaoSession session = handler.getDaoSession();
|
final DaoSession session = handler.getDaoSession();
|
||||||
@ -271,7 +285,7 @@ public class SleepDetailsParser extends XiaomiActivityParser {
|
|||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
GB.toast(support.getContext(), "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(support.getContext(), "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
LOG.error("Error saving sleep sample", e);
|
LOG.error("Error saving sleep sample", e);
|
||||||
return false;
|
persistSuccess = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stagesParseFailed && !stages.isEmpty()) {
|
if (!stagesParseFailed && !stages.isEmpty()) {
|
||||||
@ -295,11 +309,32 @@ public class SleepDetailsParser extends XiaomiActivityParser {
|
|||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
GB.toast(support.getContext(), "Error saving sleep stage samples", Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(support.getContext(), "Error saving sleep stage samples", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
LOG.error("Error saving sleep stage samples", e);
|
LOG.error("Error saving sleep stage samples", e);
|
||||||
return false;
|
persistSuccess = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !stagesParseFailed;
|
// Save the heart pulse samples
|
||||||
|
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||||
|
final DaoSession session = handler.getDaoSession();
|
||||||
|
final GBDevice gbDevice = support.getDevice();
|
||||||
|
final Device device = DBHelper.getDevice(gbDevice, session);
|
||||||
|
final User user = DBHelper.getUser(session);
|
||||||
|
|
||||||
|
final HeartPulseSampleProvider sampleProvider = new HeartPulseSampleProvider(gbDevice, session);
|
||||||
|
|
||||||
|
for (final HeartPulseSample stageSample : heartPulseSamples) {
|
||||||
|
stageSample.setDevice(device);
|
||||||
|
stageSample.setUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleProvider.addSamples(heartPulseSamples);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
GB.toast(support.getContext(), "Error saving heart pulse samples", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
LOG.error("Error saving heart pulse samples", e);
|
||||||
|
persistSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return persistSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean readStagePacketHeader(final ByteBuffer buffer) {
|
private static boolean readStagePacketHeader(final ByteBuffer buffer) {
|
||||||
|
Loading…
Reference in New Issue
Block a user