1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-15 05:04:22 +02:00

Bluetooth state machine 2.0 (#404)

simplified Bluetooth machine state
This commit is contained in:
OliE
2019-02-21 18:52:44 +01:00
committed by GitHub
parent 8675fe7b67
commit 3a577b9292
22 changed files with 354 additions and 692 deletions

View File

@@ -168,9 +168,8 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stepNr) {
switch (stateNr) {
case 0: case 0:
// Setup notification // Setup notification
setNotificationOn(CUSTOM_CHARACTERISTIC_WEIGHT); setNotificationOn(CUSTOM_CHARACTERISTIC_WEIGHT);
@@ -212,26 +211,10 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
if (currentRemoteUser != null) { if (currentRemoteUser != null) {
Timber.d("Request saved measurements for %s", currentRemoteUser.name); Timber.d("Request saved measurements for %s", currentRemoteUser.name);
sendCommand(CMD_GET_SAVED_MEASUREMENTS, encodeUserId(currentRemoteUser)); sendCommand(CMD_GET_SAVED_MEASUREMENTS, encodeUserId(currentRemoteUser));
// Wait for all measurements to be received
stopMachineState(); stopMachineState();
} else {
nextMachineStateStep();
} }
break; break;
case 6: case 6:
if (currentRemoteUser != null) {
Timber.d("Deleting saved measurements for %s", currentRemoteUser.name);
sendCommand(CMD_DELETE_SAVED_MEASUREMENTS, encodeUserId(currentRemoteUser));
// Return to the previous state until all users have been processed
repeatMachineStateSteps(2);
stopMachineState();
} else {
nextMachineStateStep();
}
break;
case 7:
// Create a remote user for selected openScale user if needed // Create a remote user for selected openScale user if needed
currentRemoteUser = null; currentRemoteUser = null;
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser(); final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
@@ -244,14 +227,20 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
if (currentRemoteUser == null) { if (currentRemoteUser == null) {
createRemoteUser(selectedUser); createRemoteUser(selectedUser);
stopMachineState(); stopMachineState();
} else {
nextMachineStateStep();
} }
break; break;
case 8: case 7:
sendCommand(CMD_USER_DETAILS, encodeUserId(currentRemoteUser)); sendCommand(CMD_USER_DETAILS, encodeUserId(currentRemoteUser));
stopMachineState(); stopMachineState();
break; break;
case 8:
if (!currentRemoteUser.isNew) {
sendCommand(CMD_DO_MEASUREMENT, encodeUserId(currentRemoteUser));
stopMachineState();
} else {
return false;
}
break;
default: default:
// Finish init if everything is done // Finish init if everything is done
return false; return false;
@@ -260,40 +249,6 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
case 0:
if (!currentRemoteUser.isNew) {
sendCommand(CMD_DO_MEASUREMENT, encodeUserId(currentRemoteUser));
stopMachineState();
} else {
nextMachineStateStep();
}
break;
case 1:
setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE);
break;
default:
return false;
}
return true;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
switch (stateNr) {
case 0:
// Force disconnect
sendAlternativeStartCode(ID_START_NIBBLE_DISCONNECT, (byte) 0x02);
break;
default:
return false;
}
return true;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
byte[] data = value; byte[] data = value;
@@ -303,7 +258,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
if (data[0] == getAlternativeStartByte(ID_START_NIBBLE_INIT)) { if (data[0] == getAlternativeStartByte(ID_START_NIBBLE_INIT)) {
Timber.d("Got init ack from scale; scale is ready"); Timber.d("Got init ack from scale; scale is ready");
resumeMachineState(true); resumeMachineState();
return; return;
} }
@@ -377,7 +332,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
} }
// All users received // All users received
resumeMachineState(true); resumeMachineState();
} }
private void processMeasurementData(byte[] data, int offset, boolean firstPart) { private void processMeasurementData(byte[] data, int offset, boolean firstPart) {
@@ -401,11 +356,18 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
int current = data[3] & 0xFF; int current = data[3] & 0xFF;
processMeasurementData(data, 4, current % 2 == 1); processMeasurementData(data, 4, current % 2 == 1);
if (current == count) {
// Resume but don't do next step until ACK has been sent
resumeMachineState(false);
}
sendAck(data); sendAck(data);
if (current == count) {
Timber.d("Deleting saved measurements for %s", currentRemoteUser.name);
sendCommand(CMD_DELETE_SAVED_MEASUREMENTS, encodeUserId(currentRemoteUser));
if (currentRemoteUser.remoteUserId != remoteUsers.get(remoteUsers.size() - 1).remoteUserId) {
jumpNextToStepNr(5);
resumeMachineState();
}
}
} }
private void processWeightMeasurement(byte[] data) { private void processWeightMeasurement(byte[] data) {
@@ -483,7 +445,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
Timber.d("Set scale unit to %s (%d)", user.getScaleUnit(), requestedUnit); Timber.d("Set scale unit to %s (%d)", user.getScaleUnit(), requestedUnit);
sendCommand(CMD_SET_UNIT, requestedUnit); sendCommand(CMD_SET_UNIT, requestedUnit);
} else { } else {
resumeMachineState(true); resumeMachineState();
} }
break; break;
@@ -491,7 +453,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
if (data[3] == 0) { if (data[3] == 0) {
Timber.d("Scale unit successfully set"); Timber.d("Scale unit successfully set");
} }
resumeMachineState(true); resumeMachineState();
break; break;
case CMD_USER_LIST: case CMD_USER_LIST:
@@ -499,7 +461,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
int maxUserCount = data[5] & 0xFF; int maxUserCount = data[5] & 0xFF;
Timber.d("Have %d users (max is %d)", userCount, maxUserCount); Timber.d("Have %d users (max is %d)", userCount, maxUserCount);
if (userCount == 0) { if (userCount == 0) {
resumeMachineState(true); resumeMachineState();
} }
// Otherwise wait for CMD_USER_INFO notifications // Otherwise wait for CMD_USER_INFO notifications
break; break;
@@ -508,8 +470,9 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
int measurementCount = data[3] & 0xFF; int measurementCount = data[3] & 0xFF;
if (measurementCount == 0) { if (measurementCount == 0) {
// Skip delete all measurements step (since there are no measurements to delete) // Skip delete all measurements step (since there are no measurements to delete)
repeatMachineStateSteps(1); Timber.d("No saved measurements found for user " + currentRemoteUser.name);
resumeMachineState(true); jumpNextToStepNr(5);
resumeMachineState();
} }
// Otherwise wait for CMD_SAVED_MEASUREMENT notifications which will, // Otherwise wait for CMD_SAVED_MEASUREMENT notifications which will,
// once all measurements have been received, resume the state machine. // once all measurements have been received, resume the state machine.
@@ -517,9 +480,9 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
case CMD_DELETE_SAVED_MEASUREMENTS: case CMD_DELETE_SAVED_MEASUREMENTS:
if (data[3] == 0) { if (data[3] == 0) {
Timber.d("Saved measurements successfully deleted"); Timber.d("Saved measurements successfully deleted for user " + currentRemoteUser.name);
} }
resumeMachineState(true); resumeMachineState();
break; break;
case CMD_USER_ADD: case CMD_USER_ADD:
@@ -533,7 +496,10 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
Timber.d("Cannot create additional scale user (error 0x%02x)", data[3]); Timber.d("Cannot create additional scale user (error 0x%02x)", data[3]);
sendMessage(R.string.error_max_scale_users, 0); sendMessage(R.string.error_max_scale_users, 0);
setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); // Force disconnect
Timber.d("Send disconnect command to scale");
jumpNextToStepNr(8);
resumeMachineState();
break; break;
case CMD_DO_MEASUREMENT: case CMD_DO_MEASUREMENT:
@@ -556,7 +522,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
Timber.d("Name: %s, Birthday: %d-%02d-%02d, Height: %d, Sex: %s, activity: %d", Timber.d("Name: %s, Birthday: %d-%02d-%02d, Height: %d, Sex: %s, activity: %d",
name, year, month, day, height, male ? "male" : "female", activity); name, year, month, day, height, male ? "male" : "female", activity);
} }
resumeMachineState(true); resumeMachineState();
break; break;
default: default:
@@ -596,7 +562,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
receivedMeasurement.setMuscle(muscle); receivedMeasurement.setMuscle(muscle);
receivedMeasurement.setBone(bone); receivedMeasurement.setBone(bone);
addScaleData(receivedMeasurement); addScaleMeasurement(receivedMeasurement);
} }
private void writeBytes(byte[] data) { private void writeBytes(byte[] data) {

View File

@@ -38,40 +38,38 @@ import java.util.UUID;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.exceptions.UndeliverableException; import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject; import io.reactivex.subjects.PublishSubject;
import timber.log.Timber; import timber.log.Timber;
import static android.content.Context.LOCATION_SERVICE; import static android.content.Context.LOCATION_SERVICE;
public abstract class BluetoothCommunication { public abstract class BluetoothCommunication {
public enum BT_STATUS_CODE { public enum BT_STATUS {
BT_RETRIEVE_SCALE_DATA, RETRIEVE_SCALE_DATA,
BT_INIT_PROCESS, INIT_PROCESS,
BT_CONNECTION_RETRYING, CONNECTION_RETRYING,
BT_CONNECTION_ESTABLISHED, CONNECTION_ESTABLISHED,
BT_CONNECTION_DISCONNECT, CONNECTION_DISCONNECT,
BT_CONNECTION_LOST, CONNECTION_LOST,
BT_NO_DEVICE_FOUND, NO_DEVICE_FOUND,
BT_UNEXPECTED_ERROR, UNEXPECTED_ERROR,
BT_SCALE_MESSAGE SCALE_MESSAGE
} }
public enum BT_MACHINE_STATE { private int stepNr;
BT_INIT_STATE, private boolean stopped;
BT_CMD_STATE,
BT_CLEANUP_STATE,
BT_STOPPED_STATE
}
private final int BT_RETRY_TIMES_ON_ERROR = 3;
protected Context context; protected Context context;
private final int BT_RETRY_TIMES_ON_ERROR = 3;
private RxBleClient bleClient; private RxBleClient bleClient;
private RxBleDevice bleDevice; private RxBleDevice bleDevice;
private Observable<RxBleConnection> connectionObservable; private Observable<RxBleConnection> connectionObservable;
@@ -80,13 +78,6 @@ public abstract class BluetoothCommunication {
private PublishSubject<Boolean> disconnectTriggerSubject = PublishSubject.create(); private PublishSubject<Boolean> disconnectTriggerSubject = PublishSubject.create();
private Handler callbackBtHandler; private Handler callbackBtHandler;
private int cmdStepNr;
private int initStepNr;
private int cleanupStepNr;
private BT_MACHINE_STATE btMachineState;
private BT_MACHINE_STATE btStopppedMachineState;
private Handler disconnectHandler; private Handler disconnectHandler;
public BluetoothCommunication(Context context) public BluetoothCommunication(Context context)
@@ -95,6 +86,8 @@ public abstract class BluetoothCommunication {
this.bleClient = OpenScale.getInstance().getBleClient(); this.bleClient = OpenScale.getInstance().getBleClient();
this.scanSubscription = null; this.scanSubscription = null;
this.disconnectHandler = new Handler(); this.disconnectHandler = new Handler();
this.stepNr = 0;
this.stopped = false;
RxJavaPlugins.setErrorHandler(e -> { RxJavaPlugins.setErrorHandler(e -> {
if (e instanceof UndeliverableException && e.getCause() instanceof BleException) { if (e instanceof UndeliverableException && e.getCause() instanceof BleException) {
@@ -126,7 +119,7 @@ public abstract class BluetoothCommunication {
} }
/** /**
* Register a callback Bluetooth handler that notify any BT_STATUS_CODE changes for GUI/CORE. * Register a callback Bluetooth handler that notify any BT_STATUS changes for GUI/CORE.
* *
* @param cbBtHandler a handler that is registered * @param cbBtHandler a handler that is registered
*/ */
@@ -137,10 +130,10 @@ public abstract class BluetoothCommunication {
/** /**
* Set for the openScale GUI/CORE the Bluetooth status code. * Set for the openScale GUI/CORE the Bluetooth status code.
* *
* @param statusCode the status code that should be set * @param status the status code that should be set
*/ */
protected void setBtStatus(BT_STATUS_CODE statusCode) { protected void setBluetoothStatus(BT_STATUS status) {
setBtStatus(statusCode, ""); setBluetoothStatus(status, "");
} }
/** /**
@@ -149,7 +142,7 @@ public abstract class BluetoothCommunication {
* @param statusCode the status code that should be set * @param statusCode the status code that should be set
* @param infoText the information text that is displayed to the status code. * @param infoText the information text that is displayed to the status code.
*/ */
protected void setBtStatus(BT_STATUS_CODE statusCode, String infoText) { protected void setBluetoothStatus(BT_STATUS statusCode, String infoText) {
if (callbackBtHandler != null) { if (callbackBtHandler != null) {
callbackBtHandler.obtainMessage( callbackBtHandler.obtainMessage(
statusCode.ordinal(), infoText).sendToTarget(); statusCode.ordinal(), infoText).sendToTarget();
@@ -161,10 +154,10 @@ public abstract class BluetoothCommunication {
* *
* @param scaleMeasurement the scale data that should be added to openScale * @param scaleMeasurement the scale data that should be added to openScale
*/ */
protected void addScaleData(ScaleMeasurement scaleMeasurement) { protected void addScaleMeasurement(ScaleMeasurement scaleMeasurement) {
if (callbackBtHandler != null) { if (callbackBtHandler != null) {
callbackBtHandler.obtainMessage( callbackBtHandler.obtainMessage(
BT_STATUS_CODE.BT_RETRIEVE_SCALE_DATA.ordinal(), scaleMeasurement).sendToTarget(); BT_STATUS.RETRIEVE_SCALE_DATA.ordinal(), scaleMeasurement).sendToTarget();
} }
} }
@@ -177,7 +170,7 @@ public abstract class BluetoothCommunication {
protected void sendMessage(int msg, Object value) { protected void sendMessage(int msg, Object value) {
if (callbackBtHandler != null) { if (callbackBtHandler != null) {
callbackBtHandler.obtainMessage( callbackBtHandler.obtainMessage(
BT_STATUS_CODE.BT_SCALE_MESSAGE.ordinal(), msg, 0, value).sendToTarget(); BT_STATUS.SCALE_MESSAGE.ordinal(), msg, 0, value).sendToTarget();
} }
} }
@@ -191,70 +184,10 @@ public abstract class BluetoothCommunication {
/** /**
* State machine for the initialization process of the Bluetooth device. * State machine for the initialization process of the Bluetooth device.
* *
* @param stateNr the current step number * @param stepNr the current step number
* @return false if no next step is available otherwise true * @return false if no next step is available otherwise true
*/ */
abstract protected boolean nextInitCmd(int stateNr); abstract protected boolean onNextStep(int stepNr);
/**
* State machine for the normal/command process of the Bluetooth device.
*
* This state machine is automatically triggered if initialization process is finished.
*
* @param stateNr the current step number
* @return false if no next step is available otherwise true
*/
abstract protected boolean nextBluetoothCmd(int stateNr);
/**
* Step the current machine state backwards. Needs to be called before a command.
*
* @param steps Number of steps to back the machine.
*/
protected void repeatMachineStateSteps(int steps) {
switch (btMachineState) {
case BT_INIT_STATE:
initStepNr = initStepNr - steps;
break;
case BT_CMD_STATE:
cmdStepNr = cmdStepNr - steps;
break;
case BT_CLEANUP_STATE:
cleanupStepNr = cleanupStepNr - steps;
break;
}
}
/**
* Stopped the current machine state
*/
protected void stopMachineState() {
Timber.d("Machine state stopped");
btStopppedMachineState = btMachineState;
btMachineState = BT_MACHINE_STATE.BT_STOPPED_STATE;
}
/**
* Resumed the current machine state
*/
protected void resumeMachineState(boolean doNextStep) {
Timber.d("Machine state resumed");
btMachineState = btStopppedMachineState;
if (doNextStep) {
nextMachineStateStep();
}
}
/**
* State machine for the clean up process for the Bluetooth device.
*
* This state machine is *not* automatically triggered. You have to setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE) to trigger this process if necessary.
*
* @param stateNr the current step number
* @return false if no next step is available otherwise true
*/
abstract protected boolean nextCleanUpCmd(int stateNr);
/** /**
* Method is triggered if a Bluetooth data is read from a device. * Method is triggered if a Bluetooth data is read from a device.
@@ -279,17 +212,20 @@ public abstract class BluetoothCommunication {
*/ */
protected void onBluetoothDiscovery(RxBleDeviceServices rxBleDeviceServices) { } protected void onBluetoothDiscovery(RxBleDeviceServices rxBleDeviceServices) { }
/** protected synchronized void stopMachineState() {
* Set the Bluetooth machine state to a specific state. Timber.d("Stop machine state");
* stopped = true;
* @note after setting a new state the next step is automatically triggered. }
*
* @param btMachineState the machine state that should be set.
*/
protected void setBtMachineState(BT_MACHINE_STATE btMachineState) {
this.btMachineState = btMachineState;
nextMachineStateStep(); protected synchronized void resumeMachineState() {
Timber.d("Resume machine state");
stopped = false;
nextMachineStep();
}
protected synchronized void jumpNextToStepNr(int nr) {
Timber.d("Jump next to step nr " + nr);
stepNr = nr;
} }
/** /**
@@ -297,22 +233,25 @@ 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 void writeBytes(UUID characteristic, byte[] bytes) { protected Observable<byte[]> writeBytes(UUID characteristic, byte[] bytes) {
final Disposable disposable = connectionObservable Timber.d("Invoke write bytes [" + byteInHex(bytes) + "] on " + BluetoothGattUuid.prettyPrint(characteristic));
Observable<byte[]> observable = connectionObservable
.flatMapSingle(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristic, bytes)) .flatMapSingle(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristic, bytes))
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.retry(BT_RETRY_TIMES_ON_ERROR) .retry(BT_RETRY_TIMES_ON_ERROR);
.subscribe(
compositeDisposable.add(observable.subscribe(
value -> { value -> {
Timber.d("Write characteristic %s: %s", Timber.d("Write characteristic %s: %s",
BluetoothGattUuid.prettyPrint(characteristic), BluetoothGattUuid.prettyPrint(characteristic),
byteInHex(value)); byteInHex(value));
nextMachineStateStep();
}, },
throwable -> onError(throwable) throwable -> onError(throwable)
)
); );
compositeDisposable.add(disposable); return observable;
} }
/** /**
@@ -321,20 +260,25 @@ 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 void readBytes(UUID characteristic) { protected Single<byte[]> readBytes(UUID characteristic) {
final Disposable disposable = connectionObservable Timber.d("Invoke read bytes on " + BluetoothGattUuid.prettyPrint(characteristic));
Single<byte[]> observable = connectionObservable
.firstOrError() .firstOrError()
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristic)) .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristic))
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.retry(BT_RETRY_TIMES_ON_ERROR) .retry(BT_RETRY_TIMES_ON_ERROR);
compositeDisposable.add(observable
.subscribe(bytes -> { .subscribe(bytes -> {
Timber.d("Read characteristic %s", BluetoothGattUuid.prettyPrint(characteristic)); Timber.d("Read characteristic %s", BluetoothGattUuid.prettyPrint(characteristic));
onBluetoothRead(characteristic, bytes); onBluetoothRead(characteristic, bytes);
}, },
throwable -> onError(throwable) throwable -> onError(throwable)
)
); );
compositeDisposable.add(disposable); return observable;
} }
/** /**
@@ -342,29 +286,32 @@ public abstract class BluetoothCommunication {
* *
* @param characteristic the Bluetooth UUID characteristic * @param characteristic the Bluetooth UUID characteristic
*/ */
protected void setIndicationOn(UUID characteristic) { protected Observable<byte[]> setIndicationOn(UUID characteristic) {
final Disposable disposable = connectionObservable Timber.d("Invoke set indication on " + BluetoothGattUuid.prettyPrint(characteristic));
Observable<byte[]> observable = connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.setupIndication(characteristic)) .flatMap(rxBleConnection -> rxBleConnection.setupIndication(characteristic))
.doOnNext(notificationObservable -> { .doOnNext(notificationObservable -> {
Timber.d("Successful set indication on for %s", BluetoothGattUuid.prettyPrint(characteristic)); Timber.d("Successful set indication on for %s", BluetoothGattUuid.prettyPrint(characteristic));
nextMachineStateStep();
} }
) )
.flatMap(indicationObservable -> indicationObservable) .flatMap(indicationObservable -> indicationObservable)
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.retry(BT_RETRY_TIMES_ON_ERROR) .retry(BT_RETRY_TIMES_ON_ERROR);
.subscribe(
compositeDisposable.add(observable.subscribe(
bytes -> { bytes -> {
onBluetoothNotify(characteristic, bytes);
Timber.d("onCharacteristicChanged %s: %s", Timber.d("onCharacteristicChanged %s: %s",
BluetoothGattUuid.prettyPrint(characteristic), BluetoothGattUuid.prettyPrint(characteristic),
byteInHex(bytes)); byteInHex(bytes));
onBluetoothNotify(characteristic, bytes);
resetDisconnectTimer(); resetDisconnectTimer();
}, },
throwable -> onError(throwable) throwable -> onError(throwable)
)
); );
compositeDisposable.add(disposable); return observable;
} }
/** /**
@@ -372,46 +319,69 @@ public abstract class BluetoothCommunication {
* *
* @param characteristic the Bluetooth UUID characteristic * @param characteristic the Bluetooth UUID characteristic
*/ */
protected void setNotificationOn(UUID characteristic) { protected Observable<byte[]> setNotificationOn(UUID characteristic) {
final Disposable disposable = connectionObservable Timber.d("Invoke set notification on " + BluetoothGattUuid.prettyPrint(characteristic));
stopped = true;
Observable<byte[]> observable = connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristic)) .flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristic))
.doOnNext(notificationObservable -> { .doOnNext(notificationObservable -> {
Timber.d("Successful set notification on for %s", BluetoothGattUuid.prettyPrint(characteristic)); Timber.d("Successful set notification on for %s", BluetoothGattUuid.prettyPrint(characteristic));
nextMachineStateStep(); stopped = false;
nextMachineStep();
} }
) )
.flatMap(notificationObservable -> notificationObservable) .flatMap(notificationObservable -> notificationObservable)
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.retry(BT_RETRY_TIMES_ON_ERROR) .retry(BT_RETRY_TIMES_ON_ERROR);
.subscribe(
compositeDisposable.add(observable.subscribe(
bytes -> { bytes -> {
onBluetoothNotify(characteristic, bytes);
Timber.d("onCharacteristicChanged %s: %s", Timber.d("onCharacteristicChanged %s: %s",
BluetoothGattUuid.prettyPrint(characteristic), BluetoothGattUuid.prettyPrint(characteristic),
byteInHex(bytes)); byteInHex(bytes));
onBluetoothNotify(characteristic, bytes);
resetDisconnectTimer(); resetDisconnectTimer();
}, },
throwable -> onError(throwable) throwable -> onError(throwable)
); ));
compositeDisposable.add(disposable); return observable;
} }
public void doBluetoothDiscoverServices() { protected Observable<RxBleDeviceServices> discoverBluetoothServices() {
final Disposable connectionDisposable = connectionObservable Timber.d("Invoke discover Bluetooth services");
final Observable<RxBleDeviceServices> observable = connectionObservable
.flatMapSingle(RxBleConnection::discoverServices) .flatMapSingle(RxBleConnection::discoverServices)
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.retry(BT_RETRY_TIMES_ON_ERROR) .retry(BT_RETRY_TIMES_ON_ERROR);
.subscribe(
compositeDisposable.add(observable.subscribe(
deviceServices -> { deviceServices -> {
Timber.d("Successful Bluetooth services discovered"); Timber.d("Successful Bluetooth services discovered");
onBluetoothDiscovery(deviceServices); onBluetoothDiscovery(deviceServices);
nextMachineStateStep();
}, },
throwable -> onError(throwable) throwable -> onError(throwable)
)
); );
compositeDisposable.add(connectionDisposable); return observable;
}
/**
* Disconnect from a Bluetooth device
*/
public void disconnect() {
Timber.d("Bluetooth disconnect");
setBluetoothStatus(BT_STATUS.CONNECTION_DISCONNECT);
if (scanSubscription != null) {
scanSubscription.dispose();
}
callbackBtHandler = null;
disconnectHandler.removeCallbacksAndMessages(null);
disconnectTriggerSubject.onNext(true);
compositeDisposable.clear();
} }
/** /**
@@ -496,11 +466,12 @@ public abstract class BluetoothCommunication {
//.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) //.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build() .build()
) )
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(bleScanResult -> { .subscribe(bleScanResult -> {
if (bleScanResult.getBleDevice().getMacAddress().equals(macAddress)) { if (bleScanResult.getBleDevice().getMacAddress().equals(macAddress)) {
connectToDevice(macAddress); connectToDevice(macAddress);
}}, throwable -> setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND)); }}, throwable -> setBluetoothStatus(BT_STATUS.NO_DEVICE_FOUND));
} }
else { else {
Timber.d("No coarse location permission, connecting without LE scan"); Timber.d("No coarse location permission, connecting without LE scan");
@@ -515,7 +486,6 @@ public abstract class BluetoothCommunication {
Timber.d("Stop Le san"); Timber.d("Stop Le san");
scanSubscription.dispose(); scanSubscription.dispose();
scanSubscription = null; scanSubscription = null;
} }
Handler handler = new Handler(); Handler handler = new Handler();
@@ -527,19 +497,18 @@ public abstract class BluetoothCommunication {
connectionObservable = bleDevice connectionObservable = bleDevice
.establishConnection(false) .establishConnection(false)
.takeUntil(disconnectTriggerSubject) .takeUntil(disconnectTriggerSubject)
.doOnError(throwable -> setBtStatus(BT_STATUS_CODE.BT_CONNECTION_RETRYING)) .doOnError(throwable -> setBluetoothStatus(BT_STATUS.CONNECTION_RETRYING))
.subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.compose(ReplayingShare.instance()); .compose(ReplayingShare.instance());
if (isConnected()) { if (isConnected()) {
disconnect(); disconnect();
} else { } else {
initStepNr = -1; stepNr = 0;
cmdStepNr = -1;
cleanupStepNr = -1;
setBtMonitoringOn(); setBtMonitoringOn();
setBtMachineState(BT_MACHINE_STATE.BT_INIT_STATE); nextMachineStep();
resetDisconnectTimer(); resetDisconnectTimer();
} }
} }
@@ -552,7 +521,7 @@ public abstract class BluetoothCommunication {
connectionState -> { connectionState -> {
switch (connectionState) { switch (connectionState) {
case CONNECTED: case CONNECTED:
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED); setBluetoothStatus(BT_STATUS.CONNECTION_ESTABLISHED);
break; break;
case CONNECTING: case CONNECTING:
// empty // empty
@@ -561,7 +530,7 @@ public abstract class BluetoothCommunication {
// empty // empty
break; break;
case DISCONNECTED: case DISCONNECTED:
// setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); // setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
break; break;
} }
}, },
@@ -571,20 +540,20 @@ public abstract class BluetoothCommunication {
compositeDisposable.add(disposableConnectionState); compositeDisposable.add(disposableConnectionState);
} }
private void onError(Throwable throwable) { protected void onError(Throwable throwable) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, throwable.getMessage()); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, throwable.getMessage());
} }
private boolean isConnected() { private boolean isConnected() {
return bleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED; return bleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED;
} }
public void resetDisconnectTimer() { private void resetDisconnectTimer() {
disconnectHandler.removeCallbacksAndMessages(null); disconnectHandler.removeCallbacksAndMessages(null);
disconnectWithDelay(); disconnectWithDelay();
} }
public void disconnectWithDelay() { private void disconnectWithDelay() {
disconnectHandler.postDelayed(new Runnable() { disconnectHandler.postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -594,48 +563,16 @@ public abstract class BluetoothCommunication {
}, 60000); // 60s timeout }, 60000); // 60s timeout
} }
/** private synchronized void nextMachineStep() {
* Disconnect from a Bluetooth device if (!stopped) {
*/ Timber.d("Step Nr " + stepNr);
public void disconnect() { if (onNextStep(stepNr)) {
Timber.d("Bluetooth disconnect"); stepNr++;
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_DISCONNECT); nextMachineStep();
if (scanSubscription != null) { } else {
scanSubscription.dispose(); Timber.d("Invoke delayed disconnect in 60s");
}
callbackBtHandler = null;
disconnectHandler.removeCallbacksAndMessages(null);
disconnectTriggerSubject.onNext(true);
compositeDisposable.clear();
}
/**
* Invoke next step for internal Bluetooth state machine.
*/
protected void nextMachineStateStep() {
switch (btMachineState) {
case BT_INIT_STATE:
initStepNr++;
Timber.d("INIT STATE: %d", initStepNr);
if (!nextInitCmd(initStepNr)) {
setBtMachineState(BT_MACHINE_STATE.BT_CMD_STATE);
}
break;
case BT_CMD_STATE:
cmdStepNr++;
Timber.d("CMD STATE: %d", cmdStepNr);
if (!nextBluetoothCmd(cmdStepNr)) {
disconnectWithDelay(); disconnectWithDelay();
} }
break;
case BT_CLEANUP_STATE:
cleanupStepNr++;
Timber.d("CLEANUP STATE: %d", cleanupStepNr);
if (!nextCleanUpCmd(cleanupStepNr)) {
Timber.d("Cleanup Bluetooth disconnect");
disconnect();
}
break;
} }
} }
} }

View File

@@ -18,7 +18,6 @@ package com.health.openscale.core.bluetooth;
import android.content.Context; import android.content.Context;
import com.health.openscale.core.datatypes.ScaleMeasurement; import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.polidea.rxandroidble2.RxBleClient;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -44,8 +43,8 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC
); );
@@ -70,16 +69,6 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
public void clearEEPROM() public void clearEEPROM()
{ {
byte[] cmd = {(byte)'9'}; byte[] cmd = {(byte)'9'};
@@ -106,7 +95,7 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
btString = btString.substring(0, btString.length() - 1); // delete newline '\n' of the string btString = btString.substring(0, btString.length() - 1); // delete newline '\n' of the string
if (btString.charAt(0) != '$' && btString.charAt(2) != '$') { if (btString.charAt(0) != '$' && btString.charAt(2) != '$') {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Parse error of bluetooth string. String has not a valid format: " + btString); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Parse error of bluetooth string. String has not a valid format: " + btString);
} }
String btMsg = btString.substring(3, btString.length()); // message string String btMsg = btString.substring(3, btString.length()); // message string
@@ -156,18 +145,18 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
scaleBtData.setWater(Float.parseFloat(csvField[8])); scaleBtData.setWater(Float.parseFloat(csvField[8]));
scaleBtData.setMuscle(Float.parseFloat(csvField[9])); scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} else { } else {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different"); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different");
} }
} catch (ParseException e) { } catch (ParseException e) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")"); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding a number of bluetooth string (" + e.getMessage() + ")"); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Error while decoding a number of bluetooth string (" + e.getMessage() + ")");
} }
break; break;
default: default:
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error unknown MCU command : " + btString); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Error unknown MCU command : " + btString);
} }
} }
} }

