Skip to main content

bluez_async/
adapter.rs

1use crate::Modalias;
2use crate::{AddressType, BluetoothError, MacAddress};
3use bluez_generated::OrgBluezAdapter1Properties;
4use dbus::Path;
5use serde::{Deserialize, Serialize};
6use std::fmt::{self, Display, Formatter};
7
8/// Opaque identifier for a Bluetooth adapter on the system.
9#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
10pub struct AdapterId {
11    #[serde(with = "crate::serde_path")]
12    pub(crate) object_path: Path<'static>,
13}
14
15impl AdapterId {
16    pub(crate) fn new(object_path: &str) -> Self {
17        Self {
18            object_path: object_path.to_owned().into(),
19        }
20    }
21}
22
23impl From<AdapterId> for Path<'static> {
24    fn from(id: AdapterId) -> Self {
25        id.object_path
26    }
27}
28
29impl Display for AdapterId {
30    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
31        write!(
32            f,
33            "{}",
34            self.object_path
35                .to_string()
36                .strip_prefix("/org/bluez/")
37                .ok_or(fmt::Error)?
38        )
39    }
40}
41
42/// Information about a Bluetooth adapter on the system.
43#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct AdapterInfo {
45    /// An opaque identifier for the adapter. This can be used to perform operations on it.
46    pub id: AdapterId,
47    /// The MAC address of the adapter.
48    pub mac_address: MacAddress,
49    /// The type of MAC address the adapter uses.
50    pub address_type: AddressType,
51    /// The Bluetooth system hostname.
52    pub name: String,
53    /// The Bluetooth friendly name. This defaults to the system hostname.
54    pub alias: String,
55    /// Information about the Bluetooth adapter, mostly useful for debug purposes.
56    pub modalias: Modalias,
57    /// Whether the adapter is currently turned on.
58    pub powered: bool,
59    /// Whether the adapter is currently discovering devices.
60    pub discovering: bool,
61}
62
63impl AdapterInfo {
64    pub(crate) fn from_properties(
65        id: AdapterId,
66        adapter_properties: OrgBluezAdapter1Properties,
67    ) -> Result<AdapterInfo, BluetoothError> {
68        let mac_address = adapter_properties
69            .address()
70            .ok_or(BluetoothError::RequiredPropertyMissing("Address"))?
71            .parse()?;
72        let address_type = adapter_properties
73            .address_type()
74            .ok_or(BluetoothError::RequiredPropertyMissing("AddressType"))?
75            .parse()?;
76        let modalias = adapter_properties
77            .modalias()
78            .ok_or(BluetoothError::RequiredPropertyMissing("Modalias"))?
79            .parse()?;
80
81        Ok(AdapterInfo {
82            id,
83            mac_address,
84            address_type,
85            name: adapter_properties
86                .name()
87                .ok_or(BluetoothError::RequiredPropertyMissing("Name"))?
88                .to_owned(),
89            alias: adapter_properties
90                .alias()
91                .ok_or(BluetoothError::RequiredPropertyMissing("Alias"))?
92                .to_owned(),
93            modalias,
94            powered: adapter_properties
95                .powered()
96                .ok_or(BluetoothError::RequiredPropertyMissing("Powered"))?,
97            discovering: adapter_properties
98                .discovering()
99                .ok_or(BluetoothError::RequiredPropertyMissing("Discovering"))?,
100        })
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use crate::modalias::ModaliasSubtype;
108    use dbus::arg::{PropMap, Variant};
109    use std::collections::HashMap;
110
111    #[test]
112    fn adapter_info_minimal() {
113        let id = AdapterId::new("/org/bluez/hci0");
114        let mut adapter_properties: PropMap = HashMap::new();
115        adapter_properties.insert(
116            "Address".to_string(),
117            Variant(Box::new("00:11:22:33:44:55".to_string())),
118        );
119        adapter_properties.insert(
120            "AddressType".to_string(),
121            Variant(Box::new("public".to_string())),
122        );
123        adapter_properties.insert("Name".to_string(), Variant(Box::new("name".to_string())));
124        adapter_properties.insert("Alias".to_string(), Variant(Box::new("alias".to_string())));
125        adapter_properties.insert(
126            "Modalias".to_string(),
127            Variant(Box::new("usb:v1234p5678d90AB".to_string())),
128        );
129        adapter_properties.insert("Powered".to_string(), Variant(Box::new(false)));
130        adapter_properties.insert("Discovering".to_string(), Variant(Box::new(false)));
131
132        let adapter = AdapterInfo::from_properties(
133            id.clone(),
134            OrgBluezAdapter1Properties(&adapter_properties),
135        )
136        .unwrap();
137        assert_eq!(
138            adapter,
139            AdapterInfo {
140                id,
141                mac_address: "00:11:22:33:44:55".parse().unwrap(),
142                address_type: AddressType::Public,
143                name: "name".to_string(),
144                alias: "alias".to_string(),
145                modalias: Modalias {
146                    subtype: ModaliasSubtype::Usb,
147                    vendor_id: 0x1234,
148                    product_id: 0x5678,
149                    device_id: 0x90ab
150                },
151                powered: false,
152                discovering: false
153            }
154        )
155    }
156
157    #[test]
158    fn to_string() {
159        let adapter_id = AdapterId::new("/org/bluez/hci0");
160        assert_eq!(adapter_id.to_string(), "hci0");
161    }
162}