tauri-plugin-blec 0.14.1

BLE-Client plugin for Tauri
docs.rs failed to build tauri-plugin-blec-0.14.1
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.
Visit the last successful build: tauri-plugin-blec-0.6.0

Tauri Plugin blec

A BLE-Client plugin based on btlelug.

The main difference to using btleplug directly is that this uses the tauri plugin system for android. All other platforms use the btleplug implementation.

Docs

Installation

Install the rust part of the plugin

cargo add tauri-plugin-blec

Or manually add it to the src-tauri/Cargo.toml

[dependencies]
tauri-plugin-blec = "0.12"

Install the js bindings

use your preferred JavaScript package manager to add @mnlphlp/plugin-blec:

yarn add @mnlphlp/plugin-blec
npm add @mnlphlp/plugin-blec

Register the plugin in Tauri

src-tauri/src/lib.rs

tauri::Builder::default()
    .plugin(tauri_plugin_blec::init())
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
let mut app = tauri::Builder::default();
app = match tauri_plugin_blec::try_init() {
    Ok(plugin) => app.plugin(plugin),
    Err(e) => {
        eprintln!("Failed to initialize blec plugin: {:?}", e);
        app
    }
};
app.run(tauri::generate_context!())
    .expect("error while running tauri application");

Allow calls from Frontend

Add blec:default to the permissions in your capabilities file.

Explanation about capabilities

IOS Setup

Add an entry to the info.plist of your app:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>The App uses Bluetooth to communicate with BLE devices</string>

Add the CoreBluetooth Framework in your xcode procjet:

  • open with tauri ios dev --open
  • click on your project to open settings
  • Add Framework under General -> Frameworks,Libraries and Embedded Content

Usage in Frontend

See examples/plugin-blec-example for a full working example that scans for devices, connects and sends/receives data. In order to use it run examples/test-server on another device and connect to that server.

Short example:

import { connect, sendString } from '@mnlphlp/plugin-blec'
// get address by scanning for devices and selecting the desired one
let address = ...
// connect and run a callback on disconnect
await connect(address, () => console.log('disconnected'))
// send some text to a characteristic
const CHARACTERISTIC_UUID = '51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B'
await sendString(CHARACTERISTIC_UUID, 'Test', 'withResponse')

Testing

Unit tests with the mock backend

The crate contains a mock of the btleplug backend (tauri_plugin_blec::mock) that simulates an adapter and devices in process: devices can disappear, drop their link, answer slowly, fail or hang operations, and send notifications. The handler tests in src/handler/tests drive the plugin through those situations, including a seeded randomized stress test.

cargo test
# with the handler's logs
RUST_LOG=trace cargo test -- --nocapture
# longer randomized run, reproducible via the seed
BLEC_STRESS_SEED=7 BLEC_STRESS_ITERS=5000 cargo test stress -- --nocapture
# tests documenting known bugs are ignored; run them to see the current behaviour
cargo test -- --ignored

The mock can also replace the real backend in an app (desktop targets only) with the mock cargo feature. The plugin then talks to MockWorld::global(), which the app can script:

use btleplug::api::CharPropFlags;
use tauri_plugin_blec::mock::{DeviceSpec, MockWorld, ServiceSpec};
let device = MockWorld::global().add_device(
    DeviceSpec::new([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x01]).name("fake").service(
        ServiceSpec::new(SERVICE_UUID).characteristic(CHARACTERISTIC_UUID, CharPropFlags::NOTIFY),
    ),
);
device.drop_link(); // simulate a lost connection

Stress testing the full stack

For the real stack there are two example programs:

  • examples/stress-server: a GATT peripheral (Linux/BlueZ, runs on the host PC) that can drop the link, stop advertising, flood notifications, answer slowly, fail requests or restart, on request from the client or from its REPL.
  • examples/stress-client: a Tauri app for the phone or a second PC. Its integration scenario runs predefined scripts: one write starts a script, both sides follow the same timeline (drop, disappear, storm, slow responses, restart, rapid reconnects), verify their side and the client merges the server's report into a PASS/FAIL table. A hold scenario keeps a connection open for soak testing.

The scripts and the GATT protocol are defined in examples/stress-protocol.md.

Usage in Backend

The plugin can also be used from the rust backend.

The handler returned by get_handler() is the same that is used by the frontend commands. This means if you connect from the frontend you can send data from rust without having to call connect on the backend.

use uuid::{uuid, Uuid};
use tauri_plugin_blec::models::WriteType;

const CHARACTERISTIC_UUID: Uuid = uuid!("51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B");
const DATA: [u8; 500] = [0; 500];
let handler = tauri_plugin_blec::get_handler().unwrap();
handler
    .send_data(CHARACTERISTIC_UUID, &DATA, WriteType::WithResponse)
    .await
    .unwrap();