View File

@@ -164,14 +164,14 @@ public class BluetoothDebug extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) switch (stepNr)
{ {
case 0: case 0:
doBluetoothDiscoverServices(); discoverBluetoothServices();
break; break;
case 1: case 1:
int offset = stateNr; int offset = stepNr;
for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) { for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) {
offset = readServiceCharacteristics(service, offset); offset = readServiceCharacteristics(service, offset);
@@ -181,7 +181,7 @@ public class BluetoothDebug extends BluetoothCommunication {
logService(service, false); logService(service, false);
} }
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
break; break;
case 2: case 2:
disconnect(); disconnect();
@@ -192,14 +192,4 @@ public class BluetoothDebug extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
} }

View File

@@ -56,8 +56,8 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { 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_CHARACTERISTIC);
@@ -65,19 +65,11 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
case 1: case 1:
sendMessage(R.string.info_step_on_scale, 0); sendMessage(R.string.info_step_on_scale, 0);
break; break;
} default:
return false; return false;
} }
@Override return true;
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
} }
private void parseBytes(byte[] weightBytes) { private void parseBytes(byte[] weightBytes) {
@@ -135,7 +127,7 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
scaleBtData.setVisceralFat(visceralFat); scaleBtData.setVisceralFat(visceralFat);
} }
scaleBtData.setWeight(weight); scaleBtData.setWeight(weight);
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} }
} }
} }

