|
|
@ -71,160 +71,162 @@ class IConsole : ActiveDriver() { |
|
|
|
current?.cancelConnection() |
|
|
|
tryingSince = Instant.now() |
|
|
|
|
|
|
|
val cbPeripheral = object : BluetoothPeripheralCallback() { |
|
|
|
override fun onServicesDiscovered(peripheral: BluetoothPeripheral, services: MutableList<BluetoothGattService>) { |
|
|
|
logger.info("Checking device...") |
|
|
|
|
|
|
|
val service1 = services.firstOrNull { it.uuid == S1_SERVICE } |
|
|
|
val service2 = services.firstOrNull { it.uuid == S2_SERVICE } |
|
|
|
|
|
|
|
if (service1 != null && service2 != null) { |
|
|
|
btCommandInput = service1.getCharacteristic(S1_CHAR_COMMAND_INPUT) |
|
|
|
val s1out = service1.getCharacteristic(S1_CHAR_DATA_OUTPUT) |
|
|
|
val s1mys = service1.getCharacteristic(S1_CHAR_MYSTERY_OUTPUT) |
|
|
|
val s2mys = service2.getCharacteristic(S2_CHAR_MYSTERY_OUTPUT) |
|
|
|
if (btCommandInput == null || s1out == null || s1mys == null || s2mys == null) { |
|
|
|
logger.warn("Something is off about this i-CONSOLE device") |
|
|
|
output.emitBlocking(ErrorOccurred("(Maybe) a malfunctioning i-CONSOLE device")) |
|
|
|
peripheral.cancelConnection() |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
peripheral.setNotify(btCommandInput!!, true) |
|
|
|
peripheral.setNotify(s1out, true) |
|
|
|
peripheral.setNotify(s1mys, true) |
|
|
|
peripheral.setNotify(s2mys, true) |
|
|
|
|
|
|
|
logger.info("Device setup successfully; sending first request...") |
|
|
|
|
|
|
|
runBlocking { |
|
|
|
peripheral.writeCharacteristic( |
|
|
|
btCommandInput!!, |
|
|
|
AckRequest.toBytes(), |
|
|
|
WITHOUT_RESPONSE, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
current = peripheral |
|
|
|
} else { |
|
|
|
logger.warn("This is maybe not an i-CONSOLE device, since it lacks the required services") |
|
|
|
output.emitBlocking(ErrorOccurred("(Maybe) not an i-CONSOLE device")) |
|
|
|
peripheral.cancelConnection() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
override fun onCharacteristicUpdate( |
|
|
|
peripheral: BluetoothPeripheral, |
|
|
|
value: ByteArray?, |
|
|
|
characteristic: BluetoothGattCharacteristic, |
|
|
|
status: BluetoothCommandStatus, |
|
|
|
) { |
|
|
|
val res = value?.let { Response.fromBytes(it) } |
|
|
|
|
|
|
|
if (!connected && res is AckResponse) { |
|
|
|
logger.info("Device is now ready") |
|
|
|
|
|
|
|
connected = true |
|
|
|
output.emitBlocking(Connected) |
|
|
|
} |
|
|
|
|
|
|
|
if (res is MaxLevelResponse) { |
|
|
|
maxLevel = res.level |
|
|
|
if (central == null) { |
|
|
|
val cbScan = object : BluetoothCentralManagerCallback() { |
|
|
|
override fun onScanStarted() { |
|
|
|
logger.info("Scanning for $connectionString...") |
|
|
|
} |
|
|
|
|
|
|
|
if (res is ControlStateResponse) { |
|
|
|
if (res.value == 1) { |
|
|
|
running = true |
|
|
|
output.emitBlocking(Started) |
|
|
|
} else { |
|
|
|
running = false |
|
|
|
output.emitBlocking(Stopped) |
|
|
|
override fun onScanStopped() { |
|
|
|
logger.info("Scan stopped") |
|
|
|
|
|
|
|
tryingSince?.let { ts -> |
|
|
|
if (ts.isBefore(Instant.now().minusSeconds(20))) { |
|
|
|
central?.stopScan() |
|
|
|
tryingSince = null |
|
|
|
runBlocking { |
|
|
|
output.emit(ErrorOccurred("Connection timeout after 20 seconds")) |
|
|
|
output.emit(Disconnected) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (res is WorkoutStatusResponse) { |
|
|
|
val currentTime = (res.minutes * 60) + res.seconds |
|
|
|
|
|
|
|
if (currentTime > lastTime) { |
|
|
|
lastTime = currentTime |
|
|
|
lastCals = maxOf(res.calories, lastCals) |
|
|
|
lastMeters = maxOf((res.distance * 1000).toInt(), lastMeters) |
|
|
|
|
|
|
|
output.emitBlocking( |
|
|
|
ValuesReceived( |
|
|
|
listOf( |
|
|
|
Time(lastTime + bonusTime), |
|
|
|
Calories(lastCals + bonusCals), |
|
|
|
Distance(lastMeters + bonusMeters), |
|
|
|
Level(res.level), |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
override fun onDiscoveredPeripheral(peripheral: BluetoothPeripheral, scanResult: ScanResult) { |
|
|
|
logger.info("Connecting to ${peripheral.name} (${peripheral.address})...") |
|
|
|
|
|
|
|
if (tryingSince != null) { |
|
|
|
val cbPeripheral = object : BluetoothPeripheralCallback() { |
|
|
|
override fun onServicesDiscovered(peripheral: BluetoothPeripheral, services: MutableList<BluetoothGattService>) { |
|
|
|
logger.info("Checking device...") |
|
|
|
|
|
|
|
val service1 = services.firstOrNull { it.uuid == S1_SERVICE } |
|
|
|
val service2 = services.firstOrNull { it.uuid == S2_SERVICE } |
|
|
|
|
|
|
|
if (service1 != null && service2 != null) { |
|
|
|
btCommandInput = service1.getCharacteristic(S1_CHAR_COMMAND_INPUT) |
|
|
|
val s1out = service1.getCharacteristic(S1_CHAR_DATA_OUTPUT) |
|
|
|
val s1mys = service1.getCharacteristic(S1_CHAR_MYSTERY_OUTPUT) |
|
|
|
val s2mys = service2.getCharacteristic(S2_CHAR_MYSTERY_OUTPUT) |
|
|
|
if (btCommandInput == null || s1out == null || s1mys == null || s2mys == null) { |
|
|
|
logger.warn("Something is off about this i-CONSOLE device") |
|
|
|
output.emitBlocking(ErrorOccurred("(Maybe) a malfunctioning i-CONSOLE device")) |
|
|
|
peripheral.cancelConnection() |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
peripheral.setNotify(btCommandInput!!, true) |
|
|
|
peripheral.setNotify(s1out, true) |
|
|
|
peripheral.setNotify(s1mys, true) |
|
|
|
peripheral.setNotify(s2mys, true) |
|
|
|
|
|
|
|
logger.info("Device setup successfully; sending first request...") |
|
|
|
|
|
|
|
runBlocking { |
|
|
|
peripheral.writeCharacteristic( |
|
|
|
btCommandInput!!, |
|
|
|
AckRequest.toBytes(), |
|
|
|
WITHOUT_RESPONSE, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
current = peripheral |
|
|
|
} else { |
|
|
|
logger.warn("This is maybe not an i-CONSOLE device, since it lacks the required services") |
|
|
|
output.emitBlocking(ErrorOccurred("(Maybe) not an i-CONSOLE device")) |
|
|
|
peripheral.cancelConnection() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
override fun onCharacteristicUpdate( |
|
|
|
peripheral: BluetoothPeripheral, |
|
|
|
value: ByteArray?, |
|
|
|
characteristic: BluetoothGattCharacteristic, |
|
|
|
status: BluetoothCommandStatus, |
|
|
|
) { |
|
|
|
val res = value?.let { Response.fromBytes(it) } |
|
|
|
|
|
|
|
if (!connected && res is AckResponse) { |
|
|
|
logger.info("Device is now ready") |
|
|
|
|
|
|
|
connected = true |
|
|
|
output.emitBlocking(Connected) |
|
|
|
} |
|
|
|
|
|
|
|
if (res is MaxLevelResponse) { |
|
|
|
maxLevel = res.level |
|
|
|
} |
|
|
|
|
|
|
|
if (res is ControlStateResponse) { |
|
|
|
if (res.value == 1) { |
|
|
|
running = true |
|
|
|
output.emitBlocking(Started) |
|
|
|
} else { |
|
|
|
running = false |
|
|
|
output.emitBlocking(Stopped) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (res is WorkoutStatusResponse) { |
|
|
|
val currentTime = (res.minutes * 60) + res.seconds |
|
|
|
|
|
|
|
if (currentTime > lastTime) { |
|
|
|
lastTime = currentTime |
|
|
|
lastCals = maxOf(res.calories, lastCals) |
|
|
|
lastMeters = maxOf((res.distance * 1000).toInt(), lastMeters) |
|
|
|
|
|
|
|
output.emitBlocking( |
|
|
|
ValuesReceived( |
|
|
|
listOf( |
|
|
|
Time(lastTime + bonusTime), |
|
|
|
Calories(lastCals + bonusCals), |
|
|
|
Distance(lastMeters + bonusMeters), |
|
|
|
Level(res.level), |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
tryingSince = null |
|
|
|
central?.connectPeripheral(peripheral, cbPeripheral) |
|
|
|
central?.stopScan() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val cbScan = object : BluetoothCentralManagerCallback() { |
|
|
|
override fun onScanStarted() { |
|
|
|
logger.info("Scanning for $connectionString...") |
|
|
|
} |
|
|
|
|
|
|
|
override fun onScanStopped() { |
|
|
|
logger.info("Scan stopped") |
|
|
|
|
|
|
|
tryingSince?.let { ts -> |
|
|
|
if (ts.isBefore(Instant.now().minusSeconds(20))) { |
|
|
|
central?.stopScan() |
|
|
|
tryingSince = null |
|
|
|
runBlocking { |
|
|
|
output.emit(ErrorOccurred("Connection timeout after 20 seconds")) |
|
|
|
output.emit(Disconnected) |
|
|
|
override fun onDisconnectedPeripheral(peripheral: BluetoothPeripheral, status: BluetoothCommandStatus) { |
|
|
|
if (peripheral.address == current?.address) { |
|
|
|
if (wantsConnection) { |
|
|
|
logger.warn("Will try restarting in 10 seconds (disconnection)") |
|
|
|
prepareReconnect(connectionString, output) |
|
|
|
} else { |
|
|
|
current = null |
|
|
|
output.emitBlocking(Disconnected) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
override fun onDiscoveredPeripheral(peripheral: BluetoothPeripheral, scanResult: ScanResult) { |
|
|
|
logger.info("Connecting to ${peripheral.name} (${peripheral.address})...") |
|
|
|
|
|
|
|
if (tryingSince != null) { |
|
|
|
tryingSince = null |
|
|
|
central?.connectPeripheral(peripheral, cbPeripheral) |
|
|
|
override fun onConnectionFailed(peripheral: BluetoothPeripheral, status: BluetoothCommandStatus) { |
|
|
|
central?.stopScan() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
override fun onDisconnectedPeripheral(peripheral: BluetoothPeripheral, status: BluetoothCommandStatus) { |
|
|
|
if (peripheral.address == current?.address) { |
|
|
|
if (wantsConnection) { |
|
|
|
logger.warn("Will try restarting in 10 seconds (disconnection)") |
|
|
|
prepareReconnect(connectionString, output) |
|
|
|
} else { |
|
|
|
current = null |
|
|
|
output.emitBlocking(Disconnected) |
|
|
|
runBlocking { |
|
|
|
if (health > 1) { |
|
|
|
logger.warn("Will try restarting in 10 seconds (connection error)") |
|
|
|
prepareReconnect(connectionString, output) |
|
|
|
} else { |
|
|
|
output.emit(ErrorOccurred("Failure: $status")) |
|
|
|
output.emit(Disconnected) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
override fun onConnectionFailed(peripheral: BluetoothPeripheral, status: BluetoothCommandStatus) { |
|
|
|
central?.stopScan() |
|
|
|
|
|
|
|
runBlocking { |
|
|
|
if (health > 1) { |
|
|
|
logger.warn("Will try restarting in 10 seconds (connection error)") |
|
|
|
prepareReconnect(connectionString, output) |
|
|
|
} else { |
|
|
|
output.emit(ErrorOccurred("Failure: $status")) |
|
|
|
output.emit(Disconnected) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
central = BluetoothCentralManager(cbScan) |
|
|
|
} |
|
|
|
|
|
|
|
central = BluetoothCentralManager(cbScan).apply { |
|
|
|
scanForPeripheralsWithAddresses(arrayOf(connectionString)) |
|
|
|
} |
|
|
|
central?.scanForPeripheralsWithAddresses(arrayOf(connectionString)) |
|
|
|
} |
|
|
|
|
|
|
|
private fun onDisconnect() { |
|
|
|