mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Moyoung: Handle stress packets from Rainbuvvy T97, Colmi V89 and Advan SE1
This commit is contained in:
+10
@@ -39,6 +39,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungActivitySampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungSpo2SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungStressSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings.MoyoungEnumDeviceVersion;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings.MoyoungEnumMetricSystem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings.MoyoungEnumTimeSystem;
|
||||
@@ -57,10 +58,12 @@ import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungBloodPressureSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungHeartRateSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungSpo2SampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungStressSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.moyoung.MoyoungDeviceSupport;
|
||||
|
||||
@@ -98,6 +101,8 @@ public abstract class AbstractMoyoungDeviceCoordinator extends AbstractBLEDevice
|
||||
qb.where(MoyoungSpo2SampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
|
||||
qb = session.getMoyoungBloodPressureSampleDao().queryBuilder();
|
||||
qb.where(MoyoungBloodPressureSampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
|
||||
qb = session.getMoyoungStressSampleDao().queryBuilder();
|
||||
qb.where(MoyoungStressSampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,6 +130,11 @@ public abstract class AbstractMoyoungDeviceCoordinator extends AbstractBLEDevice
|
||||
return new MoyoungSpo2SampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends StressSample> getStressSampleProvider(GBDevice device, DaoSession session) {
|
||||
return new MoyoungStressSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount(GBDevice device) {
|
||||
return 3;
|
||||
|
||||
+5
@@ -80,4 +80,9 @@ public class AdvanWatchSE1AICoordinator extends AbstractMoyoungDeviceCoordinator
|
||||
public boolean supportsRemSleep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStressMeasurement() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+5
@@ -80,4 +80,9 @@ public class ColmiV89Coordinator extends AbstractMoyoungDeviceCoordinator {
|
||||
public boolean supportsRemSleep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStressMeasurement() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+1
@@ -234,6 +234,7 @@ public class MoyoungConstants {
|
||||
public static final byte ARG_ADVANCED_SET_ALARM = 0x05;
|
||||
public static final byte ARG_ADVANCED_SET_CALENDAR = 0x08;
|
||||
public static final byte ARG_ADVANCED_QUERY_STOCKS = 0x0e;
|
||||
public static final byte ARG_ADVANCED_STRESS_PACKET = 0x11;
|
||||
public static final byte ARG_ADVANCED_QUERY_ALARMS = 0x15;
|
||||
|
||||
public static final byte ARG_ALARM_SET = 0x00;
|
||||
|
||||
+5
@@ -85,4 +85,9 @@ public class RainbuvvyT97Coordinator extends AbstractMoyoungDeviceCoordinator {
|
||||
public boolean supportsRemSleep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStressMeasurement() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/* Copyright (C) 2024 Arjan Schrijver
|
||||
|
||||
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.moyoung.samples;
|
||||
|
||||
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.MoyoungStressSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungStressSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class MoyoungStressSampleProvider extends AbstractTimeSampleProvider<MoyoungStressSample> {
|
||||
public MoyoungStressSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
super(device, session);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AbstractDao<MoyoungStressSample, ?> getSampleDao() {
|
||||
return getSession().getMoyoungStressSampleDao();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getTimestampSampleProperty() {
|
||||
return MoyoungStressSampleDao.Properties.Timestamp;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getDeviceIdentifierSampleProperty() {
|
||||
return MoyoungStressSampleDao.Properties.DeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MoyoungStressSample createSample() {
|
||||
return new MoyoungStressSample();
|
||||
}
|
||||
}
|
||||
+95
@@ -75,6 +75,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungBlood
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungHeartRateSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungSleepStageSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungSpo2SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungStressSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings.MoyoungEnumDeviceVersion;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings.MoyoungEnumLanguage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings.MoyoungEnumMetricSystem;
|
||||
@@ -91,6 +92,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungBloodPressureSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungHeartRateSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungSleepStageSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungSpo2Sample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MoyoungStressSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksController;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@@ -488,6 +490,12 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (packetType == MoyoungConstants.CMD_ADVANCED_QUERY && payload[0] == MoyoungConstants.ARG_ADVANCED_STRESS_PACKET)
|
||||
{
|
||||
handleStressPacket(payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (packetType == MoyoungConstants.CMD_QUERY_POWER_SAVING)
|
||||
{
|
||||
LOG.info("Power saving set to: {}", payload[0] == 0x01);
|
||||
@@ -667,6 +675,85 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStressPacket(byte[] payload) {
|
||||
byte packetType = payload[1];
|
||||
switch (packetType) {
|
||||
case 0x00:
|
||||
// Single stress measurement result
|
||||
int stressLevel = payload[2] & 0xff;
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
MoyoungStressSampleProvider sampleProvider = new MoyoungStressSampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
|
||||
MoyoungStressSample sample = new MoyoungStressSample();
|
||||
sample.setTimestamp(System.currentTimeMillis());
|
||||
sample.setStress(stressLevel);
|
||||
sample.setDeviceId(deviceId);
|
||||
sample.setUserId(userId);
|
||||
|
||||
sampleProvider.addSample(sample);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error acquiring database for recording stress samples", e);
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
// Unknown
|
||||
break;
|
||||
case 0x02:
|
||||
// Unknown
|
||||
break;
|
||||
case 0x03:
|
||||
// Day stress measurements sync result
|
||||
byte daysAgo = payload[2];
|
||||
ArrayList<MoyoungStressSample> stressSamples = new ArrayList<>();
|
||||
Calendar sampleCal = Calendar.getInstance();
|
||||
sampleCal.add(Calendar.DAY_OF_MONTH, -daysAgo);
|
||||
sampleCal.set(Calendar.SECOND, 0);
|
||||
sampleCal.set(Calendar.MILLISECOND, 0);
|
||||
int startIndex = 3;
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (payload[startIndex + i] != 0x00) {
|
||||
// Determine time of day
|
||||
sampleCal.set(Calendar.HOUR_OF_DAY, Math.floorDiv(i, 2));
|
||||
if (i % 2 == 0) {
|
||||
sampleCal.set(Calendar.MINUTE, 0);
|
||||
} else {
|
||||
sampleCal.set(Calendar.MINUTE, 30);
|
||||
}
|
||||
if (sampleCal.after(Calendar.getInstance())) {
|
||||
continue;
|
||||
}
|
||||
LOG.info("Stress level is {} at {}", payload[startIndex + i] & 0xff, sampleCal.getTime());
|
||||
// Build sample object and save in database
|
||||
MoyoungStressSample gbSample = new MoyoungStressSample();
|
||||
gbSample.setTimestamp(sampleCal.getTimeInMillis());
|
||||
gbSample.setStress(payload[startIndex + i] & 0xff);
|
||||
stressSamples.add(gbSample);
|
||||
}
|
||||
}
|
||||
if (!stressSamples.isEmpty()) {
|
||||
try (DBHandler db = GBApplication.acquireDB()) {
|
||||
MoyoungStressSampleProvider sampleProvider = new MoyoungStressSampleProvider(getDevice(), db.getDaoSession());
|
||||
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId();
|
||||
for (final MoyoungStressSample sample : stressSamples) {
|
||||
sample.setDeviceId(deviceId);
|
||||
sample.setUserId(userId);
|
||||
}
|
||||
LOG.info("Will persist {} stress samples", stressSamples.size());
|
||||
sampleProvider.addSamples(stressSamples);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error acquiring database for recording stress samples", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x04:
|
||||
// Unknown
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGetAlarmsResponse(byte[] payload)
|
||||
{
|
||||
boolean payloadV2 = false;
|
||||
@@ -1031,6 +1118,14 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
LOG.error("Error fetching workouts: ", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("fetchStress");
|
||||
sendPacket(builder, MoyoungPacketOut.buildPacket(getMtu(), MoyoungConstants.CMD_ADVANCED_QUERY, new byte[] { MoyoungConstants.ARG_ADVANCED_STRESS_PACKET, 0x03, 0x00 }));
|
||||
sendPacket(builder, MoyoungPacketOut.buildPacket(getMtu(), MoyoungConstants.CMD_ADVANCED_QUERY, new byte[] { MoyoungConstants.ARG_ADVANCED_STRESS_PACKET, 0x03, 0x01 }));
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error while sending stress sync request: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Runnable updateIdleStepsRunnable = new Runnable() {
|
||||
|
||||
Reference in New Issue
Block a user