View File

@@ -44,13 +44,8 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
return false; switch (stepNr) {
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
case 0: case 0:
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser(); final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
@@ -103,12 +98,7 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
return false; return false;
} }
return false; return true;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
} }
@Override @Override
@@ -150,6 +140,6 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
scaleBtData.setBone(bone); scaleBtData.setBone(bone);
scaleBtData.setVisceralFat(visceralFat); scaleBtData.setVisceralFat(visceralFat);
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} }
} }

View File

@@ -42,11 +42,10 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
);
break; break;
case 1: case 1:
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser(); final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
@@ -71,16 +70,6 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -118,6 +107,6 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
scaleBtData.setVisceralFat(visc_fat); scaleBtData.setVisceralFat(visc_fat);
scaleBtData.setDateTime(new Date()); scaleBtData.setDateTime(new Date());
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} }
} }

View File

@@ -39,8 +39,8 @@ public class BluetoothHesley extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC); setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
break; break;
@@ -58,16 +58,6 @@ public class BluetoothHesley extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -101,6 +91,6 @@ public class BluetoothHesley extends BluetoothCommunication {
scaleBtData.setBone(bone); scaleBtData.setBone(bone);
scaleBtData.setDateTime(new Date()); scaleBtData.setDateTime(new Date());
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} }
} }

