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 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 pub async fn properties(&self) -> Option<PeripheralProperties> {
65 self.peripheral.properties().await.ok().flatten()
66 }
67
68 #[inline]
70 pub async fn rssi(&self) -> Option<i16> {
71 self.properties().await.map(|p| p.rssi).flatten()
72 }
73
74 #[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 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 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 #[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 #[inline]
112 pub async fn disconnect(&self) -> Result<()> {
113 log::info!("Disconnecting device.");
114 self.peripheral.disconnect().await
115 }
116
117 #[inline]
119 pub async fn is_connected(&self) -> Result<bool> {
120 self.peripheral.is_connected().await
121 }
122
123 pub async fn services(&self) -> Result<Vec<Service>> {
125 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 pub async fn service_count(&self) -> Result<usize> {
138 Ok(self.services().await?.len())
139 }
140
141 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 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 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}