feat: add bindings for BlueZ
This commit is contained in:
parent
e0d6a04d6e
commit
8059e6bdaa
7 changed files with 763 additions and 0 deletions
|
|
@ -12,6 +12,7 @@ members = [
|
||||||
"switcheroo-control",
|
"switcheroo-control",
|
||||||
"timedate",
|
"timedate",
|
||||||
"upower",
|
"upower",
|
||||||
|
"bluez",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|
|
||||||
17
bluez/Cargo.toml
Normal file
17
bluez/Cargo.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "bluez-zbus"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "dbus bindings for org.bluez with zbus"
|
||||||
|
repository = "https://github.com/pop-os/dbus-settings-bindings"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MPL-2.0"
|
||||||
|
categories = ["os::linux-apis"]
|
||||||
|
keywords = ["dbus", "bluez", "zbus", "bluetooth"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = "0.3.30"
|
||||||
|
zbus = "4.2.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pico-args = "0.5.0"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
275
bluez/examples/bluetoothctl.rs
Normal file
275
bluez/examples/bluetoothctl.rs
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
|
||||||
|
let connection = zbus::Connection::system().await?;
|
||||||
|
|
||||||
|
let adapters = bluez_zbus::adapters(&connection).await?;
|
||||||
|
|
||||||
|
// if adapters.is_empty() {
|
||||||
|
// eprintln!("No adapter found");
|
||||||
|
// return Ok(ExitCode::FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if adapters.len() > 1 {
|
||||||
|
// eprintln!("More than one adapter found. Using the first one");
|
||||||
|
// }
|
||||||
|
// let adapter = adapters.first().unwrap();
|
||||||
|
|
||||||
|
let mut parser = pico_args::Arguments::from_env();
|
||||||
|
|
||||||
|
match parser.subcommand()?.as_deref() {
|
||||||
|
Some("connected-devices") => match bluez_zbus::get_devices(&connection, None).await {
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("error: could not get devices: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
Ok(devices) => {
|
||||||
|
for proxy in devices {
|
||||||
|
if !proxy.device.connected().await? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"{} ({})",
|
||||||
|
proxy
|
||||||
|
.device
|
||||||
|
.name()
|
||||||
|
.await
|
||||||
|
.unwrap_or(proxy.device.address().await.unwrap()),
|
||||||
|
proxy
|
||||||
|
.device
|
||||||
|
.inner()
|
||||||
|
.get_property::<String>("Icon")
|
||||||
|
.await
|
||||||
|
.unwrap_or("unknown".to_owned())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some("paired-devices") => match bluez_zbus::get_devices(&connection, None).await {
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("error: could not get devices: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
Ok(devices) => {
|
||||||
|
for proxy in devices {
|
||||||
|
if !proxy.device.paired().await? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"{} ({})",
|
||||||
|
proxy
|
||||||
|
.device
|
||||||
|
.name()
|
||||||
|
.await
|
||||||
|
.unwrap_or(proxy.device.address().await.unwrap()),
|
||||||
|
proxy
|
||||||
|
.device
|
||||||
|
.inner()
|
||||||
|
.get_property::<String>("Icon")
|
||||||
|
.await
|
||||||
|
.unwrap_or("unknown".to_owned())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some("nearby-devices") => {
|
||||||
|
futures::future::join_all(adapters.iter().map(|adapter| adapter.start_discovery()))
|
||||||
|
.await;
|
||||||
|
match bluez_zbus::get_devices(&connection, None).await {
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("error: could not get devices: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
Ok(devices) => {
|
||||||
|
for proxy in devices {
|
||||||
|
if proxy.device.paired().await? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"{} ({})",
|
||||||
|
proxy
|
||||||
|
.device
|
||||||
|
.name()
|
||||||
|
.await
|
||||||
|
.unwrap_or(proxy.device.address().await.unwrap()),
|
||||||
|
proxy
|
||||||
|
.device
|
||||||
|
.inner()
|
||||||
|
.get_property::<String>("Icon")
|
||||||
|
.await
|
||||||
|
.unwrap_or("unknown".to_owned())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some("connect") => match parser.free_from_str::<String>().ok().as_deref() {
|
||||||
|
Some(addr_or_alias) => match bluez_zbus::get_devices(&connection, None).await {
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("error: could not get devices: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
Ok(devices) => {
|
||||||
|
let devices: Vec<bluez_zbus::BluetoothDevice> =
|
||||||
|
futures::future::join_all(devices.into_iter().map(|proxy| async {
|
||||||
|
match (proxy.device.name().await, proxy.device.address().await) {
|
||||||
|
(Ok(alias), _) if alias == addr_or_alias => Some(proxy),
|
||||||
|
(_, Ok(addr)) if addr == addr_or_alias => Some(proxy),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
if devices.is_empty() {
|
||||||
|
eprintln!("No device found");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if devices.len() > 1 {
|
||||||
|
eprintln!("More than one one found. Use the address.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
let proxy = devices.first().unwrap();
|
||||||
|
|
||||||
|
if proxy.device.connected().await? {
|
||||||
|
eprintln!("Device already connected.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(why) = proxy.device.connect().await {
|
||||||
|
eprintln!("error: could not connect: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
eprintln!("error: device address or alias missing");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some("disconnect") => match parser.free_from_str::<String>().ok().as_deref() {
|
||||||
|
Some(addr_or_alias) => match bluez_zbus::get_devices(&connection, None).await {
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("error: could not get devices: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
Ok(devices) => {
|
||||||
|
let devices: Vec<bluez_zbus::BluetoothDevice> =
|
||||||
|
futures::future::join_all(devices.into_iter().map(|proxy| async {
|
||||||
|
match (proxy.device.name().await, proxy.device.address().await) {
|
||||||
|
(Ok(alias), _) if alias == addr_or_alias => Some(proxy),
|
||||||
|
(_, Ok(addr)) if addr == addr_or_alias => Some(proxy),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
if devices.is_empty() {
|
||||||
|
eprintln!("No device found");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if devices.len() > 1 {
|
||||||
|
eprintln!("More than one one found. Use the address.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
let proxy = devices.first().unwrap();
|
||||||
|
|
||||||
|
if !proxy.device.connected().await? {
|
||||||
|
eprintln!("Device not connected.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(why) = proxy.device.disconnect().await {
|
||||||
|
eprintln!("error: could not disconnect: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
eprintln!("error: device address or alias missing");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some("forget") => match parser.free_from_str::<String>().ok().as_deref() {
|
||||||
|
Some(addr_or_alias) => match bluez_zbus::get_devices(&connection, None).await {
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("error: could not get devices: {why}");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
Ok(devices) => {
|
||||||
|
let devices: Vec<bluez_zbus::BluetoothDevice> =
|
||||||
|
futures::future::join_all(devices.into_iter().map(|proxy| async {
|
||||||
|
match (proxy.device.name().await, proxy.device.address().await) {
|
||||||
|
(Ok(alias), _) if alias == addr_or_alias => Some(proxy),
|
||||||
|
(_, Ok(addr)) if addr == addr_or_alias => Some(proxy),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
if devices.is_empty() {
|
||||||
|
eprintln!("No device found");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if devices.len() > 1 {
|
||||||
|
eprintln!("More than one one found. Use the address.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
let proxy = devices.first().unwrap();
|
||||||
|
|
||||||
|
if !proxy.device.paired().await? {
|
||||||
|
eprintln!("Device not connected.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxy.device.connected().await? {
|
||||||
|
eprintln!("Cannot remove a connected proxy.device.");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
eprintln!("error: device address or alias missing");
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => print_help(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExitCode::SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_help() {
|
||||||
|
println!(
|
||||||
|
"\
|
||||||
|
bluetoothctl
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
bluetoothctl connected-devices
|
||||||
|
bluetoothctl paired-devices
|
||||||
|
bluetoothctl nearby-devices
|
||||||
|
bluetoothctl connect ADDRESS
|
||||||
|
bluetoothctl disconnect ADDRESS
|
||||||
|
bluetoothctl forget ADDRESS
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
119
bluez/src/adapter1.rs
Normal file
119
bluez/src/adapter1.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
//! # D-Bus interface proxy for: `org.bluez.Adapter1`
|
||||||
|
//!
|
||||||
|
//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data.
|
||||||
|
//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`.
|
||||||
|
//!
|
||||||
|
//! You may prefer to adapt it, instead of using it verbatim.
|
||||||
|
//!
|
||||||
|
//! More information can be found in the [Writing a client proxy] section of the zbus
|
||||||
|
//! documentation.
|
||||||
|
//!
|
||||||
|
//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the
|
||||||
|
//! following zbus API can be used:
|
||||||
|
//!
|
||||||
|
//! * [`zbus::fdo::IntrospectableProxy`]
|
||||||
|
//! * [`zbus::fdo::PropertiesProxy`]
|
||||||
|
//!
|
||||||
|
//! Consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||||
|
//!
|
||||||
|
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
|
||||||
|
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
|
||||||
|
use zbus::proxy;
|
||||||
|
#[proxy(interface = "org.bluez.Adapter1", default_service = "org.bluez")]
|
||||||
|
trait Adapter1 {
|
||||||
|
/// ConnectDevice method
|
||||||
|
fn connect_device(
|
||||||
|
&self,
|
||||||
|
properties: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// GetDiscoveryFilters method
|
||||||
|
fn get_discovery_filters(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// RemoveDevice method
|
||||||
|
fn remove_device(&self, device: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// SetDiscoveryFilter method
|
||||||
|
fn set_discovery_filter(
|
||||||
|
&self,
|
||||||
|
properties: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// StartDiscovery method
|
||||||
|
fn start_discovery(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// StopDiscovery method
|
||||||
|
fn stop_discovery(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Address property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn address(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// AddressType property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn address_type(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// Alias property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn alias(&self) -> zbus::Result<String>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_alias(&self, value: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Class property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn class(&self) -> zbus::Result<u32>;
|
||||||
|
|
||||||
|
/// Discoverable property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn discoverable(&self) -> zbus::Result<bool>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_discoverable(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// DiscoverableTimeout property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn discoverable_timeout(&self) -> zbus::Result<u32>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_discoverable_timeout(&self, value: u32) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Discovering property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn discovering(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// ExperimentalFeatures property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn experimental_features(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// Modalias property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn modalias(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// Name property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn name(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// Pairable property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn pairable(&self) -> zbus::Result<bool>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_pairable(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// PairableTimeout property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn pairable_timeout(&self) -> zbus::Result<u32>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_pairable_timeout(&self, value: u32) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Powered property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn powered(&self) -> zbus::Result<bool>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_powered(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Roles property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn roles(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// UUIDs property
|
||||||
|
#[zbus(property, name = "UUIDs")]
|
||||||
|
fn uuids(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
}
|
||||||
31
bluez/src/battery1.rs
Normal file
31
bluez/src/battery1.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
//! # D-Bus interface proxy for: `org.bluez.Battery1`
|
||||||
|
//!
|
||||||
|
//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data.
|
||||||
|
//! Source: `Interface '/org/bluez/hci0/dev_14_3F_A6_A8_16_68' from service 'org.bluez' on system bus`.
|
||||||
|
//!
|
||||||
|
//! You may prefer to adapt it, instead of using it verbatim.
|
||||||
|
//!
|
||||||
|
//! More information can be found in the [Writing a client proxy] section of the zbus
|
||||||
|
//! documentation.
|
||||||
|
//!
|
||||||
|
//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the
|
||||||
|
//! following zbus API can be used:
|
||||||
|
//!
|
||||||
|
//! * [`zbus::fdo::IntrospectableProxy`]
|
||||||
|
//! * [`zbus::fdo::PropertiesProxy`]
|
||||||
|
//!
|
||||||
|
//! Consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||||
|
//!
|
||||||
|
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
|
||||||
|
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
|
||||||
|
use zbus::proxy;
|
||||||
|
#[proxy(interface = "org.bluez.Battery1", default_service = "org.bluez")]
|
||||||
|
trait Battery1 {
|
||||||
|
/// Percentage property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn percentage(&self) -> zbus::Result<u8>;
|
||||||
|
|
||||||
|
/// Source property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn source(&self) -> zbus::Result<String>;
|
||||||
|
}
|
||||||
147
bluez/src/device1.rs
Normal file
147
bluez/src/device1.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
//! # D-Bus interface proxy for: `org.bluez.Device1`
|
||||||
|
//!
|
||||||
|
//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data.
|
||||||
|
//! Source: `Interface '/org/bluez/hci0/dev_14_3F_A6_A8_16_68' from service 'org.bluez' on system bus`.
|
||||||
|
//!
|
||||||
|
//! You may prefer to adapt it, instead of using it verbatim.
|
||||||
|
//!
|
||||||
|
//! More information can be found in the [Writing a client proxy] section of the zbus
|
||||||
|
//! documentation.
|
||||||
|
//!
|
||||||
|
//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the
|
||||||
|
//! following zbus API can be used:
|
||||||
|
//!
|
||||||
|
//! * [`zbus::fdo::IntrospectableProxy`]
|
||||||
|
//! * [`zbus::fdo::PropertiesProxy`]
|
||||||
|
//!
|
||||||
|
//! Consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||||
|
//!
|
||||||
|
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
|
||||||
|
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
|
||||||
|
use zbus::proxy;
|
||||||
|
#[proxy(interface = "org.bluez.Device1", default_service = "org.bluez")]
|
||||||
|
trait Device1 {
|
||||||
|
/// CancelPairing method
|
||||||
|
fn cancel_pairing(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Connect method
|
||||||
|
fn connect(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// ConnectProfile method
|
||||||
|
fn connect_profile(&self, UUID: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Disconnect method
|
||||||
|
fn disconnect(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// DisconnectProfile method
|
||||||
|
fn disconnect_profile(&self, UUID: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Pair method
|
||||||
|
fn pair(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Adapter property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn adapter(&self) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
|
||||||
|
|
||||||
|
/// Address property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn address(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// AddressType property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn address_type(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// AdvertisingData property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn advertising_data(
|
||||||
|
&self,
|
||||||
|
) -> zbus::Result<std::collections::HashMap<u8, zbus::zvariant::OwnedValue>>;
|
||||||
|
|
||||||
|
/// AdvertisingFlags property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn advertising_flags(&self) -> zbus::Result<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Alias property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn alias(&self) -> zbus::Result<String>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_alias(&self, value: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Appearance property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn appearance(&self) -> zbus::Result<u16>;
|
||||||
|
|
||||||
|
/// Blocked property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn blocked(&self) -> zbus::Result<bool>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_blocked(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Class property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn class(&self) -> zbus::Result<u32>;
|
||||||
|
|
||||||
|
/// Connected property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn connected(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// Icon property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn icon(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// LegacyPairing property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn legacy_pairing(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// ManufacturerData property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn manufacturer_data(
|
||||||
|
&self,
|
||||||
|
) -> zbus::Result<std::collections::HashMap<u16, zbus::zvariant::OwnedValue>>;
|
||||||
|
|
||||||
|
/// Modalias property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn modalias(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// Name property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn name(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// Paired property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn paired(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// RSSI property
|
||||||
|
#[zbus(property, name = "RSSI")]
|
||||||
|
fn rssi(&self) -> zbus::Result<i16>;
|
||||||
|
|
||||||
|
/// ServiceData property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn service_data(
|
||||||
|
&self,
|
||||||
|
) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
|
||||||
|
|
||||||
|
/// ServicesResolved property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn services_resolved(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// Trusted property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn trusted(&self) -> zbus::Result<bool>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_trusted(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// TxPower property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn tx_power(&self) -> zbus::Result<i16>;
|
||||||
|
|
||||||
|
/// UUIDs property
|
||||||
|
#[zbus(property, name = "UUIDs")]
|
||||||
|
fn uuids(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// WakeAllowed property
|
||||||
|
#[zbus(property)]
|
||||||
|
fn wake_allowed(&self) -> zbus::Result<bool>;
|
||||||
|
#[zbus(property)]
|
||||||
|
fn set_wake_allowed(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
173
bluez/src/lib.rs
Normal file
173
bluez/src/lib.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use futures::join;
|
||||||
|
|
||||||
|
pub mod adapter1;
|
||||||
|
pub mod battery1;
|
||||||
|
pub mod device1;
|
||||||
|
|
||||||
|
pub async fn get_adapters<'a>(
|
||||||
|
connection: &zbus::Connection,
|
||||||
|
) -> zbus::Result<HashMap<zbus::zvariant::OwnedObjectPath, adapter1::Adapter1Proxy<'a>>> {
|
||||||
|
let managed_object_proxy =
|
||||||
|
zbus::fdo::ObjectManagerProxy::new(connection, "org.bluez", "/").await?;
|
||||||
|
let managed_object: zbus::fdo::ManagedObjects =
|
||||||
|
managed_object_proxy.get_managed_objects().await?;
|
||||||
|
let adapter_addresses: Vec<zbus::zvariant::OwnedObjectPath> = managed_object
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(move |(path, interfaces)| {
|
||||||
|
interfaces
|
||||||
|
.contains_key("org.bluez.Adapter1")
|
||||||
|
.then_some(path.to_owned())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let adapters: Vec<
|
||||||
|
zbus::Result<(zbus::zvariant::OwnedObjectPath, adapter1::Adapter1Proxy<'a>)>,
|
||||||
|
> = futures::future::join_all(adapter_addresses.into_iter().map(|path| async {
|
||||||
|
Ok((
|
||||||
|
path.clone(),
|
||||||
|
adapter1::Adapter1Proxy::new(connection, path).await?,
|
||||||
|
))
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let errors = adapters.iter().filter(|device| device.is_err());
|
||||||
|
if errors.count() > 0 {
|
||||||
|
let mut errors: Vec<zbus::Error> = adapters
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::err)
|
||||||
|
.collect();
|
||||||
|
if errors.len() > 1 {
|
||||||
|
eprintln!("Multiple errors occurs when fetching connected device: {errors:?}. Only the last one will be returned.");
|
||||||
|
}
|
||||||
|
return Err(errors.pop().unwrap());
|
||||||
|
}
|
||||||
|
Ok(adapters
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::ok)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BluetoothDevice<'a> {
|
||||||
|
pub device: device1::Device1Proxy<'a>,
|
||||||
|
pub battery: Option<battery1::Battery1Proxy<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BluetoothDevice<'a> {
|
||||||
|
pub async fn new<'b: 'a>(
|
||||||
|
connection: &zbus::Connection,
|
||||||
|
path: zbus::zvariant::ObjectPath<'b>,
|
||||||
|
) -> zbus::Result<Self> {
|
||||||
|
let (device, battery) = join!(
|
||||||
|
device1::Device1Proxy::builder(connection)
|
||||||
|
.path(&path)?
|
||||||
|
.build(),
|
||||||
|
battery1::Battery1Proxy::builder(connection)
|
||||||
|
.path(path)?
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
match (device, battery) {
|
||||||
|
(Ok(device), Ok(battery)) if battery.percentage().await.is_err() => Ok(Self {
|
||||||
|
device,
|
||||||
|
battery: None,
|
||||||
|
}),
|
||||||
|
(Ok(device), Ok(battery)) => Ok(Self {
|
||||||
|
device,
|
||||||
|
battery: Some(battery),
|
||||||
|
}),
|
||||||
|
(Ok(device), Err(zbus::Error::InterfaceNotFound)) => Ok(Self {
|
||||||
|
device,
|
||||||
|
battery: None,
|
||||||
|
}),
|
||||||
|
(Err(why), _) => Err(why),
|
||||||
|
(_, Err(why)) => Err(why),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn icon(&self) -> String {
|
||||||
|
self.device
|
||||||
|
.inner()
|
||||||
|
.get_property::<String>("Icon")
|
||||||
|
.await
|
||||||
|
.unwrap_or("unknown".to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> zbus::zvariant::OwnedObjectPath {
|
||||||
|
self.device.inner().path().to_owned().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_device<'a>(
|
||||||
|
connection: &zbus::Connection,
|
||||||
|
device_path: zbus::zvariant::OwnedObjectPath,
|
||||||
|
) -> zbus::Result<BluetoothDevice<'a>> {
|
||||||
|
BluetoothDevice::new(
|
||||||
|
connection,
|
||||||
|
device_path.into(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_adapter<'a>(
|
||||||
|
connection: &zbus::Connection,
|
||||||
|
adapter_path: impl TryInto<zbus::zvariant::ObjectPath<'a>>,
|
||||||
|
) -> zbus::Result<adapter1::Adapter1Proxy<'a>> {
|
||||||
|
adapter1::Adapter1Proxy::builder(connection)
|
||||||
|
.path(
|
||||||
|
adapter_path
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| zbus::Error::Failure("Invalid adapter path".to_owned()))?,
|
||||||
|
)?
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_devices<'a>(
|
||||||
|
connection: &zbus::Connection,
|
||||||
|
adapter: Option<&str>,
|
||||||
|
) -> zbus::Result<HashMap<zbus::zvariant::OwnedObjectPath, BluetoothDevice<'a>>> {
|
||||||
|
let managed_object_proxy =
|
||||||
|
zbus::fdo::ObjectManagerProxy::new(connection, "org.bluez", "/").await?;
|
||||||
|
let managed_object: zbus::fdo::ManagedObjects =
|
||||||
|
managed_object_proxy.get_managed_objects().await?;
|
||||||
|
let device_addresses: Vec<zbus::zvariant::OwnedObjectPath> = managed_object
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(move |(path, interfaces)| {
|
||||||
|
if matches!(
|
||||||
|
adapter.map(|adapter| path.as_str().starts_with(&format!("{}/", adapter))),
|
||||||
|
None | Some(true)
|
||||||
|
) {
|
||||||
|
return interfaces
|
||||||
|
.contains_key("org.bluez.Device1")
|
||||||
|
.then_some(path.to_owned());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let devices: Vec<zbus::Result<(zbus::zvariant::OwnedObjectPath, BluetoothDevice<'a>)>> =
|
||||||
|
futures::future::join_all(device_addresses.into_iter().map(|path| async {
|
||||||
|
Ok((
|
||||||
|
path.clone(),
|
||||||
|
BluetoothDevice::new(connection, path.into()).await?,
|
||||||
|
))
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let errors = devices.iter().filter(|device| device.is_err());
|
||||||
|
if errors.count() > 0 {
|
||||||
|
let mut errors: Vec<zbus::Error> = devices
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::err)
|
||||||
|
.collect();
|
||||||
|
if errors.len() > 1 {
|
||||||
|
eprintln!("Multiple errors occurs when fetching connected device: {errors:?}. Only the last one will be returned.");
|
||||||
|
}
|
||||||
|
return Err(errors.pop().unwrap());
|
||||||
|
}
|
||||||
|
Ok(devices
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::ok)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue