bleasy/
device.rs

1use btleplug::{
2    api::{
3        BDAddr, Central as _, CentralEvent, Characteristic as BleCharacteristic, Peripheral as _,
4        Service,
5    },
6    platform::{Adapter, Peripheral, PeripheralId},
7    Result,
8};
9use std::{collections::BTreeSet, sync::Arc, time::Duration};
10use tokio::{task::JoinHandle, time};
11use tokio_stream::StreamExt;
12use uuid::Uuid;
13
14use crate::Characteristic;
15
16#[derive(Debug, Clone)]
17pub struct Device {
18    pub(self) _adapter: Adapter,
19    pub(crate) peripheral: Peripheral,
20    pub(crate) task: Arc<Option<JoinHandle<()>>>,
21}
22
23impl Device {
24    pub(crate) fn new(adapter: Adapter, peripheral: Peripheral) -> Self {
25        Self {
26            _adapter: adapter,
27            peripheral,
28            task: Arc::new(None),
29        }
30    }
31
32    #[inline]
33    pub fn address(&self) -> BDAddr {
34        self.peripheral.address()
35    }
36
37    /// add device disconnected callback
38    pub fn on_disconnected<F>(&mut self, f: F)
39    where
40        F: FnOnce(PeripheralId) + Send + 'static,
41    {
42        let adapter_clone = self._adapter.clone();
43        let peripheral_clone = self.peripheral.clone();
44        let handle = tokio::spawn(async move {
45            if let Ok(mut stream) = adapter_clone.events().await {
46                while let Some(event) = stream.next().await {
47                    match event {
48                        CentralEvent::DeviceDisconnected(per) => {
49                            if per == peripheral_clone.id() {
50                                f(per);
51                                break;
52                            }
53                        }
54                        _ => time::sleep(Duration::from_micros(100)).await,
55                    }
56                }
57            }
58        });
59
60        self.task = Arc::new(Some(handle));
61    }
62
63    /// Signal strength
64    #[inline]
65    pub async fn rssi(&self) -> Option<i16> {
66        self.peripheral
67            .properties()
68            .await
69            .ok()
70            .flatten()
71            .and_then(|props| props.rssi)
72    }
73
74    /// Local name of the device
75    #[inline]
76    pub async fn local_name(&self) -> Option<String> {
77        self.peripheral
78            .properties()
79            .await
80            .ok()
81            .flatten()
82            .and_then(|props| props.local_name)
83    }
84
85    /// Connect the device
86    #[inline]
87    pub async fn connect(&self) -> Result<()> {
88        if !self.is_connected().await? {
89            log::info!("Connecting device.");
90            self.peripheral.connect().await?;
91        }
92
93        Ok(())
94    }
95
96    /// Disconnect from the device
97    #[inline]
98    pub async fn disconnect(&self) -> Result<()> {
99        log::info!("Disconnecting device.");
100        self.peripheral.disconnect().await
101    }
102
103    /// Get the connected state
104    #[inline]
105    pub async fn is_connected(&self) -> Result<bool> {
106        self.peripheral.is_connected().await
107    }
108
109    /// Services advertised by the device
110    pub async fn services(&self) -> Result<Vec<Service>> {
111        // self.connect().await?;
112
113        let mut services = self.peripheral.services();
114        if services.is_empty() {
115            self.peripheral.discover_services().await?;
116            services = self.peripheral.services();
117        }
118
119        Ok(services.into_iter().collect::<Vec<_>>())
120    }
121
122    /// Number of services advertised by the device
123    pub async fn service_count(&self) -> Result<usize> {
124        Ok(self.services().await?.len())
125    }
126
127    /// Characteristics advertised by the device
128    pub async fn characteristics(&self) -> Result<Vec<Characteristic>> {
129        let characteristics = self.original_characteristics().await?;
130        Ok(characteristics
131            .into_iter()
132            .map(|characteristic| Characteristic {
133                peripheral: self.peripheral.clone(),
134                characteristic,
135            })
136            .collect::<Vec<_>>())
137    }
138
139    /// Get characteristic by UUID
140    pub async fn characteristic(&self, uuid: Uuid) -> Result<Option<Characteristic>> {
141        let characteristics = self.original_characteristics().await?;
142
143        Ok(characteristics
144            .into_iter()
145            .find(|characteristic| characteristic.uuid == uuid)
146            .map(|characteristic| Characteristic {
147                peripheral: self.peripheral.clone(),
148                characteristic,
149            }))
150    }
151
152    #[inline]
153    async fn original_characteristics(&self) -> Result<BTreeSet<BleCharacteristic>> {
154        // self.connect().await?;
155
156        let mut characteristics = self.peripheral.characteristics();
157        if characteristics.is_empty() {
158            self.peripheral.discover_services().await?;
159            characteristics = self.peripheral.characteristics();
160        }
161
162        Ok(characteristics)
163    }
164}
165
166#[derive(Debug, Clone)]
167pub enum DeviceEvent {
168    Discovered(Device),
169    Connected(Device),
170    Disconnected(Device),
171    Updated(Device),
172}