bluez_async/
adapter.rs

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