mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-28 12:56:49 +01:00
Huami devices: Fix seldom fetch failures (better support for app level ble feedback)
Fixes #1264
This commit is contained in:
parent
b6d68207cb
commit
d6f5e36e12
@ -108,6 +108,16 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLEDeviceSupport>
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionBuilder createTransactionBuilder(String taskName) {
|
||||||
|
TransactionBuilder builder = getSupport().createTransactionBuilder(taskName);
|
||||||
|
builder.setGattCallback(this);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performImmediately(TransactionBuilder builder) throws IOException {
|
||||||
|
mSupport.performImmediately(builder);
|
||||||
|
}
|
||||||
|
|
||||||
protected Context getContext() {
|
protected Context getContext() {
|
||||||
return mSupport.getContext();
|
return mSupport.getContext();
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,11 @@ public final class BtLEQueue {
|
|||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("About to run action: " + action);
|
LOG.debug("About to run action: " + action);
|
||||||
}
|
}
|
||||||
|
if (action instanceof GattListenerAction) {
|
||||||
|
// this special action overwrites the transaction gatt listener (if any), it must
|
||||||
|
// always be the last action in the transaction
|
||||||
|
internalGattCallback.setTransactionGattCallback(((GattListenerAction)action).getGattCallback());
|
||||||
|
}
|
||||||
if (action.run(mBluetoothGatt)) {
|
if (action.run(mBluetoothGatt)) {
|
||||||
// check again, maybe due to some condition, action did not need to write, so we can't wait
|
// check again, maybe due to some condition, action did not need to write, so we can't wait
|
||||||
boolean waitForResult = action.expectsResult();
|
boolean waitForResult = action.expectsResult();
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
||||||
|
|
||||||
|
public interface GattListenerAction {
|
||||||
|
GattCallback getGattCallback();
|
||||||
|
}
|
@ -110,4 +110,8 @@ public class TransactionBuilder {
|
|||||||
public Transaction getTransaction() {
|
public Transaction getTransaction() {
|
||||||
return mTransaction;
|
return mTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getTaskName() {
|
||||||
|
return mTransaction.getTaskName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractGattCallback;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattListenerAction;
|
||||||
|
|
||||||
|
public abstract class AbstractGattListenerWriteAction extends WriteAction implements GattListenerAction {
|
||||||
|
private final BtLEQueue queue;
|
||||||
|
|
||||||
|
public AbstractGattListenerWriteAction(BtLEQueue queue, BluetoothGattCharacteristic characteristic, byte[] value) {
|
||||||
|
super(characteristic, value);
|
||||||
|
this.queue = queue;
|
||||||
|
Objects.requireNonNull(queue, "queue must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GattCallback getGattCallback() {
|
||||||
|
return new AbstractGattCallback() {
|
||||||
|
@Override
|
||||||
|
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
return AbstractGattListenerWriteAction.this.onCharacteristicChanged(gatt, characteristic);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
|
||||||
|
}
|
@ -18,6 +18,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action that will cause the queue to sleep for the specified time.
|
||||||
|
* Note that this is usually a bad idea, since it will not be able to process messages
|
||||||
|
* during that time. It is also likely to cause race conditions.
|
||||||
|
*/
|
||||||
public class WaitAction extends PlainAction {
|
public class WaitAction extends PlainAction {
|
||||||
|
|
||||||
private final int mMillis;
|
private final int mMillis;
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -30,17 +32,21 @@ import java.util.Calendar;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||||
|
|
||||||
public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
|
public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipFetchLogsOperation.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipFetchLogsOperation.class);
|
||||||
@ -72,15 +78,38 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String taskName = StringUtils.ensureNotNull(builder.getTaskName());
|
||||||
GregorianCalendar sinceWhen = BLETypeConversions.createCalendar();
|
GregorianCalendar sinceWhen = BLETypeConversions.createCalendar();
|
||||||
sinceWhen.add(Calendar.DAY_OF_MONTH, -10);
|
sinceWhen.add(Calendar.DAY_OF_MONTH, -10);
|
||||||
builder.write(characteristicFetch, BLETypeConversions.join(new byte[]{
|
byte[] fetchBytes = BLETypeConversions.join(new byte[]{
|
||||||
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
|
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
|
||||||
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_DEBUGLOGS},
|
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_DEBUGLOGS},
|
||||||
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
|
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES));
|
||||||
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
|
builder.add(new AbstractGattListenerWriteAction(getQueue(), characteristicFetch, fetchBytes) {
|
||||||
builder.notify(characteristicActivityData, true);
|
@Override
|
||||||
builder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
|
protected boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
UUID characteristicUUID = characteristic.getUuid();
|
||||||
|
if (HuamiService.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) {
|
||||||
|
byte[] value = characteristic.getValue();
|
||||||
|
|
||||||
|
if (ArrayUtils.equals(value, HuamiService.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) {
|
||||||
|
handleActivityMetadata(value);
|
||||||
|
TransactionBuilder newBuilder = createTransactionBuilder(taskName + " Step 2");
|
||||||
|
newBuilder.notify(characteristicActivityData, true);
|
||||||
|
newBuilder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
|
||||||
|
try {
|
||||||
|
performImmediately(newBuilder);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
GB.toast(getContext(), "Error fetching debug logs: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
handleActivityMetadata(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -24,9 +26,11 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
@ -34,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||||
@ -41,10 +46,14 @@ import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An operation that fetches activity data. For every fetch, a new operation must
|
* An operation that fetches activity data. For every fetch, a new operation must
|
||||||
@ -68,11 +77,34 @@ public class FetchActivityOperation extends AbstractFetchOperation {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startFetching(TransactionBuilder builder) {
|
protected void startFetching(TransactionBuilder builder) {
|
||||||
|
final String taskName = StringUtils.ensureNotNull(builder.getTaskName());
|
||||||
GregorianCalendar sinceWhen = getLastSuccessfulSyncTime();
|
GregorianCalendar sinceWhen = getLastSuccessfulSyncTime();
|
||||||
builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { HuamiService.COMMAND_ACTIVITY_DATA_START_DATE, HuamiService.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
|
byte[] fetchBytes = BLETypeConversions.join(new byte[] { HuamiService.COMMAND_ACTIVITY_DATA_START_DATE, HuamiService.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES));
|
||||||
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
|
builder.add(new AbstractGattListenerWriteAction(getQueue(), characteristicFetch, fetchBytes) {
|
||||||
builder.notify(characteristicActivityData, true);
|
@Override
|
||||||
builder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA});
|
protected boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
UUID characteristicUUID = characteristic.getUuid();
|
||||||
|
if (HuamiService.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) {
|
||||||
|
byte[] value = characteristic.getValue();
|
||||||
|
|
||||||
|
if (ArrayUtils.equals(value, HuamiService.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) {
|
||||||
|
handleActivityMetadata(value);
|
||||||
|
TransactionBuilder newBuilder = createTransactionBuilder(taskName + " Step 2");
|
||||||
|
newBuilder.notify(characteristicActivityData, true);
|
||||||
|
newBuilder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA});
|
||||||
|
try {
|
||||||
|
performImmediately(newBuilder);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
GB.toast(getContext(), "Error fetching activity data: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
handleActivityMetadata(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleActivityFetchFinish(boolean success) {
|
protected void handleActivityFetchFinish(boolean success) {
|
||||||
|
@ -137,12 +137,6 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransactionBuilder createTransactionBuilder(String task) {
|
|
||||||
TransactionBuilder builder = getSupport().createTransactionBuilder(task);
|
|
||||||
builder.setGattCallback(this);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException {
|
private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException {
|
||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||||
return md5.digest(message);
|
return md5.digest(message);
|
||||||
|
Loading…
Reference in New Issue
Block a user