dioxus-blec 0.16.0

blec, the cross platform BLE client, for Dioxus: hooks and signals, plus the android permissions
docs.rs failed to build dioxus-blec-0.16.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

dioxus-blec

blec, the cross platform BLE (Bluetooth Low Energy) client, for Dioxus 0.7.

  • use_ble() initializes blec once for the app and mirrors its state into signals: ready, scanning, found devices, connected device. It has async methods for scanning, connecting, reading and writing.
  • use_ble_notifications(characteristic, service) follows a characteristic for as long as the component lives, resubscribing after every reconnect.
  • On android the crate carries the bluetooth permissions and the Kotlin side of the BLE client as a gradle module that dx builds into the app, so there is no manifest to write and no gradle module to add.

The full blec API stays available through ble.handler() and the re-exported dioxus_blec::blec.

Usage

[dependencies]
dioxus-blec = "0.16.0" # x-release-please-version
use dioxus::prelude::*;
use dioxus_blec::models::{ScanFilter, WriteType};
use dioxus_blec::{use_ble, use_ble_notifications, InitState};
use uuid::{uuid, Uuid};

const CHARACTERISTIC: Uuid = uuid!("51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B");

#[component]
fn App() -> Element {
    let ble = use_ble();
    let notified = use_ble_notifications(CHARACTERISTIC, None);

    if let InitState::Failed(e) = ble.init_state() {
        return rsx! { p { "bluetooth unavailable: {e}" } };
    }

    rsx! {
        button {
            disabled: !ble.is_ready() || ble.scanning(),
            onclick: move |_| async move {
                if let Err(e) = ble.scan(5000, ScanFilter::None).await {
                    tracing::error!("scan failed: {e}");
                }
            },
            "Scan"
        }
        if let Some(device) = ble.connected() {
            p { "connected to {device.name}, last notification: {notified():?}" }
            button {
                onclick: move |_| async move {
                    let _ = ble.write(CHARACTERISTIC, None, b"hello", WriteType::WithResponse).await;
                },
                "Send"
            }
            button { onclick: move |_| async move { let _ = ble.disconnect().await; }, "Disconnect" }
        } else {
            ul {
                for device in ble.devices() {
                    li {
                        key: "{device.address}",
                        button {
                            onclick: move |_| {
                                let address = device.address.clone();
                                async move { let _ = ble.connect(&address).await; }
                            },
                            "{device.name} ({device.address})"
                        }
                    }
                }
            }
        }
    }
}

Ble is Copy, like a signal. Reading one of its getters in a component subscribes the component to that piece of state. The async methods spawn their forwarding tasks on the Dioxus runtime, so call them from components and event handlers, not from a plain tokio task.

../../examples/dioxus-example is a complete app.

Android

The only android setting the app needs is the minimum sdk blec requires:

# Dioxus.toml
[android]
min_sdk = 26

Everything else is in this crate. #[manganis::ffi("android")] in src/lib.rs embeds the path of the crate's android/ directory in the binary. dx picks it up when building for android, copies the directory into the generated gradle project as a library module and builds it with the app. The module's AndroidManifest.xml is merged into the app's; it declares the permissions with the attributes [android.permissions] in Dioxus.toml cannot express (neverForLocation on BLUETOOTH_SCAN, maxSdkVersion on the legacy BLUETOOTH) and the bluetooth_le feature. The module's Kotlin is the android backend of blec, which finds it in the app's class loader at startup. The directory is a symlink to crates/blec/android/lib, the one copy of that module; see Android internals.

Runtime permissions are requested by the first scan, or explicitly with ble.check_permissions(ask_if_denied).

Build for an attached phone with dx serve --platform android --device; without --device, dx builds for the emulator (x86_64) even when none is running.

Without a bluetooth adapter

The mock feature (desktop only) replaces the platform backend with blec::mock, an in-process simulation with no devices until the app adds some. See the mock section of the blec README.

License

MIT or Apache-2.0, at your option.