sparkbler/
lib.rs

1use std::marker::PhantomData;
2
3use bluest::{Adapter, Device};
4use color::ADO_LIGHT_CHARACTERISTIC_UUID;
5use futures::{Stream, StreamExt, TryStreamExt, stream};
6use lightstick::{Connected, Discovered, LightStick};
7use thiserror::Error;
8
9pub mod color;
10pub mod lightstick;
11
12#[derive(Error, Debug)]
13pub enum LightStickErrors {
14    #[error("No adapter exists!")]
15    NoAdapter,
16}
17
18/// Scan for all lightsticks detected by their characteristic UUID
19///
20/// This function returns a stream that will continuously hunt for
21/// new lightsticks to connect to.
22///
23/// It requires an adapter instance to scan over
24pub async fn scan_for_lightsticks(
25    adapter: &Adapter,
26) -> Result<
27    stream::MapOk<
28        impl Stream<Item = Result<Device, bluest::Error>> + Send + Unpin,
29        impl FnMut(Device) -> LightStick<Discovered>,
30    >,
31    bluest::Error,
32> {
33    let stream = adapter
34        .discover_devices(&[ADO_LIGHT_CHARACTERISTIC_UUID])
35        .await?;
36    Ok(stream.map_ok(|d| LightStick {
37        device: d,
38        adapter: adapter.clone(),
39        _marker: PhantomData::<Discovered>,
40    }))
41}
42
43/// Returns the first found lightstick that is succesfully connected
44///
45/// If passed an adapter uses that, if not, uses the default adapter
46pub async fn find_first_lightstick(
47    adapter: Option<Adapter>,
48) -> Result<LightStick<Connected>, Box<dyn std::error::Error>> {
49    // grab the adapter if none
50    let adapter = {
51        match adapter {
52            Some(adapter) => adapter,
53            None => {
54                let adapter = Adapter::default().await;
55                match adapter {
56                    Some(adapter) => adapter,
57                    None => return Err(Box::new(LightStickErrors::NoAdapter)),
58                }
59            }
60        }
61    };
62
63    // start the scan
64    let mut scan = scan_for_lightsticks(&adapter).await?;
65
66    // forever, try to grab a lightstick
67    loop {
68        // if we discover one succesfully
69        if let Some(Ok(lightstick)) = scan.next().await {
70            // and if we can connect to it, return it
71            if let Ok(lightstick) = lightstick.connect().await {
72                return Ok(lightstick);
73            }
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {}