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:
cpfeiffer 2016-02-25 23:52:34 +01:00
parent defa97b882
commit 095ef56c14
1 changed files with 31 additions and 16 deletions

View File

@ -27,7 +27,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample;
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.actions.SetDeviceBusyAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
@ -52,11 +51,13 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
private final int activityMetadataLength = 11;
//temporary buffer, size is a multiple of 60 because we want to store complete minutes (1 minute = 3 bytes)
private static final int activityDataHolderSize = 3 * 60 * 4; // 4h
//temporary buffer, size is a multiple of 60 because we want to store complete minutes (1 minute = 3 or 4 bytes)
private final int activityDataHolderSize;
private final boolean hasExtendedActivityData;
private static class ActivityStruct {
private final byte[] activityDataHolder = new byte[activityDataHolderSize];
private final byte[] activityDataHolder;
private final int activityDataHolderSize;
//index of the buffer above
private int activityDataHolderProgress = 0;
//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
private GregorianCalendar activityDataTimestampToAck = null;
ActivityStruct(int activityDataHolderSize) {
this.activityDataHolderSize = activityDataHolderSize;
activityDataHolder = new byte[activityDataHolderSize];
}
public boolean hasRoomFor(byte[] value) {
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) {
super(support);
hasExtendedActivityData = support.getDeviceInfo().isMilli1S();
activityDataHolderSize = getBytesPerMinuteOfActivityData() * 60 * 4; // 4h
activityStruct = new ActivityStruct(activityDataHolderSize);
}
@Override
@ -208,30 +217,34 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
// counter of all data held by the band
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
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
// after dataUntilNextHeader bytes we will get a new packet of 11 bytes that should be parsed
// as we just did
if (activityStruct.isFirstChunk() && dataUntilNextHeader != 0) {
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);
}
LOG.info("total data to read: " + totalDataToRead + " len: " + (totalDataToRead / 3) + " minute(s)");
LOG.info("data to read until next header: " + dataUntilNextHeader + " len: " + (dataUntilNextHeader / 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 / getBytesPerMinuteOfActivityData()) + " minute(s)");
LOG.info("TIMESTAMP: " + DateFormat.getDateTimeInstance().format(timestamp.getTime()) + " magic byte: " + 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.
* <p/>
@ -290,7 +303,8 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
LOG.debug("nothing to flush, struct is already null");
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;
DBHandler dbHandler = null;
@ -299,18 +313,19 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
int minutes = 0;
try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples
int timestampInSeconds = (int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000);
if ((activityStruct.activityDataHolderProgress % 3) != 0) {
throw new IllegalStateException("Unexpected data, progress should be mutiple of 3: " + activityStruct.activityDataHolderProgress);
if ((activityStruct.activityDataHolderProgress % bpm) != 0) {
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];
SampleProvider sampleProvider = new MiBandSampleProvider();
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];
intensity = activityStruct.activityDataHolder[i + 1];
steps = activityStruct.activityDataHolder[i + 2];
byte unknown = activityStruct.activityDataHolder[i + 3];
samples[minutes] = new GBActivitySample(
sampleProvider,