EucBleClient.kt
package io.github.tritbool.euc.ble
import android.Manifest
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission
import io.github.tritbool.euc.ble.core.AndroidLogger
import io.github.tritbool.euc.ble.core.BLEConstants
import io.github.tritbool.euc.ble.core.BLEManager
import io.github.tritbool.euc.ble.core.ConnectionCallback
import io.github.tritbool.euc.ble.core.DataCallback
import io.github.tritbool.euc.ble.core.ErrorCallback
import io.github.tritbool.euc.ble.core.Logger
import io.github.tritbool.euc.ble.core.ProtocolSelectionMode
import io.github.tritbool.euc.ble.core.QueryTraceEvent
import io.github.tritbool.euc.ble.models.EUCDevice
import io.github.tritbool.euc.ble.protocols.CommandSupport
import io.github.tritbool.euc.ble.protocols.CommandType
import io.github.tritbool.euc.ble.protocols.EUCProtocol
import io.github.tritbool.euc.ble.protocols.ExtremeBullProtocol
import io.github.tritbool.euc.ble.protocols.GotwayProtocol
import io.github.tritbool.euc.ble.protocols.InMotionProtocol
import io.github.tritbool.euc.ble.protocols.KingsongProtocol
import io.github.tritbool.euc.ble.protocols.LeaperkimProtocol
import io.github.tritbool.euc.ble.protocols.NinebotProtocol
import io.github.tritbool.euc.ble.protocols.NinebotZProtocol
import io.github.tritbool.euc.ble.protocols.NosfetProtocol
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.SharedFlow
/**
* Main public entry point for EUC BLE discovery, connection and telemetry.
*
* Protocol registration is fully managed internally; client applications should not register
* brand-specific protocols, but may inspect and manually select from the registered protocols
* using [getRegisteredProtocols].
*/
class EucBleClient(
context: Context,
logger: Logger = AndroidLogger(),
coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
) {
internal val bleManager = BLEManager(context, logger, coroutineScope)
init {
registerBuiltInProtocols(coroutineScope)
}
fun initialize() {
bleManager.initialize()
}
@RequiresApi(Build.VERSION_CODES.M)
@RequiresPermission(Manifest.permission.BLUETOOTH_SCAN)
fun startScan() {
bleManager.startScan()
}
@RequiresPermission(Manifest.permission.BLUETOOTH_SCAN)
fun stopScan() {
bleManager.stopScan()
}
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun connect(device: EUCDevice) {
bleManager.connect(device)
}
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun disconnect() {
bleManager.disconnect()
}
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun sendCommand(commandType: CommandType, value: Any = Unit) {
bleManager.sendCommand(commandType, value)
}
fun getCommandSupport(commandType: CommandType): CommandSupport {
return bleManager.getCommandSupport(commandType)
}
fun getConnectionState(): BLEConstants.ConnectionState = bleManager.getConnectionState()
fun getConnectedDevice(): EUCDevice? = bleManager.getConnectedDevice()
fun setProtocolSelectionMode(mode: ProtocolSelectionMode) {
bleManager.setProtocolSelectionMode(mode)
}
fun getProtocolSelectionMode(): ProtocolSelectionMode = bleManager.getProtocolSelectionMode()
/**
* Returns all registered protocols. Callers can use this list to freely choose a protocol
* for manual selection via [selectProtocol] or [forceProtocol].
*/
fun getRegisteredProtocols(): List<EUCProtocol> = bleManager.getRegisteredProtocols()
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun selectProtocol(protocol: EUCProtocol): Boolean {
return bleManager.selectProtocol(protocol)
}
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun forceProtocol(protocol: EUCProtocol): Boolean {
return bleManager.forceProtocol(protocol)
}
fun clearForcedProtocol() {
bleManager.clearForcedProtocol()
}
fun setConnectionCallback(callback: ConnectionCallback) {
bleManager.setConnectionCallback(callback)
}
fun setDataCallback(callback: DataCallback) {
bleManager.setDataCallback(callback)
}
fun setErrorCallback(callback: ErrorCallback) {
bleManager.setErrorCallback(callback)
}
val rawFrameFlow: SharedFlow<ByteArray> = bleManager.rawFrameFlow
val queryTraceFlow: SharedFlow<QueryTraceEvent> = bleManager.queryTraceFlow
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun cleanup() {
bleManager.cleanup()
}
private fun registerBuiltInProtocols(scope: CoroutineScope) {
bleManager.registerProtocol(KingsongProtocol(scope = scope))
bleManager.registerProtocol(GotwayProtocol(scope = scope))
bleManager.registerProtocol(ExtremeBullProtocol(scope = scope))
bleManager.registerProtocol(InMotionProtocol())
bleManager.registerProtocol(NinebotZProtocol())
bleManager.registerProtocol(NinebotProtocol())
bleManager.registerProtocol(NosfetProtocol(scope = scope))
bleManager.registerProtocol(LeaperkimProtocol(scope = scope))
}
}