#rgb-led #ble #bluetooth-le #led-controller

ble-ledly

A customizable and extensible cross-platform high-level Bluetooth Low Energy light controller

4 releases (2 breaking)

0.3.0 Jul 27, 2022
0.2.0 Jul 25, 2022
0.1.0-alpha.1 Jul 21, 2022
0.1.0-alpha.0 Jul 19, 2022

#735 in Hardware support

MIT license

2MB
789 lines

ble-ledly

Crates.io Tests Doc Crates.io Downloads MIT

Customizable and extensible cross-platform high-level Bluetooth Low Energy light controller.

ble-ledly

Provides out-of-the-box support for generic RGB led strips and BLE lamps and light bulbs. Designed to be extensible, allows to implement your own devices, communication protocol or both (See the readme file for more). Supports hardware specific animations (transferrable) and software non-transferrable animations.

Capabilities

Each device supports multiple capabilities available as featurs through conditional compilation in the .toml file.

ble-ledly = {version = "0.3", features = ["all"]}

Each capability provides a single point access to a specific device characteristic that can be manipulated through the publicly available set() method. All the methods are available through the standard API Capability::set(opts) or through more idiomatic methods as follow.

// standard
Light::set(light, &protocol, &LightOption::On).await?;

// idiomatic syntactic sugar
light.turn_on(&protocol).await?;
Capability Description Implemented?
Light Light state (on/off)
Color Light Color (RGB)
Brightness Light white levels
HWAnimate Hardware specific animations (subjected to protocol)
SWAnimate Software animation (require continuous communication, but allows custom effect on any device)
Temperature Light temperature (K)

Animations

Transferrable vs. Non-transferrable

Transferrable animations are built-in in the target ble device which takes care of driving the led(s); once the command is sent, no extra communication needed between the client and ble light controller. Non-transferrable animations bypass the controller's built-in effects and allow for higher degree of customization while providing support for legacy or cheap light controllers allowing to provide effects otherwise not available to the target device. As a consequence, this requires continuous connection between the controller and client.

Current support

Animation Capability Color Support Implemented?
Pulsating HWAnimate Red/Green/Blue
Breathing SWAnimate RGB/Any
Rainbow Pulsating HWAnimate N/A
Pulsating Bicolor HWAnimate Red/Green, Red/Blue, Green/Blue
Rainbow flashing HWAnimate N/A
Cross-fade SWAnimate RGB/Any
Rainbow jumping HWAnimate N/A
Flashing HWAnimate Red, Green, Blue, Yellow, Purple, Cyan

Extensibility

This library has been designed with extensibility in mind.

  • It is possible to create your own device by implementing the Device trait and use the built-in communication protocol.
  • You can add your own communication protocol by implementing the Protocol trait and use it to drive one of the built-in devices.
  • Create your own device and communication protocol.

Usage

An example using built-in device LedDevice and GenericRGB communication protocol. For more examples, see the examples folder.

Minimal Configuration

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Create a new Light controller with prefix
    // Auto-filter devices that contain the prefix
    let mut controller = Controller::<LedDevice>::new_with_prefix("QHM-").await?;

    // Connect
    controller.connect().await?;

    // Choose your communication protocol
    let protocol = GenericRGB::default();

    // set default write characteristic for all connected
    // devices
    controller.set_all_char(&CharKind::Write, &UuidKind::Uuid16(0xFFD9))?;

    // Setting first found light color to red
    let first_light = controller.list().get(0).unwrap();
    first_light.color(&protocol, 255, 0, 0).await?;

    Ok(())
}

Light controls

use ble_ledly::capability::color::*;
use ble_ledly::capability::light::*;
use ble_ledly::capability::sw_animate::*;
use ble_ledly::communication_protocol::GenericRGB;
use ble_ledly::Controller;
use ble_ledly::device::LedDevice;
use ble_ledly::device::{CharKind, UuidKind};

use std::error::Error;
use std::time::Duration;
use tokio::time;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Create a new Light controller
    let mut controller = Controller::<LedDevice>::new().await?;

    // Discover devices (scan)
    let led_devices = controller.device_discovery().await?;

    // inspect all found devices
    for device in led_devices.iter() {
        println!("Found device: {}", device);
    }
    // filter devices
    let lights: Vec<LedDevice> = led_devices
        .into_iter()
        .filter(|device| device.name.contains("QHM-"))
        .collect();

    // Connect
    controller.connect_with_devices(lights).await?;

    // Choose your communication protocol
    let protocol = GenericRGB::default();

    // set the default write Characteristic
    // for all devices. Optionally you can also
    // set it per-device. Look the examples folder for more
    controller.set_all_char(&CharKind::Write, &UuidKind::Uuid16(0xFFD9))?;

    // list all connected devices
    let connected_lights = controller.list();
    for light in connected_lights.iter_mut() {
        println!("Connected to : {}", light.name);

        // Control the lights
        println!("Turning light on...");
        light.turn_on(&protocol).await?;

        // Set color
        println!("Setting color...");
        light.color(&protocol, 255, 0, 0).await?;
        time::sleep(Duration::from_millis(800)).await;
        light.color(&protocol, 0, 255, 0).await?;
        time::sleep(Duration::from_millis(800)).await;
        light.color(&protocol, 0, 0, 255).await?;
        time::sleep(Duration::from_millis(800)).await;

        println!("SW Animation - Breathing effect...");
        light
            .breathing(
                &GenericRGB {},
                &ColorOption::RGB(255, 0, 0),
                &SWAnimationRepeat::FiniteCount(2),
                &SWAnimationSpeed::Fastest,
            )
            .await?;
        light
            .breathing(
                &GenericRGB {},
                &ColorOption::RGB(0, 255, 0),
                &SWAnimationRepeat::FiniteCount(2),
                &SWAnimationSpeed::Fastest,
            )
            .await?;
        light
            .breathing(
                &GenericRGB {},
                &ColorOption::RGB(0, 0, 255),
                &SWAnimationRepeat::FiniteCount(2),
                &SWAnimationSpeed::Fastest,
            )
            .await?;

        // Control the lights
        println!("Turning light off...");
        light.turn_off(&protocol).await?;
    }

    Ok(())
}

Contributing

Open a PR or issue

License

MIT

Dependencies

~5–37MB
~506K SLoC