View File

@@ -23,14 +23,13 @@ import android.bluetooth.BluetoothSocket;
import android.content.Context; import android.content.Context;
import com.health.openscale.core.datatypes.ScaleMeasurement; import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.polidea.rxandroidble2.RxBleClient;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.UUID;
import java.util.Date;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import timber.log.Timber; import timber.log.Timber;
@@ -57,20 +56,8 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
Timber.w("ihealthHS3 - nextInitCmd - returning false"); Timber.w("ihealthHS3 - onNextStep - returning false");
return false;
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
Timber.w("ihealthHS3 - nextBluetoothCmd - returning false");
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
Timber.w("ihealthHS3 - nextCleanUpCmd - returning false");
return false; return false;
} }
@@ -79,7 +66,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter == null) { if (btAdapter == null) {
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND); setBluetoothStatus(BT_STATUS.NO_DEVICE_FOUND);
return; return;
} }
@@ -88,7 +75,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
// Get a BluetoothSocket to connect with the given BluetoothDevice // Get a BluetoothSocket to connect with the given BluetoothDevice
btSocket = btDevice.createRfcommSocketToServiceRecord(uuid); btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
} catch (IOException e) { } catch (IOException e) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't get a bluetooth socket"); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Can't get a bluetooth socket");
btDevice = null; btDevice = null;
return; return;
} }
@@ -103,7 +90,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
btSocket.connect(); btSocket.connect();
// Bluetooth connection was successful // Bluetooth connection was successful
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED); setBluetoothStatus(BT_STATUS.CONNECTION_ESTABLISHED);
btConnectThread = new BluetoothConnectedThread(); btConnectThread = new BluetoothConnectedThread();
btConnectThread.start(); btConnectThread.start();
@@ -111,7 +98,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
} catch (IOException connectException) { } catch (IOException connectException) {
// Unable to connect; close the socket and get out // Unable to connect; close the socket and get out
disconnect(); disconnect();
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND); setBluetoothStatus(BT_STATUS.NO_DEVICE_FOUND);
} }
} }
}; };
@@ -129,7 +116,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
btSocket.close(); btSocket.close();
btSocket = null; btSocket = null;
} catch (IOException closeException) { } catch (IOException closeException) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't close bluetooth socket"); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Can't close bluetooth socket");
} }
} }
} }
@@ -171,7 +158,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
btInStream = btSocket.getInputStream(); btInStream = btSocket.getInputStream();
btOutStream = btSocket.getOutputStream(); btOutStream = btSocket.getOutputStream();
} catch (IOException e) { } catch (IOException e) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't get bluetooth input or output stream " + e.getMessage()); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Can't get bluetooth input or output stream " + e.getMessage());
} }
} }
@@ -209,7 +196,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
ScaleMeasurement scaleMeasurement = parseWeightArray(weightBytes); ScaleMeasurement scaleMeasurement = parseWeightArray(weightBytes);
if (scaleMeasurement != null) { if (scaleMeasurement != null) {
addScaleData(scaleMeasurement); addScaleMeasurement(scaleMeasurement);
} }
} }
@@ -227,7 +214,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
} catch (IOException e) { } catch (IOException e) {
cancel(); cancel();
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
} }
} }
} }
@@ -266,7 +253,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
try { try {
btOutStream.write(bytes); btOutStream.write(bytes);
} catch (IOException e) { } catch (IOException e) {
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while writing to bluetooth socket " + e.getMessage()); setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Error while writing to bluetooth socket " + e.getMessage());
} }
} }

