7 releases

new 0.2.0 Oct 15, 2020
0.1.3 Jun 28, 2020
0.1.2 Feb 23, 2020
0.1.1 Jan 10, 2020
0.0.2 Feb 11, 2019

#68 in Hardware support

Download history 76/week @ 2020-06-28 64/week @ 2020-07-05 57/week @ 2020-07-12 58/week @ 2020-07-19 58/week @ 2020-07-26 51/week @ 2020-08-02 70/week @ 2020-08-09 52/week @ 2020-08-16 60/week @ 2020-08-23 57/week @ 2020-08-30 68/week @ 2020-09-06 52/week @ 2020-09-13 53/week @ 2020-09-20 54/week @ 2020-09-27 56/week @ 2020-10-04 63/week @ 2020-10-11

244 downloads per month

MIT license



A library for controlling Bluetooth on Linux.

crates.io crates.io

Documentation Examples

Some of the examples require elevated permissions. For example, to run the discover example, clone this repository, cargo build, and then sudo target/debug/discover. sudo is necessary because many of the functions of this crate are not possible without the CAP_NET_RAW capability.


This project is licensed under the MIT license.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in bluez by you, shall be licensed as MIT, without any additional terms or conditions.


Getting Started

The most important type in this crate is the BlueZClient, which is used to issue commands to and listen for events from the Bluetooth controller(s). It's fully decked out in async/.await bling, too.

# use std::error::Error;
# use bluez::client::BlueZClient;
# #[async_std::main]
# pub async fn main() -> Result<(), Box<dyn Error>> {
let mut client = BlueZClient::new().unwrap();

let version = client.get_mgmt_version().await?;
    "management version: {}.{}",
    version.version, version.revision

let controllers = client.get_controller_list().await?;

for controller in controllers {
    let info = client.get_controller_info(controller).await?;
    println!("{:?}", info)

#   Ok(())
# }

Aside from directly issuing commands to the Bluetooth controller and recieving a response, you may want to listen for events or perform processes that span multiple commands. For this to work, you need to supply a callback to your client and call process(). The callback will be called any time that the client processes an event (excluding events that indicate that a command has completed), while process() will cause the client to check the underlying socket for new input.

# use std::error::Error;
# use std::time::Duration;
# use bluez::client::*;
# use bluez::interface::event::Event;
# #[async_std::main]
# pub async fn main() -> Result<(), Box<dyn Error>> {
#    let mut client = BlueZClient::new().unwrap();

let controllers = client.get_controller_list().await?;
let controller = controllers.first().expect("no bluetooth controllers available");

client.set_handler(|controller, event| match event {
    Event::DeviceFound {
    } => {
            "[{:?}] found device {} ({:?})",
            controller, address, address_type
        println!("\tflags: {:?}", flags);
        println!("\trssi: {:?}", rssi);
    _ => (),

        AddressTypeFlag::BREDR | AddressTypeFlag::LEPublic | AddressTypeFlag::LERandom,

for _ in 0usize..5000usize {
#  Ok(())
# }

The process() loop

Since process() returns the latest response to be processed, you may be wondering why you would use a callback at all; isn't it easier to just take the return values inside the loop?

In the case demonstrated here, it would be. In fact, this is how it is implemented in the version of the sample on GitHub, but the reason for the callback is that events can arrive while a command is being executed. Since the client is mutably borrowed for the span of each command (i.e., between the instruction being sent to the kernel and the kernel sending a Command Status event), but another event may arrive before the Command Status event, a way is needed to capture such an event. Internally, each command just calls process() repeatedly until a relevant Command Status event appears, and process() will call your handler.


Commands that just query information, such as BlueZClient::get_controller_info, will usually work. However, commands that try to change any settings, such as BlueZClient::set_powered will fail with 'permission denied' errors if your process does not have the CAP_NET_RAW capability.


~96K SLoC