Skip to main content

btleplug/bluez/
adapter.rs

1use super::peripheral::{Peripheral, PeripheralId};
2use crate::api::{Central, CentralEvent, ScanFilter};
3use crate::{Error, Result};
4use async_trait::async_trait;
5use bluez_async::{
6    AdapterId, BluetoothError, BluetoothEvent, BluetoothSession, DeviceEvent, DiscoveryFilter,
7    Transport,
8};
9use futures::stream::{self, Stream, StreamExt};
10use std::pin::Pin;
11
12/// Implementation of [api::Central](crate::api::Central).
13#[derive(Clone, Debug)]
14pub struct Adapter {
15    session: BluetoothSession,
16    adapter: AdapterId,
17}
18
19impl Adapter {
20    pub(crate) fn new(session: BluetoothSession, adapter: AdapterId) -> Self {
21        Self { session, adapter }
22    }
23}
24
25#[async_trait]
26impl Central for Adapter {
27    type Peripheral = Peripheral;
28
29    async fn events(&self) -> Result<Pin<Box<dyn Stream<Item = CentralEvent> + Send>>> {
30        // There's a race between getting this event stream and getting the current set of devices.
31        // Get the stream first, on the basis that it's better to have a duplicate DeviceDiscovered
32        // event than to miss one. It's unlikely to happen in any case.
33        let events = self.session.adapter_event_stream(&self.adapter).await?;
34
35        // Synthesise `DeviceDiscovered' and `DeviceConnected` events for existing peripherals.
36        let devices = self.session.get_devices().await?;
37        let adapter_id = self.adapter.clone();
38        let initial_events = stream::iter(
39            devices
40                .into_iter()
41                .filter(move |device| device.id.adapter() == adapter_id)
42                .flat_map(|device| {
43                    let mut events = vec![CentralEvent::DeviceDiscovered(device.id.clone().into())];
44                    if device.connected {
45                        events.push(CentralEvent::DeviceConnected(device.id.into()));
46                    }
47                    events.into_iter()
48                }),
49        );
50
51        let session = self.session.clone();
52        let adapter_id = self.adapter.clone();
53        let events = events
54            .filter_map(move |event| central_event(event, session.clone(), adapter_id.clone()));
55
56        Ok(Box::pin(initial_events.chain(events)))
57    }
58
59    async fn start_scan(&self, filter: ScanFilter) -> Result<()> {
60        let filter = DiscoveryFilter {
61            service_uuids: filter.services,
62            duplicate_data: Some(true),
63            transport: Some(Transport::Auto),
64            ..Default::default()
65        };
66        self.session
67            .start_discovery_on_adapter_with_filter(&self.adapter, &filter)
68            .await?;
69        Ok(())
70    }
71
72    async fn stop_scan(&self) -> Result<()> {
73        self.session
74            .stop_discovery_on_adapter(&self.adapter)
75            .await?;
76        Ok(())
77    }
78
79    async fn peripherals(&self) -> Result<Vec<Peripheral>> {
80        let devices = self.session.get_devices_on_adapter(&self.adapter).await?;
81        Ok(devices
82            .into_iter()
83            .map(|device| Peripheral::new(self.session.clone(), device))
84            .collect())
85    }
86
87    async fn peripheral(&self, id: &PeripheralId) -> Result<Peripheral> {
88        let device = self.session.get_device_info(&id.0).await.map_err(|e| {
89            if let BluetoothError::DbusError(_) = e {
90                Error::DeviceNotFound
91            } else {
92                e.into()
93            }
94        })?;
95        Ok(Peripheral::new(self.session.clone(), device))
96    }
97
98    async fn add_peripheral(&self, _address: &PeripheralId) -> Result<Peripheral> {
99        Err(Error::NotSupported(
100            "Can't add a Peripheral from a PeripheralId".to_string(),
101        ))
102    }
103
104    async fn adapter_info(&self) -> Result<String> {
105        let adapter_info = self.session.get_adapter_info(&self.adapter).await?;
106        Ok(format!("{} ({})", adapter_info.id, adapter_info.modalias))
107    }
108}
109
110impl From<BluetoothError> for Error {
111    fn from(error: BluetoothError) -> Self {
112        Error::Other(Box::new(error))
113    }
114}
115
116async fn central_event(
117    event: BluetoothEvent,
118    session: BluetoothSession,
119    adapter_id: AdapterId,
120) -> Option<CentralEvent> {
121    match event {
122        BluetoothEvent::Device {
123            id,
124            event: device_event,
125        } if id.adapter() == adapter_id => match device_event {
126            DeviceEvent::Discovered => {
127                let device = session.get_device_info(&id).await.ok()?;
128                Some(CentralEvent::DeviceDiscovered(device.id.into()))
129            }
130            DeviceEvent::Connected { connected } => {
131                let device = session.get_device_info(&id).await.ok()?;
132                if connected {
133                    Some(CentralEvent::DeviceConnected(device.id.into()))
134                } else {
135                    Some(CentralEvent::DeviceDisconnected(device.id.into()))
136                }
137            }
138            DeviceEvent::Rssi { rssi: _ } => {
139                let device = session.get_device_info(&id).await.ok()?;
140                Some(CentralEvent::DeviceUpdated(device.id.into()))
141            }
142            DeviceEvent::ManufacturerData { manufacturer_data } => {
143                let device = session.get_device_info(&id).await.ok()?;
144                Some(CentralEvent::ManufacturerDataAdvertisement {
145                    id: device.id.into(),
146                    manufacturer_data,
147                })
148            }
149            DeviceEvent::ServiceData { service_data } => {
150                let device = session.get_device_info(&id).await.ok()?;
151                Some(CentralEvent::ServiceDataAdvertisement {
152                    id: device.id.into(),
153                    service_data,
154                })
155            }
156            DeviceEvent::Services { services } => {
157                let device = session.get_device_info(&id).await.ok()?;
158                Some(CentralEvent::ServicesAdvertisement {
159                    id: device.id.into(),
160                    services,
161                })
162            }
163            _ => None,
164        },
165        _ => None,
166    }
167}