bleasy/
device.rs

1use btleplug::{
2    api::{
3        BDAddr, Central as _, CentralEvent, Characteristic as BleCharacteristic, Peripheral as _,
4        PeripheralProperties, 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    /// Get the peripheral properties from device.
64    pub async fn properties(&self) -> Option<PeripheralProperties> {
65        self.peripheral.properties().await.ok().flatten()
66    }
67
68    /// Signal strength
69    #[inline]
70    pub async fn rssi(&self) -> Option<i16> {
71        self.properties().await.map(|p| p.rssi).flatten()
72    }
73
74    /// Local name of the device
75    #[inline]
76    pub async fn local_name(&self) -> Option<String> {
77        self.properties()
78            .await
79            .map(|props| props.local_name)
80            .flatten()
81    }
82
83    /// Get the manufacturer data from device.
84    pub async fn manufacturer_data(&self, val: &u16) -> Option<Vec<u8>> {
85        self.properties()
86            .await
87            .map(|p| p.manufacturer_data.get(val).cloned())
88            .flatten()
89    }
90
91    /// Get the service data from device.
92    pub async fn service_data(&self, uuid: &Uuid) -> Option<Vec<u8>> {
93        self.properties()
94            .await
95            .map(|p| p.service_data.get(uuid).cloned())
96            .flatten()
97    }
98
99    /// Connect the device
100    #[inline]
101    pub async fn connect(&self) -> Result<()> {
102        if !self.is_connected().await? {
103            log::info!("Connecting device.");
104            self.peripheral.connect().await?;
105        }
106
107        Ok(())
108    }
109
110    /// Disconnect from the device
111    #[inline]
112    pub async fn disconnect(&self) -> Result<()> {
113        log::info!("Disconnecting device.");
114        self.peripheral.disconnect().await
115    }
116
117    /// Get the connected state
118    #[inline]
119    pub async fn is_connected(&self) -> Result<bool> {
120        self.peripheral.is_connected().await
121    }
122
123    /// Services advertised by the device
124    pub async fn services(&self) -> Result<Vec<Service>> {
125        // self.connect().await?;
126
127        let mut services = self.peripheral.services();
128        if services.is_empty() {
129            self.peripheral.discover_services().await?;
130            services = self.peripheral.services();
131        }
132
133        Ok(services.into_iter().collect::<Vec<_>>())
134    }
135
136    /// Number of services advertised by the device
137    pub async fn service_count(&self) -> Result<usize> {
138        Ok(self.services().await?.len())
139    }
140
141    /// Characteristics advertised by the device
142    pub async fn characteristics(&self) -> Result<Vec<Characteristic>> {
143        let characteristics = self.original_characteristics().await?;
144        Ok(characteristics
145            .into_iter()
146            .map(|characteristic| Characteristic {
147                peripheral: self.peripheral.clone(),
148                characteristic,
149            })
150            .collect::<Vec<_>>())
151    }
152
153    /// Get characteristic by UUID
154    pub async fn characteristic(&self, uuid: Uuid) -> Result<Option<Characteristic>> {
155        let characteristics = self.original_characteristics().await?;
156
157        Ok(characteristics
158            .into_iter()
159            .find(|characteristic| characteristic.uuid == uuid)
160            .map(|characteristic| Characteristic {
161                peripheral: self.peripheral.clone(),
162                characteristic,
163            }))
164    }
165
166    #[inline]
167    async fn original_characteristics(&self) -> Result<BTreeSet<BleCharacteristic>> {
168        // self.connect().await?;
169
170        let mut characteristics = self.peripheral.characteristics();
171        if characteristics.is_empty() {
172            self.peripheral.discover_services().await?;
173            characteristics = self.peripheral.characteristics();
174        }
175
176        Ok(characteristics)
177    }
178}
179
180#[derive(Debug, Clone)]
181pub enum DeviceEvent {
182    Discovered(Device),
183    Connected(Device),
184    Disconnected(Device),
185    Updated(Device),
186}