mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-30 14:02:56 +01:00
Storage and improved way of realtime data (hr, steps so far)
This commit is contained in:
parent
5a2ddaaec0
commit
125c0092cb
@ -162,6 +162,7 @@ public class GBDaoGenerator {
|
||||
|
||||
private static Entity addMiBandActivitySample(Schema schema, Entity user, Entity device) {
|
||||
Entity activitySample = addEntity(schema, "MiBandActivitySample");
|
||||
activitySample.implementsSerializable();
|
||||
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
|
@ -40,6 +40,9 @@ public abstract class AbstractActivitySample implements ActivitySample {
|
||||
public void setSteps(int steps) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unix timestamp of the sample, i.e. the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
*/
|
||||
public abstract void setTimestamp(int timestamp);
|
||||
|
||||
public abstract void setUserId(long userId);
|
||||
|
@ -37,6 +37,11 @@ public interface DeviceService extends EventHandler {
|
||||
String ACTION_SET_CONSTANT_VIBRATION = PREFIX + ".action.set_constant_vibration";
|
||||
String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms";
|
||||
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
||||
String ACTION_REALTIME_SAMPLES = PREFIX + ".action.realtime_samples";
|
||||
/**
|
||||
* Use EXTRA_REALTIME_SAMPLE instead
|
||||
*/
|
||||
@Deprecated
|
||||
String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps";
|
||||
String ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT = PREFIX + ".action.realtime_hr_measurement";
|
||||
String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support";
|
||||
@ -77,8 +82,17 @@ public interface DeviceService extends EventHandler {
|
||||
String EXTRA_ALARMS = "alarms";
|
||||
String EXTRA_PERFORM_PAIR = "perform_pair";
|
||||
String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps";
|
||||
/**
|
||||
* Use EXTRA_REALTIME_SAMPLE instead
|
||||
*/
|
||||
@Deprecated
|
||||
String EXTRA_REALTIME_STEPS = "realtime_steps";
|
||||
String EXTRA_REALTIME_SAMPLE = "realtime_sample";
|
||||
String EXTRA_TIMESTAMP = "timestamp";
|
||||
/**
|
||||
* Use EXTRA_REALTIME_SAMPLE instead
|
||||
*/
|
||||
@Deprecated
|
||||
String EXTRA_HEART_RATE_VALUE = "hr_value";
|
||||
String EXTRA_CALENDAREVENT_ID = "calendarevent_id";
|
||||
String EXTRA_CALENDAREVENT_TYPE = "calendarevent_type";
|
||||
|
@ -21,16 +21,26 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
|
||||
@ -93,6 +103,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
|
||||
private RealtimeSamplesSupport realtimeSamplesSupport;
|
||||
|
||||
public MiBandSupport() {
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
|
||||
@ -663,6 +674,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
}
|
||||
builder.queue(getQueue());
|
||||
enableRealtimeSamplesTimer(enable);
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex);
|
||||
}
|
||||
@ -713,6 +725,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
performInitialized(enable ? "Enabling realtime steps notifications" : "Disabling realtime steps notifications")
|
||||
.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), enable ? getLowLatency() : getHighLatency())
|
||||
.write(controlPoint, enable ? startRealTimeStepsNotifications : stopRealTimeStepsNotifications).queue(getQueue());
|
||||
enableRealtimeSamplesTimer(enable);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to change realtime steps notification to: " + enable, e);
|
||||
}
|
||||
@ -909,10 +922,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("heart rate: " + hrValue);
|
||||
}
|
||||
Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT)
|
||||
.putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue)
|
||||
.putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
RealtimeSamplesSupport realtimeSamplesSupport = getRealtimeSamplesSupport();
|
||||
realtimeSamplesSupport.setHeartrateBpm(hrValue);
|
||||
if (!realtimeSamplesSupport.isRunning()) {
|
||||
// single shot measurement, manually invoke storage and result publishing
|
||||
realtimeSamplesSupport.triggerCurrentSample();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,11 +936,76 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("realtime steps: " + steps);
|
||||
}
|
||||
getRealtimeSamplesSupport().setSteps(steps);
|
||||
}
|
||||
|
||||
private void enableRealtimeSamplesTimer(boolean enable) {
|
||||
if (enable) {
|
||||
getRealtimeSamplesSupport().start();
|
||||
} else {
|
||||
if (realtimeSamplesSupport != null) {
|
||||
realtimeSamplesSupport.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MiBandActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) {
|
||||
MiBandActivitySample sample = new MiBandActivitySample();
|
||||
sample.setDevice(device);
|
||||
sample.setUser(user);
|
||||
sample.setTimestamp(timestampInSeconds);
|
||||
sample.setProvider(provider);
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
private RealtimeSamplesSupport getRealtimeSamplesSupport() {
|
||||
if (realtimeSamplesSupport == null) {
|
||||
realtimeSamplesSupport = new RealtimeSamplesSupport(1000, 1000) {
|
||||
@Override
|
||||
public void doCurrentSample() {
|
||||
|
||||
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||
DaoSession session = handler.getDaoSession();
|
||||
|
||||
Device device = DBHelper.getDevice(getDevice(), session);
|
||||
User user = DBHelper.getUser(session);
|
||||
int ts = (int) (System.currentTimeMillis() / 1000);
|
||||
MiBandSampleProvider provider = new MiBandSampleProvider(gbDevice, session);
|
||||
MiBandActivitySample sample = createActivitySample(device, user, ts, provider);
|
||||
sample.setHeartRate(getHeartrateBpm());
|
||||
sample.setSteps(getSteps());
|
||||
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||
sample.setRawKind(MiBandSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that?
|
||||
|
||||
// TODO: remove this once fully ported to REALTIME_SAMPLES
|
||||
if (sample.getSteps() != ActivitySample.NOT_MEASURED) {
|
||||
Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS)
|
||||
.putExtra(DeviceService.EXTRA_REALTIME_STEPS, steps)
|
||||
.putExtra(DeviceService.EXTRA_REALTIME_STEPS, sample.getSteps())
|
||||
.putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
}
|
||||
if (sample.getHeartRate() != ActivitySample.NOT_MEASURED) {
|
||||
Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT)
|
||||
.putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, sample.getHeartRate())
|
||||
.putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
// Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
|
||||
// .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample);
|
||||
// LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
|
||||
LOG.debug("Storing realtime sample: " + sample);
|
||||
provider.addGBActivitySample(sample);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Unable to acquire db for saving realtime samples", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return realtimeSamplesSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* React to unsolicited messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION
|
||||
|
@ -0,0 +1,86 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
|
||||
/**
|
||||
* Basic support for aggregating different sources of realtime data that comes in in a mostly
|
||||
* fixed interval. The aggregated data will be stored together.
|
||||
*
|
||||
* start() and stop() may be called multiple times, but the first stop() call will really
|
||||
* stop the timer.
|
||||
* manner.
|
||||
*
|
||||
* Subclasses must implement #doCurrentSample() and should override #resetCurrentValues()
|
||||
* (but call super!).
|
||||
*/
|
||||
public abstract class RealtimeSamplesSupport {
|
||||
private final long delay;
|
||||
private final long period;
|
||||
|
||||
protected int steps;
|
||||
protected int heartrateBpm;
|
||||
// subclasses may add more
|
||||
|
||||
private Timer realtimeStorageTimer;
|
||||
|
||||
public RealtimeSamplesSupport(long delay, long period) {
|
||||
this.delay = delay;
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
if (isRunning()) {
|
||||
return; // already running
|
||||
}
|
||||
realtimeStorageTimer = new Timer("Mi Band Realtime Storage Timer");
|
||||
realtimeStorageTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
triggerCurrentSample();
|
||||
}
|
||||
}, delay, period);
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
if (realtimeStorageTimer != null) {
|
||||
realtimeStorageTimer.cancel();
|
||||
realtimeStorageTimer.purge();
|
||||
realtimeStorageTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isRunning() {
|
||||
return realtimeStorageTimer != null;
|
||||
}
|
||||
|
||||
public void setSteps(int stepsPerMinute) {
|
||||
this.steps = stepsPerMinute;
|
||||
}
|
||||
|
||||
public int getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
public void setHeartrateBpm(int hrBpm) {
|
||||
this.heartrateBpm = hrBpm;
|
||||
}
|
||||
|
||||
public int getHeartrateBpm() {
|
||||
return heartrateBpm;
|
||||
}
|
||||
|
||||
public void triggerCurrentSample() {
|
||||
doCurrentSample();
|
||||
resetCurrentValues();
|
||||
}
|
||||
|
||||
protected void resetCurrentValues() {
|
||||
steps = ActivitySample.NOT_MEASURED;
|
||||
heartrateBpm = ActivitySample.NOT_MEASURED;
|
||||
}
|
||||
|
||||
protected abstract void doCurrentSample();
|
||||
}
|
@ -22,7 +22,9 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
||||
@ -307,8 +309,8 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
||||
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()){
|
||||
MiBandSampleProvider provider = new MiBandSampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
User user = DBHelper.getUser(dbHandler.getDaoSession());
|
||||
Device device = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession());
|
||||
int minutes = 0;
|
||||
try {
|
||||
int timestampInSeconds = (int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000);
|
||||
@ -327,15 +329,12 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
||||
LOG.debug("heartrate received: " + (heartrate & 0xff));
|
||||
}
|
||||
|
||||
samples[minutes] = new MiBandActivitySample(
|
||||
timestampInSeconds,
|
||||
deviceId,
|
||||
userId,
|
||||
intensity & 0xff,
|
||||
steps & 0xff,
|
||||
category & 0xff,
|
||||
heartrate & 0xff);
|
||||
samples[minutes].setProvider(provider);
|
||||
MiBandActivitySample sample = getSupport().createActivitySample(device, user, timestampInSeconds, provider);
|
||||
sample.setRawIntensity(intensity & 0xff);
|
||||
sample.setSteps(steps & 0xff);
|
||||
sample.setRawKind(category & 0xff);
|
||||
sample.setHeartRate(heartrate & 0xff);
|
||||
samples[minutes] = sample;
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("sample: " + samples[minutes]);
|
||||
|
Loading…
Reference in New Issue
Block a user