btleplug/bluez/
adapter.rs

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