Releases: JuulLabs/kable
0.36.0
🚀 Changes
Peripheral.scope
property
To be more explicit, Peripheral
's CoroutineScope
has been moved to the scope
property on the Peripheral
:
Old | New |
---|---|
peripheral.launch {
// ..
} |
peripheral.scope.launch {
// ..
} |
peripheral.cancel() |
peripheral.close()
// or..
peripheral.scope.cancel() |
Peripheral
AutoCloseable
Peripheral
now implements AutoCloseable
if you need to use a Peripheral
for a quick/short task:
// `peripheral` will automatically `close` after the `use` block.
peripheral.use {
it.connect()
it.write(..)
it.disconnect()
}
Connection scope on Connected
state
The Connected
state now carries the active connection CoroutineScope
. Previously, the connection CoroutineScope
was only available from the return of the connect
function, but now it is also available from the Connected
state, for example:
peripheral.state
.filterIsInstance<State.Connected>()
.onEach { (connectionScope) ->
// `connectionScope` is automatically cancelled on disconnect
connectionScope.launch {
// ..
}
}
.launchIn(peripheral.scope)
Standard library Uuid
Kable now uses UUIDs as provided by the Kotlin standard library. Be sure and update your import
s and Uuid
usages appropriately, for example:
Old | New |
---|---|
import com.benasher44.uuid.Uuid
import com.juul.kable.Options
import com.juul.kable.requestPeripheral
val options = Options {
filters {
match {
services = listOf(
uuidFrom("0000aa80-0000-1000-8000-00805f9b34fb"),
)
}
}
// ..
} |
import com.juul.kable.Bluetooth
import com.juul.kable.Options
import com.juul.kable.requestPeripheral
val options = Options {
filters {
match {
services = listOf(
Bluetooth.BaseUuid + 0xAA80,
)
}
}
// ..
} |
import com.juul.kable.descriptorOf
val descriptor = descriptorOf(
service = "00001815-0000-1000-8000-00805f9b34fb",
characteristic = "00002a56-0000-1000-8000-00805f9b34fb",
descriptor = "00002902-0000-1000-8000-00805f9b34fb",
) |
import com.juul.kable.Bluetooth
import com.juul.kable.descriptor
import com.juul.kable.descriptorOf
import kotlin.uuid.Uuid
val descriptor = descriptorOf(
service = Bluetooth.BaseUuid + 0x1815,
characteristic = Bluetooth.BaseUuid + 0x2A56,
descriptor = Uuid.descriptor("gatt.client_characteristic_configuration"),
) |
Common
- Allow filtering by company ID only (#855), thanks to @tsuijten for reporting
- Propagate unknown
onScanFailed
error codes asIllegalStateException
s (#851), thanks to @mjordan-gpfw for reporting - Expose connection scope on
State.Connected
(#852) - Provide
Peripheral
CoroutineScope
viascope
property (#846) - Convert discovered services, characteristics, descriptors to interfaces (#839), thanks to @UnknownJoe796 for reporting
- Support Web Bluetooth named UUIDs (#835)
- Move
GattRequestRejectedException
to common (#838), thanks to @francismariano for reporting - Allow for advertisement manufacturer data length to exceed filter data mask length (#842), thanks to @eygraber for reporting
- Expose Android GATT status
Int
onGattStatusException
(#836), thanks to @joharei for reporting - Create copy of
observations
inObservers.onConnected
(#841), thanks to @eygraber for reporting - Fix error string for
GATT_INVALID_ATTRIBUTE_LENGTH
(#831), thanks to @angusholder for the contribution - Use
Uuid
from Kotlin standard library (#758), thanks to @ykws for the contribution
Android
- Simplify "client characteristic config" UUID definition (#837)
Apple
- Support 32-bit (8 hex characters)
CBUUID
s (#834), thanks to @liamzhouacc for reporting
🧰 Maintenance
- Update "maven central" badge to use
kable-core
artifact (#823) - Update plugin dokka to v2 (#845)
- Update dependency gradle to v8.12.1 (#816, #821, #848)
- Update dependency org.jetbrains.kotlinx:kotlinx-io-core to v0.6.0 (#820)
- Update plugin android-library to v8.8.0 (#812, #832)
- Update plugin kotlinter to v5.0.1 (#818)
- Update coroutines to v1.10.1 (#819)
- Update dependency org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom to v2025.1.6 (#807, #825, #828, #833, #844)
- Update plugin maven-publish to v0.30.0 (#826)
- Update dependency com.juul.khronicle:khronicle-core to v0.5.1 (#813, #824)
- Update dependency nl.jqno.equalsverifier:equalsverifier to v3.19 (#849, #859)
- Update dependency org.jetbrains.kotlin.multiplatform to v2.1.10 (#817, #858)
- Update gradle/actions action to v4 (#827)
- Update plugin api to v0.17.0 (#822)
- Update atomicfu to v0.27.0 (#829)
0.35.0
- Adds
maximumWriteValueLength
toPeripheral
(#809), special thanks to @ErikHellman for the contribution (and @NaiRo94s for reporting)!
Note
If you haven't been using the 0.35.0
release candidate (RC) releases, be sure and see the release notes for 0.35.0-rc
and 0.35.0-rc2
, as 0.35.0
brings Peripheral
lifecycle changes (specifically: a Peripheral
no longer has a parent CoroutineScope
and needs to be explicitly disposed via cancel
when no longer needed).
0.35.0-rc2
- Improve exception message when attempting to
connect
a cancelled peripheral (#808) - Change
InternalException
toInternalError
(#801) - Make peripheral builder argument optional (#795), thanks to @PavlosTze for reporting
- Have deprecated
CoroutineScope.peripheral
uphold previous behavior (#779)
Android
- Provide
Peripheral(Identifier)
builder function (#803), thanks to @PavlosTze for reporting
🧰 Maintenance
- Update
AndroidManifest.xml
reference (#787), thanks to @emmanuel-ferdman for the contribution - Update atomicfu to v0.26.1 (#781, #806)
- Update dependency org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom to v1.0.0-pre.835 (#780, #782, #785, #796, #799)
- Update dependency com.juul.khronicle:khronicle-core to v0.4.1 (#800)
- Update dependency androidx.startup:startup-runtime to v1.2.0 (#797)
- Update dependency androidx.core:core-ktx to v1.15.0 (#786)
- Update plugin android-library to v8.7.2 (#783)
0.35.0-rc
Tip
The SensorTag sample app has been updated to use this release of Kable.
🚀 Changes
This release includes major internal re-workings of Kable, as well as some externally facing API changes. Most notably, the scanning and connection handling internals have been refactored to better leverage structured concurrency and provide a more predictable connection shutdown procedure.
The exception hierarchy has been significantly simplified, making error handling more consistent and possible in common code.
In general, you should now only ever need to catch IOException
when calling Kable functions, all other exceptions should be considered programming error (could've been prevented by pre-checking conditions — e.g. is bluetooth available). Pre-conditions that could change between a check and call (e.g. is peripheral connected) are thrown as IOException
s.
Deprecate Peripheral
creation via CoroutineScope
extension function
Creating a Peripheral
via CoroutineScope
extension function has been deprecated.
A Peripheral
's connection needs to be explicitly managed, having its lifecycle be implicitly governed by a parent CroutineScope
provided a footgun.
It is clearer / easier to reason about if a Peripheral
shall be disposed of manually when it is no longer needed: the Peripheral.cancel
function provides an explicit means of disposing of a Peripheral
.
Old | New |
---|---|
val scope: CoroutineScope
val peripheral = scope.peripheral(advertisement) {
// Configure peripheral.
} |
val peripheral = Peripheral(advertisement) {
// Configure peripheral.
} |
val peripheral: Peripheral = ..
peripheral.disconnect()
scope.cancel() // Dispose of `Peripheral`. |
val peripheral: Peripheral = ..
peripheral.disconnect()
peripheral.cancel() // Dispose of `Peripheral`. |
Peripheral
is now a CoroutineScope
Every Peripheral
is now itself a CoroutineScope
. You can launch
jobs from peripherals that you wish to run until the Peripheral
is disposed (via Peripheral.cancel
).
val peripheral: Peripheral = ..
peripheral.launch {
// Long running task that will be shutdown when `Peripheral` is disposed via `cancel`.
}
Every connection is now a CoroutineScope
Whenever a connection is established (Peripheral.connect
) the function call returns a CoroutineScope
that can be used to launch
jobs that should run until the connection is terminated (either via Peripheral.disconnect
or connection drop).
val peripheral: Peripheral = ..
val scope = peripheral.connect()
scope.launch {
// Long running task that will be shutdown when connection terminates.
}
Deprecate Bluetooth.availability
Having a consistent means of providing realtime bluetooth availability (Bluetooth.availability
flow) proved impossible — mostly due to Core Bluetooth limitations. As such, it is expected that library consumers will roll their own mechanism of determining bluetooth availability, and Bluetooth.availability
will be removed in a future version. See #737 for more details.
Dropped kable-exceptions
Maven artifact
The kable-exceptions
Maven artifact has been dropped in favor of having exceptions provided by kable-core
artifact. If you were previously pulling in com.juul.kable:kable-exceptions
dependency, you should remove it and you only need to pull in com.juul.kable:kable-core
.
Release candidate
This release is considered a "release candidate" as it has not had thorough testing to be considered ready for wider use. The API surface changed significantly as well, and may warrant additional feedback to drive its final form. Please try this version and report any issues.
Common
- Drop
kable-exceptions
module and remove unused exceptions (#769) - Deprecate
Bluetooth.availability
(#772) - Fix advertisement equality/hash (#759)
- Leverage structured concurrency for connection handling (#749)
- Prevent bluetooth permission dialog when spinning up
CBCentralManager
(#739) - Add
Bluetooth.isSupported()
function (#738) - Unify scan exceptions (#733)
- Thanks to @FilippoVigani for reporting #103
- Thanks to @mark-dumontier for reporting #239, and @BluestormDNA for adding details
- Escalate deprecations (#732)
- Use
IOException
provided by kotlinx-io (#728)
Android
- Drop
ConnectionRejectedException
(#771) - Handle when peripheral services change (#754), thanks to @JonatanPlesko for reporting!
- Add basic threading strategy support (#612)
Apple
- Have
allowDuplicateKeys
default totrue
(#760) - Make
isSupported
alwaystrue
on Apple (#752) - Handle when peripheral services change (#754)
JavaScript
- Use
jso
from kotlin-wrappers library (#726)
🧰 Maintenance
- Fix broken links in README.md (#775), special thanks to @maribox!
- Fix closing parenthesis in
Options
builder example in README (#727), special thanks to @khebrati! - Suppress Kotlin compilation warnings on CI (#734)
- Update dependency org.jetbrains.kotlinx:kotlinx-io-core to v0.5.4 (#745, #757)
- Update plugin android-library to v8.7.1 (#741, #763, #770, #777)
- Update plugin api to v0.16.3 (#747)
- Update coroutines to v1.9.0 (#767)
- Update dependency gradle to v8.10.2 (#751)
- Update dependency org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom to v1.0.0-pre.819 (#735, #740, #742, #744, #746, #750, #756, #762, #766, #773, #776)
- Update dependency org.jetbrains.kotlin.multiplatform to v2.0.21 (#736, #743, #774)
0.34.0
Note
This version of Kable provides a DSL for specifying scan filters and should be used instead of the old deprecated filter list (which will be removed in a future Kable release).
Some example migrations to the new scan filter DSL:
Deprecated | New filter DSL |
---|---|
Scanner {
filters = listOf(Filter.Name("My device"))
} |
Scanner {
filters {
match {
name = Filter.Name.Exact("My device")
}
}
} |
Scanner {
filters = listOf(
Filter.Service(uuidFrom(..)),
Filter.NamePrefix("Ex"),
)
} |
Scanner {
filters {
match {
services = listOf(uuidFrom(..))
}
match {
name = Filter.Name.Prefix("Ex")
}
} |
val options = Options(
filters = listOf(
Filter.NamePrefix("Example"),
),
optionalServices = listOf(
uuidFrom(..),
),
)
scope.requestPeripheral(options).await() |
val options = Options {
filters {
match {
name = Filter.Name.Prefix("Example")
}
}
optionalServices = listOf(
uuidFrom(..),
)
)
scope.requestPeripheral(options).await() |
See Scanning section of README and #695 for more details.
Android
- Fix nullability of
connectGattCompat
function (#720)
Apple
- Fix reading of descriptor value as
NSData
(#706), special thanks to @FabioCornelli for the contribution
🧰 Maintenance
0.33.2
Apple
- Discover descriptors on connect (#704), special thanks to @FabioCornelli for the contribution
🧰 Maintenance
- Use GitHub Actions for documentation deployment (#702)
0.33.1
0.33.0
Important
Kable's Maven artifact IDs have changed (prefixed with kable-
):
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.juul.kable:kable-core:${kableVersion}")
implementation("com.juul.kable:kable-exceptions:${kableVersion}")
implementation("com.juul.kable:kable-log-engine-khronicle:${kableVersion}")
}
}
}
Kable's package names were not changed, only the Maven coordinates; you should only need to update your Gradle configuration (e.g. libs.versions.toml
).
Kotlin 2.0.0 (K2) Upgrade
Kable is now built against Kotlin 2.0.0 (K2). 🎉
Note
With this upgrade, the Kotlin compiler became more restrictive with expect
/actual
declarations, as a result, some Kable APIs have changed:
- Advertisement interfaces (e.g.
AndroidAdvertisement
,CoreBluetoothAdvertisement
, etc) have been consolidated to a commonPlatformAdvertisement
Scanner
interface now defines a generic type for advertisements it will emit:Scanner<PlatformAdvertisement>
For most users, these changes should not impact your code. Some notable instances where you'll need to make code changes:
If you hold a reference to a Scanner
in a variable or property, you'll need to change Scanner
to use the PlatformScanner
type alias, or specify the generic type explicitly, for example:
val scanner: PlatformScanner // or...
val scanner: Scanner<PlatformAdvertisement>
If you have any classes that implement the Scanner
interface, you'll need to change the interface to PlatformScanner
(or Scanner<PlatformAdvertisement>
) and change the overridden type of the advertisements
property to PlatformAdvertisement
, for example:
class ExampleScanner(..) : PlatformScanner {
override val advertisements: Flow<PlatformAdvertisement> = ..
}
🚀 Changes
- Update dependency org.jetbrains.kotlin.multiplatform to v2 (#683)
- Replace Tuulbox logging w/ Khronicle (#690)
- Prefix Maven artifacts with
kable-
(#692) - Remove
log-engine-tuulbox
module (#689)
🧰 Maintenance
0.32.0
Android
- Provide ability to pre-conflate scanned advertisements (#684), special thanks to @djweber for the contribution
🧰 Maintenance
- Update plugin android-library to v8.4.1 (#674, #682)
- Update coroutines to v1.8.1 (#678)
- Migrate to
gradle/actions/setup-gradle
(#680) - Update dependency com.juul.khronicle:khronicle-core to v0.3.0 (#676)
- Update dependency org.jetbrains.kotlin.multiplatform to v1.9.24 (#675)
- Update dependency androidx.core:core-ktx to v1.13.1 (#671, #673)
- Update gradle/wrapper-validation-action action to v3 (#672)
- Update atomicfu to v0.24.0 (#670)