View File

@@ -73,8 +73,8 @@ public class BluetoothInlife extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC); setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
break; break;
@@ -98,16 +98,6 @@ public class BluetoothInlife extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -229,7 +219,7 @@ public class BluetoothInlife extends BluetoothCommunication {
measurement.setLbm(lbm); measurement.setLbm(lbm);
measurement.setVisceralFat(clamp(visceral, 1, 50)); measurement.setVisceralFat(clamp(visceral, 1, 50));
addScaleData(measurement); addScaleMeasurement(measurement);
sendCommand(0xd4); sendCommand(0xd4);
} }

View File

@@ -82,8 +82,8 @@ public class BluetoothMGB extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn(uuid_char_ctrl setNotificationOn(uuid_char_ctrl
); );
@@ -126,19 +126,6 @@ public class BluetoothMGB extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
packet_buf = value; packet_buf = value;
@@ -202,7 +189,7 @@ public class BluetoothMGB extends BluetoothCommunication {
popInt(); // unknown =02 popInt(); // unknown =02
popInt(); // unknown =47;48;4e;4b;42 popInt(); // unknown =47;48;4e;4b;42
addScaleData(measurement); addScaleMeasurement(measurement);
// Visceral fat? // Visceral fat?
// Standard weight? // Standard weight?

View File

@@ -51,13 +51,8 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
return false; switch (stepNr) {
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
case 0: case 0:
// set indication on for feature characteristic // set indication on for feature characteristic
setIndicationOn(FEATURE_MEASUREMENT_CHARACTERISTIC); setIndicationOn(FEATURE_MEASUREMENT_CHARACTERISTIC);
@@ -92,12 +87,6 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -109,7 +98,7 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
if (characteristic.equals(FEATURE_MEASUREMENT_CHARACTERISTIC)) { if (characteristic.equals(FEATURE_MEASUREMENT_CHARACTERISTIC)) {
parseFeatureData(data); parseFeatureData(data);
addScaleData(btScaleMeasurement); addScaleMeasurement(btScaleMeasurement);
} }
} }

