Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Date for Version 2.0.0 #152

Closed
ahmadshoh-orc opened this issue Jun 26, 2024 · 6 comments
Closed

Release Date for Version 2.0.0 #152

ahmadshoh-orc opened this issue Jun 26, 2024 · 6 comments
Labels
question Further information is requested

Comments

@ahmadshoh-orc
Copy link

Hello,

I've read through various issues and noticed mentions of an upcoming version 2.0.0. I am about to start a new project and need to decide whether to wait for the new release or proceed with the current version of the Android-Ble-library with Java. Could you please provide an estimated release date for Version 2.0.0?

Thank you for your help.

@philips77
Copy link
Member

Hi,
I'd recommend the current Android-Ble-library with Java. I hoped to have an alpha version before I go for vacation (end of next week), but some other tasks are popping up like mushrooms after a rain, so it has a high risk.
The Java BLE library is popular and went through lots of fixes, so should be stable. I can't say when the Kotlin BLE 2.0 will reach similar status.

Try to keep BLE API isolated so future migration will be possible :)

@philips77
Copy link
Member

And sorry for the delays! We're doing everything we can to push projects fast.

@philips77 philips77 added the question Further information is requested label Jun 26, 2024
@philips77 philips77 pinned this issue Jun 26, 2024
@philips77
Copy link
Member

Hello,
I pushed my changes to version/2.0 branch.

Supported features:

  1. Client
    1. Scanner
    2. Service discovery
    3. Reading / Writing / Subscribing to values
    4. Reading RSSI
    5. Reading / setting preferred PHY
    6. Subscribing to / requesting connection parameters
    7. Refreshing cache (probably, maybe some delay may be required)
    8. Stub implementation for @Previews, etc.
  2. Server
    1. Not stable API, but no implementation.
  3. Advertiser

Not (yet) supported features:

  1. Client
    1. Bonding / GATT operations when bonded / subscribing to bond state
    2. Mock client implementation
  2. Server
    1. Implementation
    2. Mock implementation

@philips77
Copy link
Member

Scanning sample:

fun startScan() {
_devices.update { listOf(PreviewPeripheral(scope)) }
_isScanning.update { true }
centralManager
.scan(1250) {
Any {
Name("Pixel 5")
Name("Pixel 7")
Name("Nordic_LBS")
Name("DFU2A16")
// TODO Filtering by Regex and other runtime filters
}
}
.distinctByPeripheral()
.map {
it.peripheral
}
//.distinct()
.onEach { newPeripheral ->
Timber.w("Found new device: ${newPeripheral.name} (${newPeripheral.address})")
_devices.update { devices.value + newPeripheral }
}
.onEach { peripheral ->
// Track state of each peripheral.
peripheral.state
.onEach {
Timber.w("State: $it")
}
.launchIn(scope)
}
.catch { t ->
Timber.e("Scan failed: $t")
}
.onCompletion {
_isScanning.update { false }
}
.launchIn(viewModelScope)
}

@philips77
Copy link
Member

Connection sample:

fun connect(peripheral: Peripheral, autoConnect: Boolean) {
if (!peripheral.isDisconnected) { return }
scope.launch {
try {
withTimeout(5000) {
Timber.w("Connecting to ${peripheral.name}...")
centralManager.connect(
peripheral = peripheral,
options = if (autoConnect) {
CentralManager.ConnectionOptions.AutoConnect
} else {
CentralManager.ConnectionOptions.Direct(
timeout = 24.seconds,
retry = 2,
retryDelay = 1.seconds,
Phy.PHY_LE_2M,
)
},
)
}
Timber.w("Connected to ${peripheral.name}!")
// Observe PHY
peripheral.phy
.onEach {
Timber.w("PHY changed to: $it")
}
.launchIn(scope)
// Observe connection parameters
peripheral.connectionParameters
.onEach {
Timber.w("Connection parameters changed to: $it")
}
.launchIn(scope)
// Request MTU
peripheral.requestHighestValueLength()
// Check maximum write length
val writeType = WriteType.WITHOUT_RESPONSE
val length = peripheral.maximumWriteValueLength(writeType)
Timber.w("Maximum write length for $writeType: $length")
// Read RSSI
val rssi = peripheral.readRssi()
Timber.w("RSSI: $rssi dBm")
peripheral.requestConnectionPriority(ConnectionPriority.HIGH)
Timber.w("Connection priority changed to HIGH")
// Discover services and do some GATT operations.
peripheral.services()
.onEach {
Timber.w("Services changed: $it")
// Read values of all characteristics.
it.forEach { remoteService ->
remoteService.characteristics.forEach { remoteCharacteristic ->
try {
val value = remoteCharacteristic.read()
Timber.w("Value of ${remoteCharacteristic.uuid}: 0x${value.toHexString()}")
} catch (e: Exception) {
Timber.e("Failed to read ${remoteCharacteristic.uuid}: ${e.message}")
}
}
}
it.forEach { remoteService ->
remoteService.characteristics.forEach { remoteCharacteristic ->
try {
Timber.w("Subscribing to ${remoteCharacteristic.uuid}...")
remoteCharacteristic.subscribe()
.onEach { newValue ->
Timber.w("Value of ${remoteCharacteristic.uuid} changed: 0x${newValue.toHexString()}")
}
.launchIn(scope)
Timber.w("Subscribing to ${remoteCharacteristic.uuid} complete")
} catch (e: Exception) {
Timber.e("Failed to subscribe to ${remoteCharacteristic.uuid}: ${e.message}")
}
}
}
}
.launchIn(scope)
// When the above operations are in progress, do some other operations in parallel.
// This time service discovery won't be initiated and we're just subscribing to the
// service flow.
peripheral.services()
.onEach {
Timber.w("-- Services changed: $it")
// Read values of all characteristics.
it.forEach { remoteService ->
remoteService.characteristics.forEach { remoteCharacteristic ->
try {
val value = remoteCharacteristic.read()
Timber.w("-- Value of ${remoteCharacteristic.uuid}: 0x${value.toHexString()}")
} catch (e: Exception) {
Timber.e("-- Failed to read ${remoteCharacteristic.uuid}: ${e.message}")
}
}
}
}
.launchIn(scope)
} catch (e: Exception) {
Timber.e(e, "OMG!")
}
}
}

@philips77
Copy link
Member

How to get an instance of a CentralManager?
Here's how:

There's also CentralManager.Factory.mock:

/**
* Creates an implementation of the [CentralManager] which is using native Android API to
* scan and connect to physical Bluetooth LE devices.
*
* @param scope The coroutine scope.
* @property initialState The initial state of the Central Manager,
* defaults to [Manager.State.POWERED_ON].
* @property environment The environment to use for the mock, defaults to the latest supported API.
*/
fun CentralManager.Factory.mock(
scope: CoroutineScope,
initialState: Manager.State = Manager.State.POWERED_ON,
environment: MockEnvironment = MockEnvironment.Api31()
) =
CentralManager(MockCentralManagerEngine(scope, initialState, environment))

but it's not working yet.
The idea is that using the environment one may emulate any platform and possible issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants