pair/
pair.rs

1use std::error::Error;
2
3use async_trait::async_trait;
4use bluest::pairing::{IoCapability, PairingAgent, PairingRejected, Passkey};
5use bluest::{btuuid, Adapter, Device};
6use futures_lite::StreamExt;
7use tracing::info;
8use tracing::metadata::LevelFilter;
9
10struct StdioPairingAgent;
11
12#[async_trait]
13impl PairingAgent for StdioPairingAgent {
14    /// The input/output capabilities of this agent
15    fn io_capability(&self) -> IoCapability {
16        IoCapability::KeyboardDisplay
17    }
18
19    async fn confirm(&self, device: &Device) -> Result<(), PairingRejected> {
20        tokio::task::block_in_place(move || {
21            println!("Do you want to pair with {:?}? (Y/n)", device.name().unwrap());
22            let mut buf = String::new();
23            std::io::stdin()
24                .read_line(&mut buf)
25                .map_err(|_| PairingRejected::default())?;
26            let response = buf.trim();
27            if response.is_empty() || response == "y" || response == "Y" {
28                Ok(())
29            } else {
30                Err(PairingRejected::default())
31            }
32        })
33    }
34
35    async fn confirm_passkey(&self, device: &Device, passkey: Passkey) -> Result<(), PairingRejected> {
36        tokio::task::block_in_place(move || {
37            println!(
38                "Is the passkey \"{}\" displayed on {:?}? (Y/n)",
39                passkey,
40                device.name().unwrap()
41            );
42            let mut buf = String::new();
43            std::io::stdin()
44                .read_line(&mut buf)
45                .map_err(|_| PairingRejected::default())?;
46            let response = buf.trim();
47            if response.is_empty() || response == "y" || response == "Y" {
48                Ok(())
49            } else {
50                Err(PairingRejected::default())
51            }
52        })
53    }
54
55    async fn request_passkey(&self, device: &Device) -> Result<Passkey, PairingRejected> {
56        tokio::task::block_in_place(move || {
57            println!("Please enter the 6-digit passkey for {:?}: ", device.name().unwrap());
58            let mut buf = String::new();
59            std::io::stdin()
60                .read_line(&mut buf)
61                .map_err(|_| PairingRejected::default())?;
62            buf.trim().parse().map_err(|_| PairingRejected::default())
63        })
64    }
65
66    fn display_passkey(&self, device: &Device, passkey: Passkey) {
67        println!("The passkey is \"{}\" for {:?}.", passkey, device.name().unwrap());
68    }
69}
70
71#[tokio::main]
72async fn main() -> Result<(), Box<dyn Error>> {
73    use tracing_subscriber::prelude::*;
74    use tracing_subscriber::{fmt, EnvFilter};
75
76    tracing_subscriber::registry()
77        .with(fmt::layer())
78        .with(
79            EnvFilter::builder()
80                .with_default_directive(LevelFilter::INFO.into())
81                .from_env_lossy(),
82        )
83        .init();
84
85    let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
86    adapter.wait_available().await?;
87
88    let discovered_device = {
89        info!("starting scan");
90        let mut scan = adapter.scan(&[btuuid::services::HUMAN_INTERFACE_DEVICE]).await?;
91        info!("scan started");
92        scan.next().await.ok_or("scan terminated")?
93    };
94
95    info!("{:?} {:?}", discovered_device.rssi, discovered_device.adv_data);
96    let device = discovered_device.device;
97
98    adapter.connect_device(&device).await?;
99    info!("connected!");
100
101    device.pair_with_agent(&StdioPairingAgent).await?;
102    info!("paired!");
103
104    adapter.disconnect_device(&device).await?;
105    info!("disconnected!");
106
107    Ok(())
108}