mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-25 18:15:49 +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.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,
|
||||
|
Loading…
Reference in New Issue
Block a user