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 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}