pub struct Device(/* private fields */);
Expand description
A Bluetooth LE device
Implementations§
source§impl Device
impl Device
sourcepub fn id(&self) -> DeviceId
pub fn id(&self) -> DeviceId
This device’s unique identifier
Examples found in repository?
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let device_id = {
let adapter = Adapter::default().await.unwrap();
adapter.wait_available().await?;
info!("looking for device");
let device = adapter
.discover_devices(&[btuuid::services::BATTERY])
.await?
.next()
.await
.ok_or("Failed to discover device")??;
info!(
"found device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
device.id()
};
info!("Time passes...");
tokio::time::sleep(Duration::from_secs(5)).await;
{
let adapter = Adapter::default().await.unwrap();
adapter.wait_available().await?;
info!("re-opening previously found device");
let device = adapter.open_device(&device_id).await?;
info!(
"re-opened device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
}
Ok(())
}
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
info!("looking for device");
let device = adapter
.discover_devices(&[NORDIC_LED_AND_BUTTON_SERVICE])
.await?
.next()
.await
.ok_or("Failed to discover device")??;
info!(
"found device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
adapter.connect_device(&device).await?;
info!("connected!");
let service = match device
.discover_services_with_uuid(NORDIC_LED_AND_BUTTON_SERVICE)
.await?
.get(0)
{
Some(service) => service.clone(),
None => return Err("service not found".into()),
};
info!("found LED and button service");
let characteristics = service.characteristics().await?;
info!("discovered characteristics");
let button_characteristic = characteristics
.iter()
.find(|x| x.uuid() == BLINKY_BUTTON_STATE_CHARACTERISTIC)
.ok_or("button characteristic not found")?;
let button_fut = async {
info!("enabling button notifications");
let mut updates = button_characteristic.notify().await?;
info!("waiting for button changes");
while let Some(val) = updates.next().await {
info!("Button state changed: {:?}", val?);
}
Ok(())
};
let led_characteristic = characteristics
.iter()
.find(|x| x.uuid() == BLINKY_LED_STATE_CHARACTERISTIC)
.ok_or("led characteristic not found")?;
let blink_fut = async {
info!("blinking LED");
tokio::time::sleep(Duration::from_secs(1)).await;
loop {
led_characteristic.write(&[0x01]).await?;
info!("LED on");
tokio::time::sleep(Duration::from_secs(1)).await;
led_characteristic.write(&[0x00]).await?;
info!("LED off");
tokio::time::sleep(Duration::from_secs(1)).await;
}
};
type R = Result<(), Box<dyn Error>>;
let button_fut = async move {
let res: R = button_fut.await;
error!("Button task exited: {:?}", res);
};
let blink_fut = async move {
let res: R = blink_fut.await;
error!("Blink task exited: {:?}", res);
};
future::zip(blink_fut, button_fut).await;
Ok(())
}
sourcepub fn name(&self) -> Result<String>
pub fn name(&self) -> Result<String>
The local name for this device, if available
This can either be a name advertised or read from the device, or a name assigned to the device by the OS.
Panics
On Linux, this method will panic if there is a current Tokio runtime and it is single-threaded or if there is no current Tokio runtime and creating one fails.
Examples found in repository?
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
async fn confirm(&self, device: &Device) -> Result<(), PairingRejected> {
tokio::task::block_in_place(move || {
println!("Do you want to pair with {:?}? (Y/n)", device.name().unwrap());
let mut buf = String::new();
std::io::stdin()
.read_line(&mut buf)
.map_err(|_| PairingRejected::default())?;
let response = buf.trim();
if response.is_empty() || response == "y" || response == "Y" {
Ok(())
} else {
Err(PairingRejected::default())
}
})
}
async fn confirm_passkey(&self, device: &Device, passkey: Passkey) -> Result<(), PairingRejected> {
tokio::task::block_in_place(move || {
println!(
"Is the passkey \"{}\" displayed on {:?}? (Y/n)",
passkey,
device.name().unwrap()
);
let mut buf = String::new();
std::io::stdin()
.read_line(&mut buf)
.map_err(|_| PairingRejected::default())?;
let response = buf.trim();
if response.is_empty() || response == "y" || response == "Y" {
Ok(())
} else {
Err(PairingRejected::default())
}
})
}
async fn request_passkey(&self, device: &Device) -> Result<Passkey, PairingRejected> {
tokio::task::block_in_place(move || {
println!("Please enter the 6-digit passkey for {:?}: ", device.name().unwrap());
let mut buf = String::new();
std::io::stdin()
.read_line(&mut buf)
.map_err(|_| PairingRejected::default())?;
buf.trim().parse().map_err(|_| PairingRejected::default())
})
}
fn display_passkey(&self, device: &Device, passkey: Passkey) {
println!("The passkey is \"{}\" for {:?}.", passkey, device.name().unwrap());
}
More examples
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
info!("starting scan");
let mut scan = adapter.scan(&[]).await?;
info!("scan started");
while let Some(discovered_device) = scan.next().await {
info!(
"{}{}: {:?}",
discovered_device.device.name().as_deref().unwrap_or("(unknown)"),
discovered_device
.rssi
.map(|x| format!(" ({}dBm)", x))
.unwrap_or_default(),
discovered_device.adv_data.services
);
}
Ok(())
}
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let device_id = {
let adapter = Adapter::default().await.unwrap();
adapter.wait_available().await?;
info!("looking for device");
let device = adapter
.discover_devices(&[btuuid::services::BATTERY])
.await?
.next()
.await
.ok_or("Failed to discover device")??;
info!(
"found device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
device.id()
};
info!("Time passes...");
tokio::time::sleep(Duration::from_secs(5)).await;
{
let adapter = Adapter::default().await.unwrap();
adapter.wait_available().await?;
info!("re-opening previously found device");
let device = adapter.open_device(&device_id).await?;
info!(
"re-opened device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
}
Ok(())
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
info!("looking for device");
let device = adapter
.discover_devices(&[NORDIC_LED_AND_BUTTON_SERVICE])
.await?
.next()
.await
.ok_or("Failed to discover device")??;
info!(
"found device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
adapter.connect_device(&device).await?;
info!("connected!");
let service = match device
.discover_services_with_uuid(NORDIC_LED_AND_BUTTON_SERVICE)
.await?
.get(0)
{
Some(service) => service.clone(),
None => return Err("service not found".into()),
};
info!("found LED and button service");
let characteristics = service.characteristics().await?;
info!("discovered characteristics");
let button_characteristic = characteristics
.iter()
.find(|x| x.uuid() == BLINKY_BUTTON_STATE_CHARACTERISTIC)
.ok_or("button characteristic not found")?;
let button_fut = async {
info!("enabling button notifications");
let mut updates = button_characteristic.notify().await?;
info!("waiting for button changes");
while let Some(val) = updates.next().await {
info!("Button state changed: {:?}", val?);
}
Ok(())
};
let led_characteristic = characteristics
.iter()
.find(|x| x.uuid() == BLINKY_LED_STATE_CHARACTERISTIC)
.ok_or("led characteristic not found")?;
let blink_fut = async {
info!("blinking LED");
tokio::time::sleep(Duration::from_secs(1)).await;
loop {
led_characteristic.write(&[0x01]).await?;
info!("LED on");
tokio::time::sleep(Duration::from_secs(1)).await;
led_characteristic.write(&[0x00]).await?;
info!("LED off");
tokio::time::sleep(Duration::from_secs(1)).await;
}
};
type R = Result<(), Box<dyn Error>>;
let button_fut = async move {
let res: R = button_fut.await;
error!("Button task exited: {:?}", res);
};
let blink_fut = async move {
let res: R = blink_fut.await;
error!("Blink task exited: {:?}", res);
};
future::zip(blink_fut, button_fut).await;
Ok(())
}
sourcepub async fn name_async(&self) -> Result<String>
pub async fn name_async(&self) -> Result<String>
The local name for this device, if available
This can either be a name advertised or read from the device, or a name assigned to the device by the OS.
sourcepub async fn is_connected(&self) -> bool
pub async fn is_connected(&self) -> bool
The connection status for this device
sourcepub async fn pair(&self) -> Result<()>
pub async fn pair(&self) -> Result<()>
Attempt to pair this device using the system default pairing UI
Platform specific
MacOS/iOS
Device pairing is performed automatically by the OS when a characteristic requiring security is accessed. This method is a no-op.
Windows
This will fail unless it is called from a UWP application.
sourcepub async fn pair_with_agent<T: PairingAgent + 'static>(
&self,
agent: &T
) -> Result<()>
pub async fn pair_with_agent<T: PairingAgent + 'static>( &self, agent: &T ) -> Result<()>
Attempt to pair this device using the system default pairing UI
Platform specific
On MacOS/iOS, device pairing is performed automatically by the OS when a characteristic requiring security is accessed. This method is a no-op.
Examples found in repository?
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
let discovered_device = {
info!("starting scan");
let mut scan = adapter.scan(&[btuuid::services::HUMAN_INTERFACE_DEVICE]).await?;
info!("scan started");
scan.next().await.ok_or("scan terminated")?
};
info!("{:?} {:?}", discovered_device.rssi, discovered_device.adv_data);
let device = discovered_device.device;
adapter.connect_device(&device).await?;
info!("connected!");
device.pair_with_agent(&StdioPairingAgent).await?;
info!("paired!");
adapter.disconnect_device(&device).await?;
info!("disconnected!");
Ok(())
}
sourcepub async fn discover_services(&self) -> Result<Vec<Service>>
pub async fn discover_services(&self) -> Result<Vec<Service>>
Discover the primary services of this device.
sourcepub async fn discover_services_with_uuid(
&self,
uuid: Uuid
) -> Result<Vec<Service>>
pub async fn discover_services_with_uuid( &self, uuid: Uuid ) -> Result<Vec<Service>>
Discover the primary service(s) of this device with the given Uuid
.
Examples found in repository?
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
info!("looking for device");
let device = adapter
.discover_devices(&[NORDIC_LED_AND_BUTTON_SERVICE])
.await?
.next()
.await
.ok_or("Failed to discover device")??;
info!(
"found device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);
adapter.connect_device(&device).await?;
info!("connected!");
let service = match device
.discover_services_with_uuid(NORDIC_LED_AND_BUTTON_SERVICE)
.await?
.get(0)
{
Some(service) => service.clone(),
None => return Err("service not found".into()),
};
info!("found LED and button service");
let characteristics = service.characteristics().await?;
info!("discovered characteristics");
let button_characteristic = characteristics
.iter()
.find(|x| x.uuid() == BLINKY_BUTTON_STATE_CHARACTERISTIC)
.ok_or("button characteristic not found")?;
let button_fut = async {
info!("enabling button notifications");
let mut updates = button_characteristic.notify().await?;
info!("waiting for button changes");
while let Some(val) = updates.next().await {
info!("Button state changed: {:?}", val?);
}
Ok(())
};
let led_characteristic = characteristics
.iter()
.find(|x| x.uuid() == BLINKY_LED_STATE_CHARACTERISTIC)
.ok_or("led characteristic not found")?;
let blink_fut = async {
info!("blinking LED");
tokio::time::sleep(Duration::from_secs(1)).await;
loop {
led_characteristic.write(&[0x01]).await?;
info!("LED on");
tokio::time::sleep(Duration::from_secs(1)).await;
led_characteristic.write(&[0x00]).await?;
info!("LED off");
tokio::time::sleep(Duration::from_secs(1)).await;
}
};
type R = Result<(), Box<dyn Error>>;
let button_fut = async move {
let res: R = button_fut.await;
error!("Button task exited: {:?}", res);
};
let blink_fut = async move {
let res: R = blink_fut.await;
error!("Blink task exited: {:?}", res);
};
future::zip(blink_fut, button_fut).await;
Ok(())
}
sourcepub async fn services(&self) -> Result<Vec<Service>>
pub async fn services(&self) -> Result<Vec<Service>>
Get previously discovered services.
If no services have been discovered yet, this method will perform service discovery.
Examples found in repository?
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
info!("getting connected devices");
let devices = adapter.connected_devices().await?;
for device in devices {
info!("found {:?}", device);
adapter.connect_device(&device).await?;
let services = device.services().await?;
for service in services {
info!(" {:?}", service);
let characteristics = service.characteristics().await?;
for characteristic in characteristics {
info!(" {:?}", characteristic);
let props = characteristic.properties().await?;
info!(" props: {:?}", props);
if props.read {
info!(" value: {:?}", characteristic.read().await);
}
if props.write_without_response {
info!(" max_write_len: {:?}", characteristic.max_write_len());
}
let descriptors = characteristic.descriptors().await?;
for descriptor in descriptors {
info!(" {:?}: {:?}", descriptor, descriptor.read().await);
}
}
}
}
info!("done");
Ok(())
}
sourcepub async fn services_changed(&self) -> Result<()>
pub async fn services_changed(&self) -> Result<()>
Asynchronously blocks until a GATT services changed packet is received
Platform specific
sourcepub async fn service_changed_indications(
&self
) -> Result<impl Stream<Item = Result<ServicesChanged>> + Send + Unpin + '_>
pub async fn service_changed_indications( &self ) -> Result<impl Stream<Item = Result<ServicesChanged>> + Send + Unpin + '_>
Monitors the device for service changed indications.
Platform specific
On Windows an event is generated whenever the services
value is updated. In addition to actual service change
indications this occurs when, for example, discover_services
is called or when an unpaired device disconnects.
sourcepub async fn rssi(&self) -> Result<i16>
pub async fn rssi(&self) -> Result<i16>
Get the current signal strength from the device in dBm.
Platform specific
Returns NotSupported
on Windows and Linux.