# ble-ledly
[![Crates.io](https://img.shields.io/crates/v/ble-ledly)](https://crates.io/crates/ble-ledly)
![Tests](https://github.com/espressoshock/ble-ledly/actions/workflows/test.yml/badge.svg)
![Doc](https://github.com/espressoshock/ble-ledly/actions/workflows/doc.yml/badge.svg)
[![Crates.io Downloads](https://img.shields.io/crates/d/ble-ledly?color=bright%20green)](https://crates.io/crates/ble-ledly)
![MIT](https://img.shields.io/github/license/espressoshock/ble-ledly?color=blue)

> _Customizable_ and _extensible_ cross-platform high-level _Bluetooth Low Energy_ light controller. 

![ble-ledly](./assets/ble-ledly-logo-animated.png)


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. 

```toml
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.

```rust
// 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](https://github.com/espressoshock/ble-ledly/tree/main/examples) folder.

### Minimal Configuration

```rust
#[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 

```rust
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