Skip to main content

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 peripheral_id: PeripheralId = device.id.into();
51                    let mut events = vec![CentralEvent::DeviceDiscovered(peripheral_id.clone())];
52                    if !device.services.is_empty() {
53                        events.push(CentralEvent::ServicesAdvertisement {
54                            id: peripheral_id.clone(),
55                            services: device.services,
56                        });
57                    }
58                    if device.connected {
59                        events.push(CentralEvent::DeviceConnected(peripheral_id));
60                    }
61                    events.into_iter()
62                }),
63        );
64
65        let session = self.session.clone();
66        let adapter_id = self.adapter.clone();
67        let events = events
68            .filter_map(move |event| central_events(event, session.clone(), adapter_id.clone()))
69            .flat_map(stream::iter);
70
71        Ok(Box::pin(initial_events.chain(events)))
72    }
73
74    async fn start_scan(&self, filter: ScanFilter) -> Result<()> {
75        let filter = DiscoveryFilter {
76            service_uuids: filter.services,
77            duplicate_data: Some(true),
78            transport: Some(Transport::Auto),
79            ..Default::default()
80        };
81        self.session
82            .start_discovery_on_adapter_with_filter(&self.adapter, &filter)
83            .await?;
84        Ok(())
85    }
86
87    async fn stop_scan(&self) -> Result<()> {
88        self.session
89            .stop_discovery_on_adapter(&self.adapter)
90            .await?;
91        Ok(())
92    }
93
94    async fn peripherals(&self) -> Result<Vec<Peripheral>> {
95        let devices = self.session.get_devices_on_adapter(&self.adapter).await?;
96        Ok(devices
97            .into_iter()
98            .map(|device| Peripheral::new(self.session.clone(), device))
99            .collect())
100    }
101
102    async fn peripheral(&self, id: &PeripheralId) -> Result<Peripheral> {
103        let device = self.session.get_device_info(&id.0).await.map_err(|e| {
104            if let BluetoothError::DbusError(_) = e {
105                Error::DeviceNotFound
106            } else {
107                e.into()
108            }
109        })?;
110        Ok(Peripheral::new(self.session.clone(), device))
111    }
112
113    async fn add_peripheral(&self, _address: &PeripheralId) -> Result<Peripheral> {
114        Err(Error::NotSupported(
115            "Can't add a Peripheral from a PeripheralId".to_string(),
116        ))
117    }
118
119    async fn clear_peripherals(&self) -> Result<()> {
120        // BlueZ queries the daemon live; peripherals aren't cached locally.
121        Ok(())
122    }
123
124    async fn adapter_info(&self) -> Result<String> {
125        let adapter_info = self.session.get_adapter_info(&self.adapter).await?;
126        Ok(format!("{} ({})", adapter_info.id, adapter_info.modalias))
127    }
128
129    async fn adapter_state(&self) -> Result<CentralState> {
130        let mut powered = false;
131        if let Ok(info) = self.session.get_adapter_info(&self.adapter).await {
132            powered = info.powered;
133        }
134        Ok(get_central_state(powered))
135    }
136}
137
138impl From<BluetoothError> for Error {
139    fn from(error: BluetoothError) -> Self {
140        Error::Other(Box::new(error))
141    }
142}
143
144async fn central_events(
145    event: BluetoothEvent,
146    session: BluetoothSession,
147    adapter_id: AdapterId,
148) -> Option<Vec<CentralEvent>> {
149    match event {
150        BluetoothEvent::Device {
151            id,
152            event: device_event,
153        } if id.adapter() == adapter_id => match device_event {
154            DeviceEvent::Discovered => {
155                let device = session.get_device_info(&id).await.ok()?;
156                let peripheral_id: PeripheralId = device.id.into();
157                let mut events = vec![CentralEvent::DeviceDiscovered(peripheral_id.clone())];
158                // BlueZ may already know the device's services (from cache or the
159                // advertisement).  Emit a ServicesAdvertisement so listeners don't
160                // have to wait for a separate PropertiesChanged signal that may
161                // never arrive for cached devices.
162                if !device.services.is_empty() {
163                    events.push(CentralEvent::ServicesAdvertisement {
164                        id: peripheral_id,
165                        services: device.services,
166                    });
167                }
168                Some(events)
169            }
170            DeviceEvent::Connected { connected } => {
171                if connected {
172                    Some(vec![CentralEvent::DeviceConnected(id.into())])
173                } else {
174                    Some(vec![CentralEvent::DeviceDisconnected(id.into())])
175                }
176            }
177            DeviceEvent::Rssi { rssi } => {
178                let device = session.get_device_info(&id).await.ok()?;
179                Some(vec![CentralEvent::RssiUpdate {
180                    id: device.id.into(),
181                    rssi,
182                }])
183            }
184            DeviceEvent::ManufacturerData { manufacturer_data } => {
185                let device = session.get_device_info(&id).await.ok()?;
186                Some(vec![CentralEvent::ManufacturerDataAdvertisement {
187                    id: device.id.into(),
188                    manufacturer_data,
189                }])
190            }
191            DeviceEvent::ServiceData { service_data } => {
192                let device = session.get_device_info(&id).await.ok()?;
193                Some(vec![CentralEvent::ServiceDataAdvertisement {
194                    id: device.id.into(),
195                    service_data,
196                }])
197            }
198            DeviceEvent::Services { services } => {
199                let device = session.get_device_info(&id).await.ok()?;
200                Some(vec![CentralEvent::ServicesAdvertisement {
201                    id: device.id.into(),
202                    services,
203                }])
204            }
205            _ => None,
206        },
207        BluetoothEvent::Adapter {
208            id,
209            event: adapter_event,
210        } if id == adapter_id => match adapter_event {
211            AdapterEvent::Powered { powered } => {
212                let state = get_central_state(powered);
213                Some(vec![CentralEvent::StateUpdate(state)])
214            }
215            _ => None,
216        },
217        _ => None,
218    }
219}