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 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 #[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 #[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 #[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 #[inline]
98 pub async fn disconnect(&self) -> Result<()> {
99 log::info!("Disconnecting device.");
100 self.peripheral.disconnect().await
101 }
102
103 #[inline]
105 pub async fn is_connected(&self) -> Result<bool> {
106 self.peripheral.is_connected().await
107 }
108
109 pub async fn services(&self) -> Result<Vec<Service>> {
111 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 pub async fn service_count(&self) -> Result<usize> {
124 Ok(self.services().await?.len())
125 }
126
127 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 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 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}