1mod adapter;
11mod bleuuid;
12mod characteristic;
13mod descriptor;
14mod device;
15mod events;
16mod introspect;
17mod macaddress;
18mod messagestream;
19mod modalias;
20mod serde_path;
21mod service;
22
23pub use self::adapter::{AdapterId, AdapterInfo};
24pub use self::bleuuid::{BleUuid, uuid_from_u16, uuid_from_u32};
25pub use self::characteristic::{CharacteristicFlags, CharacteristicId, CharacteristicInfo};
26pub use self::descriptor::{DescriptorId, DescriptorInfo};
27pub use self::device::{AddressType, DeviceId, DeviceInfo};
28pub use self::events::{AdapterEvent, BluetoothEvent, CharacteristicEvent, DeviceEvent};
29use self::introspect::IntrospectParse;
30pub use self::macaddress::{MacAddress, ParseMacAddressError};
31use self::messagestream::MessageStream;
32pub use self::modalias::{Modalias, ModaliasSubtype, ParseModaliasError};
33pub use self::service::{ServiceId, ServiceInfo};
34use bluez_generated::{
35 ORG_BLUEZ_ADAPTER1_NAME, ORG_BLUEZ_DEVICE1_NAME, ORG_BLUEZ_GATT_CHARACTERISTIC1_NAME,
36 OrgBluezAdapter1, OrgBluezAdapter1Properties, OrgBluezDevice1, OrgBluezDevice1Properties,
37 OrgBluezGattCharacteristic1, OrgBluezGattCharacteristic1Properties, OrgBluezGattDescriptor1,
38 OrgBluezGattService1,
39};
40use dbus::Path;
41use dbus::arg::{PropMap, Variant};
42use dbus::nonblock::stdintf::org_freedesktop_dbus::{Introspectable, ObjectManager, Properties};
43use dbus::nonblock::{Proxy, SyncConnection};
44use dbus_tokio::connection::IOResourceError;
45use futures::stream::{self, StreamExt, select_all};
46use futures::{FutureExt, Stream};
47use std::collections::HashMap;
48use std::fmt::{self, Debug, Display, Formatter};
49use std::future::Future;
50use std::sync::Arc;
51use std::time::Duration;
52use thiserror::Error;
53use tokio::task::JoinError;
54use tokio::time::timeout;
55use uuid::Uuid;
56
57const DBUS_METHOD_CALL_TIMEOUT: Duration = Duration::from_secs(30);
58const DBUS_METHOD_CALL_MAX_TIMEOUT: Duration = Duration::from_secs(i32::MAX as u64);
61const SERVICE_DISCOVERY_TIMEOUT: Duration = Duration::from_secs(30);
62
63#[derive(Debug, Error)]
65pub enum BluetoothError {
66 #[error("No Bluetooth adapters found.")]
68 NoBluetoothAdapters,
69 #[error(transparent)]
71 DbusError(#[from] dbus::Error),
72 #[error("Error parsing XML for introspection: {0}")]
74 XmlParseError(#[from] serde_xml_rs::Error),
75 #[error("Service or characteristic UUID {uuid} not found.")]
77 UuidNotFound { uuid: Uuid },
78 #[error("Error parsing UUID string: {0}")]
80 UuidParseError(#[from] uuid::Error),
81 #[error("Invalid characteristic flag {0:?}")]
83 FlagParseError(String),
84 #[error("Invalid address type {0}")]
86 AddressTypeParseError(String),
87 #[error("Required property {0} missing.")]
89 RequiredPropertyMissing(&'static str),
90 #[error("Service discovery timed out")]
92 ServiceDiscoveryTimedOut,
93 #[error(transparent)]
95 MacAddressParseError(#[from] ParseMacAddressError),
96 #[error(transparent)]
98 ModaliasParseError(#[from] ParseModaliasError),
99 #[error("Error parsing DeviceId string: {0}")]
101 DeviceIdParseError(String),
102}
103
104#[derive(Debug, Error)]
106pub enum SpawnError {
107 #[error("D-Bus connection lost: {0}")]
108 DbusConnectionLost(#[source] IOResourceError),
109 #[error("Task failed: {0}")]
110 Join(#[from] JoinError),
111}
112
113#[derive(Copy, Clone, Debug, Eq, PartialEq)]
115pub enum Transport {
116 Auto,
118 BrEdr,
120 Le,
122}
123
124impl Transport {
125 fn as_str(&self) -> &'static str {
126 match self {
127 Self::Auto => "auto",
128 Self::BrEdr => "bredr",
129 Self::Le => "le",
130 }
131 }
132}
133
134impl Display for Transport {
135 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
136 f.write_str(self.as_str())
137 }
138}
139
140#[derive(Clone, Debug, Default, Eq, PartialEq)]
146pub struct DiscoveryFilter {
147 pub service_uuids: Vec<Uuid>,
149 pub rssi_threshold: Option<i16>,
151 pub pathloss_threshold: Option<u16>,
152 pub transport: Option<Transport>,
154 pub duplicate_data: Option<bool>,
158 pub discoverable: Option<bool>,
160 pub pattern: Option<String>,
162}
163
164impl From<&DiscoveryFilter> for PropMap {
165 fn from(filter: &DiscoveryFilter) -> Self {
166 let mut map: PropMap = HashMap::new();
167 if !filter.service_uuids.is_empty() {
168 let uuids: Vec<String> = filter.service_uuids.iter().map(Uuid::to_string).collect();
169 map.insert("UUIDs".to_string(), Variant(Box::new(uuids)));
170 }
171 if let Some(rssi_threshold) = filter.rssi_threshold {
172 map.insert("RSSI".to_string(), Variant(Box::new(rssi_threshold)));
173 }
174 if let Some(pathloss_threshold) = filter.pathloss_threshold {
175 map.insert(
176 "Pathloss".to_string(),
177 Variant(Box::new(pathloss_threshold)),
178 );
179 }
180 if let Some(transport) = filter.transport {
181 map.insert(
182 "Transport".to_string(),
183 Variant(Box::new(transport.to_string())),
184 );
185 }
186 if let Some(duplicate_data) = filter.duplicate_data {
187 map.insert(
188 "DuplicateData".to_string(),
189 Variant(Box::new(duplicate_data)),
190 );
191 }
192 if let Some(discoverable) = filter.discoverable {
193 map.insert("Discoverable".to_string(), Variant(Box::new(discoverable)));
194 }
195 if let Some(pattern) = &filter.pattern {
196 map.insert("Pattern".to_string(), Variant(Box::new(pattern.to_owned())));
197 }
198 map
199 }
200}
201
202#[derive(Clone, Copy, Debug, Eq, PartialEq)]
204pub enum WriteType {
205 WithResponse,
209 WithoutResponse,
212 Reliable,
215}
216
217impl WriteType {
218 fn as_str(&self) -> &'static str {
219 match self {
220 Self::WithResponse => "request",
221 Self::WithoutResponse => "command",
222 Self::Reliable => "reliable",
223 }
224 }
225}
226
227impl Display for WriteType {
228 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
229 f.write_str(self.as_str())
230 }
231}
232
233#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
235pub struct WriteOptions {
236 pub offset: usize,
238 pub write_type: Option<WriteType>,
246}
247
248impl From<WriteOptions> for PropMap {
249 fn from(options: WriteOptions) -> Self {
250 let mut map: PropMap = HashMap::new();
251 if options.offset != 0 {
252 map.insert(
253 "offset".to_string(),
254 Variant(Box::new(options.offset as u64)),
255 );
256 }
257 if let Some(write_type) = options.write_type {
258 map.insert(
259 "type".to_string(),
260 Variant(Box::new(write_type.to_string())),
261 );
262 }
263 map
264 }
265}
266
267#[derive(Clone)]
270pub struct BluetoothSession {
271 connection: Arc<SyncConnection>,
272}
273
274impl Debug for BluetoothSession {
275 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
276 write!(f, "BluetoothSession")
277 }
278}
279
280impl BluetoothSession {
281 pub async fn new()
287 -> Result<(impl Future<Output = Result<(), SpawnError>>, Self), BluetoothError> {
288 let (dbus_resource, connection) = dbus_tokio::connection::new_system_sync()?;
290 connection.set_signal_match_mode(true);
293 let dbus_handle = tokio::spawn(async {
296 let err = dbus_resource.await;
297 Err(SpawnError::DbusConnectionLost(err))
298 });
299 Ok((dbus_handle.map(|res| res?), BluetoothSession { connection }))
300 }
301
302 pub async fn set_powered(
304 &self,
305 adapter_id: &AdapterId,
306 powered: bool,
307 ) -> Result<(), BluetoothError> {
308 self.adapter(adapter_id).set_powered(powered).await?;
309 Ok(())
310 }
311
312 pub async fn start_discovery(&self) -> Result<(), BluetoothError> {
317 self.start_discovery_with_filter(&DiscoveryFilter::default())
318 .await
319 }
320
321 pub async fn start_discovery_on_adapter(
327 &self,
328 adapter: &AdapterId,
329 ) -> Result<(), BluetoothError> {
330 self.start_discovery_on_adapter_with_filter(adapter, &DiscoveryFilter::default())
331 .await
332 }
333
334 pub async fn start_discovery_with_filter(
343 &self,
344 discovery_filter: &DiscoveryFilter,
345 ) -> Result<(), BluetoothError> {
346 let adapters = self.get_adapters().await?;
347 if adapters.is_empty() {
348 return Err(BluetoothError::NoBluetoothAdapters);
349 }
350
351 for adapter in adapters {
352 log::trace!("Starting discovery on adapter {}", adapter.id);
353 self.start_discovery_on_adapter_with_filter(&adapter.id, discovery_filter)
354 .await
355 .unwrap_or_else(|err| log::error!("starting discovery failed {:?}", err));
356 }
357 Ok(())
358 }
359
360 pub async fn start_discovery_on_adapter_with_filter(
369 &self,
370 adapter_id: &AdapterId,
371 discovery_filter: &DiscoveryFilter,
372 ) -> Result<(), BluetoothError> {
373 let adapter = self.adapter(adapter_id);
374 adapter.set_powered(true).await?;
375 adapter
376 .set_discovery_filter(discovery_filter.into())
377 .await?;
378 adapter.start_discovery().await?;
379 Ok(())
380 }
381
382 pub async fn stop_discovery(&self) -> Result<(), BluetoothError> {
384 let adapters = self.get_adapters().await?;
385 if adapters.is_empty() {
386 return Err(BluetoothError::NoBluetoothAdapters);
387 }
388
389 for adapter in adapters {
390 self.stop_discovery_on_adapter(&adapter.id).await?;
391 }
392
393 Ok(())
394 }
395
396 pub async fn stop_discovery_on_adapter(
398 &self,
399 adapter_id: &AdapterId,
400 ) -> Result<(), BluetoothError> {
401 let adapter = self.adapter(adapter_id);
402 adapter.stop_discovery().await?;
403 Ok(())
404 }
405
406 pub async fn get_adapters(&self) -> Result<Vec<AdapterInfo>, BluetoothError> {
408 let bluez_root = Proxy::new(
409 "org.bluez",
410 "/",
411 DBUS_METHOD_CALL_TIMEOUT,
412 self.connection.clone(),
413 );
414 let tree = bluez_root.get_managed_objects().await?;
417 Ok(tree
418 .into_iter()
419 .filter_map(|(object_path, interfaces)| {
420 let adapter_properties = OrgBluezAdapter1Properties::from_interfaces(&interfaces)?;
421 AdapterInfo::from_properties(AdapterId { object_path }, adapter_properties).ok()
422 })
423 .collect())
424 }
425
426 pub async fn get_devices(&self) -> Result<Vec<DeviceInfo>, BluetoothError> {
428 let bluez_root = Proxy::new(
429 "org.bluez",
430 "/",
431 DBUS_METHOD_CALL_TIMEOUT,
432 self.connection.clone(),
433 );
434 let tree = bluez_root.get_managed_objects().await?;
435
436 let devices = tree
437 .into_iter()
438 .filter_map(|(object_path, interfaces)| {
439 let device_properties = OrgBluezDevice1Properties::from_interfaces(&interfaces)?;
440 DeviceInfo::from_properties(DeviceId { object_path }, device_properties).ok()
441 })
442 .collect();
443 Ok(devices)
444 }
445
446 pub async fn get_devices_on_adapter(
448 &self,
449 adapter: &AdapterId,
450 ) -> Result<Vec<DeviceInfo>, BluetoothError> {
451 let devices = self.get_devices().await?;
452 Ok(devices
453 .into_iter()
454 .filter(|device| device.id.adapter() == *adapter)
455 .collect())
456 }
457
458 pub async fn get_services(
462 &self,
463 device: &DeviceId,
464 ) -> Result<Vec<ServiceInfo>, BluetoothError> {
465 let device_node = self
466 .device(device, DBUS_METHOD_CALL_TIMEOUT)
467 .introspect_parse()
468 .await?;
469 let mut services = vec![];
470 for subnode in device_node.nodes {
471 let subnode_name = subnode.name.as_ref().unwrap();
472 if subnode_name.starts_with("service") {
475 let service_id = ServiceId {
476 object_path: format!("{}/{}", device.object_path, subnode_name).into(),
477 };
478 let service = self.service(&service_id);
479 let uuid = Uuid::parse_str(&service.uuid().await?)?;
480 let primary = service.primary().await?;
481 services.push(ServiceInfo {
482 id: service_id,
483 uuid,
484 primary,
485 });
486 }
487 }
488 Ok(services)
489 }
490
491 pub async fn get_characteristics(
493 &self,
494 service: &ServiceId,
495 ) -> Result<Vec<CharacteristicInfo>, BluetoothError> {
496 let service_node = self.service(service).introspect_parse().await?;
497 let mut characteristics = vec![];
498 for subnode in service_node.nodes {
499 let subnode_name = subnode.name.as_ref().unwrap();
500 if subnode_name.starts_with("char") {
503 let characteristic_id = CharacteristicId {
504 object_path: format!("{}/{}", service.object_path, subnode_name).into(),
505 };
506 characteristics.push(self.get_characteristic_info(&characteristic_id).await?);
507 }
508 }
509 Ok(characteristics)
510 }
511
512 pub async fn get_descriptors(
514 &self,
515 characteristic: &CharacteristicId,
516 ) -> Result<Vec<DescriptorInfo>, BluetoothError> {
517 let characteristic_node = self
518 .characteristic(characteristic)
519 .introspect_parse()
520 .await?;
521 let mut descriptors = vec![];
522 for subnode in characteristic_node.nodes {
523 let subnode_name = subnode.name.as_ref().unwrap();
524 if subnode_name.starts_with("desc") {
527 let descriptor_id = DescriptorId {
528 object_path: format!("{}/{}", characteristic.object_path, subnode_name).into(),
529 };
530 let uuid = Uuid::parse_str(&self.descriptor(&descriptor_id).uuid().await?)?;
531 descriptors.push(DescriptorInfo {
532 id: descriptor_id,
533 uuid,
534 });
535 }
536 }
537 Ok(descriptors)
538 }
539
540 pub async fn get_service_by_uuid(
544 &self,
545 device: &DeviceId,
546 uuid: Uuid,
547 ) -> Result<ServiceInfo, BluetoothError> {
548 let services = self.get_services(device).await?;
549 services
550 .into_iter()
551 .find(|service_info| service_info.uuid == uuid)
552 .ok_or(BluetoothError::UuidNotFound { uuid })
553 }
554
555 pub async fn get_characteristic_by_uuid(
558 &self,
559 service: &ServiceId,
560 uuid: Uuid,
561 ) -> Result<CharacteristicInfo, BluetoothError> {
562 let characteristics = self.get_characteristics(service).await?;
563 characteristics
564 .into_iter()
565 .find(|characteristic_info| characteristic_info.uuid == uuid)
566 .ok_or(BluetoothError::UuidNotFound { uuid })
567 }
568
569 pub async fn get_service_characteristic_by_uuid(
574 &self,
575 device: &DeviceId,
576 service_uuid: Uuid,
577 characteristic_uuid: Uuid,
578 ) -> Result<CharacteristicInfo, BluetoothError> {
579 let service = self.get_service_by_uuid(device, service_uuid).await?;
580 self.get_characteristic_by_uuid(&service.id, characteristic_uuid)
581 .await
582 }
583
584 pub async fn get_device_info(&self, id: &DeviceId) -> Result<DeviceInfo, BluetoothError> {
586 let device = self.device(id, DBUS_METHOD_CALL_TIMEOUT);
587 let properties = device.get_all(ORG_BLUEZ_DEVICE1_NAME).await?;
588 DeviceInfo::from_properties(id.to_owned(), OrgBluezDevice1Properties(&properties))
589 }
590
591 pub async fn get_adapter_info(&self, id: &AdapterId) -> Result<AdapterInfo, BluetoothError> {
593 let adapter = self.adapter(id);
594 let properties = adapter.get_all(ORG_BLUEZ_ADAPTER1_NAME).await?;
595 AdapterInfo::from_properties(id.to_owned(), OrgBluezAdapter1Properties(&properties))
596 }
597
598 pub async fn get_service_info(&self, id: &ServiceId) -> Result<ServiceInfo, BluetoothError> {
600 let service = self.service(id);
601 let uuid = Uuid::parse_str(&service.uuid().await?)?;
602 let primary = service.primary().await?;
603 Ok(ServiceInfo {
604 id: id.to_owned(),
605 uuid,
606 primary,
607 })
608 }
609
610 pub async fn get_characteristic_info(
612 &self,
613 id: &CharacteristicId,
614 ) -> Result<CharacteristicInfo, BluetoothError> {
615 let characteristic = self.characteristic(id);
616 let properties = characteristic
617 .get_all(ORG_BLUEZ_GATT_CHARACTERISTIC1_NAME)
618 .await?;
619 CharacteristicInfo::from_properties(
620 id.to_owned(),
621 OrgBluezGattCharacteristic1Properties(&properties),
622 )
623 }
624
625 pub async fn get_descriptor_info(
627 &self,
628 id: &DescriptorId,
629 ) -> Result<DescriptorInfo, BluetoothError> {
630 let uuid = Uuid::parse_str(&self.descriptor(id).uuid().await?)?;
631 Ok(DescriptorInfo {
632 id: id.to_owned(),
633 uuid,
634 })
635 }
636
637 fn adapter(
638 &self,
639 id: &AdapterId,
640 ) -> impl OrgBluezAdapter1 + Introspectable + Properties + use<> {
641 Proxy::new(
642 "org.bluez",
643 id.object_path.to_owned(),
644 DBUS_METHOD_CALL_TIMEOUT,
645 self.connection.clone(),
646 )
647 }
648
649 fn device(
650 &self,
651 id: &DeviceId,
652 timeout: Duration,
653 ) -> impl OrgBluezDevice1 + Introspectable + Properties + use<> {
654 let timeout = timeout.min(DBUS_METHOD_CALL_MAX_TIMEOUT);
655 Proxy::new(
656 "org.bluez",
657 id.object_path.to_owned(),
658 timeout,
659 self.connection.clone(),
660 )
661 }
662
663 fn service(
664 &self,
665 id: &ServiceId,
666 ) -> impl OrgBluezGattService1 + Introspectable + Properties + use<> {
667 Proxy::new(
668 "org.bluez",
669 id.object_path.to_owned(),
670 DBUS_METHOD_CALL_TIMEOUT,
671 self.connection.clone(),
672 )
673 }
674
675 fn characteristic(
676 &self,
677 id: &CharacteristicId,
678 ) -> impl OrgBluezGattCharacteristic1 + Introspectable + Properties + use<> {
679 Proxy::new(
680 "org.bluez",
681 id.object_path.to_owned(),
682 DBUS_METHOD_CALL_TIMEOUT,
683 self.connection.clone(),
684 )
685 }
686
687 fn descriptor(
688 &self,
689 id: &DescriptorId,
690 ) -> impl OrgBluezGattDescriptor1 + Introspectable + Properties + use<> {
691 Proxy::new(
692 "org.bluez",
693 id.object_path.to_owned(),
694 DBUS_METHOD_CALL_TIMEOUT,
695 self.connection.clone(),
696 )
697 }
698
699 async fn await_service_discovery(&self, device_id: &DeviceId) -> Result<(), BluetoothError> {
701 let mut events = self.device_event_stream(device_id).await?;
703 if self
704 .device(device_id, DBUS_METHOD_CALL_TIMEOUT)
705 .services_resolved()
706 .await?
707 {
708 log::info!("Services already resolved.");
709 return Ok(());
710 }
711 timeout(SERVICE_DISCOVERY_TIMEOUT, async {
712 while let Some(event) = events.next().await {
713 if matches!(event, BluetoothEvent::Device {
714 id,
715 event: DeviceEvent::ServicesResolved,
716 } if device_id == &id)
717 {
718 return Ok(());
719 }
720 }
721
722 Err(BluetoothError::ServiceDiscoveryTimedOut)
724 })
725 .await
726 .unwrap_or(Err(BluetoothError::ServiceDiscoveryTimedOut))
727 }
728
729 pub async fn pair_with_timeout(
731 &self,
732 id: &DeviceId,
733 timeout: Duration,
734 ) -> Result<(), BluetoothError> {
735 self.device(id, timeout).pair().await?;
736 self.await_service_discovery(id).await
737 }
738
739 pub async fn cancel_pairing(&self, id: &DeviceId) -> Result<(), BluetoothError> {
741 Ok(self
742 .device(id, DBUS_METHOD_CALL_TIMEOUT)
743 .cancel_pairing()
744 .await?)
745 }
746
747 pub async fn connect(&self, id: &DeviceId) -> Result<(), BluetoothError> {
749 self.connect_with_timeout(id, DBUS_METHOD_CALL_TIMEOUT)
750 .await
751 }
752
753 pub async fn connect_with_timeout(
755 &self,
756 id: &DeviceId,
757 timeout: Duration,
758 ) -> Result<(), BluetoothError> {
759 self.device(id, timeout).connect().await?;
760 self.await_service_discovery(id).await
761 }
762
763 pub async fn disconnect(&self, id: &DeviceId) -> Result<(), BluetoothError> {
765 Ok(self
766 .device(id, DBUS_METHOD_CALL_TIMEOUT)
767 .disconnect()
768 .await?)
769 }
770
771 pub async fn read_characteristic_value(
775 &self,
776 id: &CharacteristicId,
777 ) -> Result<Vec<u8>, BluetoothError> {
778 self.read_characteristic_value_with_offset(id, 0).await
779 }
780
781 pub async fn read_characteristic_value_with_offset(
783 &self,
784 id: &CharacteristicId,
785 offset: usize,
786 ) -> Result<Vec<u8>, BluetoothError> {
787 let characteristic = self.characteristic(id);
788 Ok(characteristic.read_value(offset_to_propmap(offset)).await?)
789 }
790
791 pub async fn write_characteristic_value(
795 &self,
796 id: &CharacteristicId,
797 value: impl Into<Vec<u8>>,
798 ) -> Result<(), BluetoothError> {
799 self.write_characteristic_value_with_options(id, value, WriteOptions::default())
800 .await
801 }
802
803 pub async fn write_characteristic_value_with_options(
805 &self,
806 id: &CharacteristicId,
807 value: impl Into<Vec<u8>>,
808 options: WriteOptions,
809 ) -> Result<(), BluetoothError> {
810 let characteristic = self.characteristic(id);
811 Ok(characteristic
812 .write_value(value.into(), options.into())
813 .await?)
814 }
815
816 pub async fn read_descriptor_value(
820 &self,
821 id: &DescriptorId,
822 ) -> Result<Vec<u8>, BluetoothError> {
823 self.read_descriptor_value_with_offset(id, 0).await
824 }
825
826 pub async fn read_descriptor_value_with_offset(
828 &self,
829 id: &DescriptorId,
830 offset: usize,
831 ) -> Result<Vec<u8>, BluetoothError> {
832 let descriptor = self.descriptor(id);
833 Ok(descriptor.read_value(offset_to_propmap(offset)).await?)
834 }
835
836 pub async fn write_descriptor_value(
840 &self,
841 id: &DescriptorId,
842 value: impl Into<Vec<u8>>,
843 ) -> Result<(), BluetoothError> {
844 self.write_descriptor_value_with_offset(id, value, 0).await
845 }
846
847 pub async fn write_descriptor_value_with_offset(
849 &self,
850 id: &DescriptorId,
851 value: impl Into<Vec<u8>>,
852 offset: usize,
853 ) -> Result<(), BluetoothError> {
854 let descriptor = self.descriptor(id);
855 Ok(descriptor
856 .write_value(value.into(), offset_to_propmap(offset))
857 .await?)
858 }
859
860 pub async fn start_notify(&self, id: &CharacteristicId) -> Result<(), BluetoothError> {
862 let characteristic = self.characteristic(id);
863 characteristic.start_notify().await?;
864 Ok(())
865 }
866
867 pub async fn stop_notify(&self, id: &CharacteristicId) -> Result<(), BluetoothError> {
869 let characteristic = self.characteristic(id);
870 characteristic.stop_notify().await?;
871 Ok(())
872 }
873
874 pub async fn event_stream(
876 &self,
877 ) -> Result<impl Stream<Item = BluetoothEvent> + use<>, BluetoothError> {
878 self.filtered_event_stream(None::<&DeviceId>, true).await
879 }
880
881 pub async fn adapter_event_stream(
884 &self,
885 adapter: &AdapterId,
886 ) -> Result<impl Stream<Item = BluetoothEvent> + use<>, BluetoothError> {
887 self.filtered_event_stream(Some(adapter), true).await
888 }
889
890 pub async fn device_event_stream(
896 &self,
897 device: &DeviceId,
898 ) -> Result<impl Stream<Item = BluetoothEvent> + use<>, BluetoothError> {
899 self.filtered_event_stream(Some(device), false).await
900 }
901
902 pub async fn characteristic_event_stream(
904 &self,
905 characteristic: &CharacteristicId,
906 ) -> Result<impl Stream<Item = BluetoothEvent> + use<>, BluetoothError> {
907 self.filtered_event_stream(Some(characteristic), false)
908 .await
909 }
910
911 async fn filtered_event_stream<P: Into<Path<'static>> + Clone>(
912 &self,
913 object: Option<&P>,
914 device_discovery: bool,
915 ) -> Result<impl Stream<Item = BluetoothEvent> + use<P>, BluetoothError> {
916 let mut message_streams = vec![];
917 for match_rule in BluetoothEvent::match_rules(object.cloned(), device_discovery) {
918 let msg_match = self.connection.add_match(match_rule).await?;
919 message_streams.push(MessageStream::new(msg_match, self.connection.clone()));
920 }
921 Ok(select_all(message_streams)
922 .flat_map(|message| stream::iter(BluetoothEvent::message_to_events(message))))
923 }
924}
925
926fn offset_to_propmap(offset: usize) -> PropMap {
927 let mut map: PropMap = HashMap::new();
928 if offset != 0 {
929 map.insert("offset".to_string(), Variant(Box::new(offset as u64)));
930 }
931 map
932}