View File

@@ -35,7 +35,7 @@ import java.util.UUID;
import timber.log.Timber; import timber.log.Timber;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_STATUS_CODE.BT_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_HISTORY_CHARACTERISTIC = UUID.fromString("00002a2f-0000-3512-2118-0009af100700"); private final UUID WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC = UUID.fromString("00002a2f-0000-3512-2118-0009af100700");
@@ -60,11 +60,21 @@ public class BluetoothMiScale extends BluetoothCommunication {
int scaleMonth = (int) data[2]; int scaleMonth = (int) data[2];
int scaleDay = (int) data[3]; int scaleDay = (int) data[3];
if (currentYear == scaleYear && currentMonth == scaleMonth && currentDay == scaleDay) { if (!(currentYear == scaleYear && currentMonth == scaleMonth && currentDay == scaleDay)) {
setBtMachineState(BT_MACHINE_STATE.BT_CMD_STATE);
} else {
Timber.d("Current year and scale year is different"); Timber.d("Current year and scale year is different");
nextMachineStateStep();
// set current time
Calendar currentDateTime = Calendar.getInstance();
int year = currentDateTime.get(Calendar.YEAR);
byte month = (byte)(currentDateTime.get(Calendar.MONTH)+1);
byte day = (byte)currentDateTime.get(Calendar.DAY_OF_MONTH);
byte hour = (byte)currentDateTime.get(Calendar.HOUR_OF_DAY);
byte min = (byte)currentDateTime.get(Calendar.MINUTE);
byte sec = (byte)currentDateTime.get(Calendar.SECOND);
byte[] dateTimeByte = {(byte)(year), (byte)(year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
writeBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
} }
} }
@@ -76,7 +86,15 @@ 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) {
setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); // send stop command to mi scale
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
// acknowledge that you received the last history data
int uniqueNumber = getUniqueNumber();
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
resumeMachineState();
} }
if (data.length == 20) { if (data.length == 20) {
@@ -95,82 +113,37 @@ public class BluetoothMiScale extends BluetoothCommunication {
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
// read device time // read device time
readBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME); readBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME);
break; break;
case 1: case 1:
// set current time
Calendar currentDateTime = Calendar.getInstance();
int year = currentDateTime.get(Calendar.YEAR);
byte month = (byte)(currentDateTime.get(Calendar.MONTH)+1);
byte day = (byte)currentDateTime.get(Calendar.DAY_OF_MONTH);
byte hour = (byte)currentDateTime.get(Calendar.HOUR_OF_DAY);
byte min = (byte)currentDateTime.get(Calendar.MINUTE);
byte sec = (byte)currentDateTime.get(Calendar.SECOND);
byte[] dateTimeByte = {(byte)(year), (byte)(year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
writeBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
break;
case 2:
// 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_HISTORY_CHARACTERISTIC, magicBytes);
break; break;
default: case 2:
return false;
}
return true;
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
case 0:
// set notification on for weight measurement history // set notification on for weight measurement history
setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC); setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
break; break;
case 1: case 3:
// set notification on for weight measurement // set notification on for weight measurement
setNotificationOn(BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT); setNotificationOn(BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT);
break; break;
case 2: 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_HISTORY_CHARACTERISTIC, userIdentifier);
break; break;
case 3: case 5:
// invoke receiving history data // invoke receiving history data
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02}); writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
break; stopMachineState();
default:
return false;
}
return true;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
switch (stateNr) {
case 0:
// send stop command to mi scale
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
break;
case 1:
// acknowledge that you received the last history data
int uniqueNumber = getUniqueNumber();
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
break; break;
default: default:
return false; return false;
@@ -225,13 +198,13 @@ public class BluetoothMiScale extends BluetoothCommunication {
scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit())); scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit()));
scaleBtData.setDateTime(date_time); scaleBtData.setDateTime(date_time);
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} else { } else {
Timber.e("Invalid Mi scale weight year %d", year); Timber.e("Invalid Mi scale weight year %d", year);
} }
} }
} catch (ParseException e) { } catch (ParseException e) {
setBtStatus(BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")"); setBluetoothStatus(UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
} }
} }

