1#![warn(clippy::all, future_incompatible, nonstandard_style, rust_2018_idioms)]
42
43mod characteristic;
44mod device;
45mod scanner;
46
47pub mod common;
48
49pub use self::{
50 characteristic::Characteristic,
51 device::{Device, DeviceEvent},
52 scanner::{
53 config::{Filter, ScanConfig},
54 Scanner,
55 },
56};
57pub use btleplug::{api::BDAddr, Error, Result};
58
59#[cfg(test)]
60mod tests {
61 use crate::{Device, Filter, ScanConfig, Scanner};
62 use btleplug::{api::BDAddr, Error};
63 use std::{future::Future, time::Duration};
64 use tokio_stream::StreamExt;
65 use uuid::Uuid;
66
67 #[tokio::test]
68 async fn test_discover() -> anyhow::Result<()> {
69 rsutil::log::Log4rsConfig::default().initialize().unwrap();
70
71 let duration = Duration::from_secs(10);
72 let config = ScanConfig::default().stop_after_timeout(duration);
73
74 let mut scanner = Scanner::new();
75 scanner.start(config).await?;
76
77 while let Some(device) = scanner.device_stream()?.next().await {
78 println!("Found device: {}", device.address());
79 }
80
81 Ok(())
82 }
83
84 async fn device_stream<T: Future<Output = ()>>(
85 scanner: Scanner,
86 callback: impl Fn(Device) -> T,
87 ) {
88 let duration = Duration::from_millis(15_000);
89 if let Err(_) = tokio::time::timeout(duration, async move {
90 if let Ok(mut stream) = scanner.device_stream() {
91 while let Some(device) = stream.next().await {
92 callback(device).await;
93 break;
94 }
95 }
96 })
97 .await
98 {
99 eprintln!("timeout....");
100 }
101 }
102
103 #[tokio::test]
104 async fn test_filter_by_address() -> Result<(), Error> {
105 rsutil::log::Log4rsConfig::default().initialize().unwrap();
106
107 let mac_addr = [0xE3, 0x9E, 0x2A, 0x4D, 0xAA, 0x97];
108 let filers = vec![Filter::Address("E3:9E:2A:4D:AA:97".into())];
109 let cfg = ScanConfig::default()
110 .with_filters(&filers)
111 .stop_after_first_match();
112 let mut scanner = Scanner::default();
113
114 scanner.start(cfg).await?;
115 device_stream(scanner, |device| async move {
116 assert_eq!(device.address(), BDAddr::from(mac_addr));
117 })
118 .await;
119
120 Ok(())
121 }
122
123 #[tokio::test]
124 async fn test_filter_by_character() -> Result<(), Error> {
125 rsutil::log::Log4rsConfig::default().initialize().unwrap();
126
127 let filers = vec![Filter::Characteristic(Uuid::from_u128(
128 0x6e400001_b5a3_f393_e0a9_e50e24dcca9e,
129 ))];
130 let cfg = ScanConfig::default()
131 .with_filters(&filers)
132 .stop_after_first_match();
133 let mut scanner = Scanner::default();
134
135 scanner.start(cfg).await?;
136 device_stream(scanner, |device| async move {
137 println!("device: {:?} found", device);
138 })
139 .await;
140
141 Ok(())
142 }
143
144 #[tokio::test]
145 async fn test_filter_by_name() -> Result<(), Error> {
146 rsutil::log::Log4rsConfig::default().initialize().unwrap();
147
148 let name = "73429485";
149 let filers = vec![Filter::Name(name.into())];
150 let cfg = ScanConfig::default()
151 .with_filters(&filers)
152 .stop_after_first_match();
153 let mut scanner = Scanner::default();
154
155 scanner.start(cfg).await?;
156 device_stream(scanner, |device| async move {
157 assert_eq!(device.local_name().await, Some(name.into()));
158 })
159 .await;
160
161 Ok(())
162 }
163
164 #[tokio::test]
165 async fn test_filter_by_rssi() -> Result<(), Error> {
166 rsutil::log::Log4rsConfig::default().initialize().unwrap();
167
168 let filers = vec![Filter::Rssi(-70)];
169 let cfg = ScanConfig::default()
170 .with_filters(&filers)
171 .stop_after_first_match();
172 let mut scanner = Scanner::default();
173
174 scanner.start(cfg).await?;
175 device_stream(scanner, |device| async move {
176 println!("device: {:?} found", device);
177 })
178 .await;
179
180 Ok(())
181 }
182
183 #[tokio::test]
184 async fn test_filter_by_service() -> Result<(), Error> {
185 rsutil::log::Log4rsConfig::default().initialize().unwrap();
186
187 let service = Uuid::from_u128(0x6e400001_b5a3_f393_e0a9_e50e24dcca9e);
188 let filers = vec![Filter::Service(service)];
189 let cfg = ScanConfig::default()
190 .with_filters(&filers)
191 .stop_after_first_match();
192 let mut scanner = Scanner::default();
193
194 scanner.start(cfg).await?;
195 device_stream(scanner, |device| async move {
196 println!("device: {:?} found", device);
197 })
198 .await;
199
200 Ok(())
201 }
202}