mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-12 19:54:29 +02:00
@@ -168,9 +168,8 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
// Setup notification
|
||||
setNotificationOn(CUSTOM_CHARACTERISTIC_WEIGHT);
|
||||
@@ -212,26 +211,10 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
if (currentRemoteUser != null) {
|
||||
Timber.d("Request saved measurements for %s", currentRemoteUser.name);
|
||||
sendCommand(CMD_GET_SAVED_MEASUREMENTS, encodeUserId(currentRemoteUser));
|
||||
|
||||
// Wait for all measurements to be received
|
||||
stopMachineState();
|
||||
} else {
|
||||
nextMachineStateStep();
|
||||
}
|
||||
break;
|
||||
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
|
||||
currentRemoteUser = null;
|
||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
@@ -244,14 +227,20 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
if (currentRemoteUser == null) {
|
||||
createRemoteUser(selectedUser);
|
||||
stopMachineState();
|
||||
} else {
|
||||
nextMachineStateStep();
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
case 7:
|
||||
sendCommand(CMD_USER_DETAILS, encodeUserId(currentRemoteUser));
|
||||
stopMachineState();
|
||||
break;
|
||||
case 8:
|
||||
if (!currentRemoteUser.isNew) {
|
||||
sendCommand(CMD_DO_MEASUREMENT, encodeUserId(currentRemoteUser));
|
||||
stopMachineState();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Finish init if everything is done
|
||||
return false;
|
||||
@@ -260,40 +249,6 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
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
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
byte[] data = value;
|
||||
@@ -303,7 +258,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
|
||||
if (data[0] == getAlternativeStartByte(ID_START_NIBBLE_INIT)) {
|
||||
Timber.d("Got init ack from scale; scale is ready");
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -377,7 +332,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
// All users received
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
}
|
||||
|
||||
private void processMeasurementData(byte[] data, int offset, boolean firstPart) {
|
||||
@@ -401,11 +356,18 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
int current = data[3] & 0xFF;
|
||||
|
||||
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);
|
||||
|
||||
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) {
|
||||
@@ -483,7 +445,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
Timber.d("Set scale unit to %s (%d)", user.getScaleUnit(), requestedUnit);
|
||||
sendCommand(CMD_SET_UNIT, requestedUnit);
|
||||
} else {
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -491,7 +453,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
if (data[3] == 0) {
|
||||
Timber.d("Scale unit successfully set");
|
||||
}
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
break;
|
||||
|
||||
case CMD_USER_LIST:
|
||||
@@ -499,7 +461,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
int maxUserCount = data[5] & 0xFF;
|
||||
Timber.d("Have %d users (max is %d)", userCount, maxUserCount);
|
||||
if (userCount == 0) {
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
}
|
||||
// Otherwise wait for CMD_USER_INFO notifications
|
||||
break;
|
||||
@@ -508,8 +470,9 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
int measurementCount = data[3] & 0xFF;
|
||||
if (measurementCount == 0) {
|
||||
// Skip delete all measurements step (since there are no measurements to delete)
|
||||
repeatMachineStateSteps(1);
|
||||
resumeMachineState(true);
|
||||
Timber.d("No saved measurements found for user " + currentRemoteUser.name);
|
||||
jumpNextToStepNr(5);
|
||||
resumeMachineState();
|
||||
}
|
||||
// Otherwise wait for CMD_SAVED_MEASUREMENT notifications which will,
|
||||
// once all measurements have been received, resume the state machine.
|
||||
@@ -517,9 +480,9 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
|
||||
case CMD_DELETE_SAVED_MEASUREMENTS:
|
||||
if (data[3] == 0) {
|
||||
Timber.d("Saved measurements successfully deleted");
|
||||
Timber.d("Saved measurements successfully deleted for user " + currentRemoteUser.name);
|
||||
}
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
break;
|
||||
|
||||
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]);
|
||||
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;
|
||||
|
||||
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",
|
||||
name, year, month, day, height, male ? "male" : "female", activity);
|
||||
}
|
||||
resumeMachineState(true);
|
||||
resumeMachineState();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -596,7 +562,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication {
|
||||
receivedMeasurement.setMuscle(muscle);
|
||||
receivedMeasurement.setBone(bone);
|
||||
|
||||
addScaleData(receivedMeasurement);
|
||||
addScaleMeasurement(receivedMeasurement);
|
||||
}
|
||||
|
||||
private void writeBytes(byte[] data) {
|
||||
|
@@ -38,40 +38,38 @@ import java.util.UUID;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.exceptions.UndeliverableException;
|
||||
import io.reactivex.plugins.RxJavaPlugins;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.Context.LOCATION_SERVICE;
|
||||
|
||||
public abstract class BluetoothCommunication {
|
||||
public enum BT_STATUS_CODE {
|
||||
BT_RETRIEVE_SCALE_DATA,
|
||||
BT_INIT_PROCESS,
|
||||
BT_CONNECTION_RETRYING,
|
||||
BT_CONNECTION_ESTABLISHED,
|
||||
BT_CONNECTION_DISCONNECT,
|
||||
BT_CONNECTION_LOST,
|
||||
BT_NO_DEVICE_FOUND,
|
||||
BT_UNEXPECTED_ERROR,
|
||||
BT_SCALE_MESSAGE
|
||||
public enum BT_STATUS {
|
||||
RETRIEVE_SCALE_DATA,
|
||||
INIT_PROCESS,
|
||||
CONNECTION_RETRYING,
|
||||
CONNECTION_ESTABLISHED,
|
||||
CONNECTION_DISCONNECT,
|
||||
CONNECTION_LOST,
|
||||
NO_DEVICE_FOUND,
|
||||
UNEXPECTED_ERROR,
|
||||
SCALE_MESSAGE
|
||||
}
|
||||
|
||||
public enum BT_MACHINE_STATE {
|
||||
BT_INIT_STATE,
|
||||
BT_CMD_STATE,
|
||||
BT_CLEANUP_STATE,
|
||||
BT_STOPPED_STATE
|
||||
}
|
||||
|
||||
private final int BT_RETRY_TIMES_ON_ERROR = 3;
|
||||
private int stepNr;
|
||||
private boolean stopped;
|
||||
|
||||
protected Context context;
|
||||
|
||||
private final int BT_RETRY_TIMES_ON_ERROR = 3;
|
||||
|
||||
private RxBleClient bleClient;
|
||||
private RxBleDevice bleDevice;
|
||||
private Observable<RxBleConnection> connectionObservable;
|
||||
@@ -80,13 +78,6 @@ public abstract class BluetoothCommunication {
|
||||
private PublishSubject<Boolean> disconnectTriggerSubject = PublishSubject.create();
|
||||
|
||||
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;
|
||||
|
||||
public BluetoothCommunication(Context context)
|
||||
@@ -95,6 +86,8 @@ public abstract class BluetoothCommunication {
|
||||
this.bleClient = OpenScale.getInstance().getBleClient();
|
||||
this.scanSubscription = null;
|
||||
this.disconnectHandler = new Handler();
|
||||
this.stepNr = 0;
|
||||
this.stopped = false;
|
||||
|
||||
RxJavaPlugins.setErrorHandler(e -> {
|
||||
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
|
||||
*/
|
||||
@@ -137,10 +130,10 @@ public abstract class BluetoothCommunication {
|
||||
/**
|
||||
* 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) {
|
||||
setBtStatus(statusCode, "");
|
||||
protected void setBluetoothStatus(BT_STATUS status) {
|
||||
setBluetoothStatus(status, "");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +142,7 @@ public abstract class BluetoothCommunication {
|
||||
* @param statusCode the status code that should be set
|
||||
* @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) {
|
||||
callbackBtHandler.obtainMessage(
|
||||
statusCode.ordinal(), infoText).sendToTarget();
|
||||
@@ -161,10 +154,10 @@ public abstract class BluetoothCommunication {
|
||||
*
|
||||
* @param scaleMeasurement the scale data that should be added to openScale
|
||||
*/
|
||||
protected void addScaleData(ScaleMeasurement scaleMeasurement) {
|
||||
protected void addScaleMeasurement(ScaleMeasurement scaleMeasurement) {
|
||||
if (callbackBtHandler != null) {
|
||||
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) {
|
||||
if (callbackBtHandler != null) {
|
||||
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.
|
||||
*
|
||||
* @param stateNr the current step number
|
||||
* @param stepNr the current step number
|
||||
* @return false if no next step is available otherwise true
|
||||
*/
|
||||
abstract protected boolean nextInitCmd(int stateNr);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
abstract protected boolean onNextStep(int stepNr);
|
||||
|
||||
/**
|
||||
* 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) { }
|
||||
|
||||
/**
|
||||
* Set the Bluetooth machine state to a specific state.
|
||||
*
|
||||
* @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;
|
||||
protected synchronized void stopMachineState() {
|
||||
Timber.d("Stop machine state");
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
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 bytes the bytes that should be write
|
||||
*/
|
||||
protected void writeBytes(UUID characteristic, byte[] bytes) {
|
||||
final Disposable disposable = connectionObservable
|
||||
protected Observable<byte[]> writeBytes(UUID characteristic, byte[] bytes) {
|
||||
Timber.d("Invoke write bytes [" + byteInHex(bytes) + "] on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||
Observable<byte[]> observable = connectionObservable
|
||||
.flatMapSingle(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristic, bytes))
|
||||
.subscribeOn(Schedulers.trampoline())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR)
|
||||
.subscribe(
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
||||
|
||||
compositeDisposable.add(observable.subscribe(
|
||||
value -> {
|
||||
Timber.d("Write characteristic %s: %s",
|
||||
BluetoothGattUuid.prettyPrint(characteristic),
|
||||
byteInHex(value));
|
||||
nextMachineStateStep();
|
||||
},
|
||||
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!
|
||||
*@param characteristic the Bluetooth UUID characteristic
|
||||
*/
|
||||
protected void readBytes(UUID characteristic) {
|
||||
final Disposable disposable = connectionObservable
|
||||
protected Single<byte[]> readBytes(UUID 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())
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR)
|
||||
.subscribe(bytes -> {
|
||||
Timber.d("Read characteristic %s", BluetoothGattUuid.prettyPrint(characteristic));
|
||||
onBluetoothRead(characteristic, bytes);
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
);
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
||||
|
||||
compositeDisposable.add(disposable);
|
||||
compositeDisposable.add(observable
|
||||
.subscribe(bytes -> {
|
||||
Timber.d("Read characteristic %s", BluetoothGattUuid.prettyPrint(characteristic));
|
||||
onBluetoothRead(characteristic, bytes);
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
)
|
||||
);
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,29 +286,32 @@ public abstract class BluetoothCommunication {
|
||||
*
|
||||
* @param characteristic the Bluetooth UUID characteristic
|
||||
*/
|
||||
protected void setIndicationOn(UUID characteristic) {
|
||||
final Disposable disposable = connectionObservable
|
||||
protected Observable<byte[]> setIndicationOn(UUID characteristic) {
|
||||
Timber.d("Invoke set indication on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||
Observable<byte[]> observable = connectionObservable
|
||||
.flatMap(rxBleConnection -> rxBleConnection.setupIndication(characteristic))
|
||||
.doOnNext(notificationObservable -> {
|
||||
Timber.d("Successful set indication on for %s", BluetoothGattUuid.prettyPrint(characteristic));
|
||||
nextMachineStateStep();
|
||||
}
|
||||
)
|
||||
.flatMap(indicationObservable -> indicationObservable)
|
||||
.subscribeOn(Schedulers.trampoline())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR)
|
||||
.subscribe(
|
||||
bytes -> {
|
||||
onBluetoothNotify(characteristic, bytes);
|
||||
Timber.d("onCharacteristicChanged %s: %s",
|
||||
BluetoothGattUuid.prettyPrint(characteristic),
|
||||
byteInHex(bytes));
|
||||
resetDisconnectTimer();
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
);
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
||||
|
||||
compositeDisposable.add(disposable);
|
||||
compositeDisposable.add(observable.subscribe(
|
||||
bytes -> {
|
||||
Timber.d("onCharacteristicChanged %s: %s",
|
||||
BluetoothGattUuid.prettyPrint(characteristic),
|
||||
byteInHex(bytes));
|
||||
onBluetoothNotify(characteristic, bytes);
|
||||
resetDisconnectTimer();
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
)
|
||||
);
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,46 +319,69 @@ public abstract class BluetoothCommunication {
|
||||
*
|
||||
* @param characteristic the Bluetooth UUID characteristic
|
||||
*/
|
||||
protected void setNotificationOn(UUID characteristic) {
|
||||
final Disposable disposable = connectionObservable
|
||||
protected Observable<byte[]> setNotificationOn(UUID characteristic) {
|
||||
Timber.d("Invoke set notification on " + BluetoothGattUuid.prettyPrint(characteristic));
|
||||
stopped = true;
|
||||
Observable<byte[]> observable = connectionObservable
|
||||
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristic))
|
||||
.doOnNext(notificationObservable -> {
|
||||
Timber.d("Successful set notification on for %s", BluetoothGattUuid.prettyPrint(characteristic));
|
||||
nextMachineStateStep();
|
||||
stopped = false;
|
||||
nextMachineStep();
|
||||
}
|
||||
)
|
||||
.flatMap(notificationObservable -> notificationObservable)
|
||||
.subscribeOn(Schedulers.trampoline())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR)
|
||||
.subscribe(
|
||||
bytes -> {
|
||||
onBluetoothNotify(characteristic, bytes);
|
||||
Timber.d("onCharacteristicChanged %s: %s",
|
||||
BluetoothGattUuid.prettyPrint(characteristic),
|
||||
byteInHex(bytes));
|
||||
resetDisconnectTimer();
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
);
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
||||
|
||||
compositeDisposable.add(disposable);
|
||||
compositeDisposable.add(observable.subscribe(
|
||||
bytes -> {
|
||||
Timber.d("onCharacteristicChanged %s: %s",
|
||||
BluetoothGattUuid.prettyPrint(characteristic),
|
||||
byteInHex(bytes));
|
||||
onBluetoothNotify(characteristic, bytes);
|
||||
resetDisconnectTimer();
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
));
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
public void doBluetoothDiscoverServices() {
|
||||
final Disposable connectionDisposable = connectionObservable
|
||||
protected Observable<RxBleDeviceServices> discoverBluetoothServices() {
|
||||
Timber.d("Invoke discover Bluetooth services");
|
||||
final Observable<RxBleDeviceServices> observable = connectionObservable
|
||||
.flatMapSingle(RxBleConnection::discoverServices)
|
||||
.subscribeOn(Schedulers.trampoline())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR)
|
||||
.subscribe(
|
||||
deviceServices -> {
|
||||
Timber.d("Successful Bluetooth services discovered");
|
||||
onBluetoothDiscovery(deviceServices);
|
||||
nextMachineStateStep();
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
);
|
||||
.retry(BT_RETRY_TIMES_ON_ERROR);
|
||||
|
||||
compositeDisposable.add(connectionDisposable);
|
||||
compositeDisposable.add(observable.subscribe(
|
||||
deviceServices -> {
|
||||
Timber.d("Successful Bluetooth services discovered");
|
||||
onBluetoothDiscovery(deviceServices);
|
||||
},
|
||||
throwable -> onError(throwable)
|
||||
)
|
||||
);
|
||||
|
||||
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)
|
||||
.build()
|
||||
)
|
||||
.subscribeOn(Schedulers.trampoline())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(bleScanResult -> {
|
||||
if (bleScanResult.getBleDevice().getMacAddress().equals(macAddress)) {
|
||||
connectToDevice(macAddress);
|
||||
}}, throwable -> setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND));
|
||||
}}, throwable -> setBluetoothStatus(BT_STATUS.NO_DEVICE_FOUND));
|
||||
}
|
||||
else {
|
||||
Timber.d("No coarse location permission, connecting without LE scan");
|
||||
@@ -515,7 +486,6 @@ public abstract class BluetoothCommunication {
|
||||
Timber.d("Stop Le san");
|
||||
scanSubscription.dispose();
|
||||
scanSubscription = null;
|
||||
|
||||
}
|
||||
|
||||
Handler handler = new Handler();
|
||||
@@ -527,19 +497,18 @@ public abstract class BluetoothCommunication {
|
||||
connectionObservable = bleDevice
|
||||
.establishConnection(false)
|
||||
.takeUntil(disconnectTriggerSubject)
|
||||
.doOnError(throwable -> setBtStatus(BT_STATUS_CODE.BT_CONNECTION_RETRYING))
|
||||
.doOnError(throwable -> setBluetoothStatus(BT_STATUS.CONNECTION_RETRYING))
|
||||
.subscribeOn(Schedulers.trampoline())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.compose(ReplayingShare.instance());
|
||||
|
||||
if (isConnected()) {
|
||||
disconnect();
|
||||
} else {
|
||||
initStepNr = -1;
|
||||
cmdStepNr = -1;
|
||||
cleanupStepNr = -1;
|
||||
stepNr = 0;
|
||||
|
||||
setBtMonitoringOn();
|
||||
setBtMachineState(BT_MACHINE_STATE.BT_INIT_STATE);
|
||||
nextMachineStep();
|
||||
resetDisconnectTimer();
|
||||
}
|
||||
}
|
||||
@@ -552,7 +521,7 @@ public abstract class BluetoothCommunication {
|
||||
connectionState -> {
|
||||
switch (connectionState) {
|
||||
case CONNECTED:
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED);
|
||||
setBluetoothStatus(BT_STATUS.CONNECTION_ESTABLISHED);
|
||||
break;
|
||||
case CONNECTING:
|
||||
// empty
|
||||
@@ -561,7 +530,7 @@ public abstract class BluetoothCommunication {
|
||||
// empty
|
||||
break;
|
||||
case DISCONNECTED:
|
||||
// setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
||||
// setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@@ -571,20 +540,20 @@ public abstract class BluetoothCommunication {
|
||||
compositeDisposable.add(disposableConnectionState);
|
||||
}
|
||||
|
||||
private void onError(Throwable throwable) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, throwable.getMessage());
|
||||
protected void onError(Throwable throwable) {
|
||||
setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, throwable.getMessage());
|
||||
}
|
||||
|
||||
private boolean isConnected() {
|
||||
return bleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED;
|
||||
}
|
||||
|
||||
public void resetDisconnectTimer() {
|
||||
private void resetDisconnectTimer() {
|
||||
disconnectHandler.removeCallbacksAndMessages(null);
|
||||
disconnectWithDelay();
|
||||
}
|
||||
|
||||
public void disconnectWithDelay() {
|
||||
private void disconnectWithDelay() {
|
||||
disconnectHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -594,48 +563,16 @@ public abstract class BluetoothCommunication {
|
||||
}, 60000); // 60s timeout
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from a Bluetooth device
|
||||
*/
|
||||
public void disconnect() {
|
||||
Timber.d("Bluetooth disconnect");
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_DISCONNECT);
|
||||
if (scanSubscription != null) {
|
||||
scanSubscription.dispose();
|
||||
}
|
||||
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();
|
||||
}
|
||||
break;
|
||||
case BT_CLEANUP_STATE:
|
||||
cleanupStepNr++;
|
||||
Timber.d("CLEANUP STATE: %d", cleanupStepNr);
|
||||
if (!nextCleanUpCmd(cleanupStepNr)) {
|
||||
Timber.d("Cleanup Bluetooth disconnect");
|
||||
disconnect();
|
||||
}
|
||||
break;
|
||||
private synchronized void nextMachineStep() {
|
||||
if (!stopped) {
|
||||
Timber.d("Step Nr " + stepNr);
|
||||
if (onNextStep(stepNr)) {
|
||||
stepNr++;
|
||||
nextMachineStep();
|
||||
} else {
|
||||
Timber.d("Invoke delayed disconnect in 60s");
|
||||
disconnectWithDelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ package com.health.openscale.core.bluetooth;
|
||||
import android.content.Context;
|
||||
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
import com.polidea.rxandroidble2.RxBleClient;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -44,8 +43,8 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC
|
||||
);
|
||||
@@ -70,16 +69,6 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clearEEPROM()
|
||||
{
|
||||
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
|
||||
|
||||
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
|
||||
@@ -156,18 +145,18 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
scaleBtData.setWater(Float.parseFloat(csvField[8]));
|
||||
scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
} 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) {
|
||||
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) {
|
||||
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;
|
||||
default:
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error unknown MCU command : " + btString);
|
||||
setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Error unknown MCU command : " + btString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -164,14 +164,14 @@ public class BluetoothDebug extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr)
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr)
|
||||
{
|
||||
case 0:
|
||||
doBluetoothDiscoverServices();
|
||||
discoverBluetoothServices();
|
||||
break;
|
||||
case 1:
|
||||
int offset = stateNr;
|
||||
int offset = stepNr;
|
||||
|
||||
for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) {
|
||||
offset = readServiceCharacteristics(service, offset);
|
||||
@@ -181,7 +181,7 @@ public class BluetoothDebug extends BluetoothCommunication {
|
||||
logService(service, false);
|
||||
}
|
||||
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
||||
setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
||||
break;
|
||||
case 2:
|
||||
disconnect();
|
||||
@@ -192,14 +192,4 @@ public class BluetoothDebug extends BluetoothCommunication {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -56,8 +56,8 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
//Tell device to send us weight measurements
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||
@@ -65,19 +65,11 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
|
||||
case 1:
|
||||
sendMessage(R.string.info_step_on_scale, 0);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void parseBytes(byte[] weightBytes) {
|
||||
@@ -135,7 +127,7 @@ public class BluetoothDigooDGSO38H extends BluetoothCommunication {
|
||||
scaleBtData.setVisceralFat(visceralFat);
|
||||
}
|
||||
scaleBtData.setWeight(weight);
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -44,13 +44,8 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
|
||||
@@ -103,12 +98,7 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,6 +140,6 @@ public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
|
||||
scaleBtData.setBone(bone);
|
||||
scaleBtData.setVisceralFat(visceralFat);
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
}
|
||||
}
|
||||
|
@@ -42,11 +42,10 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC
|
||||
);
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||
break;
|
||||
case 1:
|
||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
@@ -71,16 +70,6 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -118,6 +107,6 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
|
||||
scaleBtData.setVisceralFat(visc_fat);
|
||||
scaleBtData.setDateTime(new Date());
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
}
|
||||
}
|
||||
|
@@ -39,8 +39,8 @@ public class BluetoothHesley extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||
break;
|
||||
@@ -58,16 +58,6 @@ public class BluetoothHesley extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -101,6 +91,6 @@ public class BluetoothHesley extends BluetoothCommunication {
|
||||
scaleBtData.setBone(bone);
|
||||
scaleBtData.setDateTime(new Date());
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
}
|
||||
}
|
@@ -23,14 +23,13 @@ import android.bluetooth.BluetoothSocket;
|
||||
import android.content.Context;
|
||||
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
import com.polidea.rxandroidble2.RxBleClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.UUID;
|
||||
import java.util.Date;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -57,20 +56,8 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
Timber.w("ihealthHS3 - nextInitCmd - 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");
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
Timber.w("ihealthHS3 - onNextStep - returning false");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -79,7 +66,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
if (btAdapter == null) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND);
|
||||
setBluetoothStatus(BT_STATUS.NO_DEVICE_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -88,7 +75,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
// Get a BluetoothSocket to connect with the given BluetoothDevice
|
||||
btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
|
||||
} 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;
|
||||
return;
|
||||
}
|
||||
@@ -103,7 +90,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
btSocket.connect();
|
||||
|
||||
// Bluetooth connection was successful
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED);
|
||||
setBluetoothStatus(BT_STATUS.CONNECTION_ESTABLISHED);
|
||||
|
||||
btConnectThread = new BluetoothConnectedThread();
|
||||
btConnectThread.start();
|
||||
@@ -111,7 +98,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
} catch (IOException connectException) {
|
||||
// Unable to connect; close the socket and get out
|
||||
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 = null;
|
||||
} 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();
|
||||
btOutStream = btSocket.getOutputStream();
|
||||
} 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);
|
||||
|
||||
if (scaleMeasurement != null) {
|
||||
addScaleData(scaleMeasurement);
|
||||
addScaleMeasurement(scaleMeasurement);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -227,7 +214,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
|
||||
} catch (IOException e) {
|
||||
cancel();
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
||||
setBluetoothStatus(BT_STATUS.CONNECTION_LOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +253,7 @@ public class BluetoothIhealthHS3 extends BluetoothCommunication {
|
||||
try {
|
||||
btOutStream.write(bytes);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -73,8 +73,8 @@ public class BluetoothInlife extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||
break;
|
||||
@@ -98,16 +98,6 @@ public class BluetoothInlife extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -229,7 +219,7 @@ public class BluetoothInlife extends BluetoothCommunication {
|
||||
measurement.setLbm(lbm);
|
||||
measurement.setVisceralFat(clamp(visceral, 1, 50));
|
||||
|
||||
addScaleData(measurement);
|
||||
addScaleMeasurement(measurement);
|
||||
|
||||
sendCommand(0xd4);
|
||||
}
|
||||
|
@@ -82,8 +82,8 @@ public class BluetoothMGB extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(uuid_char_ctrl
|
||||
);
|
||||
@@ -126,19 +126,6 @@ public class BluetoothMGB extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
packet_buf = value;
|
||||
@@ -202,7 +189,7 @@ public class BluetoothMGB extends BluetoothCommunication {
|
||||
popInt(); // unknown =02
|
||||
popInt(); // unknown =47;48;4e;4b;42
|
||||
|
||||
addScaleData(measurement);
|
||||
addScaleMeasurement(measurement);
|
||||
|
||||
// Visceral fat?
|
||||
// Standard weight?
|
||||
|
@@ -51,13 +51,8 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
// set indication on for feature characteristic
|
||||
setIndicationOn(FEATURE_MEASUREMENT_CHARACTERISTIC);
|
||||
@@ -92,12 +87,6 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -109,7 +98,7 @@ public class BluetoothMedisanaBS44x extends BluetoothCommunication {
|
||||
if (characteristic.equals(FEATURE_MEASUREMENT_CHARACTERISTIC)) {
|
||||
parseFeatureData(data);
|
||||
|
||||
addScaleData(btScaleMeasurement);
|
||||
addScaleMeasurement(btScaleMeasurement);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ import java.util.UUID;
|
||||
|
||||
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 {
|
||||
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 scaleDay = (int) data[3];
|
||||
|
||||
if (currentYear == scaleYear && currentMonth == scaleMonth && currentDay == scaleDay) {
|
||||
setBtMachineState(BT_MACHINE_STATE.BT_CMD_STATE);
|
||||
} else {
|
||||
if (!(currentYear == scaleYear && currentMonth == scaleMonth && currentDay == scaleDay)) {
|
||||
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
|
||||
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) {
|
||||
@@ -95,82 +113,37 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
// read device time
|
||||
readBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME);
|
||||
break;
|
||||
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
|
||||
byte[] magicBytes = new byte[]{(byte)0x01, (byte)0x96, (byte)0x8a, (byte)0xbd, (byte)0x62};
|
||||
|
||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, magicBytes);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
case 0:
|
||||
case 2:
|
||||
// set notification on for weight measurement history
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
// set notification on for weight measurement
|
||||
setNotificationOn(BluetoothGattUuid.CHARACTERISTIC_WEIGHT_MEASUREMENT);
|
||||
break;
|
||||
case 2:
|
||||
case 4:
|
||||
// configure scale to get only last measurements
|
||||
int uniqueNumber = getUniqueNumber();
|
||||
|
||||
byte[] userIdentifier = new byte[]{(byte)0x01, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
|
||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
// invoke receiving history data
|
||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
||||
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);
|
||||
stopMachineState();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -225,13 +198,13 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
||||
scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit()));
|
||||
scaleBtData.setDateTime(date_time);
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
} else {
|
||||
Timber.e("Invalid Mi scale weight year %d", year);
|
||||
}
|
||||
}
|
||||
} 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() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,7 +31,6 @@ import com.health.openscale.gui.views.FatMeasurementView;
|
||||
import com.health.openscale.gui.views.LBMMeasurementView;
|
||||
import com.health.openscale.gui.views.MeasurementViewSettings;
|
||||
import com.health.openscale.gui.views.WaterMeasurementView;
|
||||
import com.polidea.rxandroidble2.RxBleClient;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -43,7 +42,7 @@ import java.util.UUID;
|
||||
|
||||
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 {
|
||||
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
|
||||
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) {
|
||||
@@ -88,8 +98,8 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
// set scale units
|
||||
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};
|
||||
|
||||
writeBytes(
|
||||
BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
|
||||
writeBytes(BluetoothGattUuid.CHARACTERISTIC_CURRENT_TIME, dateTimeByte);
|
||||
break;
|
||||
case 2:
|
||||
// set notification on for weight measurement history
|
||||
setNotificationOn(
|
||||
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC
|
||||
);
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
case 0:
|
||||
case 3:
|
||||
// configure scale to get only last measurements
|
||||
int uniqueNumber = getUniqueNumber();
|
||||
|
||||
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;
|
||||
case 1:
|
||||
// set notification on for weight measurement history
|
||||
setNotificationOn(
|
||||
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
case 4:
|
||||
// invoke receiving history data
|
||||
writeBytes(
|
||||
WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
||||
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
|
||||
);
|
||||
writeBytes(WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
||||
stopMachineState();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -239,13 +199,13 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
||||
EstimatedLBMMetric.FORMULA.valueOf(settings.getEstimationFormula()));
|
||||
scaleBtData.setLbm(lbmMetric.getLBM(selectedUser, scaleBtData));
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
} else {
|
||||
Timber.e("Invalid Mi scale weight year %d", year);
|
||||
}
|
||||
}
|
||||
} 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() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -47,12 +47,10 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(
|
||||
WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION
|
||||
);
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION);
|
||||
break;
|
||||
case 1:
|
||||
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
@@ -83,16 +81,6 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -155,6 +143,6 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
||||
|
||||
Timber.d("scale measurement [%s]", scaleBtData);
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
}
|
||||
}
|
||||
|
@@ -84,13 +84,8 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
// set notification on for custom characteristic 1 (weight, time, and others)
|
||||
setNotificationOn(CUSTOM1_MEASUREMENT_CHARACTERISTIC);
|
||||
@@ -101,7 +96,7 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
||||
break;
|
||||
case 2:
|
||||
// write magicnumber 0x130915011000000042 to 0xffe3
|
||||
byte[] ffe3magicBytes = new byte[] {(byte)0x13, (byte)0x09, (byte)0x15, (byte)0x01, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x42};
|
||||
byte[] ffe3magicBytes = new byte[]{(byte) 0x13, (byte) 0x09, (byte) 0x15, (byte) 0x01, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x42};
|
||||
writeBytes(CUSTOM3_MEASUREMENT_CHARACTERISTIC, ffe3magicBytes);
|
||||
break;
|
||||
case 3:
|
||||
@@ -110,34 +105,23 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
||||
timestamp -= SCALE_UNIX_TIMESTAMP_OFFSET;
|
||||
byte[] date = new byte[4];
|
||||
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);
|
||||
break;
|
||||
case 4:
|
||||
sendMessage(R.string.info_step_on_scale, 0);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
|
||||
switch (stateNr) {
|
||||
case 0:
|
||||
/*case 5:
|
||||
// send stop command to scale (0x1f05151049)
|
||||
writeBytes(CUSTOM3_MEASUREMENT_CHARACTERISTIC, new byte[]{(byte)0x1f, (byte)0x05, (byte)0x15, (byte)0x10, (byte)0x49});
|
||||
break;
|
||||
break;*/
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -198,7 +182,7 @@ public class BluetoothQNScale extends BluetoothCommunication {
|
||||
btScaleMeasurement.setMuscle(qnscalelib.getMuscle(weightKg, impedance));
|
||||
btScaleMeasurement.setBone(qnscalelib.getBone(weightKg, impedance));
|
||||
btScaleMeasurement.setWeight(weightKg);
|
||||
addScaleData(btScaleMeasurement);
|
||||
addScaleMeasurement(btScaleMeasurement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -68,8 +68,8 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||
break;
|
||||
@@ -85,16 +85,6 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -107,7 +97,7 @@ public class BluetoothSenssun extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
if (isBitSet(WeightFatMus,2) ) {
|
||||
addScaleData(measurement);
|
||||
addScaleMeasurement(measurement);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -92,7 +92,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
|
||||
* set-broadcast-id command, and should disconnect after the write succeeds.
|
||||
*
|
||||
* @see #onPasswordReceived
|
||||
* @see #nextBluetoothCmd
|
||||
* @see #onNextStep
|
||||
*/
|
||||
private boolean pairing = false;
|
||||
|
||||
@@ -119,13 +119,13 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
Timber.i("nextInitCmd(%d)", stateNr);
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
Timber.i("onNextStep(%d)", stepNr);
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
// Register for notifications of the measurement characteristic.
|
||||
setIndicationOn(MEASUREMENT_CHARACTERISTIC_UUID);
|
||||
return true; // more commands follow
|
||||
break; // more commands follow
|
||||
case 1:
|
||||
// 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
|
||||
// to handle pairing correctly.
|
||||
setIndicationOn(UPLOAD_COMMAND_CHARACTERISTIC_UUID);
|
||||
// falls through
|
||||
default:
|
||||
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:
|
||||
break;
|
||||
case 2:
|
||||
// This state is triggered by the write in onPasswordReceived()
|
||||
if (pairing) {
|
||||
pairing = false;
|
||||
disconnect();
|
||||
}
|
||||
return false; // no more commands;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
Timber.i("nextCleanUpCmd(%d)", stateNr);
|
||||
switch (stateNr) {
|
||||
case 0:
|
||||
break;
|
||||
case 3:
|
||||
writeCommand(DOWNLOAD_INFORMATION_ENABLE_DISCONNECT_COMMAND);
|
||||
// falls through
|
||||
break;
|
||||
default:
|
||||
return false; // no more commands
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,7 +229,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication {
|
||||
return;
|
||||
}
|
||||
|
||||
addScaleData(measurement);
|
||||
addScaleMeasurement(measurement);
|
||||
}
|
||||
|
||||
public ScaleMeasurement parseScaleMeasurementData(byte[] data, ScaleUser user) {
|
||||
|
@@ -52,8 +52,8 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch (stepNr) {
|
||||
case 0:
|
||||
byte[] userId = Converters.toInt16Be(getUniqueNumber());
|
||||
|
||||
@@ -82,8 +82,7 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
||||
writeBytes(WEIGHT_CMD_CHARACTERISTIC, set_time);
|
||||
break;
|
||||
case 2:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC
|
||||
);
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
|
||||
break;
|
||||
case 3:
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextCleanUpCmd(int stateNr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] value) {
|
||||
final byte[] data = value;
|
||||
@@ -154,7 +143,7 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
||||
Timber.d("scale measurement [%s]", scaleBtData);
|
||||
}
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
addScaleMeasurement(scaleBtData);
|
||||
}
|
||||
|
||||
private int getUniqueNumber() {
|
||||
|
@@ -154,8 +154,8 @@ public class MainActivity extends BaseAppCompatActivity
|
||||
if(prefs.edit().putInt("launchCount", ++launchCount).commit()){
|
||||
valueOfCountModified = true;
|
||||
|
||||
// ask the user once for feedback on the 30th app launch
|
||||
if(launchCount == 30){
|
||||
// ask the user once for feedback on the 15th app launch
|
||||
if(launchCount == 15){
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
builder.setMessage(R.string.label_feedback_message_enjoying)
|
||||
@@ -476,10 +476,10 @@ public class MainActivity extends BaseAppCompatActivity
|
||||
@Override
|
||||
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) {
|
||||
case BT_RETRIEVE_SCALE_DATA:
|
||||
switch (btStatus) {
|
||||
case RETRIEVE_SCALE_DATA:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success);
|
||||
ScaleMeasurement scaleBtData = (ScaleMeasurement) msg.obj;
|
||||
|
||||
@@ -496,42 +496,42 @@ public class MainActivity extends BaseAppCompatActivity
|
||||
|
||||
openScale.addScaleData(scaleBtData, true);
|
||||
break;
|
||||
case BT_INIT_PROCESS:
|
||||
case INIT_PROCESS:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success);
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_init), Toast.LENGTH_SHORT).show();
|
||||
Timber.d("Bluetooth initializing");
|
||||
break;
|
||||
case BT_CONNECTION_LOST:
|
||||
case CONNECTION_LOST:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_lost), Toast.LENGTH_SHORT).show();
|
||||
Timber.d("Bluetooth connection lost");
|
||||
break;
|
||||
case BT_NO_DEVICE_FOUND:
|
||||
case NO_DEVICE_FOUND:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device), Toast.LENGTH_SHORT).show();
|
||||
Timber.e("No Bluetooth device found");
|
||||
break;
|
||||
case BT_CONNECTION_RETRYING:
|
||||
case CONNECTION_RETRYING:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_searching);
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device_retrying), Toast.LENGTH_SHORT).show();
|
||||
Timber.e("No Bluetooth device found retrying");
|
||||
break;
|
||||
case BT_CONNECTION_ESTABLISHED:
|
||||
case CONNECTION_ESTABLISHED:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_success);
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_successful), Toast.LENGTH_SHORT).show();
|
||||
Timber.d("Bluetooth connection successful established");
|
||||
break;
|
||||
case BT_CONNECTION_DISCONNECT:
|
||||
case CONNECTION_DISCONNECT:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
|
||||
Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_connection_disconnected), Toast.LENGTH_SHORT).show();
|
||||
Timber.d("Bluetooth connection successful disconnected");
|
||||
break;
|
||||
case BT_UNEXPECTED_ERROR:
|
||||
case UNEXPECTED_ERROR:
|
||||
setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost);
|
||||
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);
|
||||
break;
|
||||
case BT_SCALE_MESSAGE:
|
||||
case SCALE_MESSAGE:
|
||||
String toastMessage = String.format(getResources().getString(msg.arg1), msg.obj);
|
||||
Toast.makeText(getApplicationContext(), toastMessage, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
|
@@ -31,7 +31,6 @@ import android.preference.Preference;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.health.openscale.R;
|
||||
@@ -289,8 +288,8 @@ public class BluetoothPreferences extends PreferenceFragment {
|
||||
Handler btHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (BluetoothCommunication.BT_STATUS_CODE.values()[msg.what]) {
|
||||
case BT_CONNECTION_LOST:
|
||||
switch (BluetoothCommunication.BT_STATUS.values()[msg.what]) {
|
||||
case CONNECTION_LOST:
|
||||
OpenScale.getInstance().disconnectFromBluetoothDevice();
|
||||
dialog.dismiss();
|
||||
break;
|
||||
|
@@ -230,8 +230,9 @@ public class ChartMeasurementView extends LineChart {
|
||||
}
|
||||
|
||||
private void setMeasurementList(List<ScaleMeasurement> measurementList) {
|
||||
scaleMeasurementList = measurementList;
|
||||
|
||||
if (!measurementList.isEmpty()) {
|
||||
scaleMeasurementList = measurementList;
|
||||
lastMeasurement = measurementList.get(0);
|
||||
Collections.reverse(measurementList);
|
||||
firstMeasurement = measurementList.get(0);
|
||||
@@ -409,12 +410,12 @@ public class ChartMeasurementView extends LineChart {
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
clear();
|
||||
|
||||
if (scaleMeasurementList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
clear();
|
||||
|
||||
List<ILineDataSet> lineDataSets = new ArrayList<>();
|
||||
|
||||
ScaleMeasurement[] avgMeasurementList = averageScaleMeasurementList(scaleMeasurementList);
|
||||
|
Reference in New Issue
Block a user