View File

@@ -31,7 +31,6 @@ import com.health.openscale.gui.views.FatMeasurementView;
import com.health.openscale.gui.views.LBMMeasurementView; 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.polidea.rxandroidble2.RxBleClient;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -43,7 +42,7 @@ import java.util.UUID;
import timber.log.Timber; import timber.log.Timber;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_STATUS_CODE.BT_UNEXPECTED_ERROR; import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_STATUS.UNEXPECTED_ERROR;
public class BluetoothMiScale2 extends BluetoothCommunication { public class BluetoothMiScale2 extends BluetoothCommunication {
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");
@@ -69,7 +68,18 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
// Stop command from mi scale received // Stop command from mi scale received
if (data[0] == 0x03) { if (data[0] == 0x03) {
setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); Timber.d("Scale stop byte received");
// send stop command to mi scale
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
// acknowledge that you received the last history data
int uniqueNumber = getUniqueNumber();
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
disconnect();
resumeMachineState();
} }
if (data.length == 26) { if (data.length == 26) {
@@ -88,8 +98,8 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
// set scale units // set scale units
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser(); final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
@@ -108,73 +118,23 @@ 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( writeBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
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( setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC
);
break; break;
default: case 3:
return false;
}
return true;
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
case 0:
// 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( writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
break; break;
case 1: case 4:
// set notification on for weight measurement history
setNotificationOn(
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC
);
break;
case 2:
// invoke receiving history data // invoke receiving history data
writeBytes( writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02}); stopMachineState();
break;
default:
return false;
}
return true;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
switch (stateNr) {
case 0:
// send stop command to mi scale
writeBytes(
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
break;
case 1:
// acknowledge that you received the last history data
int uniqueNumber = getUniqueNumber();
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
writeBytes(
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
break;
case 2:
// set notification on for body composition measurement
setNotificationOn(
BluetoothGattUuid.CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT
);
break; break;
default: default:
return false; return false;
@@ -239,13 +199,13 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
EstimatedLBMMetric.FORMULA.valueOf(settings.getEstimationFormula())); EstimatedLBMMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleBtData.setLbm(lbmMetric.getLBM(selectedUser, scaleBtData)); scaleBtData.setLbm(lbmMetric.getLBM(selectedUser, scaleBtData));
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} else { } else {
Timber.e("Invalid Mi scale weight year %d", year); Timber.e("Invalid Mi scale weight year %d", year);
} }
} }
} catch (ParseException e) { } catch (ParseException e) {
setBtStatus(BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")"); setBluetoothStatus(UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
} }
} }

View File

@@ -47,12 +47,10 @@ public class BluetoothOneByone extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn( setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION);
WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION
);
break; break;
case 1: case 1:
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser(); ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
@@ -83,16 +81,6 @@ public class BluetoothOneByone extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -155,6 +143,6 @@ public class BluetoothOneByone extends BluetoothCommunication {
Timber.d("scale measurement [%s]", scaleBtData); Timber.d("scale measurement [%s]", scaleBtData);
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} }
} }

View File

@@ -84,13 +84,8 @@ public class BluetoothQNScale extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
return false; switch (stepNr) {
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
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(CUSTOM1_MEASUREMENT_CHARACTERISTIC);
@@ -116,28 +111,17 @@ public class BluetoothQNScale extends BluetoothCommunication {
case 4: case 4:
sendMessage(R.string.info_step_on_scale, 0); sendMessage(R.string.info_step_on_scale, 0);
break; break;
default: /*case 5:
return false;
}
return true;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
switch (stateNr) {
case 0:
// send stop command to scale (0x1f05151049) // send stop command to scale (0x1f05151049)
writeBytes(CUSTOM3_MEASUREMENT_CHARACTERISTIC, new byte[]{(byte)0x1f, (byte)0x05, (byte)0x15, (byte)0x10, (byte)0x49}); writeBytes(CUSTOM3_MEASUREMENT_CHARACTERISTIC, new byte[]{(byte)0x1f, (byte)0x05, (byte)0x15, (byte)0x10, (byte)0x49});
break; break;*/
default: default:
return false; return false;
} }
return true; return true;
} }
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -198,7 +182,7 @@ public class BluetoothQNScale extends BluetoothCommunication {
btScaleMeasurement.setMuscle(qnscalelib.getMuscle(weightKg, impedance)); btScaleMeasurement.setMuscle(qnscalelib.getMuscle(weightKg, impedance));
btScaleMeasurement.setBone(qnscalelib.getBone(weightKg, impedance)); btScaleMeasurement.setBone(qnscalelib.getBone(weightKg, impedance));
btScaleMeasurement.setWeight(weightKg); btScaleMeasurement.setWeight(weightKg);
addScaleData(btScaleMeasurement); addScaleMeasurement(btScaleMeasurement);
} }
} }
} }

View File

@@ -68,8 +68,8 @@ public class BluetoothSenssun extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC); setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
break; break;
@@ -85,16 +85,6 @@ public class BluetoothSenssun extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -107,7 +97,7 @@ public class BluetoothSenssun extends BluetoothCommunication {
} }
if (isBitSet(WeightFatMus,2) ) { if (isBitSet(WeightFatMus,2) ) {
addScaleData(measurement); addScaleMeasurement(measurement);
} }
} }

View File

