mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-01-13 11:17:33 +01:00
Initial support for activity data sync with Mi 1S #205
Looks like the activity type is somehow wrong though, or I'm sleeping all day ;-)
This commit is contained in:
parent
defa97b882
commit
095ef56c14
@ -27,7 +27,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
||||||
@ -52,11 +51,13 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
|||||||
|
|
||||||
private final int activityMetadataLength = 11;
|
private final int activityMetadataLength = 11;
|
||||||
|
|
||||||
//temporary buffer, size is a multiple of 60 because we want to store complete minutes (1 minute = 3 bytes)
|
//temporary buffer, size is a multiple of 60 because we want to store complete minutes (1 minute = 3 or 4 bytes)
|
||||||
private static final int activityDataHolderSize = 3 * 60 * 4; // 4h
|
private final int activityDataHolderSize;
|
||||||
|
private final boolean hasExtendedActivityData;
|
||||||
|
|
||||||
private static class ActivityStruct {
|
private static class ActivityStruct {
|
||||||
private final byte[] activityDataHolder = new byte[activityDataHolderSize];
|
private final byte[] activityDataHolder;
|
||||||
|
private final int activityDataHolderSize;
|
||||||
//index of the buffer above
|
//index of the buffer above
|
||||||
private int activityDataHolderProgress = 0;
|
private int activityDataHolderProgress = 0;
|
||||||
//number of bytes we will get in a single data transfer, used as counter
|
//number of bytes we will get in a single data transfer, used as counter
|
||||||
@ -68,6 +69,11 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
|||||||
//same as above, but remains untouched for the ack message
|
//same as above, but remains untouched for the ack message
|
||||||
private GregorianCalendar activityDataTimestampToAck = null;
|
private GregorianCalendar activityDataTimestampToAck = null;
|
||||||
|
|
||||||
|
ActivityStruct(int activityDataHolderSize) {
|
||||||
|
this.activityDataHolderSize = activityDataHolderSize;
|
||||||
|
activityDataHolder = new byte[activityDataHolderSize];
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasRoomFor(byte[] value) {
|
public boolean hasRoomFor(byte[] value) {
|
||||||
return activityDataRemainingBytes >= value.length;
|
return activityDataRemainingBytes >= value.length;
|
||||||
}
|
}
|
||||||
@ -127,10 +133,13 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActivityStruct activityStruct = new ActivityStruct();
|
private ActivityStruct activityStruct;
|
||||||
|
|
||||||
public FetchActivityOperation(MiBandSupport support) {
|
public FetchActivityOperation(MiBandSupport support) {
|
||||||
super(support);
|
super(support);
|
||||||
|
hasExtendedActivityData = support.getDeviceInfo().isMilli1S();
|
||||||
|
activityDataHolderSize = getBytesPerMinuteOfActivityData() * 60 * 4; // 4h
|
||||||
|
activityStruct = new ActivityStruct(activityDataHolderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -208,30 +217,34 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
|||||||
|
|
||||||
// counter of all data held by the band
|
// counter of all data held by the band
|
||||||
int totalDataToRead = (value[7] & 0xff) | ((value[8] & 0xff) << 8);
|
int totalDataToRead = (value[7] & 0xff) | ((value[8] & 0xff) << 8);
|
||||||
totalDataToRead *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? 3 : 1;
|
totalDataToRead *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? getBytesPerMinuteOfActivityData() : 1;
|
||||||
|
|
||||||
|
|
||||||
// counter of this data block
|
// counter of this data block
|
||||||
int dataUntilNextHeader = (value[9] & 0xff) | ((value[10] & 0xff) << 8);
|
int dataUntilNextHeader = (value[9] & 0xff) | ((value[10] & 0xff) << 8);
|
||||||
dataUntilNextHeader *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? 3 : 1;
|
dataUntilNextHeader *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? getBytesPerMinuteOfActivityData() : 1;
|
||||||
|
|
||||||
// there is a total of totalDataToRead that will come in chunks (3 bytes per minute if dataType == 1 (MiBandService.MODE_REGULAR_DATA_LEN_MINUTE)),
|
// there is a total of totalDataToRead that will come in chunks (3 or 4 bytes per minute if dataType == 1 (MiBandService.MODE_REGULAR_DATA_LEN_MINUTE)),
|
||||||
// these chunks are usually 20 bytes long and grouped in blocks
|
// these chunks are usually 20 bytes long and grouped in blocks
|
||||||
// after dataUntilNextHeader bytes we will get a new packet of 11 bytes that should be parsed
|
// after dataUntilNextHeader bytes we will get a new packet of 11 bytes that should be parsed
|
||||||
// as we just did
|
// as we just did
|
||||||
|
|
||||||
if (activityStruct.isFirstChunk() && dataUntilNextHeader != 0) {
|
if (activityStruct.isFirstChunk() && dataUntilNextHeader != 0) {
|
||||||
GB.toast(getContext().getString(R.string.user_feedback_miband_activity_data_transfer,
|
GB.toast(getContext().getString(R.string.user_feedback_miband_activity_data_transfer,
|
||||||
DateTimeUtils.formatDurationHoursMinutes((totalDataToRead / 3), TimeUnit.MINUTES),
|
DateTimeUtils.formatDurationHoursMinutes((totalDataToRead / getBytesPerMinuteOfActivityData()), TimeUnit.MINUTES),
|
||||||
DateFormat.getDateTimeInstance().format(timestamp.getTime())), Toast.LENGTH_LONG, GB.INFO);
|
DateFormat.getDateTimeInstance().format(timestamp.getTime())), Toast.LENGTH_LONG, GB.INFO);
|
||||||
}
|
}
|
||||||
LOG.info("total data to read: " + totalDataToRead + " len: " + (totalDataToRead / 3) + " minute(s)");
|
LOG.info("total data to read: " + totalDataToRead + " len: " + (totalDataToRead / getBytesPerMinuteOfActivityData()) + " minute(s)");
|
||||||
LOG.info("data to read until next header: " + dataUntilNextHeader + " len: " + (dataUntilNextHeader / 3) + " minute(s)");
|
LOG.info("data to read until next header: " + dataUntilNextHeader + " len: " + (dataUntilNextHeader / getBytesPerMinuteOfActivityData()) + " minute(s)");
|
||||||
LOG.info("TIMESTAMP: " + DateFormat.getDateTimeInstance().format(timestamp.getTime()) + " magic byte: " + dataUntilNextHeader);
|
LOG.info("TIMESTAMP: " + DateFormat.getDateTimeInstance().format(timestamp.getTime()) + " magic byte: " + dataUntilNextHeader);
|
||||||
|
|
||||||
activityStruct.startNewBlock(timestamp, dataUntilNextHeader);
|
activityStruct.startNewBlock(timestamp, dataUntilNextHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getBytesPerMinuteOfActivityData() {
|
||||||
|
return hasExtendedActivityData ? 4 : 3;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to store temporarily the activity data values got from the Mi Band.
|
* Method to store temporarily the activity data values got from the Mi Band.
|
||||||
* <p/>
|
* <p/>
|
||||||
@ -290,7 +303,8 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
|||||||
LOG.debug("nothing to flush, struct is already null");
|
LOG.debug("nothing to flush, struct is already null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG.debug("flushing activity data samples: " + activityStruct.activityDataHolderProgress / 3);
|
int bpm = getBytesPerMinuteOfActivityData();
|
||||||
|
LOG.debug("flushing activity data samples: " + activityStruct.activityDataHolderProgress / bpm);
|
||||||
byte category, intensity, steps;
|
byte category, intensity, steps;
|
||||||
|
|
||||||
DBHandler dbHandler = null;
|
DBHandler dbHandler = null;
|
||||||
@ -299,18 +313,19 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
|||||||
int minutes = 0;
|
int minutes = 0;
|
||||||
try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples
|
try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples
|
||||||
int timestampInSeconds = (int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000);
|
int timestampInSeconds = (int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000);
|
||||||
if ((activityStruct.activityDataHolderProgress % 3) != 0) {
|
if ((activityStruct.activityDataHolderProgress % bpm) != 0) {
|
||||||
throw new IllegalStateException("Unexpected data, progress should be mutiple of 3: " + activityStruct.activityDataHolderProgress);
|
throw new IllegalStateException("Unexpected data, progress should be mutiple of " + bpm +": " + activityStruct.activityDataHolderProgress);
|
||||||
}
|
}
|
||||||
int numSamples = activityStruct.activityDataHolderProgress/3;
|
int numSamples = activityStruct.activityDataHolderProgress / bpm;
|
||||||
ActivitySample[] samples = new ActivitySample[numSamples];
|
ActivitySample[] samples = new ActivitySample[numSamples];
|
||||||
SampleProvider sampleProvider = new MiBandSampleProvider();
|
SampleProvider sampleProvider = new MiBandSampleProvider();
|
||||||
int s = 0;
|
int s = 0;
|
||||||
|
|
||||||
for (int i = 0; i < activityStruct.activityDataHolderProgress; i += 3) {
|
for (int i = 0; i < activityStruct.activityDataHolderProgress; i += bpm) {
|
||||||
category = activityStruct.activityDataHolder[i];
|
category = activityStruct.activityDataHolder[i];
|
||||||
intensity = activityStruct.activityDataHolder[i + 1];
|
intensity = activityStruct.activityDataHolder[i + 1];
|
||||||
steps = activityStruct.activityDataHolder[i + 2];
|
steps = activityStruct.activityDataHolder[i + 2];
|
||||||
|
byte unknown = activityStruct.activityDataHolder[i + 3];
|
||||||
|
|
||||||
samples[minutes] = new GBActivitySample(
|
samples[minutes] = new GBActivitySample(
|
||||||
sampleProvider,
|
sampleProvider,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user