use of com.polidea.rxandroidble2.RxBleConnection in project xDrip by NightscoutFoundation.
the class MiBandService method ProcessAuthCommands.
@SuppressLint("CheckResult")
private void ProcessAuthCommands(RxBleConnection connection, byte[] value) {
if (value[0] == AUTH_RESPONSE && value[1] == AUTH_SEND_KEY && (value[2] & 0x0f) == AUTH_SUCCESS) {
// get random key from band
connection.writeCharacteristic(authorisation.getCharacteristicUUID(), authorisation.getAuthKeyRequest()).subscribe(val -> {
if (d)
UserError.Log.d(TAG, "Wrote OPCODE_AUTH_REQ1: " + JoH.bytesToHex(val));
}, throwable -> {
UserError.Log.e(TAG, "Could not write OPCODE_AUTH_REQ1: " + throwable);
});
} else if (value[0] == AUTH_RESPONSE && (value[1] & 0x0f) == AUTH_REQUEST_RANDOM_AUTH_NUMBER && value[2] == AUTH_SUCCESS) {
byte[] tmpValue = Arrays.copyOfRange(value, 3, 19);
try {
byte[] authReply = authorisation.calculateAuthReply(tmpValue);
// get random key from band
connection.writeCharacteristic(authorisation.getCharacteristicUUID(), authReply).subscribe(val -> {
if (d)
UserError.Log.d(TAG, "Wrote OPCODE_AUTH_REQ2: " + JoH.bytesToHex(val));
}, throwable -> {
UserError.Log.e(TAG, "Could not write OPCODE_AUTH_REQ2: " + throwable);
});
} catch (Exception e) {
JoH.static_toast_long(e.getMessage());
UserError.Log.e(TAG, (e.getMessage()));
changeState(AUTHORIZE_FAILED);
}
} else if (value[0] == AUTH_RESPONSE && (value[1] & 0x0f) == AUTH_SEND_ENCRYPTED_AUTH_NUMBER && value[2] == AUTH_SUCCESS) {
isNeedToAuthenticate = false;
if (MiBand.getPersistentAuthMac().isEmpty()) {
MiBand.setPersistentAuthMac(MiBand.getMac());
MiBand.setPersistentAuthKey(JoH.bytesToHex(authorisation.getLocalKey()), MiBand.getPersistentAuthMac());
String msg = "MiBand was successfully authenticated";
JoH.static_toast_long(msg);
UserError.Log.e(TAG, msg);
}
if (authSubscription != null) {
authSubscription.unsubscribe();
}
changeNextState();
} else if (value[0] == AUTH_RESPONSE && (((value[2] & 0x0f) == AUTH_FAIL) || (value[2] == AUTH_MIBAND4_FAIL) || (value[2] == AUTH_MIBAND4_CODE_FAIL))) {
MiBand.setPersistentAuthKey("", MiBand.getPersistentAuthMac());
if (authSubscription != null) {
authSubscription.unsubscribe();
}
String msg = "Cannot authorize miband, please recheck Auth code";
JoH.static_toast_long(msg);
UserError.Log.e(TAG, msg);
changeState(AUTHORIZE_FAILED);
}
}
use of com.polidea.rxandroidble2.RxBleConnection in project xDrip by NightscoutFoundation.
the class Ob1G5StateMachine method doGetData.
// Get Data
@SuppressLint("CheckResult")
public static boolean doGetData(Ob1G5CollectionService parent, RxBleConnection connection) {
if (connection == null)
return false;
// TODO switch modes depending on conditions as to whether we are using internal
final boolean use_g5_internal_alg = Pref.getBooleanDefaultFalse("ob1_g5_use_transmitter_alg");
UserError.Log.d(TAG, use_g5_internal_alg ? ("Requesting Glucose Data " + (usingG6() ? "G6" : "G5")) : "Requesting Sensor Data");
if (!use_g5_internal_alg) {
// not applicable
parent.lastSensorStatus = null;
parent.lastUsableGlucosePacketTime = 0;
}
connection.setupIndication(Control).doOnNext(notificationObservable -> {
if (d)
UserError.Log.d(TAG, "Notifications enabled");
speakSlowly();
connection.writeCharacteristic(Control, nn(use_g5_internal_alg ? (getEGlucose() ? new EGlucoseTxMessage().byteSequence : new GlucoseTxMessage().byteSequence) : new SensorTxMessage().byteSequence)).subscribe(characteristicValue -> {
if (d)
UserError.Log.d(TAG, "Wrote SensorTxMessage request");
}, throwable -> {
UserError.Log.e(TAG, "Failed to write SensorTxMessage: " + throwable);
if (throwable instanceof BleGattCharacteristicException) {
final int status = ((BleGattCharacteristicException) throwable).getStatus();
UserError.Log.e(TAG, "Got status message: " + getStatusName(status));
if (status == 8) {
UserError.Log.e(TAG, "Request rejected due to Insufficient Authorization failure!");
parent.authResult(false);
}
}
});
}).flatMap(notificationObservable -> notificationObservable).timeout(6, TimeUnit.SECONDS).subscribe(bytes -> {
// incoming data notifications
UserError.Log.d(TAG, "Received indication bytes: " + JoH.bytesToHex(bytes));
final PacketShop data_packet = classifyPacket(bytes);
switch(data_packet.type) {
case SensorRxMessage:
try {
checkVersionAndBattery(parent, connection);
} finally {
processSensorRxMessage((SensorRxMessage) data_packet.msg);
parent.msg("Got data");
parent.updateLast(tsl());
parent.clearErrors();
}
break;
case VersionRequest1RxMessage:
if (!setStoredFirmwareBytes(getTransmitterID(), 1, bytes, true)) {
UserError.Log.e(TAG, "Could not save out firmware version!");
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
if (JoH.ratelimit("g6-evaluate", 600)) {
Inevitable.task("evaluteG6Settings", 10000, () -> evaluateG6Settings());
}
break;
case VersionRequestRxMessage:
if (!setStoredFirmwareBytes(getTransmitterID(), 0, bytes, true)) {
UserError.Log.e(TAG, "Could not save out firmware version!");
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
if (JoH.ratelimit("g6-evaluate", 600)) {
Inevitable.task("evaluteG6Settings", 10000, () -> evaluateG6Settings());
}
break;
case VersionRequest2RxMessage:
if (!setStoredFirmwareBytes(getTransmitterID(), 2, bytes, true)) {
UserError.Log.e(TAG, "Could not save out firmware version!");
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
if (JoH.ratelimit("g6-evaluate", 600)) {
Inevitable.task("evaluteG6Settings", 10000, () -> evaluateG6Settings());
}
break;
case BatteryInfoRxMessage:
if (!setStoredBatteryBytes(getTransmitterID(), bytes)) {
UserError.Log.e(TAG, "Could not save out battery data!");
} else {
if (parent.android_wear) {
PersistentStore.setBoolean(G5_BATTERY_WEARABLE_SEND, true);
}
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
break;
case SessionStartRxMessage:
final SessionStartRxMessage session_start = (SessionStartRxMessage) data_packet.msg;
if (session_start.isOkay()) {
// TODO persist this
parent.msg("Session Started Successfully: " + JoH.dateTimeText(session_start.getSessionStart()) + " " + JoH.dateTimeText(session_start.getRequestedStart()) + " " + JoH.dateTimeText(session_start.getTransmitterTime()));
DexResetHelper.cancel();
} else {
final String msg = "Session Start Failed: " + session_start.message();
parent.msg(msg);
UserError.Log.ueh(TAG, msg);
JoH.showNotification(devName() + " Start Failed", msg, null, Constants.G5_START_REJECT, true, true, false);
UserError.Log.ueh(TAG, "Session Start failed info: " + JoH.dateTimeText(session_start.getSessionStart()) + " " + JoH.dateTimeText(session_start.getRequestedStart()) + " " + JoH.dateTimeText(session_start.getTransmitterTime()));
if (session_start.isFubar()) {
final long tk = DexTimeKeeper.getDexTime(getTransmitterID(), tsl());
if (tk > 0) {
DexResetHelper.offer("Unusual session start failure, is transmitter crashed? Try Hard Reset?");
} else {
UserError.Log.e(TAG, "No reset as TimeKeeper reports invalid: " + tk);
}
}
if (Pref.getBooleanDefaultFalse("ob1_g5_restart_sensor") && (Sensor.isActive())) {
if (pratelimit("secondary-g5-start", 1800)) {
UserError.Log.ueh(TAG, "Trying to Start sensor again");
startSensor(tsl());
}
}
}
reReadGlucoseData();
break;
case SessionStopRxMessage:
final SessionStopRxMessage session_stop = (SessionStopRxMessage) data_packet.msg;
if (session_stop.isOkay()) {
// TODO persist this
final String msg = "Session Stopped Successfully: " + JoH.dateTimeText(session_stop.getSessionStart()) + " " + JoH.dateTimeText(session_stop.getSessionStop());
parent.msg(msg);
UserError.Log.ueh(TAG, msg);
reReadGlucoseData();
enqueueUniqueCommand(new TimeTxMessage(), "Query time after stop");
} else {
// TODO what does an error when session isn't started look like? Probably best to downgrade those somewhat
final String msg = "Session Stop Failed: packet valid: " + session_stop.isValid() + " Status code: " + session_stop.getStatus();
UserError.Log.uel(TAG, msg);
}
break;
case GlucoseRxMessage:
final GlucoseRxMessage glucose = (GlucoseRxMessage) data_packet.msg;
parent.processCalibrationState(glucose.calibrationState());
if (glucose.usable()) {
parent.msg("Got " + devName() + " glucose");
} else {
parent.msg("Got data from " + devName());
}
glucoseRxCommon(glucose, parent, connection);
break;
// TODO base class duplication
case EGlucoseRxMessage:
final EGlucoseRxMessage eglucose = (EGlucoseRxMessage) data_packet.msg;
parent.processCalibrationState(eglucose.calibrationState());
if (eglucose.usable()) {
parent.msg("Got G6 glucose");
} else {
parent.msg("Got data from G6");
}
glucoseRxCommon(eglucose, parent, connection);
break;
case CalibrateRxMessage:
final CalibrateRxMessage calibrate = (CalibrateRxMessage) data_packet.msg;
if (calibrate.accepted()) {
parent.msg("Calibration accepted");
UserError.Log.ueh(TAG, "Calibration accepted by transmitter");
} else {
final String msg = "Calibration rejected: " + calibrate.message();
UserError.Log.wtf(TAG, msg);
parent.msg(msg);
JoH.showNotification("Calibration rejected", msg, null, Constants.G5_CALIBRATION_REJECT, true, true, false);
}
reReadGlucoseData();
break;
case BackFillRxMessage:
final BackFillRxMessage backfill = (BackFillRxMessage) data_packet.msg;
if (backfill.valid()) {
UserError.Log.d(TAG, "Backfill request confirmed");
} else {
UserError.Log.wtf(TAG, "Backfill request corrupted!");
}
break;
case TransmitterTimeRxMessage:
// This message is received every 120-125m
final TransmitterTimeRxMessage txtime = (TransmitterTimeRxMessage) data_packet.msg;
DexTimeKeeper.updateAge(getTransmitterID(), txtime.getCurrentTime(), true);
if (txtime.sessionInProgress()) {
UserError.Log.e(TAG, "Session start time reports: " + JoH.dateTimeText(txtime.getRealSessionStartTime()) + " Duration: " + JoH.niceTimeScalar(txtime.getSessionDuration()));
DexSessionKeeper.setStart(txtime.getRealSessionStartTime());
} else {
UserError.Log.e(TAG, "Session start time reports: No session in progress");
DexSessionKeeper.clearStart();
}
if (Pref.getBooleanDefaultFalse("ob1_g5_preemptive_restart")) {
int restartDaysThreshold = usingG6() ? 9 : 6;
if (txtime.getSessionDuration() > Constants.DAY_IN_MS * restartDaysThreshold && txtime.getSessionDuration() < Constants.MONTH_IN_MS) {
UserError.Log.uel(TAG, "Requesting preemptive session restart");
restartSensorWithTimeTravel();
}
}
break;
case F2DUnknownRxMessage:
UserError.Log.d(TAG, "Received F2D message");
try {
checkVersionAndBattery(parent, connection);
} finally {
parent.msg("Got no raw");
// TODO verify if this is ok to do here
parent.updateLast(tsl());
// TODO verify if this is ok to do here
parent.clearErrors();
}
break;
default:
UserError.Log.e(TAG, "Got unknown packet rx: " + JoH.bytesToHex(bytes));
break;
}
if (!queued(parent, connection)) {
inevitableDisconnect(parent, connection);
}
}, throwable -> {
if (!(throwable instanceof OperationSuccess)) {
if (throwable instanceof BleDisconnectedException) {
UserError.Log.d(TAG, "Disconnected when waiting to receive indication: " + throwable);
parent.changeState(Ob1G5CollectionService.STATE.CLOSE);
} else {
UserError.Log.e(TAG, "Error receiving indication: " + throwable);
// throwable.printStackTrace();
disconnectNow(parent, connection);
}
}
});
return true;
}
use of com.polidea.rxandroidble2.RxBleConnection in project xDrip-plus by jamorham.
the class Ob1G5StateMachine method doGetData.
// Get Data
@SuppressLint("CheckResult")
public static boolean doGetData(Ob1G5CollectionService parent, RxBleConnection connection) {
if (connection == null)
return false;
// TODO switch modes depending on conditions as to whether we are using internal
final boolean use_g5_internal_alg = Pref.getBooleanDefaultFalse("ob1_g5_use_transmitter_alg");
UserError.Log.d(TAG, use_g5_internal_alg ? ("Requesting Glucose Data " + (usingG6() ? "G6" : "G5")) : "Requesting Sensor Data");
if (!use_g5_internal_alg) {
// not applicable
parent.lastSensorStatus = null;
parent.lastUsableGlucosePacketTime = 0;
}
connection.setupIndication(Control).doOnNext(notificationObservable -> {
if (d)
UserError.Log.d(TAG, "Notifications enabled");
speakSlowly();
connection.writeCharacteristic(Control, nn(use_g5_internal_alg ? (getEGlucose() ? new EGlucoseTxMessage().byteSequence : new GlucoseTxMessage().byteSequence) : new SensorTxMessage().byteSequence)).subscribe(characteristicValue -> {
if (d)
UserError.Log.d(TAG, "Wrote SensorTxMessage request");
}, throwable -> {
UserError.Log.e(TAG, "Failed to write SensorTxMessage: " + throwable);
if (throwable instanceof BleGattCharacteristicException) {
final int status = ((BleGattCharacteristicException) throwable).getStatus();
UserError.Log.e(TAG, "Got status message: " + getStatusName(status));
if (status == 8) {
UserError.Log.e(TAG, "Request rejected due to Insufficient Authorization failure!");
parent.authResult(false);
}
}
});
}).flatMap(notificationObservable -> notificationObservable).timeout(6, TimeUnit.SECONDS).subscribe(bytes -> {
// incoming data notifications
UserError.Log.d(TAG, "Received indication bytes: " + JoH.bytesToHex(bytes));
final PacketShop data_packet = classifyPacket(bytes);
switch(data_packet.type) {
case SensorRxMessage:
try {
checkVersionAndBattery(parent, connection);
} finally {
processSensorRxMessage((SensorRxMessage) data_packet.msg);
parent.msg("Got data");
parent.updateLast(tsl());
parent.clearErrors();
}
break;
case VersionRequest1RxMessage:
if (!setStoredFirmwareBytes(getTransmitterID(), 1, bytes, true)) {
UserError.Log.e(TAG, "Could not save out firmware version!");
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
if (JoH.ratelimit("g6-evaluate", 600)) {
Inevitable.task("evaluteG6Settings", 10000, () -> evaluateG6Settings());
}
break;
case VersionRequestRxMessage:
if (!setStoredFirmwareBytes(getTransmitterID(), 0, bytes, true)) {
UserError.Log.e(TAG, "Could not save out firmware version!");
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
if (JoH.ratelimit("g6-evaluate", 600)) {
Inevitable.task("evaluteG6Settings", 10000, () -> evaluateG6Settings());
}
break;
case VersionRequest2RxMessage:
if (!setStoredFirmwareBytes(getTransmitterID(), 2, bytes, true)) {
UserError.Log.e(TAG, "Could not save out firmware version!");
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
if (JoH.ratelimit("g6-evaluate", 600)) {
Inevitable.task("evaluteG6Settings", 10000, () -> evaluateG6Settings());
}
break;
case BatteryInfoRxMessage:
if (!setStoredBatteryBytes(getTransmitterID(), bytes)) {
UserError.Log.e(TAG, "Could not save out battery data!");
} else {
if (parent.android_wear) {
PersistentStore.setBoolean(G5_BATTERY_WEARABLE_SEND, true);
}
}
nextBackFillCheckSize = BACKFILL_CHECK_LARGE;
break;
case SessionStartRxMessage:
final SessionStartRxMessage session_start = (SessionStartRxMessage) data_packet.msg;
if (session_start.isOkay()) {
// TODO persist this
parent.msg("Session Started Successfully: " + JoH.dateTimeText(session_start.getSessionStart()) + " " + JoH.dateTimeText(session_start.getRequestedStart()) + " " + JoH.dateTimeText(session_start.getTransmitterTime()));
DexResetHelper.cancel();
} else {
final String msg = "Session Start Failed: " + session_start.message();
parent.msg(msg);
UserError.Log.ueh(TAG, msg);
JoH.showNotification(devName() + " Start Failed", msg, null, Constants.G5_START_REJECT, true, true, false);
UserError.Log.ueh(TAG, "Session Start failed info: " + JoH.dateTimeText(session_start.getSessionStart()) + " " + JoH.dateTimeText(session_start.getRequestedStart()) + " " + JoH.dateTimeText(session_start.getTransmitterTime()));
if (session_start.isFubar()) {
final long tk = DexTimeKeeper.getDexTime(getTransmitterID(), tsl());
if (tk > 0) {
DexResetHelper.offer("Unusual session start failure, is transmitter crashed? Try Hard Reset?");
} else {
UserError.Log.e(TAG, "No reset as TimeKeeper reports invalid: " + tk);
}
}
if (Pref.getBooleanDefaultFalse("ob1_g5_restart_sensor") && (Sensor.isActive())) {
if (pratelimit("secondary-g5-start", 1800)) {
UserError.Log.ueh(TAG, "Trying to Start sensor again");
startSensor(tsl());
}
}
}
reReadGlucoseData();
break;
case SessionStopRxMessage:
final SessionStopRxMessage session_stop = (SessionStopRxMessage) data_packet.msg;
if (session_stop.isOkay()) {
// TODO persist this
final String msg = "Session Stopped Successfully: " + JoH.dateTimeText(session_stop.getSessionStart()) + " " + JoH.dateTimeText(session_stop.getSessionStop());
parent.msg(msg);
UserError.Log.ueh(TAG, msg);
reReadGlucoseData();
enqueueUniqueCommand(new TimeTxMessage(), "Query time after stop");
} else {
// TODO what does an error when session isn't started look like? Probably best to downgrade those somewhat
final String msg = "Session Stop Failed: packet valid: " + session_stop.isValid() + " Status code: " + session_stop.getStatus();
UserError.Log.uel(TAG, msg);
}
break;
case GlucoseRxMessage:
final GlucoseRxMessage glucose = (GlucoseRxMessage) data_packet.msg;
parent.processCalibrationState(glucose.calibrationState());
if (glucose.usable()) {
parent.msg("Got " + devName() + " glucose");
} else {
parent.msg("Got data from " + devName());
}
glucoseRxCommon(glucose, parent, connection);
break;
// TODO base class duplication
case EGlucoseRxMessage:
final EGlucoseRxMessage eglucose = (EGlucoseRxMessage) data_packet.msg;
parent.processCalibrationState(eglucose.calibrationState());
if (eglucose.usable()) {
parent.msg("Got G6 glucose");
} else {
parent.msg("Got data from G6");
}
glucoseRxCommon(eglucose, parent, connection);
break;
case CalibrateRxMessage:
final CalibrateRxMessage calibrate = (CalibrateRxMessage) data_packet.msg;
if (calibrate.accepted()) {
parent.msg("Calibration accepted");
UserError.Log.ueh(TAG, "Calibration accepted by transmitter");
} else {
final String msg = "Calibration rejected: " + calibrate.message();
UserError.Log.wtf(TAG, msg);
parent.msg(msg);
JoH.showNotification("Calibration rejected", msg, null, Constants.G5_CALIBRATION_REJECT, true, true, false);
}
reReadGlucoseData();
break;
case BackFillRxMessage:
final BackFillRxMessage backfill = (BackFillRxMessage) data_packet.msg;
if (backfill.valid()) {
UserError.Log.d(TAG, "Backfill request confirmed");
} else {
UserError.Log.wtf(TAG, "Backfill request corrupted!");
}
break;
case TransmitterTimeRxMessage:
// This message is received every 120-125m
final TransmitterTimeRxMessage txtime = (TransmitterTimeRxMessage) data_packet.msg;
DexTimeKeeper.updateAge(getTransmitterID(), txtime.getCurrentTime(), true);
if (txtime.sessionInProgress()) {
UserError.Log.e(TAG, "Session start time reports: " + JoH.dateTimeText(txtime.getRealSessionStartTime()) + " Duration: " + JoH.niceTimeScalar(txtime.getSessionDuration()));
DexSessionKeeper.setStart(txtime.getRealSessionStartTime());
} else {
UserError.Log.e(TAG, "Session start time reports: No session in progress");
DexSessionKeeper.clearStart();
}
if (Pref.getBooleanDefaultFalse("ob1_g5_preemptive_restart")) {
int restartDaysThreshold = usingG6() ? 9 : 6;
if (txtime.getSessionDuration() > Constants.DAY_IN_MS * restartDaysThreshold && txtime.getSessionDuration() < Constants.MONTH_IN_MS) {
UserError.Log.uel(TAG, "Requesting preemptive session restart");
restartSensorWithTimeTravel();
}
}
break;
case F2DUnknownRxMessage:
UserError.Log.d(TAG, "Received F2D message");
try {
checkVersionAndBattery(parent, connection);
} finally {
parent.msg("Got no raw");
// TODO verify if this is ok to do here
parent.updateLast(tsl());
// TODO verify if this is ok to do here
parent.clearErrors();
}
break;
default:
UserError.Log.e(TAG, "Got unknown packet rx: " + JoH.bytesToHex(bytes));
break;
}
if (!queued(parent, connection)) {
inevitableDisconnect(parent, connection);
}
}, throwable -> {
if (!(throwable instanceof OperationSuccess)) {
if (throwable instanceof BleDisconnectedException) {
UserError.Log.d(TAG, "Disconnected when waiting to receive indication: " + throwable);
parent.changeState(Ob1G5CollectionService.STATE.CLOSE);
} else {
UserError.Log.e(TAG, "Error receiving indication: " + throwable);
// throwable.printStackTrace();
disconnectNow(parent, connection);
}
}
});
return true;
}
use of com.polidea.rxandroidble2.RxBleConnection in project xDrip-plus by jamorham.
the class MiBandService method processFirmwareCommands.
@SuppressLint("CheckResult")
private void processFirmwareCommands(byte[] value, boolean isSeqCommand) {
RxBleConnection connection = I.connection;
FirmwareOperations.SequenceType seq = firmware.getSequence();
if (d)
UserError.Log.d(TAG, "processFirmwareCommands: " + bytesToHex(value) + ": seq:" + seq.toString());
if (isSeqCommand) {
switch(seq) {
case SET_NIGHTMODE:
{
if (true) {
isNeedToRestoreNightMode = true;
DisplayControllMessageMiband3_4 dispControl = new DisplayControllMessageMiband3_4();
Calendar sheduledCalendar = Calendar.getInstance();
sheduledCalendar.set(Calendar.HOUR_OF_DAY, 0);
sheduledCalendar.set(Calendar.MINUTE, 0);
Date sheduledDate = sheduledCalendar.getTime();
connection.writeCharacteristic(dispControl.getCharacteristicUUID(), dispControl.setNightModeCmd(Sheduled, sheduledDate, sheduledDate)).subscribe(valB -> {
UserError.Log.d(TAG, "Wrote nigntmode, got: " + JoH.bytesToHex(valB));
firmware.nextSequence();
processFirmwareCommands(null, true);
}, throwable -> {
UserError.Log.e(TAG, "Could not write nigntmode: " + throwable);
firmware.nextSequence();
processFirmwareCommands(null, true);
});
} else {
firmware.nextSequence();
processFirmwareCommands(null, true);
}
break;
}
case PREPARE_UPLOAD:
{
connection.writeCharacteristic(firmware.getFirmwareCharacteristicUUID(), firmware.prepareFWUploadInitCommand()).subscribe(valB -> {
UserError.Log.d(TAG, "Wrote prepareFWUploadInitCommand, got: " + JoH.bytesToHex(valB));
}, throwable -> {
UserError.Log.e(TAG, "Could not write prepareFWUploadInitCommand: " + throwable);
resetFirmwareState(false);
});
firmware.nextSequence();
break;
}
}
return;
} else {
if (value.length != 3 && value.length != 11) {
UserError.Log.e(TAG, "Notifications should be 3 or 11 bytes long.");
return;
}
boolean success = value[2] == OperationCodes.SUCCESS;
if (value[0] == OperationCodes.RESPONSE && success) {
try {
switch(value[1]) {
case OperationCodes.COMMAND_FIRMWARE_INIT:
{
if (seq == FirmwareOperations.SequenceType.TRANSFER_FW_START) {
connection.writeCharacteristic(firmware.getFirmwareCharacteristicUUID(), firmware.getFirmwareStartCommand()).subscribe(valB -> {
UserError.Log.d(TAG, "Wrote Start command, got: " + JoH.bytesToHex(valB));
}, throwable -> {
UserError.Log.e(TAG, "Could not write Start command: " + throwable);
resetFirmwareState(false);
});
firmware.nextSequence();
} else if (seq == FirmwareOperations.SequenceType.TRANSFER_SEND_WF_INFO) {
connection.writeCharacteristic(firmware.getFirmwareCharacteristicUUID(), firmware.sendFwInfo()).subscribe(valB -> {
UserError.Log.d(TAG, "Wrote sendFwInfo, got: " + JoH.bytesToHex(valB));
}, throwable -> {
UserError.Log.e(TAG, "Could not write firmware info: " + throwable);
resetFirmwareState(false);
});
firmware.nextSequence();
break;
}
break;
}
case OperationCodes.COMMAND_FIRMWARE_START_DATA:
{
sendFirmwareData();
break;
}
case OperationCodes.COMMAND_FIRMWARE_CHECKSUM:
{
firmware.nextSequence();
if (firmware.getFirmwareType() == FirmwareOperations.FirmwareType.FIRMWARE) {
// send reboot
} else {
UserError.Log.e(TAG, "Watch Face has been installed successfully");
resetFirmwareState(true);
}
break;
}
case OperationCodes.COMMAND_FIRMWARE_REBOOT:
{
UserError.Log.e(TAG, "Reboot command successfully sent.");
resetFirmwareState(true);
break;
}
default:
{
resetFirmwareState(false, "Unexpected response during firmware update");
}
}
} catch (Exception ex) {
resetFirmwareState(false);
}
} else {
String errorMessage = null;
Boolean sendBGNotification = false;
if (value[2] == OperationCodes.LOW_BATTERY_ERROR) {
errorMessage = "Cannot upload watchface, low battery, please charge device";
sendBGNotification = true;
} else if (value[2] == OperationCodes.TIMER_RUNNING) {
errorMessage = "Cannot upload watchface, timer running on band";
} else if (value[2] == OperationCodes.ON_CALL) {
errorMessage = "Cannot upload watchface, call in progress";
} else {
errorMessage = "Unexpected notification during firmware update:" + JoH.bytesToHex(value);
}
resetFirmwareState(false, errorMessage);
if (sendBGNotification) {
emptyQueue();
JoH.startService(MiBandService.class, "function", "update_bg_as_notification");
changeState(SLEEP);
}
}
}
}
use of com.polidea.rxandroidble2.RxBleConnection in project xDrip-plus by jamorham.
the class MiBandService method ProcessAuthCommands.
@SuppressLint("CheckResult")
private void ProcessAuthCommands(RxBleConnection connection, byte[] value) {
if (value[0] == AUTH_RESPONSE && value[1] == AUTH_SEND_KEY && (value[2] & 0x0f) == AUTH_SUCCESS) {
// get random key from band
connection.writeCharacteristic(authorisation.getCharacteristicUUID(), authorisation.getAuthKeyRequest()).subscribe(val -> {
if (d)
UserError.Log.d(TAG, "Wrote OPCODE_AUTH_REQ1: " + JoH.bytesToHex(val));
}, throwable -> {
UserError.Log.e(TAG, "Could not write OPCODE_AUTH_REQ1: " + throwable);
});
} else if (value[0] == AUTH_RESPONSE && (value[1] & 0x0f) == AUTH_REQUEST_RANDOM_AUTH_NUMBER && value[2] == AUTH_SUCCESS) {
byte[] tmpValue = Arrays.copyOfRange(value, 3, 19);
try {
byte[] authReply = authorisation.calculateAuthReply(tmpValue);
// get random key from band
connection.writeCharacteristic(authorisation.getCharacteristicUUID(), authReply).subscribe(val -> {
if (d)
UserError.Log.d(TAG, "Wrote OPCODE_AUTH_REQ2: " + JoH.bytesToHex(val));
}, throwable -> {
UserError.Log.e(TAG, "Could not write OPCODE_AUTH_REQ2: " + throwable);
});
} catch (Exception e) {
JoH.static_toast_long(e.getMessage());
UserError.Log.e(TAG, (e.getMessage()));
changeState(AUTHORIZE_FAILED);
}
} else if (value[0] == AUTH_RESPONSE && (value[1] & 0x0f) == AUTH_SEND_ENCRYPTED_AUTH_NUMBER && value[2] == AUTH_SUCCESS) {
isNeedToAuthenticate = false;
if (MiBand.getPersistentAuthMac().isEmpty()) {
MiBand.setPersistentAuthMac(MiBand.getMac());
MiBand.setPersistentAuthKey(JoH.bytesToHex(authorisation.getLocalKey()), MiBand.getPersistentAuthMac());
String msg = "MiBand was successfully authenticated";
JoH.static_toast_long(msg);
UserError.Log.e(TAG, msg);
}
if (authSubscription != null) {
authSubscription.unsubscribe();
}
changeNextState();
} else if (value[0] == AUTH_RESPONSE && (((value[2] & 0x0f) == AUTH_FAIL) || (value[2] == AUTH_MIBAND4_FAIL) || (value[2] == AUTH_MIBAND4_CODE_FAIL))) {
MiBand.setPersistentAuthKey("", MiBand.getPersistentAuthMac());
if (authSubscription != null) {
authSubscription.unsubscribe();
}
String msg = "Cannot authorize miband, please recheck Auth code";
JoH.static_toast_long(msg);
UserError.Log.e(TAG, msg);
changeState(AUTHORIZE_FAILED);
}
}
Aggregations