mirror of
https://github.com/oliexdev/openScale.git
synced 2025-09-01 20:33:31 +02:00
Replaced Bluetooth backend to Blessed android library (#469)
* replaced RxAndroidBlw with blesses-android library as Bluetooth backend * remove license commands * uncomment test debug devices * use blessed-android library for discovering Bluetooth devices * add support for Bluetooth Standard Weight Profile scales (e.g. Beurer BF600 and Beurer B850 scales) * stop scan on Bluetooth discovery * upgrade Blesses-Android to 0.6
This commit is contained in:
@@ -5,7 +5,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.health.openscale"
|
applicationId "com.health.openscale"
|
||||||
testApplicationId "com.health.openscale.test"
|
testApplicationId "com.health.openscale.test"
|
||||||
minSdkVersion 19
|
minSdkVersion 21
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 44
|
versionCode 44
|
||||||
versionName "2.0.3"
|
versionName "2.0.3"
|
||||||
@@ -59,7 +59,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.material:material:1.1.0-alpha04'
|
implementation 'com.google.android.material:material:1.1.0-alpha07'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||||
@@ -67,27 +67,24 @@ dependencies {
|
|||||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||||
// Simple CSV
|
// Simple CSV
|
||||||
implementation 'com.j256.simplecsv:simplecsv:2.3'
|
implementation 'com.j256.simplecsv:simplecsv:2.3'
|
||||||
// RxAndroidBle
|
// Blessed Android
|
||||||
implementation 'com.polidea.rxandroidble2:rxandroidble:1.8.2'
|
implementation 'com.github.weliem:blessed-android:0.6'
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.7'
|
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
|
||||||
implementation 'com.jakewharton.rx2:replaying-share:2.1.0'
|
|
||||||
// CustomActivityOnCrash
|
// CustomActivityOnCrash
|
||||||
implementation 'cat.ereza:customactivityoncrash:2.2.0'
|
implementation 'cat.ereza:customactivityoncrash:2.2.0'
|
||||||
// Room
|
// Room
|
||||||
implementation 'androidx.room:room-runtime:2.1.0-alpha04'
|
implementation 'androidx.room:room-runtime:2.1.0-rc01'
|
||||||
annotationProcessor 'androidx.room:room-compiler:2.1.0-alpha04'
|
annotationProcessor 'androidx.room:room-compiler:2.1.0-rc01'
|
||||||
androidTestImplementation 'androidx.room:room-testing:2.1.0-alpha04'
|
androidTestImplementation 'androidx.room:room-testing:2.1.0-rc01'
|
||||||
// Timber
|
// Timber
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
// Local unit tests
|
// Local unit tests
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
// Instrumented unit tests
|
// Instrumented unit tests
|
||||||
androidTestImplementation 'androidx.annotation:annotation:1.0.0'
|
androidTestImplementation 'androidx.annotation:annotation:1.0.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
androidTestImplementation 'androidx.test:rules:1.1.1'
|
androidTestImplementation 'androidx.test:rules:1.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test) {
|
tasks.withType(Test) {
|
||||||
|
@@ -57,8 +57,6 @@ import com.health.openscale.gui.views.LBMMeasurementView;
|
|||||||
import com.health.openscale.gui.views.MeasurementViewSettings;
|
import com.health.openscale.gui.views.MeasurementViewSettings;
|
||||||
import com.health.openscale.gui.views.WaterMeasurementView;
|
import com.health.openscale.gui.views.WaterMeasurementView;
|
||||||
import com.health.openscale.gui.widget.WidgetProvider;
|
import com.health.openscale.gui.widget.WidgetProvider;
|
||||||
import com.polidea.rxandroidble2.RxBleClient;
|
|
||||||
import com.polidea.rxandroidble2.internal.RxBleLog;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -92,7 +90,6 @@ public class OpenScale {
|
|||||||
private List<ScaleMeasurement> scaleMeasurementList;
|
private List<ScaleMeasurement> scaleMeasurementList;
|
||||||
|
|
||||||
private BluetoothCommunication btDeviceDriver;
|
private BluetoothCommunication btDeviceDriver;
|
||||||
private RxBleClient bleClient;
|
|
||||||
private AlarmHandler alarmHandler;
|
private AlarmHandler alarmHandler;
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
@@ -104,10 +101,6 @@ public class OpenScale {
|
|||||||
alarmHandler = new AlarmHandler();
|
alarmHandler = new AlarmHandler();
|
||||||
btDeviceDriver = null;
|
btDeviceDriver = null;
|
||||||
fragmentList = new ArrayList<>();
|
fragmentList = new ArrayList<>();
|
||||||
bleClient = RxBleClient.create(context);
|
|
||||||
|
|
||||||
RxBleClient.setLogLevel(RxBleLog.VERBOSE);
|
|
||||||
RxBleLog.setLogger((level, tag, msg) -> Timber.tag(tag).log(level, msg));
|
|
||||||
|
|
||||||
reopenDatabase(false);
|
reopenDatabase(false);
|
||||||
|
|
||||||
@@ -130,10 +123,6 @@ public class OpenScale {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RxBleClient getBleClient() {
|
|
||||||
return bleClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reopenDatabase(boolean truncate) throws SQLiteDatabaseCorruptException {
|
public void reopenDatabase(boolean truncate) throws SQLiteDatabaseCorruptException {
|
||||||
if (appDB != null) {
|
if (appDB != null) {
|
||||||
appDB.close();
|
appDB.close();
|
||||||
|
@@ -172,7 +172,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
|||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
// Setup notification
|
// Setup notification
|
||||||
setNotificationOn(CUSTOM_CHARACTERISTIC_WEIGHT);
|
setNotificationOn(CUSTOM_SERVICE_1, CUSTOM_CHARACTERISTIC_WEIGHT);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// Say "Hello" to the scale and wait for ack
|
// Say "Hello" to the scale and wait for ack
|
||||||
@@ -566,7 +566,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeBytes(byte[] data) {
|
private void writeBytes(byte[] data) {
|
||||||
writeBytes(CUSTOM_CHARACTERISTIC_WEIGHT, data);
|
writeBytes(CUSTOM_SERVICE_1, CUSTOM_CHARACTERISTIC_WEIGHT, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendCommand(byte command, byte... parameters) {
|
private void sendCommand(byte command, byte... parameters) {
|
||||||
|
@@ -17,39 +17,29 @@
|
|||||||
package com.health.openscale.core.bluetooth;
|
package com.health.openscale.core.bluetooth;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.bluetooth.le.ScanResult;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import com.health.openscale.core.OpenScale;
|
|
||||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
|
||||||
import com.jakewharton.rx.ReplayingShare;
|
|
||||||
import com.polidea.rxandroidble2.RxBleClient;
|
|
||||||
import com.polidea.rxandroidble2.RxBleConnection;
|
|
||||||
import com.polidea.rxandroidble2.RxBleDevice;
|
|
||||||
import com.polidea.rxandroidble2.RxBleDeviceServices;
|
|
||||||
import com.polidea.rxandroidble2.exceptions.BleException;
|
|
||||||
import com.polidea.rxandroidble2.scan.ScanSettings;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.Single;
|
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import com.welie.blessed.BluetoothCentral;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import com.welie.blessed.BluetoothCentralCallback;
|
||||||
import io.reactivex.disposables.Disposable;
|
import com.welie.blessed.BluetoothPeripheral;
|
||||||
import io.reactivex.exceptions.UndeliverableException;
|
import com.welie.blessed.BluetoothPeripheralCallback;
|
||||||
import io.reactivex.plugins.RxJavaPlugins;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import java.util.UUID;
|
||||||
import io.reactivex.subjects.PublishSubject;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static android.bluetooth.BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
|
||||||
import static android.content.Context.LOCATION_SERVICE;
|
import static android.content.Context.LOCATION_SERVICE;
|
||||||
|
import static com.welie.blessed.BluetoothPeripheral.GATT_SUCCESS;
|
||||||
|
|
||||||
public abstract class BluetoothCommunication {
|
public abstract class BluetoothCommunication {
|
||||||
public enum BT_STATUS {
|
public enum BT_STATUS {
|
||||||
@@ -69,55 +59,19 @@ public abstract class BluetoothCommunication {
|
|||||||
|
|
||||||
protected Context context;
|
protected Context context;
|
||||||
|
|
||||||
private final int BT_RETRY_TIMES_ON_ERROR = 3;
|
|
||||||
private final int BT_DELAY_MS = 10;
|
|
||||||
|
|
||||||
private RxBleClient bleClient;
|
|
||||||
private RxBleDevice bleDevice;
|
|
||||||
private Observable<RxBleConnection> connectionObservable;
|
|
||||||
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
|
||||||
private Disposable scanSubscription;
|
|
||||||
private PublishSubject<Boolean> disconnectTriggerSubject = PublishSubject.create();
|
|
||||||
|
|
||||||
private Handler callbackBtHandler;
|
private Handler callbackBtHandler;
|
||||||
private Handler disconnectHandler;
|
private Handler disconnectHandler;
|
||||||
|
|
||||||
|
private BluetoothCentral central;
|
||||||
|
private BluetoothPeripheral btPeripheral;
|
||||||
|
|
||||||
public BluetoothCommunication(Context context)
|
public BluetoothCommunication(Context context)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.bleClient = OpenScale.getInstance().getBleClient();
|
|
||||||
this.scanSubscription = null;
|
|
||||||
this.disconnectHandler = new Handler();
|
this.disconnectHandler = new Handler();
|
||||||
this.stepNr = 0;
|
this.stepNr = 0;
|
||||||
this.stopped = false;
|
this.stopped = false;
|
||||||
|
this.central = new BluetoothCentral(context, bluetoothCentralCallback, new Handler(Looper.getMainLooper()));
|
||||||
RxJavaPlugins.setErrorHandler(e -> {
|
|
||||||
if (e instanceof UndeliverableException && e.getCause() instanceof BleException) {
|
|
||||||
return; // ignore BleExceptions as they were surely delivered at least once
|
|
||||||
}
|
|
||||||
if (e instanceof UndeliverableException) {
|
|
||||||
onError(e);
|
|
||||||
}
|
|
||||||
if ((e instanceof IOException) || (e instanceof SocketException)) {
|
|
||||||
// fine, irrelevant network problem or API that throws on cancellation
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e instanceof InterruptedException) {
|
|
||||||
// fine, some blocking code was interrupted by a dispose call
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((e instanceof NullPointerException) || (e instanceof IllegalArgumentException)) {
|
|
||||||
// that's likely a bug in the application
|
|
||||||
onError(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e instanceof IllegalStateException) {
|
|
||||||
// that's a bug in RxJava or in a custom operator
|
|
||||||
onError(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onError(e);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,14 +145,6 @@ public abstract class BluetoothCommunication {
|
|||||||
*/
|
*/
|
||||||
abstract protected boolean onNextStep(int stepNr);
|
abstract protected boolean onNextStep(int stepNr);
|
||||||
|
|
||||||
/**
|
|
||||||
* Method is triggered if a Bluetooth data is read from a device.
|
|
||||||
*
|
|
||||||
* @param characteristic
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
protected void onBluetoothRead(UUID characteristic, byte[] value) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method is triggered if a Bluetooth data from a device is notified or indicated.
|
* Method is triggered if a Bluetooth data from a device is notified or indicated.
|
||||||
*
|
*
|
||||||
@@ -210,9 +156,9 @@ public abstract class BluetoothCommunication {
|
|||||||
/**
|
/**
|
||||||
* Method is triggered if a Bluetooth services from a device is discovered.
|
* Method is triggered if a Bluetooth services from a device is discovered.
|
||||||
*
|
*
|
||||||
* @param rxBleDeviceServices
|
* @param peripheral
|
||||||
*/
|
*/
|
||||||
protected void onBluetoothDiscovery(RxBleDeviceServices rxBleDeviceServices) { }
|
protected void onBluetoothDiscovery(BluetoothPeripheral peripheral) { }
|
||||||
|
|
||||||
protected synchronized void stopMachineState() {
|
protected synchronized void stopMachineState() {
|
||||||
Timber.d("Stop machine state");
|
Timber.d("Stop machine state");
|
||||||
@@ -235,26 +181,9 @@ public abstract class BluetoothCommunication {
|
|||||||
* @param characteristic the Bluetooth UUID characteristic
|
* @param characteristic the Bluetooth UUID characteristic
|
||||||
* @param bytes the bytes that should be write
|
* @param bytes the bytes that should be write
|
||||||
*/
|
*/
|
||||||
protected Observable<byte[]> writeBytes(UUID characteristic, byte[] bytes) {
|
protected void writeBytes(UUID service, UUID characteristic, byte[] bytes) {
|
||||||
Timber.d("Invoke write bytes [" + byteInHex(bytes) + "] on " + BluetoothGattUuid.prettyPrint(characteristic));
|
Timber.d("Invoke write bytes [" + byteInHex(bytes) + "] on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||||
Observable<byte[]> observable = connectionObservable
|
btPeripheral.writeCharacteristic(btPeripheral.getCharacteristic(service, characteristic), bytes, WRITE_TYPE_DEFAULT);
|
||||||
.flatMapSingle(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristic, bytes))
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.delay(BT_DELAY_MS, TimeUnit.MILLISECONDS)
|
|
||||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
|
||||||
|
|
||||||
compositeDisposable.add(observable.subscribe(
|
|
||||||
value -> {
|
|
||||||
Timber.d("Write characteristic %s: %s",
|
|
||||||
BluetoothGattUuid.prettyPrint(characteristic),
|
|
||||||
byteInHex(value));
|
|
||||||
},
|
|
||||||
throwable -> onError(throwable)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return observable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,26 +192,10 @@ public abstract class BluetoothCommunication {
|
|||||||
* @note onBluetoothRead() will be triggered if read command was successful. nextMachineStep() needs to manually called!
|
* @note onBluetoothRead() will be triggered if read command was successful. nextMachineStep() needs to manually called!
|
||||||
*@param characteristic the Bluetooth UUID characteristic
|
*@param characteristic the Bluetooth UUID characteristic
|
||||||
*/
|
*/
|
||||||
protected Single<byte[]> readBytes(UUID characteristic) {
|
void readBytes(UUID service, UUID characteristic) {
|
||||||
Timber.d("Invoke read bytes on " + BluetoothGattUuid.prettyPrint(characteristic));
|
Timber.d("Invoke read bytes on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||||
Single<byte[]> observable = connectionObservable
|
|
||||||
.firstOrError()
|
|
||||||
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristic))
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.delay(BT_DELAY_MS, TimeUnit.MILLISECONDS)
|
|
||||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
|
||||||
|
|
||||||
compositeDisposable.add(observable
|
btPeripheral.readCharacteristic(btPeripheral.getCharacteristic(service, characteristic));
|
||||||
.subscribe(bytes -> {
|
|
||||||
Timber.d("Read characteristic %s", BluetoothGattUuid.prettyPrint(characteristic));
|
|
||||||
onBluetoothRead(characteristic, bytes);
|
|
||||||
},
|
|
||||||
throwable -> onError(throwable)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return observable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,33 +203,13 @@ public abstract class BluetoothCommunication {
|
|||||||
*
|
*
|
||||||
* @param characteristic the Bluetooth UUID characteristic
|
* @param characteristic the Bluetooth UUID characteristic
|
||||||
*/
|
*/
|
||||||
protected Observable<byte[]> setIndicationOn(UUID characteristic) {
|
protected void setIndicationOn(UUID service, UUID characteristic) {
|
||||||
Timber.d("Invoke set indication on " + BluetoothGattUuid.prettyPrint(characteristic));
|
Timber.d("Invoke set indication on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||||
Observable<byte[]> observable = connectionObservable
|
if(btPeripheral.getService(service) != null) {
|
||||||
.flatMap(rxBleConnection -> rxBleConnection.setupIndication(characteristic))
|
stopMachineState();
|
||||||
.doOnNext(notificationObservable -> {
|
BluetoothGattCharacteristic currentTimeCharacteristic = btPeripheral.getCharacteristic(service, characteristic);
|
||||||
Timber.d("Successful set indication on for %s", BluetoothGattUuid.prettyPrint(characteristic));
|
btPeripheral.setNotify(currentTimeCharacteristic, true);
|
||||||
}
|
}
|
||||||
)
|
|
||||||
.flatMap(indicationObservable -> indicationObservable)
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.delay(BT_DELAY_MS, TimeUnit.MILLISECONDS)
|
|
||||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
|
||||||
|
|
||||||
compositeDisposable.add(observable.subscribe(
|
|
||||||
bytes -> {
|
|
||||||
Timber.d("onCharacteristicChanged %s: %s",
|
|
||||||
BluetoothGattUuid.prettyPrint(characteristic),
|
|
||||||
byteInHex(bytes));
|
|
||||||
onBluetoothNotify(characteristic, bytes);
|
|
||||||
resetDisconnectTimer();
|
|
||||||
},
|
|
||||||
throwable -> onError(throwable)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return observable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,56 +217,13 @@ public abstract class BluetoothCommunication {
|
|||||||
*
|
*
|
||||||
* @param characteristic the Bluetooth UUID characteristic
|
* @param characteristic the Bluetooth UUID characteristic
|
||||||
*/
|
*/
|
||||||
protected Observable<byte[]> setNotificationOn(UUID characteristic) {
|
protected void setNotificationOn(UUID service, UUID characteristic) {
|
||||||
Timber.d("Invoke set notification on " + BluetoothGattUuid.prettyPrint(characteristic));
|
Timber.d("Invoke set notification on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||||
stopped = true;
|
if(btPeripheral.getService(service) != null) {
|
||||||
Observable<byte[]> observable = connectionObservable
|
stopMachineState();
|
||||||
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristic))
|
BluetoothGattCharacteristic currentTimeCharacteristic = btPeripheral.getCharacteristic(service, characteristic);
|
||||||
.doOnNext(notificationObservable -> {
|
btPeripheral.setNotify(currentTimeCharacteristic, true);
|
||||||
Timber.d("Successful set notification on for %s", BluetoothGattUuid.prettyPrint(characteristic));
|
|
||||||
stopped = false;
|
|
||||||
nextMachineStep();
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
.flatMap(notificationObservable -> notificationObservable)
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.delay(BT_DELAY_MS, TimeUnit.MILLISECONDS)
|
|
||||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
|
||||||
|
|
||||||
compositeDisposable.add(observable.subscribe(
|
|
||||||
bytes -> {
|
|
||||||
Timber.d("onCharacteristicChanged %s: %s",
|
|
||||||
BluetoothGattUuid.prettyPrint(characteristic),
|
|
||||||
byteInHex(bytes));
|
|
||||||
onBluetoothNotify(characteristic, bytes);
|
|
||||||
resetDisconnectTimer();
|
|
||||||
},
|
|
||||||
throwable -> onError(throwable)
|
|
||||||
));
|
|
||||||
|
|
||||||
return observable;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Observable<RxBleDeviceServices> discoverBluetoothServices() {
|
|
||||||
Timber.d("Invoke discover Bluetooth services");
|
|
||||||
final Observable<RxBleDeviceServices> observable = connectionObservable
|
|
||||||
.flatMapSingle(RxBleConnection::discoverServices)
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.delay(BT_DELAY_MS, TimeUnit.MILLISECONDS)
|
|
||||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
|
||||||
|
|
||||||
compositeDisposable.add(observable.subscribe(
|
|
||||||
deviceServices -> {
|
|
||||||
Timber.d("Successful Bluetooth services discovered");
|
|
||||||
onBluetoothDiscovery(deviceServices);
|
|
||||||
},
|
|
||||||
throwable -> onError(throwable)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return observable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,13 +232,12 @@ public abstract class BluetoothCommunication {
|
|||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
Timber.d("Bluetooth disconnect");
|
Timber.d("Bluetooth disconnect");
|
||||||
setBluetoothStatus(BT_STATUS.CONNECTION_DISCONNECT);
|
setBluetoothStatus(BT_STATUS.CONNECTION_DISCONNECT);
|
||||||
if (scanSubscription != null) {
|
central.stopScan();
|
||||||
scanSubscription.dispose();
|
if (btPeripheral != null) {
|
||||||
|
central.cancelConnection(btPeripheral);
|
||||||
}
|
}
|
||||||
callbackBtHandler = null;
|
callbackBtHandler = null;
|
||||||
disconnectHandler.removeCallbacksAndMessages(null);
|
disconnectHandler.removeCallbacksAndMessages(null);
|
||||||
disconnectTriggerSubject.onNext(true);
|
|
||||||
compositeDisposable.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -452,6 +301,74 @@ public abstract class BluetoothCommunication {
|
|||||||
return (value & (1 << bit)) != 0;
|
return (value & (1 << bit)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final BluetoothPeripheralCallback peripheralCallback = new BluetoothPeripheralCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServicesDiscovered(BluetoothPeripheral peripheral) {
|
||||||
|
Timber.d("Successful Bluetooth services discovered");
|
||||||
|
onBluetoothDiscovery(peripheral);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNotificationStateUpdate(BluetoothPeripheral peripheral, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
if( status == GATT_SUCCESS) {
|
||||||
|
if(peripheral.isNotifying(characteristic)) {
|
||||||
|
Timber.d(String.format("SUCCESS: Notify set for %s", characteristic.getUuid()));
|
||||||
|
resumeMachineState();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.e(String.format("ERROR: Changing notification state failed for %s", characteristic.getUuid()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCharacteristicWrite(BluetoothPeripheral peripheral, byte[] value, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
if( status == GATT_SUCCESS) {
|
||||||
|
Timber.d(String.format("SUCCESS: Writing <%s> to <%s>", byteInHex(value), characteristic.getUuid().toString()));
|
||||||
|
nextMachineStep();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Timber.e(String.format("ERROR: Failed writing <%s> to <%s>", byteInHex(value), characteristic.getUuid().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCharacteristicUpdate(BluetoothPeripheral peripheral, byte[] value, BluetoothGattCharacteristic characteristic) {
|
||||||
|
resetDisconnectTimer();
|
||||||
|
onBluetoothNotify(characteristic.getUuid(), value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Callback for central
|
||||||
|
private final BluetoothCentralCallback bluetoothCentralCallback = new BluetoothCentralCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectedPeripheral(BluetoothPeripheral peripheral) {
|
||||||
|
Timber.d(String.format("connected to '%s'", peripheral.getName()));
|
||||||
|
setBluetoothStatus(BT_STATUS.CONNECTION_ESTABLISHED);
|
||||||
|
btPeripheral = peripheral;
|
||||||
|
nextMachineStep();
|
||||||
|
resetDisconnectTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionFailed(BluetoothPeripheral peripheral, final int status) {
|
||||||
|
Timber.e(String.format("connection '%s' failed with status %d", peripheral.getName(), status ));
|
||||||
|
setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnectedPeripheral(final BluetoothPeripheral peripheral, final int status) {
|
||||||
|
Timber.d(String.format("disconnected '%s' with status %d", peripheral.getName(), status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult) {
|
||||||
|
Timber.d(String.format("Found peripheral '%s'", peripheral.getName()));
|
||||||
|
central.stopScan();
|
||||||
|
connectToDevice(peripheral);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a Bluetooth device.
|
* Connect to a Bluetooth device.
|
||||||
*
|
*
|
||||||
@@ -461,8 +378,6 @@ public abstract class BluetoothCommunication {
|
|||||||
* @param macAddress the Bluetooth address to connect to
|
* @param macAddress the Bluetooth address to connect to
|
||||||
*/
|
*/
|
||||||
public void connect(String macAddress) {
|
public void connect(String macAddress) {
|
||||||
bleDevice = bleClient.getBleDevice(macAddress);
|
|
||||||
|
|
||||||
// Running an LE scan during connect improves connectivity on some phones
|
// Running an LE scan during connect improves connectivity on some phones
|
||||||
// (e.g. Sony Xperia Z5 compact, Android 7.1.1). For some scales (e.g. Medisana BS444)
|
// (e.g. Sony Xperia Z5 compact, Android 7.1.1). For some scales (e.g. Medisana BS444)
|
||||||
// it seems to be a requirement that the scale is discovered before connecting to it.
|
// it seems to be a requirement that the scale is discovered before connecting to it.
|
||||||
@@ -474,95 +389,30 @@ public abstract class BluetoothCommunication {
|
|||||||
|| locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
|
|| locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
|
||||||
) {
|
) {
|
||||||
Timber.d("Do LE scan before connecting to device");
|
Timber.d("Do LE scan before connecting to device");
|
||||||
disconnectWithDelay();
|
central.scanForPeripheralsWithAddresses(new String[]{macAddress});
|
||||||
scanSubscription = bleClient.scanBleDevices(
|
|
||||||
new ScanSettings.Builder()
|
|
||||||
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
|
||||||
//.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(bleScanResult -> {
|
|
||||||
if (bleScanResult.getBleDevice().getMacAddress().equals(macAddress)) {
|
|
||||||
connectToDevice(macAddress);
|
|
||||||
}}, throwable -> setBluetoothStatus(BT_STATUS.NO_DEVICE_FOUND));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Timber.d("No location permission, connecting without LE scan");
|
Timber.d("No location permission, connecting without LE scan");
|
||||||
connectToDevice(macAddress);
|
BluetoothPeripheral peripheral = central.getPeripheral(macAddress);
|
||||||
|
connectToDevice(peripheral);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectToDevice(String macAddress) {
|
private void connectToDevice(BluetoothPeripheral peripheral) {
|
||||||
|
|
||||||
// stop LE scan before connecting to device
|
|
||||||
if (scanSubscription != null) {
|
|
||||||
Timber.d("Stop Le san");
|
|
||||||
scanSubscription.dispose();
|
|
||||||
scanSubscription = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
handler.postDelayed(new Runnable() {
|
handler.postDelayed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Timber.d("Try to connect to BLE device " + macAddress);
|
Timber.d("Try to connect to BLE device " + peripheral.getAddress());
|
||||||
|
|
||||||
connectionObservable = bleDevice
|
|
||||||
.establishConnection(false)
|
|
||||||
.takeUntil(disconnectTriggerSubject)
|
|
||||||
.doOnError(throwable -> setBluetoothStatus(BT_STATUS.CONNECTION_RETRYING))
|
|
||||||
.subscribeOn(Schedulers.trampoline())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.compose(ReplayingShare.instance());
|
|
||||||
|
|
||||||
if (isConnected()) {
|
|
||||||
disconnect();
|
|
||||||
} else {
|
|
||||||
stepNr = 0;
|
stepNr = 0;
|
||||||
|
|
||||||
setBtMonitoringOn();
|
central.connectPeripheral(peripheral, peripheralCallback);
|
||||||
nextMachineStep();
|
|
||||||
resetDisconnectTimer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBtMonitoringOn() {
|
|
||||||
final Disposable disposableConnectionState = bleDevice.observeConnectionStateChanges()
|
|
||||||
.subscribe(
|
|
||||||
connectionState -> {
|
|
||||||
switch (connectionState) {
|
|
||||||
case CONNECTED:
|
|
||||||
setBluetoothStatus(BT_STATUS.CONNECTION_ESTABLISHED);
|
|
||||||
break;
|
|
||||||
case CONNECTING:
|
|
||||||
// empty
|
|
||||||
break;
|
|
||||||
case DISCONNECTING:
|
|
||||||
// empty
|
|
||||||
break;
|
|
||||||
case DISCONNECTED:
|
|
||||||
// setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
throwable -> onError(throwable)
|
|
||||||
);
|
|
||||||
|
|
||||||
compositeDisposable.add(disposableConnectionState);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onError(Throwable throwable) {
|
|
||||||
setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, throwable.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isConnected() {
|
|
||||||
return bleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetDisconnectTimer() {
|
private void resetDisconnectTimer() {
|
||||||
disconnectHandler.removeCallbacksAndMessages(null);
|
disconnectHandler.removeCallbacksAndMessages(null);
|
||||||
disconnectWithDelay();
|
disconnectWithDelay();
|
||||||
|
@@ -46,8 +46,7 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
|||||||
protected boolean onNextStep(int stepNr) {
|
protected boolean onNextStep(int stepNr) {
|
||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
@@ -60,7 +59,7 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
|||||||
cal.get(Calendar.MINUTE),
|
cal.get(Calendar.MINUTE),
|
||||||
cal.get(Calendar.SECOND));
|
cal.get(Calendar.SECOND));
|
||||||
|
|
||||||
writeBytes(WEIGHT_MEASUREMENT_CHARACTERISTIC, date_time.getBytes());
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, date_time.getBytes());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@@ -72,7 +71,7 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
|||||||
public void clearEEPROM()
|
public void clearEEPROM()
|
||||||
{
|
{
|
||||||
byte[] cmd = {(byte)'9'};
|
byte[] cmd = {(byte)'9'};
|
||||||
writeBytes(WEIGHT_MEASUREMENT_CHARACTERISTIC, cmd);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -21,15 +21,14 @@ import android.bluetooth.BluetoothGattDescriptor;
|
|||||||
import android.bluetooth.BluetoothGattService;
|
import android.bluetooth.BluetoothGattService;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.polidea.rxandroidble2.RxBleDeviceServices;
|
import com.welie.blessed.BluetoothPeripheral;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class BluetoothDebug extends BluetoothCommunication {
|
public class BluetoothDebug extends BluetoothCommunication {
|
||||||
private HashMap<Integer, String> propertyString;
|
HashMap<Integer, String> propertyString;
|
||||||
private RxBleDeviceServices rxBleDeviceServices;
|
|
||||||
|
|
||||||
BluetoothDebug(Context context) {
|
BluetoothDebug(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -140,7 +139,16 @@ public class BluetoothDebug extends BluetoothCommunication {
|
|||||||
&& !isBlacklisted(service, characteristic)) {
|
&& !isBlacklisted(service, characteristic)) {
|
||||||
|
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
readBytes(characteristic.getUuid());
|
readBytes(service.getUuid(), characteristic.getUuid());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
|
||||||
|
if (offset == 0) {
|
||||||
|
readBytes(service.getUuid(), characteristic.getUuid());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,39 +167,25 @@ public class BluetoothDebug extends BluetoothCommunication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBluetoothDiscovery(RxBleDeviceServices rxBleDeviceServices) {
|
protected void onBluetoothDiscovery(BluetoothPeripheral peripheral) {
|
||||||
this.rxBleDeviceServices = rxBleDeviceServices;
|
int offset = 0;
|
||||||
resumeMachineState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
for (BluetoothGattService service : peripheral.getServices()) {
|
||||||
protected boolean onNextStep(int stepNr) {
|
|
||||||
switch (stepNr)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
discoverBluetoothServices();
|
|
||||||
stopMachineState();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
int offset = stepNr;
|
|
||||||
|
|
||||||
for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) {
|
|
||||||
offset = readServiceCharacteristics(service, offset);
|
offset = readServiceCharacteristics(service, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) {
|
for (BluetoothGattService service : peripheral.getServices()) {
|
||||||
logService(service, false);
|
logService(service, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
disconnect();
|
disconnect();
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onNextStep(int stateNr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -60,7 +60,7 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
|
|||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
//Tell device to send us weight measurements
|
//Tell device to send us weight measurements
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
@@ -102,7 +102,7 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
|
|||||||
checksum += configBytes[i];
|
checksum += configBytes[i];
|
||||||
}
|
}
|
||||||
configBytes[15] = (byte)(checksum & 0xFF);
|
configBytes[15] = (byte)(checksum & 0xFF);
|
||||||
writeBytes(EXTRA_MEASUREMENT_CHARACTERISTIC, configBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, EXTRA_MEASUREMENT_CHARACTERISTIC, configBytes);
|
||||||
} else if (allValues) {
|
} else if (allValues) {
|
||||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||||
weight = (float) (((weightBytes[3] & 0xFF) << 8) | (weightBytes[4] & 0xFF)) / 100.0f;
|
weight = (float) (((weightBytes[3] & 0xFF) << 8) | (weightBytes[4] & 0xFF)) / 100.0f;
|
||||||
|
@@ -86,10 +86,10 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
|
|||||||
configBytes[configBytes.length - 1] =
|
configBytes[configBytes.length - 1] =
|
||||||
xorChecksum(configBytes, 1, configBytes.length - 2);
|
xorChecksum(configBytes, 1, configBytes.length - 2);
|
||||||
|
|
||||||
writeBytes(WEIGHT_MEASUREMENT_CHARACTERISTIC, configBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, configBytes);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
setNotificationOn(WEIGHT_CUSTOM0_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_CUSTOM0_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
|
@@ -45,7 +45,7 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
|
|||||||
protected boolean onNextStep(int stepNr) {
|
protected boolean onNextStep(int stepNr) {
|
||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
@@ -58,7 +58,7 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
|
|||||||
|
|
||||||
byte cmdByte[] = {(byte)0x10, (byte)userId, gender, age, height};
|
byte cmdByte[] = {(byte)0x10, (byte)userId, gender, age, height};
|
||||||
|
|
||||||
writeBytes(CMD_MEASUREMENT_CHARACTERISTIC, cmdByte);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, cmdByte);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
|
@@ -18,8 +18,6 @@ package com.health.openscale.core.bluetooth;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.polidea.rxandroidble2.RxBleClient;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class BluetoothFactory {
|
public class BluetoothFactory {
|
||||||
@@ -42,6 +40,14 @@ public class BluetoothFactory {
|
|||||||
|| name.equals("BF700".toLowerCase(Locale.US))) {
|
|| name.equals("BF700".toLowerCase(Locale.US))) {
|
||||||
return new BluetoothBeurerSanitas(context, BluetoothBeurerSanitas.DeviceType.BEURER_BF710);
|
return new BluetoothBeurerSanitas(context, BluetoothBeurerSanitas.DeviceType.BEURER_BF710);
|
||||||
}
|
}
|
||||||
|
if (name.startsWith("BEURER BF600".toLowerCase(Locale.US))
|
||||||
|
|| name.startsWith("BEURER BF850".toLowerCase(Locale.US))
|
||||||
|
|| name.startsWith("BF600".toLowerCase(Locale.US))
|
||||||
|
|| name.startsWith("BF850".toLowerCase(Locale.US))
|
||||||
|
|| name.startsWith("BF-600".toLowerCase(Locale.US))
|
||||||
|
|| name.startsWith("BF-850".toLowerCase(Locale.US))) {
|
||||||
|
return new BluetoothStandardWeightProfile(context);
|
||||||
|
}
|
||||||
if (name.equals("openScale".toLowerCase(Locale.US))) {
|
if (name.equals("openScale".toLowerCase(Locale.US))) {
|
||||||
return new BluetoothCustomOpenScale(context);
|
return new BluetoothCustomOpenScale(context);
|
||||||
}
|
}
|
||||||
|
@@ -66,6 +66,9 @@ public class BluetoothGattUuid {
|
|||||||
public static final UUID SERVICE_GENERIC_ACCESS = fromShortCode(0x1800);
|
public static final UUID SERVICE_GENERIC_ACCESS = fromShortCode(0x1800);
|
||||||
public static final UUID SERVICE_GENERIC_ATTRIBUTE = fromShortCode(0x1801);
|
public static final UUID SERVICE_GENERIC_ATTRIBUTE = fromShortCode(0x1801);
|
||||||
public static final UUID SERVICE_WEIGHT_SCALE = fromShortCode(0x181d);
|
public static final UUID SERVICE_WEIGHT_SCALE = fromShortCode(0x181d);
|
||||||
|
public static final UUID SERVICE_CURRENT_TIME = fromShortCode(0x1805);
|
||||||
|
public static final UUID SERVICE_USER_DATA = fromShortCode(0x181C);
|
||||||
|
public static final UUID SERVICE_BATTERY_LEVEL = fromShortCode(0x180F);
|
||||||
|
|
||||||
// https://www.bluetooth.com/specifications/gatt/characteristics
|
// https://www.bluetooth.com/specifications/gatt/characteristics
|
||||||
public static final UUID CHARACTERISTIC_APPEARANCE = fromShortCode(0x2a01);
|
public static final UUID CHARACTERISTIC_APPEARANCE = fromShortCode(0x2a01);
|
||||||
@@ -86,6 +89,9 @@ public class BluetoothGattUuid {
|
|||||||
public static final UUID CHARACTERISTIC_SOFTWARE_REVISION_STRING = fromShortCode(0x2a28);
|
public static final UUID CHARACTERISTIC_SOFTWARE_REVISION_STRING = fromShortCode(0x2a28);
|
||||||
public static final UUID CHARACTERISTIC_SYSTEM_ID = fromShortCode(0x2a23);
|
public static final UUID CHARACTERISTIC_SYSTEM_ID = fromShortCode(0x2a23);
|
||||||
public static final UUID CHARACTERISTIC_WEIGHT_MEASUREMENT = fromShortCode(0x2a9d);
|
public static final UUID CHARACTERISTIC_WEIGHT_MEASUREMENT = fromShortCode(0x2a9d);
|
||||||
|
public static final UUID CHARACTERISTIC_BATTERY_LEVEL = fromShortCode(0x2A19);
|
||||||
|
public static final UUID CHARACTERISTIC_CHANGE_INCREMENT = fromShortCode(0x2a99);
|
||||||
|
public static final UUID CHARACTERISTIC_USER_CONTROL_POINT = fromShortCode(0x2A9F);
|
||||||
|
|
||||||
// https://www.bluetooth.com/specifications/gatt/descriptors
|
// https://www.bluetooth.com/specifications/gatt/descriptors
|
||||||
public static final UUID DESCRIPTOR_CLIENT_CHARACTERISTIC_CONFIGURATION = fromShortCode(0x2902);
|
public static final UUID DESCRIPTOR_CLIENT_CHARACTERISTIC_CONFIGURATION = fromShortCode(0x2902);
|
||||||
|
@@ -42,11 +42,11 @@ public class BluetoothHesley extends BluetoothCommunication {
|
|||||||
protected boolean onNextStep(int stepNr) {
|
protected boolean onNextStep(int stepNr) {
|
||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
byte[] magicBytes = {(byte)0xa5, (byte)0x01, (byte)0x2c, (byte)0xab, (byte)0x50, (byte)0x5a, (byte)0x29};
|
byte[] magicBytes = {(byte)0xa5, (byte)0x01, (byte)0x2c, (byte)0xab, (byte)0x50, (byte)0x5a, (byte)0x29};
|
||||||
writeBytes(CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
|
@@ -60,7 +60,7 @@ public class BluetoothInlife extends BluetoothCommunication {
|
|||||||
data[i++] = parameter;
|
data[i++] = parameter;
|
||||||
}
|
}
|
||||||
data[data.length - 2] = xorChecksum(data, 1, data.length - 3);
|
data[data.length - 2] = xorChecksum(data, 1, data.length - 3);
|
||||||
writeBytes(WEIGHT_CMD_CHARACTERISTIC, data);
|
writeBytes(WEIGHT_SERVICE, WEIGHT_CMD_CHARACTERISTIC, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BluetoothInlife(Context context) {
|
public BluetoothInlife(Context context) {
|
||||||
@@ -76,7 +76,7 @@ public class BluetoothInlife extends BluetoothCommunication {
|
|||||||
protected boolean onNextStep(int stepNr) {
|
protected boolean onNextStep(int stepNr) {
|
||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
|
@@ -68,7 +68,7 @@ public class BluetoothMGB extends BluetoothCommunication {
|
|||||||
buf[6] = (byte)0xCC;
|
buf[6] = (byte)0xCC;
|
||||||
buf[7] = (byte)((buf[2] + buf[3] + buf[4] + buf[5] + buf[6]) & 0xFF);
|
buf[7] = (byte)((buf[2] + buf[3] + buf[4] + buf[5] + buf[6]) & 0xFF);
|
||||||
|
|
||||||
writeBytes(uuid_char_cfg, buf);
|
writeBytes(uuid_service, uuid_char_cfg, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -85,8 +85,7 @@ public class BluetoothMGB extends BluetoothCommunication {
|
|||||||
protected boolean onNextStep(int stepNr) {
|
protected boolean onNextStep(int stepNr) {
|
||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
setNotificationOn(uuid_char_ctrl
|
setNotificationOn(uuid_service, uuid_char_ctrl);
|
||||||
);
|
|
||||||
now = Calendar.getInstance();
|
now = Calendar.getInstance();
|
||||||
user = OpenScale.getInstance().getSelectedScaleUser();
|
user = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
break;
|
break;
|
||||||
|
@@ -55,15 +55,15 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
|
|||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
// set indication on for feature characteristic
|
// set indication on for feature characteristic
|
||||||
setIndicationOn(FEATURE_MEASUREMENT_CHARACTERISTIC);
|
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE, FEATURE_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// set indication on for weight measurement
|
// set indication on for weight measurement
|
||||||
setIndicationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// set indication on for custom5 measurement
|
// set indication on for custom5 measurement
|
||||||
setIndicationOn(CUSTOM5_MEASUREMENT_CHARACTERISTIC);
|
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE, CUSTOM5_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// send magic number to receive weight data
|
// send magic number to receive weight data
|
||||||
@@ -75,7 +75,7 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
|
|||||||
|
|
||||||
byte[] magicBytes = new byte[] {(byte)0x02, date[0], date[1], date[2], date[3]};
|
byte[] magicBytes = new byte[] {(byte)0x02, date[0], date[1], date[2], date[3]};
|
||||||
|
|
||||||
writeBytes(CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
|
@@ -38,6 +38,7 @@ import timber.log.Timber;
|
|||||||
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_STATUS.UNEXPECTED_ERROR;
|
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_STATUS.UNEXPECTED_ERROR;
|
||||||
|
|
||||||
public class BluetoothMiScale extends BluetoothCommunication {
|
public class BluetoothMiScale extends BluetoothCommunication {
|
||||||
|
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000181d-0000-1000-8000-00805f9b34fb");
|
||||||
private final UUID WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC = UUID.fromString("00002a2f-0000-3512-2118-0009af100700");
|
private final UUID WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC = UUID.fromString("00002a2f-0000-3512-2118-0009af100700");
|
||||||
|
|
||||||
public BluetoothMiScale(Context context) {
|
public BluetoothMiScale(Context context) {
|
||||||
@@ -49,12 +50,15 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
|||||||
return "Xiaomi Mi Scale v1";
|
return "Xiaomi Mi Scale v1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBluetoothRead(UUID characteristic, byte[] value) {
|
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||||
|
|
||||||
|
if (characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME)) {
|
||||||
byte[] data = value;
|
byte[] data = value;
|
||||||
|
|
||||||
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
|
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
|
||||||
int currentMonth = Calendar.getInstance().get(Calendar.MONTH)+1;
|
int currentMonth = Calendar.getInstance().get(Calendar.MONTH) + 1;
|
||||||
int currentDay = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
|
int currentDay = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
|
||||||
int scaleYear = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
|
int scaleYear = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
|
||||||
int scaleMonth = (int) data[2];
|
int scaleMonth = (int) data[2];
|
||||||
@@ -66,20 +70,17 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
|||||||
// set current time
|
// set current time
|
||||||
Calendar currentDateTime = Calendar.getInstance();
|
Calendar currentDateTime = Calendar.getInstance();
|
||||||
int year = currentDateTime.get(Calendar.YEAR);
|
int year = currentDateTime.get(Calendar.YEAR);
|
||||||
byte month = (byte)(currentDateTime.get(Calendar.MONTH)+1);
|
byte month = (byte) (currentDateTime.get(Calendar.MONTH) + 1);
|
||||||
byte day = (byte)currentDateTime.get(Calendar.DAY_OF_MONTH);
|
byte day = (byte) currentDateTime.get(Calendar.DAY_OF_MONTH);
|
||||||
byte hour = (byte)currentDateTime.get(Calendar.HOUR_OF_DAY);
|
byte hour = (byte) currentDateTime.get(Calendar.HOUR_OF_DAY);
|
||||||
byte min = (byte)currentDateTime.get(Calendar.MINUTE);
|
byte min = (byte) currentDateTime.get(Calendar.MINUTE);
|
||||||
byte sec = (byte)currentDateTime.get(Calendar.SECOND);
|
byte sec = (byte) currentDateTime.get(Calendar.SECOND);
|
||||||
|
|
||||||
byte[] dateTimeByte = {(byte)(year), (byte)(year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
|
byte[] dateTimeByte = {(byte) (year), (byte) (year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
|
||||||
|
|
||||||
writeBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
|
||||||
final byte[] data = value;
|
final byte[] data = value;
|
||||||
|
|
||||||
if (data != null && data.length > 0) {
|
if (data != null && data.length > 0) {
|
||||||
@@ -87,12 +88,12 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
|||||||
// Stop command from mi scale received
|
// Stop command from mi scale received
|
||||||
if (data[0] == 0x03) {
|
if (data[0] == 0x03) {
|
||||||
// send stop command to mi scale
|
// send stop command to mi scale
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
|
||||||
// acknowledge that you received the last history data
|
// acknowledge that you received the last history data
|
||||||
int uniqueNumber = getUniqueNumber();
|
int uniqueNumber = getUniqueNumber();
|
||||||
|
|
||||||
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
byte[] userIdentifier = new byte[]{(byte) 0x04, (byte) 0xFF, (byte) 0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
||||||
|
|
||||||
resumeMachineState();
|
resumeMachineState();
|
||||||
}
|
}
|
||||||
@@ -110,6 +111,7 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -117,32 +119,32 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
|||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
// read device time
|
// read device time
|
||||||
readBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME);
|
readBytes(WEIGHT_MEASUREMENT_SERVICE, BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// Set on history weight measurement
|
// Set on history weight measurement
|
||||||
byte[] magicBytes = new byte[]{(byte)0x01, (byte)0x96, (byte)0x8a, (byte)0xbd, (byte)0x62};
|
byte[] magicBytes = new byte[]{(byte)0x01, (byte)0x96, (byte)0x8a, (byte)0xbd, (byte)0x62};
|
||||||
|
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, magicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, magicBytes);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// set notification on for weight measurement history
|
// set notification on for weight measurement history
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// set notification on for weight measurement
|
// set notification on for weight measurement
|
||||||
setNotificationOn(BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
// configure scale to get only last measurements
|
// configure scale to get only last measurements
|
||||||
int uniqueNumber = getUniqueNumber();
|
int uniqueNumber = getUniqueNumber();
|
||||||
|
|
||||||
byte[] userIdentifier = new byte[]{(byte)0x01, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
byte[] userIdentifier = new byte[]{(byte)0x01, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
// invoke receiving history data
|
// invoke receiving history data
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
||||||
stopMachineState();
|
stopMachineState();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@@ -64,12 +64,12 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
if (data[0] == 0x03) {
|
if (data[0] == 0x03) {
|
||||||
Timber.d("Scale stop byte received");
|
Timber.d("Scale stop byte received");
|
||||||
// send stop command to mi scale
|
// send stop command to mi scale
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
|
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
|
||||||
// acknowledge that you received the last history data
|
// acknowledge that you received the last history data
|
||||||
int uniqueNumber = getUniqueNumber();
|
int uniqueNumber = getUniqueNumber();
|
||||||
|
|
||||||
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
||||||
|
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
// set scale units
|
// set scale units
|
||||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
byte[] setUnitCmd = new byte[]{(byte)0x06, (byte)0x04, (byte)0x00, (byte) selectedUser.getScaleUnit().toInt()};
|
byte[] setUnitCmd = new byte[]{(byte)0x06, (byte)0x04, (byte)0x00, (byte) selectedUser.getScaleUnit().toInt()};
|
||||||
writeBytes(WEIGHT_CUSTOM_CONFIG, setUnitCmd);
|
writeBytes(WEIGHT_CUSTOM_SERVICE, WEIGHT_CUSTOM_CONFIG, setUnitCmd);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// set current time
|
// set current time
|
||||||
@@ -112,22 +112,22 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
|
|
||||||
byte[] dateTimeByte = {(byte)(year), (byte)(year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
|
byte[] dateTimeByte = {(byte)(year), (byte)(year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
|
||||||
|
|
||||||
writeBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
|
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// set notification on for weight measurement history
|
// set notification on for weight measurement history
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
|
setNotificationOn(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// configure scale to get only last measurements
|
// configure scale to get only last measurements
|
||||||
int uniqueNumber = getUniqueNumber();
|
int uniqueNumber = getUniqueNumber();
|
||||||
|
|
||||||
byte[] userIdentifier = new byte[]{(byte)0x01, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
byte[] userIdentifier = new byte[]{(byte)0x01, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
// invoke receiving history data
|
// invoke receiving history data
|
||||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
||||||
stopMachineState();
|
stopMachineState();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@@ -50,7 +50,7 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
protected boolean onNextStep(int stepNr) {
|
protected boolean onNextStep(int stepNr) {
|
||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
|
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
@@ -69,7 +69,7 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
(byte)0x00, (byte)0x00, (byte)0x00};
|
(byte)0x00, (byte)0x00, (byte)0x00};
|
||||||
magicBytes[magicBytes.length - 1] =
|
magicBytes[magicBytes.length - 1] =
|
||||||
xorChecksum(magicBytes, 0, magicBytes.length - 1);
|
xorChecksum(magicBytes, 0, magicBytes.length - 1);
|
||||||
writeBytes(CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
|
@@ -88,11 +88,11 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
|||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
// set notification on for custom characteristic 1 (weight, time, and others)
|
// set notification on for custom characteristic 1 (weight, time, and others)
|
||||||
setNotificationOn(CUSTOM1_MEASUREMENT_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, CUSTOM1_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// set indication on for weight measurement
|
// set indication on for weight measurement
|
||||||
setIndicationOn(CUSTOM2_MEASUREMENT_CHARACTERISTIC);
|
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE, CUSTOM2_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
final ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
final ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
@@ -108,7 +108,7 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
|||||||
byte[] ffe3magicBytes = new byte[]{(byte) 0x13, (byte) 0x09, (byte) 0x15, weightUnitByte, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
|
byte[] ffe3magicBytes = new byte[]{(byte) 0x13, (byte) 0x09, (byte) 0x15, weightUnitByte, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
|
||||||
// Set last byte to be checksum
|
// Set last byte to be checksum
|
||||||
ffe3magicBytes[ffe3magicBytes.length -1] = sumChecksum(ffe3magicBytes, 0, ffe3magicBytes.length - 1);
|
ffe3magicBytes[ffe3magicBytes.length -1] = sumChecksum(ffe3magicBytes, 0, ffe3magicBytes.length - 1);
|
||||||
writeBytes(CUSTOM3_MEASUREMENT_CHARACTERISTIC, ffe3magicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CUSTOM3_MEASUREMENT_CHARACTERISTIC, ffe3magicBytes);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// send time magic number to receive weight data
|
// send time magic number to receive weight data
|
||||||
@@ -117,7 +117,7 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
|||||||
byte[] date = new byte[4];
|
byte[] date = new byte[4];
|
||||||
Converters.toInt32Le(date, 0, timestamp);
|
Converters.toInt32Le(date, 0, timestamp);
|
||||||
byte[] timeMagicBytes = new byte[]{(byte) 0x02, date[0], date[1], date[2], date[3]};
|
byte[] timeMagicBytes = new byte[]{(byte) 0x02, date[0], date[1], date[2], date[3]};
|
||||||
writeBytes(CUSTOM4_MEASUREMENT_CHARACTERISTIC, timeMagicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CUSTOM4_MEASUREMENT_CHARACTERISTIC, timeMagicBytes);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
|
@@ -16,14 +16,13 @@
|
|||||||
|
|
||||||
package com.health.openscale.core.bluetooth;
|
package com.health.openscale.core.bluetooth;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothGattService;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.health.openscale.R;
|
import com.health.openscale.R;
|
||||||
import com.health.openscale.core.OpenScale;
|
import com.health.openscale.core.OpenScale;
|
||||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||||
import com.health.openscale.core.datatypes.ScaleUser;
|
import com.health.openscale.core.datatypes.ScaleUser;
|
||||||
import com.polidea.rxandroidble2.RxBleDeviceServices;
|
import com.welie.blessed.BluetoothPeripheral;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -40,6 +39,7 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
|||||||
private final UUID MODEL_B_NOTIFICATION_CHARACTERISTIC = UUID.fromString("0000ffb2-0000-1000-8000-00805f9b34fb");
|
private final UUID MODEL_B_NOTIFICATION_CHARACTERISTIC = UUID.fromString("0000ffb2-0000-1000-8000-00805f9b34fb");
|
||||||
private final UUID MODEL_B_WRITE_CHARACTERISTIC = UUID.fromString("0000ffb2-0000-1000-8000-00805f9b34fb");
|
private final UUID MODEL_B_WRITE_CHARACTERISTIC = UUID.fromString("0000ffb2-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
|
private UUID writeService;
|
||||||
private UUID writeCharacteristic;
|
private UUID writeCharacteristic;
|
||||||
|
|
||||||
private int lastWeight, lastFat, lastHydration, lastMuscle, lastBone, lastKcal;
|
private int lastWeight, lastFat, lastHydration, lastMuscle, lastBone, lastKcal;
|
||||||
@@ -57,23 +57,21 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBluetoothDiscovery(RxBleDeviceServices rxBleDeviceServices) {
|
protected void onBluetoothDiscovery(BluetoothPeripheral peripheral) {
|
||||||
for (BluetoothGattService gattService : rxBleDeviceServices.getBluetoothGattServices()) {
|
|
||||||
if (gattService.getUuid().equals(MODEL_A_MEASUREMENT_SERVICE)) {
|
if (peripheral.getService(MODEL_A_MEASUREMENT_SERVICE) != null) {
|
||||||
|
writeService = MODEL_A_MEASUREMENT_SERVICE;
|
||||||
writeCharacteristic = MODEL_A_WRITE_CHARACTERISTIC;
|
writeCharacteristic = MODEL_A_WRITE_CHARACTERISTIC;
|
||||||
setNotificationOn(MODEL_A_NOTIFICATION_CHARACTERISTIC);
|
setNotificationOn(MODEL_A_MEASUREMENT_SERVICE, MODEL_A_NOTIFICATION_CHARACTERISTIC);
|
||||||
Timber.d("Found a Model A");
|
Timber.d("Found a Model A");
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (gattService.getUuid().equals(MODEL_B_MEASUREMENT_SERVICE)) {
|
|
||||||
writeCharacteristic = MODEL_B_WRITE_CHARACTERISTIC;
|
|
||||||
setNotificationOn(MODEL_B_NOTIFICATION_CHARACTERISTIC);
|
|
||||||
Timber.d("Found a Model B");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resumeMachineState();
|
if (peripheral.getService(MODEL_B_MEASUREMENT_SERVICE) != null) {
|
||||||
|
writeService = MODEL_B_MEASUREMENT_SERVICE;
|
||||||
|
writeCharacteristic = MODEL_B_WRITE_CHARACTERISTIC;
|
||||||
|
setNotificationOn(MODEL_B_MEASUREMENT_SERVICE, MODEL_B_NOTIFICATION_CHARACTERISTIC);
|
||||||
|
Timber.d("Found a Model B");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -83,14 +81,10 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
|||||||
weightStabilized = false;
|
weightStabilized = false;
|
||||||
stepMessageDisplayed = false;
|
stepMessageDisplayed = false;
|
||||||
values = 0;
|
values = 0;
|
||||||
discoverBluetoothServices();
|
|
||||||
stopMachineState();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
Timber.d("Sync Date");
|
Timber.d("Sync Date");
|
||||||
synchroniseDate();
|
synchroniseDate();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
Timber.d("Sync Time");
|
Timber.d("Sync Time");
|
||||||
synchroniseTime();
|
synchroniseTime();
|
||||||
break;
|
break;
|
||||||
@@ -202,7 +196,7 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
|||||||
|
|
||||||
addChecksum(message);
|
addChecksum(message);
|
||||||
|
|
||||||
writeBytes(writeCharacteristic, message);
|
writeBytes(writeService, writeCharacteristic, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void synchroniseTime() {
|
private void synchroniseTime() {
|
||||||
@@ -216,7 +210,7 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
|||||||
|
|
||||||
addChecksum(message);
|
addChecksum(message);
|
||||||
|
|
||||||
writeBytes(writeCharacteristic, message);
|
writeBytes(writeService, writeCharacteristic, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addChecksum(byte[] message) {
|
private void addChecksum(byte[] message) {
|
||||||
@@ -239,6 +233,6 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
|||||||
|
|
||||||
addChecksum(message);
|
addChecksum(message);
|
||||||
|
|
||||||
writeBytes(writeCharacteristic, message);
|
writeBytes(writeService, writeCharacteristic, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,303 @@
|
|||||||
|
/* Copyright (C) 2019 olie.xdev <olie.xdev@googlemail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on source-code by weliem/blessed-android
|
||||||
|
*/
|
||||||
|
package com.health.openscale.core.bluetooth;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.health.openscale.core.OpenScale;
|
||||||
|
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||||
|
import com.health.openscale.core.datatypes.ScaleUser;
|
||||||
|
import com.welie.blessed.BluetoothBytesParser;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static com.welie.blessed.BluetoothBytesParser.FORMAT_UINT16;
|
||||||
|
import static com.welie.blessed.BluetoothBytesParser.FORMAT_UINT8;
|
||||||
|
|
||||||
|
public class BluetoothStandardWeightProfile extends BluetoothCommunication {
|
||||||
|
private int CURRENT_USER_CONSENT = 3289;
|
||||||
|
|
||||||
|
// UDS control point codes
|
||||||
|
private static final byte UDS_CP_REGISTER_NEW_USER = 0x01;
|
||||||
|
private static final byte UDS_CP_CONSENT = 0x02;
|
||||||
|
private static final byte UDS_CP_DELETE_USER_DATA = 0x03;
|
||||||
|
private static final byte UDS_CP_LIST_ALL_USERS = 0x04;
|
||||||
|
private static final byte UDS_CP_DELETE_USERS = 0x05;
|
||||||
|
private static final byte UDS_CP_RESPONSE = 0x20;
|
||||||
|
|
||||||
|
// UDS response codes
|
||||||
|
private static final byte UDS_CP_RESP_VALUE_SUCCESS = 0x01;
|
||||||
|
private static final byte UDS_CP_RESP_OP_CODE_NOT_SUPPORTED = 0x02;
|
||||||
|
private static final byte UDS_CP_RESP_INVALID_PARAMETER = 0x03;
|
||||||
|
private static final byte UDS_CP_RESP_OPERATION_FAILED = 0x04;
|
||||||
|
private static final byte UDS_CP_RESP_USER_NOT_AUTHORIZED = 0x05;
|
||||||
|
|
||||||
|
public BluetoothStandardWeightProfile(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverName() {
|
||||||
|
return "Bluetooth Standard Weight Profile";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onNextStep(int stepNr) {
|
||||||
|
|
||||||
|
switch (stepNr) {
|
||||||
|
case 0:
|
||||||
|
// Read manufacturer and model number from the Device Information Service
|
||||||
|
readBytes(BluetoothGattUuid.SERVICE_DEVICE_INFORMATION, BluetoothGattUuid.CHARACTERISTIC_MANUFACTURER_NAME_STRING);
|
||||||
|
readBytes(BluetoothGattUuid.SERVICE_DEVICE_INFORMATION, BluetoothGattUuid.CHARACTERISTIC_MODEL_NUMBER_STRING);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// Write the current time
|
||||||
|
BluetoothBytesParser parser = new BluetoothBytesParser();
|
||||||
|
parser.setCurrentTime(Calendar.getInstance());
|
||||||
|
writeBytes(BluetoothGattUuid.SERVICE_CURRENT_TIME, BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, parser.getValue());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Turn on notification for Weight Service
|
||||||
|
setNotificationOn(BluetoothGattUuid.SERVICE_WEIGHT_SCALE, BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// Turn on notification for Body Composition Service
|
||||||
|
setNotificationOn(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, BluetoothGattUuid.CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
// Turn on notification for User Data Service
|
||||||
|
setNotificationOn(BluetoothGattUuid.SERVICE_USER_DATA, BluetoothGattUuid.CHARACTERISTIC_CHANGE_INCREMENT);
|
||||||
|
setNotificationOn(BluetoothGattUuid.SERVICE_USER_DATA, BluetoothGattUuid.CHARACTERISTIC_USER_CONTROL_POINT);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
// Turn on notifications for Battery Service
|
||||||
|
setNotificationOn(BluetoothGattUuid.SERVICE_BATTERY_LEVEL, BluetoothGattUuid.CHARACTERISTIC_BATTERY_LEVEL);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
|
registerUser(CURRENT_USER_CONSENT);
|
||||||
|
setUser(selectedUser.getId(), CURRENT_USER_CONSENT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||||
|
BluetoothBytesParser parser = new BluetoothBytesParser(value);
|
||||||
|
|
||||||
|
if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME)) {
|
||||||
|
Date currentTime = parser.getDateTime();
|
||||||
|
Timber.d(String.format("Received device time: %s", currentTime));
|
||||||
|
}
|
||||||
|
else if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT)) {
|
||||||
|
handleWeightMeasurement(value);
|
||||||
|
}
|
||||||
|
else if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT)) {
|
||||||
|
handleBodyCompositionMeasurement(value);
|
||||||
|
}
|
||||||
|
else if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_BATTERY_LEVEL)) {
|
||||||
|
int batteryLevel = parser.getIntValue(FORMAT_UINT8);
|
||||||
|
Timber.d(String.format("Received battery level %d%%", batteryLevel));
|
||||||
|
}
|
||||||
|
else if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_MANUFACTURER_NAME_STRING)) {
|
||||||
|
String manufacturer = parser.getStringValue(0);
|
||||||
|
Timber.d(String.format("Received manufacturer: %s", manufacturer));
|
||||||
|
}
|
||||||
|
else if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_MODEL_NUMBER_STRING)) {
|
||||||
|
String modelNumber = parser.getStringValue(0);
|
||||||
|
Timber.d(String.format("Received modelnumber: %s", modelNumber));
|
||||||
|
}
|
||||||
|
else if(characteristic.equals(BluetoothGattUuid.CHARACTERISTIC_USER_CONTROL_POINT)) {
|
||||||
|
if(value[0]==UDS_CP_RESPONSE) {
|
||||||
|
switch (value[1]) {
|
||||||
|
case UDS_CP_REGISTER_NEW_USER:
|
||||||
|
if (value[2] == UDS_CP_RESP_VALUE_SUCCESS) {
|
||||||
|
int userIndex = value[3];
|
||||||
|
Timber.d(String.format("Created user %d", userIndex));
|
||||||
|
} else {
|
||||||
|
Timber.e("ERROR: could not register new user");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UDS_CP_CONSENT:
|
||||||
|
if (value[2] == UDS_CP_RESP_VALUE_SUCCESS) {
|
||||||
|
Timber.d("Success user consent");
|
||||||
|
} else if (value[2] == UDS_CP_RESP_USER_NOT_AUTHORIZED) {
|
||||||
|
Timber.e("Not authorized");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Timber.e("Unhandled response");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.d(String.format("Got data: <%s>", byteInHex(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleWeightMeasurement(byte[] value) {
|
||||||
|
BluetoothBytesParser parser = new BluetoothBytesParser(value);
|
||||||
|
final int flags = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT8);
|
||||||
|
boolean isKg = (flags & 0x01) == 0;
|
||||||
|
final boolean timestampPresent = (flags & 0x02) > 0;
|
||||||
|
final boolean userIDPresent = (flags & 0x04) > 0;
|
||||||
|
final boolean bmiAndHeightPresent = (flags & 0x08) > 0;
|
||||||
|
|
||||||
|
ScaleMeasurement scaleMeasurement = new ScaleMeasurement();
|
||||||
|
|
||||||
|
// Determine the right weight multiplier
|
||||||
|
float weightMultiplier = isKg ? 0.005f : 0.01f;
|
||||||
|
|
||||||
|
// Get weight
|
||||||
|
float weightValue = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * weightMultiplier;
|
||||||
|
scaleMeasurement.setWeight(weightValue);
|
||||||
|
|
||||||
|
if(timestampPresent) {
|
||||||
|
Date timestamp = parser.getDateTime();
|
||||||
|
scaleMeasurement.setDateTime(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(userIDPresent) {
|
||||||
|
int userID = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT8);
|
||||||
|
Timber.d(String.format("User id: %i", userID));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bmiAndHeightPresent) {
|
||||||
|
float BMI = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * 0.1f;
|
||||||
|
float heightInMeters = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * 0.001f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.d(String.format("Got weight: %s", weightValue));
|
||||||
|
addScaleMeasurement(scaleMeasurement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBodyCompositionMeasurement(byte[] value) {
|
||||||
|
BluetoothBytesParser parser = new BluetoothBytesParser(value);
|
||||||
|
final int flags = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16);
|
||||||
|
boolean isKg = (flags & 0x0001) == 0;
|
||||||
|
float massMultiplier = (float) (isKg ? 0.005 : 0.01);
|
||||||
|
boolean timestampPresent = (flags & 0x0002) > 0;
|
||||||
|
boolean userIDPresent = (flags & 0x0004) > 0;
|
||||||
|
boolean bmrPresent = (flags & 0x0008) > 0;
|
||||||
|
boolean musclePercentagePresent = (flags & 0x0010) > 0;
|
||||||
|
boolean muscleMassPresent = (flags & 0x0020) > 0;
|
||||||
|
boolean fatFreeMassPresent = (flags & 0x0040) > 0;
|
||||||
|
boolean softLeanMassPresent = (flags & 0x0080) > 0;
|
||||||
|
boolean bodyWaterMassPresent = (flags & 0x0100) > 0;
|
||||||
|
boolean impedancePresent = (flags & 0x0200) > 0;
|
||||||
|
boolean weightPresent = (flags & 0x0400) > 0;
|
||||||
|
boolean heightPresent = (flags & 0x0800) > 0;
|
||||||
|
boolean multiPacketMeasurement = (flags & 0x1000) > 0;
|
||||||
|
|
||||||
|
ScaleMeasurement scaleMeasurement = new ScaleMeasurement();
|
||||||
|
|
||||||
|
float bodyFatPercentage = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * 0.1f;
|
||||||
|
scaleMeasurement.setFat(bodyFatPercentage);
|
||||||
|
|
||||||
|
// Read timestamp if present
|
||||||
|
if (timestampPresent) {
|
||||||
|
Date timestamp = parser.getDateTime();
|
||||||
|
scaleMeasurement.setDateTime(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read userID if present
|
||||||
|
if (userIDPresent) {
|
||||||
|
int userID = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT8);
|
||||||
|
Timber.d(String.format("user id: %i", userID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read bmr if present
|
||||||
|
if (bmrPresent) {
|
||||||
|
int bmrInJoules = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16);
|
||||||
|
int bmrInKcal = Math.round(((bmrInJoules / 4.1868f) * 10.0f) / 10.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read musclePercentage if present
|
||||||
|
if (musclePercentagePresent) {
|
||||||
|
float musclePercentage = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * 0.1f;
|
||||||
|
scaleMeasurement.setMuscle(musclePercentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read muscleMass if present
|
||||||
|
if (muscleMassPresent) {
|
||||||
|
float muscleMass = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * massMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read fatFreeMassPresent if present
|
||||||
|
if (fatFreeMassPresent) {
|
||||||
|
float fatFreeMass = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * massMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read softleanMass if present
|
||||||
|
if (softLeanMassPresent) {
|
||||||
|
float softLeanMass = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * massMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read bodyWaterMass if present
|
||||||
|
if (bodyWaterMassPresent) {
|
||||||
|
float bodyWaterMass = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * massMultiplier;
|
||||||
|
scaleMeasurement.setWater(bodyWaterMass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read impedance if present
|
||||||
|
if (impedancePresent) {
|
||||||
|
float impedance = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read weight if present
|
||||||
|
if (weightPresent) {
|
||||||
|
float weightValue = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16) * massMultiplier;
|
||||||
|
scaleMeasurement.setWeight(weightValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read height if present
|
||||||
|
if (heightPresent) {
|
||||||
|
float heightValue = parser.getIntValue(BluetoothBytesParser.FORMAT_UINT16);
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.d(String.format("Got body composition: %s", byteInHex(value)));
|
||||||
|
addScaleMeasurement(scaleMeasurement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerUser(int consentCode) {
|
||||||
|
BluetoothBytesParser parser = new BluetoothBytesParser(new byte[]{0,0,0});
|
||||||
|
parser.setIntValue(UDS_CP_REGISTER_NEW_USER, FORMAT_UINT8,0);
|
||||||
|
parser.setIntValue(consentCode, FORMAT_UINT16,1);
|
||||||
|
Timber.d(String.format("registerUser consentCode: %d", consentCode));
|
||||||
|
writeBytes(BluetoothGattUuid.SERVICE_USER_DATA, BluetoothGattUuid.CHARACTERISTIC_USER_CONTROL_POINT, parser.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUser(int userIndex, int consentCode) {
|
||||||
|
BluetoothBytesParser parser = new BluetoothBytesParser(new byte[]{0,0,0,0});
|
||||||
|
parser.setIntValue(UDS_CP_CONSENT,FORMAT_UINT8,0);
|
||||||
|
parser.setIntValue(userIndex, FORMAT_UINT8,1);
|
||||||
|
parser.setIntValue(consentCode, FORMAT_UINT16,2);
|
||||||
|
Timber.d(String.format("setUser userIndex: %d, consentCode: %d", userIndex, consentCode));
|
||||||
|
writeBytes(BluetoothGattUuid.SERVICE_USER_DATA, BluetoothGattUuid.CHARACTERISTIC_USER_CONTROL_POINT, parser.getValue());
|
||||||
|
}
|
||||||
|
}
|
@@ -19,6 +19,8 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.health.openscale.R;
|
import com.health.openscale.R;
|
||||||
import com.health.openscale.core.OpenScale;
|
import com.health.openscale.core.OpenScale;
|
||||||
import com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib;
|
import com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib;
|
||||||
@@ -29,7 +31,6 @@ import com.health.openscale.core.utils.Converters;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,7 +125,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
|
|||||||
switch (stepNr) {
|
switch (stepNr) {
|
||||||
case 0:
|
case 0:
|
||||||
// Register for notifications of the measurement characteristic.
|
// Register for notifications of the measurement characteristic.
|
||||||
setIndicationOn(MEASUREMENT_CHARACTERISTIC_UUID);
|
setIndicationOn(WEIGHT_SCALE_SERVICE_UUID, MEASUREMENT_CHARACTERISTIC_UUID);
|
||||||
break; // more commands follow
|
break; // more commands follow
|
||||||
case 1:
|
case 1:
|
||||||
// Register for notifications of the command upload characteristic.
|
// Register for notifications of the command upload characteristic.
|
||||||
@@ -132,7 +133,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
|
|||||||
// This is the last init command, which causes a switch to the main state machine
|
// This is the last init command, which causes a switch to the main state machine
|
||||||
// immediately after. This is important because we should be in the main state
|
// immediately after. This is important because we should be in the main state
|
||||||
// to handle pairing correctly.
|
// to handle pairing correctly.
|
||||||
setIndicationOn(UPLOAD_COMMAND_CHARACTERISTIC_UUID);
|
setIndicationOn(WEIGHT_SCALE_SERVICE_UUID, UPLOAD_COMMAND_CHARACTERISTIC_UUID);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// This state is triggered by the write in onPasswordReceived()
|
// This state is triggered by the write in onPasswordReceived()
|
||||||
@@ -306,7 +307,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
|
|||||||
|
|
||||||
private void writeCommandBytes(byte[] bytes) {
|
private void writeCommandBytes(byte[] bytes) {
|
||||||
Timber.d("writeCommand bytes=%s", byteInHex(bytes));
|
Timber.d("writeCommand bytes=%s", byteInHex(bytes));
|
||||||
writeBytes(DOWNLOAD_COMMAND_CHARACTERISTIC_UUID, bytes);
|
writeBytes(WEIGHT_SCALE_SERVICE_UUID, DOWNLOAD_COMMAND_CHARACTERISTIC_UUID, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDevicePasswordKey(String deviceId) {
|
private static String getDevicePasswordKey(String deviceId) {
|
||||||
|
@@ -68,7 +68,7 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
|||||||
(byte)0x00, display_unit, (byte) 0x03, (byte) 0x00};
|
(byte)0x00, display_unit, (byte) 0x03, (byte) 0x00};
|
||||||
user_add_or_query[user_add_or_query.length - 1] =
|
user_add_or_query[user_add_or_query.length - 1] =
|
||||||
xorChecksum(user_add_or_query, 1, user_add_or_query.length - 1);
|
xorChecksum(user_add_or_query, 1, user_add_or_query.length - 1);
|
||||||
writeBytes(WEIGHT_CMD_CHARACTERISTIC, user_add_or_query);
|
writeBytes(WEIGHT_CMD_SERVICE, WEIGHT_CMD_CHARACTERISTIC, user_add_or_query);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
byte[] unixTime = Converters.toInt32Be(new Date().getTime() / 1000);
|
byte[] unixTime = Converters.toInt32Be(new Date().getTime() / 1000);
|
||||||
@@ -79,14 +79,14 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
|||||||
set_time[set_time.length - 1] =
|
set_time[set_time.length - 1] =
|
||||||
xorChecksum(set_time, 1, set_time.length - 1);
|
xorChecksum(set_time, 1, set_time.length - 1);
|
||||||
|
|
||||||
writeBytes(WEIGHT_CMD_CHARACTERISTIC, set_time);
|
writeBytes(WEIGHT_CMD_SERVICE, WEIGHT_CMD_CHARACTERISTIC, set_time);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
byte[] magic_bytes = new byte[]{(byte)0x0d, (byte)0x05, (byte)0x13, (byte)0x00, (byte)0x16};
|
byte[] magic_bytes = new byte[]{(byte)0x0d, (byte)0x05, (byte)0x13, (byte)0x00, (byte)0x16};
|
||||||
writeBytes(WEIGHT_CMD_CHARACTERISTIC, magic_bytes);
|
writeBytes(WEIGHT_CMD_SERVICE, WEIGHT_CMD_CHARACTERISTIC, magic_bytes);
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@@ -1,23 +1,25 @@
|
|||||||
/* Copyright (C) 2014 olie.xdev <olie.xdev@googlemail.com>
|
/* Copyright (C) 2014 olie.xdev <olie.xdev@googlemail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* 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 com.health.openscale.gui.preferences;
|
package com.health.openscale.gui.preferences;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.le.ScanResult;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -26,6 +28,7 @@ import android.graphics.PorterDuff;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
@@ -39,16 +42,13 @@ import com.health.openscale.core.bluetooth.BluetoothCommunication;
|
|||||||
import com.health.openscale.core.bluetooth.BluetoothFactory;
|
import com.health.openscale.core.bluetooth.BluetoothFactory;
|
||||||
import com.health.openscale.gui.utils.ColorUtil;
|
import com.health.openscale.gui.utils.ColorUtil;
|
||||||
import com.health.openscale.gui.utils.PermissionHelper;
|
import com.health.openscale.gui.utils.PermissionHelper;
|
||||||
import com.polidea.rxandroidble2.RxBleClient;
|
import com.welie.blessed.BluetoothCentral;
|
||||||
import com.polidea.rxandroidble2.RxBleDevice;
|
import com.welie.blessed.BluetoothCentralCallback;
|
||||||
import com.polidea.rxandroidble2.scan.ScanResult;
|
import com.welie.blessed.BluetoothPeripheral;
|
||||||
import com.polidea.rxandroidble2.scan.ScanSettings;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
||||||
@@ -59,11 +59,10 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
public static final String PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS = "btHwAddress";
|
public static final String PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS = "btHwAddress";
|
||||||
|
|
||||||
private PreferenceScreen btScanner;
|
private PreferenceScreen btScanner;
|
||||||
private Map<String, RxBleDevice> foundDevices = new HashMap<>();
|
private Map<String, BluetoothDevice> foundDevices = new HashMap<>();
|
||||||
|
|
||||||
private Handler progressHandler;
|
private Handler progressHandler;
|
||||||
private RxBleClient bleClient;
|
private BluetoothCentral central;
|
||||||
private Disposable scanSubscription;
|
|
||||||
|
|
||||||
private static final String formatDeviceName(String name, String address) {
|
private static final String formatDeviceName(String name, String address) {
|
||||||
if (name.isEmpty() || address.isEmpty()) {
|
if (name.isEmpty() || address.isEmpty()) {
|
||||||
@@ -72,8 +71,8 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
return String.format("%s [%s]", name, address);
|
return String.format("%s [%s]", name, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String formatDeviceName(RxBleDevice device) {
|
private static final String formatDeviceName(BluetoothDevice device) {
|
||||||
return formatDeviceName(device.getName(), device.getMacAddress());
|
return formatDeviceName(device.getName(), device.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCurrentDeviceName() {
|
private String getCurrentDeviceName() {
|
||||||
@@ -83,19 +82,19 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
prefs.getString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, ""));
|
prefs.getString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final BluetoothCentralCallback bluetoothCentralCallback = new BluetoothCentralCallback() {
|
||||||
|
@Override
|
||||||
|
public void onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult) {
|
||||||
|
onDeviceFound(scanResult);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private void startBluetoothDiscovery() {
|
private void startBluetoothDiscovery() {
|
||||||
foundDevices.clear();
|
foundDevices.clear();
|
||||||
btScanner.removeAll();
|
btScanner.removeAll();
|
||||||
|
|
||||||
scanSubscription = bleClient.scanBleDevices(
|
central = new BluetoothCentral(getActivity().getApplicationContext(), bluetoothCentralCallback, new Handler(Looper.getMainLooper()));
|
||||||
new ScanSettings.Builder()
|
central.scanForPeripherals();
|
||||||
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
|
||||||
//.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doFinally(this::stopBluetoothDiscovery)
|
|
||||||
.subscribe(this::onDeviceFound, throwable -> Toast.makeText(getActivity(), throwable.getMessage(), Toast.LENGTH_LONG).show());
|
|
||||||
|
|
||||||
final Preference scanning = new Preference(getActivity());
|
final Preference scanning = new Preference(getActivity());
|
||||||
scanning.setEnabled(false);
|
scanning.setEnabled(false);
|
||||||
@@ -161,25 +160,25 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
progressHandler = null;
|
progressHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
scanSubscription.dispose();
|
central.stopScan();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDeviceFound(final ScanResult bleScanResult) {
|
private void onDeviceFound(final ScanResult bleScanResult) {
|
||||||
RxBleDevice device = bleScanResult.getBleDevice();
|
BluetoothDevice device = bleScanResult.getDevice();
|
||||||
|
|
||||||
if (device.getName() == null || foundDevices.containsKey(device.getMacAddress())) {
|
if (device.getName() == null || foundDevices.containsKey(device.getAddress())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Preference prefBtDevice = new Preference(getActivity());
|
Preference prefBtDevice = new Preference(getActivity());
|
||||||
prefBtDevice.setTitle(formatDeviceName(bleScanResult.getBleDevice()));
|
prefBtDevice.setTitle(formatDeviceName(bleScanResult.getDevice()));
|
||||||
|
|
||||||
BluetoothCommunication btDevice = BluetoothFactory.createDeviceDriver(getActivity(), device.getName());
|
BluetoothCommunication btDevice = BluetoothFactory.createDeviceDriver(getActivity(), device.getName());
|
||||||
if (btDevice != null) {
|
if (btDevice != null) {
|
||||||
Timber.d("Found supported device %s (driver: %s)",
|
Timber.d("Found supported device %s (driver: %s)",
|
||||||
formatDeviceName(device), btDevice.driverName());
|
formatDeviceName(device), btDevice.driverName());
|
||||||
prefBtDevice.setOnPreferenceClickListener(new onClickListenerDeviceSelect());
|
prefBtDevice.setOnPreferenceClickListener(new onClickListenerDeviceSelect());
|
||||||
prefBtDevice.setKey(device.getMacAddress());
|
prefBtDevice.setKey(device.getAddress());
|
||||||
prefBtDevice.setIcon(R.drawable.ic_bluetooth_device_supported);
|
prefBtDevice.setIcon(R.drawable.ic_bluetooth_device_supported);
|
||||||
prefBtDevice.setSummary(btDevice.driverName());
|
prefBtDevice.setSummary(btDevice.driverName());
|
||||||
|
|
||||||
@@ -205,7 +204,7 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foundDevices.put(device.getMacAddress(), btDevice != null ? device : null);
|
foundDevices.put(device.getAddress(), btDevice != null ? device : null);
|
||||||
btScanner.addPreference(prefBtDevice);
|
btScanner.addPreference(prefBtDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,10 +218,6 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
OpenScale openScale = OpenScale.getInstance();
|
|
||||||
|
|
||||||
bleClient = openScale.getBleClient();
|
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.bluetooth_preferences);
|
addPreferencesFromResource(R.xml.bluetooth_preferences);
|
||||||
|
|
||||||
btScanner = (PreferenceScreen) findPreference(PREFERENCE_KEY_BLUETOOTH_SCANNER);
|
btScanner = (PreferenceScreen) findPreference(PREFERENCE_KEY_BLUETOOTH_SCANNER);
|
||||||
@@ -271,7 +266,7 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getDebugInfo(final RxBleDevice device) {
|
private void getDebugInfo(final BluetoothDevice device) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
builder.setTitle("Fetching info")
|
builder.setTitle("Fetching info")
|
||||||
.setMessage("Please wait while we fetch extended info from your scale...")
|
.setMessage("Please wait while we fetch extended info from your scale...")
|
||||||
@@ -299,7 +294,7 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
String macAddress = device.getMacAddress();
|
String macAddress = device.getAddress();
|
||||||
stopBluetoothDiscovery();
|
stopBluetoothDiscovery();
|
||||||
OpenScale.getInstance().connectToBluetoothDeviceDebugMode(
|
OpenScale.getInstance().connectToBluetoothDeviceDebugMode(
|
||||||
macAddress, btHandler);
|
macAddress, btHandler);
|
||||||
@@ -308,10 +303,10 @@ public class BluetoothPreferences extends PreferenceFragment {
|
|||||||
private class onClickListenerDeviceSelect implements Preference.OnPreferenceClickListener {
|
private class onClickListenerDeviceSelect implements Preference.OnPreferenceClickListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(final Preference preference) {
|
public boolean onPreferenceClick(final Preference preference) {
|
||||||
RxBleDevice device = foundDevices.get(preference.getKey());
|
BluetoothDevice device = foundDevices.get(preference.getKey());
|
||||||
|
|
||||||
preference.getSharedPreferences().edit()
|
preference.getSharedPreferences().edit()
|
||||||
.putString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, device.getMacAddress())
|
.putString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, device.getAddress())
|
||||||
.putString(PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, device.getName())
|
.putString(PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, device.getName())
|
||||||
.apply();
|
.apply();
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.3.2'
|
classpath 'com.android.tools.build:gradle:3.4.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#Mon Jan 28 19:53:43 CET 2019
|
#Sat May 25 16:04:40 CEST 2019
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||||
|
Reference in New Issue
Block a user