@@ -92,7 +92,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
* set-broadcast-id command, and should disconnect after the write succeeds. * set-broadcast-id command, and should disconnect after the write succeeds.
* *
* @see #onPasswordReceived * @see #onPasswordReceived
* @see #nextBluetoothCmd * @see #onNextStep
*/ */
private boolean pairing = false; private boolean pairing = false;
@@ -119,13 +119,13 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
Timber.i("nextInitCmd(%d)", stateNr); Timber.i("onNextStep(%d)", stepNr);
switch (stateNr) { 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(MEASUREMENT_CHARACTERISTIC_UUID);
return true; // 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.
// //
@@ -133,40 +133,22 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
// 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(UPLOAD_COMMAND_CHARACTERISTIC_UUID);
// falls through break;
default: case 2:
return false; // no more commands
}
}
@Override
protected boolean nextBluetoothCmd(int stateNr) {
Timber.i("nextBluetoothCmd(%d)", stateNr);
switch (stateNr) {
case 0:
default:
return false; // no more commands
case 1:
// This state is triggered by the write in onPasswordReceived() // This state is triggered by the write in onPasswordReceived()
if (pairing) { if (pairing) {
pairing = false; pairing = false;
disconnect(); disconnect();
} }
return false; // no more commands; break;
} case 3:
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
Timber.i("nextCleanUpCmd(%d)", stateNr);
switch (stateNr) {
case 0:
writeCommand(DOWNLOAD_INFORMATION_ENABLE_DISCONNECT_COMMAND); writeCommand(DOWNLOAD_INFORMATION_ENABLE_DISCONNECT_COMMAND);
// falls through break;
default: default:
return false; // no more commands return false; // no more commands
} }
return true;
} }
@Override @Override
@@ -247,7 +229,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
return; return;
} }
addScaleData(measurement); addScaleMeasurement(measurement);
} }
public ScaleMeasurement parseScaleMeasurementData(byte[] data, ScaleUser user) { public ScaleMeasurement parseScaleMeasurementData(byte[] data, ScaleUser user) {

View File

@@ -52,8 +52,8 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
} }
@Override @Override
protected boolean nextInitCmd(int stateNr) { protected boolean onNextStep(int stepNr) {
switch (stateNr) { switch (stepNr) {
case 0: case 0:
byte[] userId = Converters.toInt16Be(getUniqueNumber()); byte[] userId = Converters.toInt16Be(getUniqueNumber());
@@ -82,8 +82,7 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
writeBytes(WEIGHT_CMD_CHARACTERISTIC, set_time); writeBytes(WEIGHT_CMD_CHARACTERISTIC, set_time);
break; break;
case 2: case 2:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC setNotificationOn(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};
@@ -97,16 +96,6 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
return true; return true;
} }
@Override
protected boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
protected boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override @Override
public void onBluetoothNotify(UUID characteristic, byte[] value) { public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value; final byte[] data = value;
@@ -154,7 +143,7 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
Timber.d("scale measurement [%s]", scaleBtData); Timber.d("scale measurement [%s]", scaleBtData);
} }
addScaleData(scaleBtData); addScaleMeasurement(scaleBtData);
} }
private int getUniqueNumber() { private int getUniqueNumber() {

View File

@@ -154,8 +154,8 @@ public class MainActivity extends BaseAppCompatActivity
if(prefs.edit().putInt("launchCount", ++launchCount).commit()){ if(prefs.edit().putInt("launchCount", ++launchCount).commit()){
valueOfCountModified = true; valueOfCountModified = true;
// ask the user once for feedback on the 30th app launch // ask the user once for feedback on the 15th app launch
if(launchCount == 30){ if(launchCount == 15){
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.label_feedback_message_enjoying) builder.setMessage(R.string.label_feedback_message_enjoying)
@@ -476,10 +476,10 @@ public class MainActivity extends BaseAppCompatActivity
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
BluetoothCommunication.BT_STATUS_CODE btStatusCode = BluetoothCommunication.BT_STATUS_CODE.values()[msg.what]; BluetoothCommunication.BT_STATUS btStatus = BluetoothCommunication.BT_STATUS.values()[msg.what];
switch (btStatusCode) { switch (btStatus) {
case BT_RETRIEVE_SCALE_DATA: case RETRIEVE_SCALE_DATA:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success);
ScaleMeasurement scaleBtData = (ScaleMeasurement) msg.obj; ScaleMeasurement scaleBtData = (ScaleMeasurement) msg.obj;
@@ -496,42 +496,42 @@ public class MainActivity extends BaseAppCompatActivity
openScale.addScaleData(scaleBtData, true); openScale.addScaleData(scaleBtData, true);
break; break;
case BT_INIT_PROCESS: case INIT_PROCESS:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_init), Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_init), Toast.LENGTH_SHORT).show();
Timber.d("Bluetooth initializing"); Timber.d("Bluetooth initializing");
break; break;
case BT_CONNECTION_LOST: case CONNECTION_LOST:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_lost), Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_lost), Toast.LENGTH_SHORT).show();
Timber.d("Bluetooth connection lost"); Timber.d("Bluetooth connection lost");
break; break;
case BT_NO_DEVICE_FOUND: case NO_DEVICE_FOUND:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device), Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device), Toast.LENGTH_SHORT).show();
Timber.e("No Bluetooth device found"); Timber.e("No Bluetooth device found");
break; break;
case BT_CONNECTION_RETRYING: case CONNECTION_RETRYING:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_searching); setBluetoothStatusIcon(R.drawable.ic_bluetooth_searching);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device_retrying), Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device_retrying), Toast.LENGTH_SHORT).show();
Timber.e("No Bluetooth device found retrying"); Timber.e("No Bluetooth device found retrying");
break; break;
case BT_CONNECTION_ESTABLISHED: case CONNECTION_ESTABLISHED:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_successful), Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_successful), Toast.LENGTH_SHORT).show();
Timber.d("Bluetooth connection successful established"); Timber.d("Bluetooth connection successful established");
break; break;
case BT_CONNECTION_DISCONNECT: case CONNECTION_DISCONNECT:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_disconnected), Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_disconnected), Toast.LENGTH_SHORT).show();
Timber.d("Bluetooth connection successful disconnected"); Timber.d("Bluetooth connection successful disconnected");
break; break;
case BT_UNEXPECTED_ERROR: case UNEXPECTED_ERROR:
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost); setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_error) + ": " + msg.obj, Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_error) + ": " + msg.obj, Toast.LENGTH_SHORT).show();
Timber.e("Bluetooth unexpected error: %s", msg.obj); Timber.e("Bluetooth unexpected error: %s", msg.obj);
break; break;
case BT_SCALE_MESSAGE: case SCALE_MESSAGE:
String toastMessage = String.format(getResources().getString(msg.arg1), msg.obj); String toastMessage = String.format(getResources().getString(msg.arg1), msg.obj);
Toast.makeText(getApplicationContext(), toastMessage, Toast.LENGTH_LONG).show(); Toast.makeText(getApplicationContext(), toastMessage, Toast.LENGTH_LONG).show();
break; break;

View File

@@ -31,7 +31,6 @@ import android.preference.Preference;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import com.health.openscale.R; import com.health.openscale.R;
@@ -289,8 +288,8 @@ public class BluetoothPreferences extends PreferenceFragment {
Handler btHandler = new Handler() { Handler btHandler = new Handler() {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (BluetoothCommunication.BT_STATUS_CODE.values()[msg.what]) { switch (BluetoothCommunication.BT_STATUS.values()[msg.what]) {
case BT_CONNECTION_LOST: case CONNECTION_LOST:
OpenScale.getInstance().disconnectFromBluetoothDevice(); OpenScale.getInstance().disconnectFromBluetoothDevice();
dialog.dismiss(); dialog.dismiss();
break; break;

View File

@@ -230,8 +230,9 @@ public class ChartMeasurementView extends LineChart {
} }
private void setMeasurementList(List<ScaleMeasurement> measurementList) { private void setMeasurementList(List<ScaleMeasurement> measurementList) {
if (!measurementList.isEmpty()) {
scaleMeasurementList = measurementList; scaleMeasurementList = measurementList;
if (!measurementList.isEmpty()) {
lastMeasurement = measurementList.get(0); lastMeasurement = measurementList.get(0);
Collections.reverse(measurementList); Collections.reverse(measurementList);
firstMeasurement = measurementList.get(0); firstMeasurement = measurementList.get(0);
@@ -409,12 +410,12 @@ public class ChartMeasurementView extends LineChart {
} }
private void refresh() { private void refresh() {
clear();
if (scaleMeasurementList.isEmpty()) { if (scaleMeasurementList.isEmpty()) {
return; return;
} }
clear();
List<ILineDataSet> lineDataSets = new ArrayList<>(); List<ILineDataSet> lineDataSets = new ArrayList<>();
ScaleMeasurement[] avgMeasurementList = averageScaleMeasurementList(scaleMeasurementList); ScaleMeasurement[] avgMeasurementList = averageScaleMeasurementList(scaleMeasurementList);