[+] Observers ver.1

This commit is contained in:
Azalea Gui
2023-01-24 11:43:15 -05:00
parent 876e3aa606
commit 415b1bd0c9
@@ -3,27 +3,16 @@ package org.hydev.wearsync.bles
import android.content.Context import android.content.Context
import com.welie.blessed.* import com.welie.blessed.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import org.hydev.wearsync.BluePeri import org.hydev.wearsync.BluePeri
import org.hydev.wearsync.bles.decoders.* import org.hydev.wearsync.bles.decoders.*
import timber.log.Timber import timber.log.Timber
import timber.log.Timber.DebugTree import timber.log.Timber.DebugTree
import java.util.* import kotlin.reflect.KClass
internal class BluetoothHandler private constructor(context: Context) { internal class BluetoothHandler private constructor(context: Context) {
var central: BluetoothCentralManager var central: BluetoothCentralManager
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
val batteryChannel = Channel<BatteryMeasurement>(UNLIMITED)
val heartRateChannel = Channel<HeartRateMeasurement>(UNLIMITED)
val bloodPressureChannel = Channel<BloodPressureMeasurement>(UNLIMITED)
val glucoseChannel = Channel<GlucoseMeasurement>(UNLIMITED)
val pulseOxSpotChannel = Channel<PulseOximeterSpotMeasurement>(UNLIMITED)
val pulseOxContinuousChannel = Channel<PulseOximeterContinuousMeasurement>(UNLIMITED)
val temperatureChannel = Channel<TemperatureMeasurement>(UNLIMITED)
val weightChannel = Channel<WeightMeasurement>(UNLIMITED)
private fun BluePeri.handle() { private fun BluePeri.handle() {
scope.launch { scope.launch {
try { try {
@@ -34,48 +23,42 @@ internal class BluetoothHandler private constructor(context: Context) {
Timber.i("Received: ${read(ModelDecoder())}") Timber.i("Received: ${read(ModelDecoder())}")
Timber.i("Battery level: ${read(BatteryDecoder())}") Timber.i("Battery level: ${read(BatteryDecoder())}")
observe(batteryChannel, BatteryDecoder()) decoders.forEach { observe(it) }
observe(heartRateChannel, HeartRateDecoder())
observe(temperatureChannel, TemperatureDecoder())
observe(weightChannel, WeightDecoder())
observe(bloodPressureChannel, BloodPressureDecoder())
observe(pulseOxSpotChannel, PLXSpotDecoder())
observe(pulseOxContinuousChannel, PLXContinuousDecoder())
} catch (e: IllegalArgumentException) {
Timber.e(e)
} catch (b: GattException) {
Timber.e(b)
} }
catch (e: IllegalArgumentException) { Timber.e(e) }
catch (b: GattException) { Timber.e(b) }
} }
} }
/** val listeners = HashMap<KClass<*>, MutableList<(Any) -> Unit>>()
* Observe a specific mesasurement
*/ inline fun <M : Any, reified D : IDecoder<M>> observe(crossinline cb: (M) -> Unit)
suspend fun <T> BluePeri.observe(dec: IDecoder<T>, callback: (T) -> Unit) { {
getCharacteristic(dec.sid, dec.cid)?.let { (listeners[D::class] ?: error("Cannot observe unknown decoder class ${D::class}"))
observe(it) { value -> .add { cb(it as M) }
val measurement = dec.decode(value)
callback(measurement)
Timber.d(measurement.toString())
}
}
dec.additionalSetup(this)
} }
suspend fun <T> BluePeri.observe(chan: Channel<T>, dec: IDecoder<T>) = private suspend fun <T : Any> BluePeri.observe(dec: IDecoder<T>) {
observe(dec) { chan.trySend(it) } val cls = dec::class
listeners[cls] = ArrayList()
observe(dec) {
listeners[cls]?.forEach { cb -> cb(it) }
}
}
/** /**
* Scan and connect to a peripheral at a specific address * Scan and connect to a peripheral at a specific address
*/ */
fun connectAddress(address: String, callback: (BluePeri) -> Unit = {}) { fun connectAddress(address: String, callback: (BluePeri) -> Unit = {}) {
Timber.d("Scanning for device with address $address")
central.stopScan() central.stopScan()
central.scanForPeripheralsWithAddresses(arrayOf(address), { peripheral, scanResult -> central.scanForPeripheralsWithAddresses(arrayOf(address), { peri, _ ->
if (peripheral.address != address) return@scanForPeripheralsWithAddresses if (peri.address != address) return@scanForPeripheralsWithAddresses
Timber.d("Device found, connecting...")
central.stopScan() central.stopScan()
connectPeripheral(peripheral) { callback(peripheral) } connectPeripheral(peri) { callback(peri) }
}, {}) }, {})
} }
@@ -93,6 +76,9 @@ internal class BluetoothHandler private constructor(context: Context) {
} }
companion object { companion object {
val decoders = listOf(BatteryDecoder(), BloodPressureDecoder(), GlucoseDecoder(), HeartRateDecoder(),
PLXSpotDecoder(), PLXContinuousDecoder(), TemperatureDecoder(), WeightDecoder())
private var instance: BluetoothHandler? = null private var instance: BluetoothHandler? = null
val Context.ble get(): BluetoothHandler { val Context.ble get(): BluetoothHandler {
if (instance == null) { if (instance == null) {
@@ -103,6 +89,20 @@ internal class BluetoothHandler private constructor(context: Context) {
suspend fun <T> BluePeri.read(decoder: IDecoder<T>) = suspend fun <T> BluePeri.read(decoder: IDecoder<T>) =
getCharacteristic(decoder.sid, decoder.cid)?.let { decoder.decode(readCharacteristic(it)) } getCharacteristic(decoder.sid, decoder.cid)?.let { decoder.decode(readCharacteristic(it)) }
/**
* Observe a specific measurement
*/
private suspend fun <T> BluePeri.observe(dec: IDecoder<T>, callback: (T) -> Unit) {
getCharacteristic(dec.sid, dec.cid)?.let {
observe(it) { value ->
val measurement = dec.decode(value)
callback(measurement)
Timber.d(measurement.toString())
}
}
dec.additionalSetup(this)
}
} }
init { init {