diff --git a/ykonsole-iconsole/src/main/kotlin/net/aiterp/git/ykonsole2/infrastructure/IConsole.kt b/ykonsole-iconsole/src/main/kotlin/net/aiterp/git/ykonsole2/infrastructure/IConsole.kt index 77583cb..70920a7 100644 --- a/ykonsole-iconsole/src/main/kotlin/net/aiterp/git/ykonsole2/infrastructure/IConsole.kt +++ b/ykonsole-iconsole/src/main/kotlin/net/aiterp/git/ykonsole2/infrastructure/IConsole.kt @@ -71,160 +71,162 @@ class IConsole : ActiveDriver() { current?.cancelConnection() tryingSince = Instant.now() - val cbPeripheral = object : BluetoothPeripheralCallback() { - override fun onServicesDiscovered(peripheral: BluetoothPeripheral, services: MutableList) { - 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) { + 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() {