feat: add bindings for BlueZ

This commit is contained in:
Antoine C 2024-09-06 19:49:32 +01:00 committed by Michael Aaron Murphy
parent e0d6a04d6e
commit 8059e6bdaa
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
7 changed files with 763 additions and 0 deletions

119
bluez/src/adapter1.rs Normal file
View 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
View 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
View 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
View 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())
}