1use async_trait::async_trait;
2use bluez_async::{
3 BluetoothEvent, BluetoothSession, CharacteristicEvent, CharacteristicFlags, CharacteristicId,
4 CharacteristicInfo, DescriptorInfo, DeviceId, DeviceInfo, MacAddress, ServiceInfo,
5 WriteOptions,
6};
7use futures::future::{join_all, ready};
8use futures::stream::{Stream, StreamExt};
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11#[cfg(feature = "serde")]
12use serde_cr as serde;
13use std::collections::{BTreeSet, HashMap};
14use std::fmt::{self, Display, Formatter};
15use std::pin::Pin;
16use std::sync::{Arc, Mutex};
17use uuid::Uuid;
18
19use crate::api::{
20 self, AddressType, BDAddr, CharPropFlags, Characteristic, Descriptor, PeripheralProperties,
21 Service, ValueNotification, WriteType,
22};
23use crate::{Error, Result};
24
25#[derive(Clone, Debug)]
26struct CharacteristicInternal {
27 info: CharacteristicInfo,
28 descriptors: HashMap<Uuid, DescriptorInfo>,
29}
30
31impl CharacteristicInternal {
32 fn new(info: CharacteristicInfo, descriptors: HashMap<Uuid, DescriptorInfo>) -> Self {
33 Self { info, descriptors }
34 }
35}
36
37#[derive(Clone, Debug)]
38struct ServiceInternal {
39 info: ServiceInfo,
40 characteristics: HashMap<Uuid, CharacteristicInternal>,
41}
42
43#[cfg_attr(
44 feature = "serde",
45 derive(Serialize, Deserialize),
46 serde(crate = "serde_cr")
47)]
48#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
49pub struct PeripheralId(pub(crate) DeviceId);
50
51impl Display for PeripheralId {
52 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53 self.0.fmt(f)
54 }
55}
56
57#[derive(Clone, Debug)]
59pub struct Peripheral {
60 session: BluetoothSession,
61 device: DeviceId,
62 mac_address: BDAddr,
63 services: Arc<Mutex<HashMap<Uuid, ServiceInternal>>>,
64}
65
66fn get_characteristic<'a>(
67 services: &'a HashMap<Uuid, ServiceInternal>,
68 service_uuid: &Uuid,
69 characteristic_uuid: &Uuid,
70) -> Result<&'a CharacteristicInternal> {
71 services
72 .get(service_uuid)
73 .ok_or_else(|| {
74 Error::Other(format!("Service with UUID {} not found.", service_uuid).into())
75 })?
76 .characteristics
77 .get(characteristic_uuid)
78 .ok_or_else(|| {
79 Error::Other(
80 format!(
81 "Characteristic with UUID {} not found.",
82 characteristic_uuid
83 )
84 .into(),
85 )
86 })
87}
88
89impl Peripheral {
90 pub(crate) fn new(session: BluetoothSession, device: DeviceInfo) -> Self {
91 Peripheral {
92 session,
93 device: device.id,
94 mac_address: device.mac_address.into(),
95 services: Arc::new(Mutex::new(HashMap::new())),
96 }
97 }
98
99 fn characteristic_info(&self, characteristic: &Characteristic) -> Result<CharacteristicInfo> {
100 let services = self.services.lock().map_err(Into::<Error>::into)?;
101 get_characteristic(
102 &services,
103 &characteristic.service_uuid,
104 &characteristic.uuid,
105 )
106 .map(|c| &c.info)
107 .cloned()
108 }
109
110 fn descriptor_info(&self, descriptor: &Descriptor) -> Result<DescriptorInfo> {
111 let services = self.services.lock().map_err(Into::<Error>::into)?;
112 let characteristic = get_characteristic(
113 &services,
114 &descriptor.service_uuid,
115 &descriptor.characteristic_uuid,
116 )?;
117 characteristic
118 .descriptors
119 .get(&descriptor.uuid)
120 .ok_or_else(|| {
121 Error::Other(format!("Descriptor with UUID {} not found.", descriptor.uuid).into())
122 })
123 .cloned()
124 }
125
126 async fn device_info(&self) -> Result<DeviceInfo> {
127 Ok(self.session.get_device_info(&self.device).await?)
128 }
129}
130
131#[async_trait]
132impl api::Peripheral for Peripheral {
133 fn id(&self) -> PeripheralId {
134 PeripheralId(self.device.to_owned())
135 }
136
137 fn address(&self) -> BDAddr {
138 self.mac_address
139 }
140
141 fn mtu(&self) -> u16 {
142 let services = self.services.lock().unwrap();
143 for (_, service) in services.iter() {
144 for (_, characteristic) in service.characteristics.iter() {
145 return characteristic.info.mtu.unwrap();
146 }
147 }
148
149 api::DEFAULT_MTU_SIZE
150 }
151
152 async fn properties(&self) -> Result<Option<PeripheralProperties>> {
153 let device_info = self.device_info().await?;
154 Ok(Some(PeripheralProperties {
155 address: device_info.mac_address.into(),
156 address_type: Some(device_info.address_type.into()),
157 local_name: device_info.alias.or(device_info.name.clone()),
158 advertisement_name: device_info.name,
159 tx_power_level: device_info.tx_power,
160 rssi: device_info.rssi,
161 manufacturer_data: device_info.manufacturer_data,
162 service_data: device_info.service_data,
163 services: device_info.services,
164 class: device_info.class,
165 }))
166 }
167
168 fn services(&self) -> BTreeSet<Service> {
169 self.services
170 .lock()
171 .unwrap()
172 .values()
173 .map(|service| service.into())
174 .collect()
175 }
176
177 async fn is_connected(&self) -> Result<bool> {
178 let device_info = self.device_info().await?;
179 Ok(device_info.connected)
180 }
181
182 async fn connect(&self) -> Result<()> {
183 self.session.connect(&self.device).await?;
184 Ok(())
185 }
186
187 async fn disconnect(&self) -> Result<()> {
188 self.session.disconnect(&self.device).await?;
189 Ok(())
190 }
191
192 async fn discover_services(&self) -> Result<()> {
193 let mut services_internal = HashMap::new();
194 let services = self.session.get_services(&self.device).await?;
195 for service in services {
196 let characteristics = self.session.get_characteristics(&service.id).await?;
197 let characteristics = join_all(
198 characteristics
199 .into_iter()
200 .fold(
201 HashMap::<Uuid, CharacteristicInfo>::new(),
204 |mut map, characteristic| {
205 if !map.contains_key(&characteristic.uuid) {
206 map.insert(characteristic.uuid, characteristic);
207 }
208 map
209 },
210 )
211 .into_iter()
212 .map(|mapped_characteristic| async {
213 let characteristic = mapped_characteristic.1;
214 let descriptors = self
215 .session
216 .get_descriptors(&characteristic.id)
217 .await
218 .unwrap_or(Vec::new())
219 .into_iter()
220 .map(|descriptor| (descriptor.uuid, descriptor))
221 .collect();
222 CharacteristicInternal::new(characteristic, descriptors)
223 }),
224 )
225 .await;
226 services_internal.insert(
227 service.uuid,
228 ServiceInternal {
229 info: service,
230 characteristics: characteristics
231 .into_iter()
232 .map(|characteristic| (characteristic.info.uuid, characteristic))
233 .collect(),
234 },
235 );
236 }
237 *(self.services.lock().map_err(Into::<Error>::into)?) = services_internal;
238 Ok(())
239 }
240
241 async fn write(
242 &self,
243 characteristic: &Characteristic,
244 data: &[u8],
245 write_type: WriteType,
246 ) -> Result<()> {
247 let characteristic_info = self.characteristic_info(characteristic)?;
248 let options = WriteOptions {
249 write_type: Some(write_type.into()),
250 ..Default::default()
251 };
252 Ok(self
253 .session
254 .write_characteristic_value_with_options(&characteristic_info.id, data, options)
255 .await?)
256 }
257
258 async fn read(&self, characteristic: &Characteristic) -> Result<Vec<u8>> {
259 let characteristic_info = self.characteristic_info(characteristic)?;
260 Ok(self
261 .session
262 .read_characteristic_value(&characteristic_info.id)
263 .await?)
264 }
265
266 async fn subscribe(&self, characteristic: &Characteristic) -> Result<()> {
267 let characteristic_info = self.characteristic_info(characteristic)?;
268 Ok(self.session.start_notify(&characteristic_info.id).await?)
269 }
270
271 async fn unsubscribe(&self, characteristic: &Characteristic) -> Result<()> {
272 let characteristic_info = self.characteristic_info(characteristic)?;
273 Ok(self.session.stop_notify(&characteristic_info.id).await?)
274 }
275
276 async fn notifications(&self) -> Result<Pin<Box<dyn Stream<Item = ValueNotification> + Send>>> {
277 let device_id = self.device.clone();
278 let events = self.session.device_event_stream(&device_id).await?;
279 let services = self.services.clone();
280 Ok(Box::pin(events.filter_map(move |event| {
281 ready(value_notification(event, &device_id, services.clone()))
282 })))
283 }
284
285 async fn read_rssi(&self) -> Result<i16> {
286 let device_info = self.device_info().await?;
287 device_info.rssi.ok_or(Error::NotConnected)
288 }
289
290 async fn write_descriptor(&self, descriptor: &Descriptor, data: &[u8]) -> Result<()> {
291 let descriptor_info = self.descriptor_info(descriptor)?;
292 Ok(self
293 .session
294 .write_descriptor_value(&descriptor_info.id, data)
295 .await?)
296 }
297
298 async fn read_descriptor(&self, descriptor: &Descriptor) -> Result<Vec<u8>> {
299 let descriptor_info = self.descriptor_info(descriptor)?;
300 Ok(self
301 .session
302 .read_descriptor_value(&descriptor_info.id)
303 .await?)
304 }
305}
306
307fn value_notification(
308 event: BluetoothEvent,
309 device_id: &DeviceId,
310 services: Arc<Mutex<HashMap<Uuid, ServiceInternal>>>,
311) -> Option<ValueNotification> {
312 match event {
313 BluetoothEvent::Characteristic {
314 id,
315 event: CharacteristicEvent::Value { value },
316 } if id.service().device() == *device_id => {
317 let services = services.lock().unwrap();
318 let (charac, service) = find_characteristic_by_id(&services, id.clone())?;
319 Some(ValueNotification {
320 uuid: charac.uuid,
321 service_uuid: service.uuid,
322 value,
323 })
324 }
325 _ => None,
326 }
327}
328
329fn find_characteristic_by_id(
330 services: &HashMap<Uuid, ServiceInternal>,
331 characteristic_id: CharacteristicId,
332) -> Option<(&CharacteristicInfo, &ServiceInfo)> {
333 for service in services.values() {
334 for characteristic in service.characteristics.values() {
335 if characteristic.info.id == characteristic_id {
336 return Some((&characteristic.info, &service.info));
337 }
338 }
339 }
340 None
341}
342
343impl From<WriteType> for bluez_async::WriteType {
344 fn from(write_type: WriteType) -> Self {
345 match write_type {
346 WriteType::WithoutResponse => bluez_async::WriteType::WithoutResponse,
347 WriteType::WithResponse => bluez_async::WriteType::WithResponse,
348 }
349 }
350}
351
352impl From<MacAddress> for BDAddr {
353 fn from(mac_address: MacAddress) -> Self {
354 <[u8; 6]>::into(mac_address.into())
355 }
356}
357
358impl From<DeviceId> for PeripheralId {
359 fn from(device_id: DeviceId) -> Self {
360 PeripheralId(device_id)
361 }
362}
363
364impl From<bluez_async::AddressType> for AddressType {
365 fn from(address_type: bluez_async::AddressType) -> Self {
366 match address_type {
367 bluez_async::AddressType::Public => AddressType::Public,
368 bluez_async::AddressType::Random => AddressType::Random,
369 }
370 }
371}
372
373fn make_descriptor(
374 info: &DescriptorInfo,
375 characteristic_uuid: Uuid,
376 service_uuid: Uuid,
377) -> Descriptor {
378 Descriptor {
379 uuid: info.uuid,
380 characteristic_uuid,
381 service_uuid,
382 }
383}
384
385fn make_characteristic(
386 characteristic: &CharacteristicInternal,
387 service_uuid: Uuid,
388) -> Characteristic {
389 let CharacteristicInternal { info, descriptors } = characteristic;
390 Characteristic {
391 uuid: info.uuid,
392 properties: info.flags.into(),
393 descriptors: descriptors
394 .iter()
395 .map(|(_, descriptor)| make_descriptor(descriptor, info.uuid, service_uuid))
396 .collect(),
397 service_uuid,
398 }
399}
400
401impl From<&ServiceInternal> for Service {
402 fn from(service: &ServiceInternal) -> Self {
403 Service {
404 uuid: service.info.uuid,
405 primary: service.info.primary,
406 characteristics: service
407 .characteristics
408 .values()
409 .map(|characteristic| make_characteristic(characteristic, service.info.uuid))
410 .collect(),
411 }
412 }
413}
414
415impl From<CharacteristicFlags> for CharPropFlags {
416 fn from(flags: CharacteristicFlags) -> Self {
417 let mut result = CharPropFlags::default();
418 if flags.contains(CharacteristicFlags::BROADCAST) {
419 result.insert(CharPropFlags::BROADCAST);
420 }
421 if flags.contains(CharacteristicFlags::READ) {
422 result.insert(CharPropFlags::READ);
423 }
424 if flags.contains(CharacteristicFlags::WRITE_WITHOUT_RESPONSE) {
425 result.insert(CharPropFlags::WRITE_WITHOUT_RESPONSE);
426 }
427 if flags.contains(CharacteristicFlags::WRITE) {
428 result.insert(CharPropFlags::WRITE);
429 }
430 if flags.contains(CharacteristicFlags::NOTIFY) {
431 result.insert(CharPropFlags::NOTIFY);
432 }
433 if flags.contains(CharacteristicFlags::INDICATE) {
434 result.insert(CharPropFlags::INDICATE);
435 }
436 if flags.contains(CharacteristicFlags::SIGNED_WRITE) {
437 result.insert(CharPropFlags::AUTHENTICATED_SIGNED_WRITES);
438 }
439 if flags.contains(CharacteristicFlags::EXTENDED_PROPERTIES) {
440 result.insert(CharPropFlags::EXTENDED_PROPERTIES);
441 }
442 result
443 }
444}