bleasy/
lib.rs

1//! High-level BLE communication library.
2//!
3//! The goal of this library is to provide an easy-to-use interface
4//! for communicating with BLE devices, that satisfies most use cases.
5//!
6//! ## Usage
7//!
8//! Here is an example on how to find a device with battery level characteristic and read
9//! a value from that characteristic:
10//!
11//! ```rust,no_run
12//! use bleasy::{characteristics::BATTERY_LEVEL, Error, Filter, ScanConfig, Scanner};
13//! use tokio_stream::StreamExt;
14//!
15//! #[tokio::main]
16//! async fn main() -> Result<(), Error> {
17//!     rsutil::log::Log4rsConfig::default().initialize().unwrap();
18//!
19//!     // Create a filter for devices that have battery level characteristic
20//!     let config = ScanConfig::default()
21//!         .with_filters(&[Filter::Characteristic(BATTERY_LEVEL)])
22//!         .filter_by_characteristics(|characters, bat_lvl| characters.contains(bat_lvl))
23//!         .stop_after_first_match();
24//!
25//!     // Start scanning for devices
26//!     let mut scanner = Scanner::new();
27//!     scanner.start(config).await?;
28//!
29//!     // Take the first discovered device
30//!     let device = scanner.device_stream().next().await.unwrap();
31//!     println!("{:?}", device);
32//!
33//!     // Read the battery level
34//!     let battery_level = device.characteristic(BATTERY_LEVEL).await?.unwrap();
35//!     println!("Battery level: {:?}", battery_level.read().await?);
36//!
37//!     Ok(())
38//! }
39//!```
40
41#![warn(clippy::all, future_incompatible, nonstandard_style, rust_2018_idioms)]
42
43mod characteristic;
44mod common;
45mod device;
46mod scanner;
47
48pub use self::{
49    characteristic::Characteristic,
50    common::*,
51    device::{Device, DeviceEvent},
52    scanner::{
53        config::{Filter, ScanConfig},
54        Scanner,
55    },
56};
57pub use btleplug::{
58    api::{BDAddr, PeripheralProperties},
59    Error, Result,
60};
61
62#[cfg(test)]
63mod tests {
64    use crate::{Filter, ScanConfig, Scanner};
65    use btleplug::{api::BDAddr, Error};
66    use std::time::Duration;
67    use tokio_stream::StreamExt;
68    use uuid::Uuid;
69
70    #[tokio::test]
71    async fn test_discover() -> anyhow::Result<()> {
72        rsutil::log::Log4rsConfig::default().initialize().unwrap();
73
74        let cfg = ScanConfig::default().stop_after_timeout(Duration::from_secs(10));
75
76        let mut scanner = Scanner::new();
77        scanner.start(cfg).await?;
78
79        while let Some(device) = scanner.device_stream()?.next().await {
80            println!("Found device: {}", device.address());
81        }
82
83        Ok(())
84    }
85
86    #[tokio::test]
87    async fn test_filter_by_address() -> Result<(), Error> {
88        rsutil::log::Log4rsConfig::default().initialize().unwrap();
89
90        let mac_addr = [0xE3, 0x9E, 0x2A, 0x4D, 0xAA, 0x97];
91        let filers = vec![Filter::Address("E3:9E:2A:4D:AA:97".into())];
92        let cfg = ScanConfig::default()
93            .with_filters(&filers)
94            .stop_after_timeout(Duration::from_secs(10))
95            .stop_after_first_match();
96        let mut scanner = Scanner::default();
97
98        scanner.start(cfg).await?;
99        while let Some(device) = scanner.device_stream()?.next().await {
100            assert_eq!(device.address(), BDAddr::from(mac_addr));
101        }
102
103        Ok(())
104    }
105
106    #[tokio::test]
107    async fn test_filter_by_character() -> Result<(), Error> {
108        rsutil::log::Log4rsConfig::default().initialize().unwrap();
109
110        let filers = vec![Filter::Characteristic(Uuid::from_u128(
111            0x6e400001_b5a3_f393_e0a9_e50e24dcca9e,
112        ))];
113        let cfg = ScanConfig::default()
114            .with_filters(&filers)
115            .stop_after_timeout(Duration::from_secs(10))
116            .stop_after_first_match();
117        let mut scanner = Scanner::default();
118
119        scanner.start(cfg).await?;
120        while let Some(device) = scanner.device_stream()?.next().await {
121            println!("device: {:?} found", device);
122        }
123
124        Ok(())
125    }
126
127    #[tokio::test]
128    async fn test_filter_by_name() -> Result<(), Error> {
129        rsutil::log::Log4rsConfig::default().initialize().unwrap();
130
131        let name = "73429485";
132        let filers = vec![Filter::Name(name.into())];
133        let cfg = ScanConfig::default()
134            .with_filters(&filers)
135            .stop_after_timeout(Duration::from_secs(10))
136            .stop_after_first_match();
137        let mut scanner = Scanner::default();
138
139        scanner.start(cfg).await?;
140        while let Some(device) = scanner.device_stream()?.next().await {
141            assert_eq!(device.local_name().await, Some(name.into()));
142        }
143
144        Ok(())
145    }
146
147    #[tokio::test]
148    async fn test_filter_by_rssi() -> Result<(), Error> {
149        rsutil::log::Log4rsConfig::default().initialize().unwrap();
150
151        let filers = vec![Filter::Rssi(-70)];
152        let cfg = ScanConfig::default()
153            .with_filters(&filers)
154            .stop_after_timeout(Duration::from_secs(10))
155            .stop_after_first_match();
156        let mut scanner = Scanner::default();
157
158        scanner.start(cfg).await?;
159        while let Some(device) = scanner.device_stream()?.next().await {
160            println!("device: {:?} found", device);
161        }
162
163        Ok(())
164    }
165
166    #[tokio::test]
167    async fn test_filter_by_service() -> Result<(), Error> {
168        rsutil::log::Log4rsConfig::default().initialize().unwrap();
169
170        let service = Uuid::from_u128(0x6e400001_b5a3_f393_e0a9_e50e24dcca9e);
171        let filers = vec![Filter::Service(service)];
172        let cfg = ScanConfig::default()
173            .with_filters(&filers)
174            .stop_after_timeout(Duration::from_secs(10))
175            .stop_after_first_match();
176        let mut scanner = Scanner::default();
177
178        scanner.start(cfg).await?;
179        while let Some(device) = scanner.device_stream()?.next().await {
180            println!("device: {:?} found", device);
181        }
182
183        Ok(())
184    }
185}