1use std::collections::HashMap;
4
5use bluer::AdapterEvent;
6use futures::FutureExt;
7use futures::StreamExt;
8
9#[async_trait::async_trait]
14impl super::BluetoothRfcommConnectableAsyncTrait for bluer::rfcomm::ConnectRequest {
15 async fn accept(self) -> Result<(crate::BluetoothStream, [u8; 6], u8), String> {
16 let s = bluer::rfcomm::ConnectRequest::accept(self);
17 match s {
18 Ok(s) => {
19 let addr = s.peer_addr().map_err(|e| e.to_string())?;
20 Ok((
21 crate::BluetoothStream::Bluez(Box::pin(s)),
22 *addr.addr,
23 addr.channel,
24 ))
25 }
26 Err(e) => Err(e.to_string()),
27 }
28 }
29}
30
31impl super::BluetoothRfcommProfileAsyncTrait for bluer::rfcomm::ProfileHandle {
36 async fn connectable(&mut self) -> Result<crate::BluetoothRfcommConnectableAsync, String> {
37 self.next()
38 .await
39 .map(|a| crate::BluetoothRfcommConnectableAsync::Bluez(a))
40 .ok_or_else(|| "Failed to get bluetooth connection".to_string())
41 }
42}
43
44enum BluetoothConnection {
50 Rfcomm(bluer::rfcomm::Stream),
52 L2cap(bluer::l2cap::Stream),
54}
55
56impl tokio::io::AsyncRead for BluetoothConnection {
57 fn poll_read(
58 self: std::pin::Pin<&mut Self>,
59 cx: &mut std::task::Context<'_>,
60 buf: &mut tokio::io::ReadBuf<'_>,
61 ) -> std::task::Poll<std::io::Result<()>> {
62 match self.get_mut() {
63 Self::Rfcomm(s) => {
64 tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
65 }
66 Self::L2cap(s) => {
67 tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
68 }
69 }
70 }
71}
72
73impl tokio::io::AsyncWrite for BluetoothConnection {
74 fn poll_write(
75 self: std::pin::Pin<&mut Self>,
76 cx: &mut std::task::Context<'_>,
77 buf: &[u8],
78 ) -> std::task::Poll<std::io::Result<usize>> {
79 match self.get_mut() {
80 Self::Rfcomm(s) => tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf),
81 Self::L2cap(s) => tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf),
82 }
83 }
84
85 fn poll_flush(
86 self: std::pin::Pin<&mut Self>,
87 cx: &mut std::task::Context<'_>,
88 ) -> std::task::Poll<std::io::Result<()>> {
89 match self.get_mut() {
90 Self::Rfcomm(s) => tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx),
91 Self::L2cap(s) => tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx),
92 }
93 }
94
95 fn poll_shutdown(
96 self: std::pin::Pin<&mut Self>,
97 cx: &mut std::task::Context<'_>,
98 ) -> std::task::Poll<std::io::Result<()>> {
99 match self.get_mut() {
100 Self::Rfcomm(s) => tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx),
101 Self::L2cap(s) => tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx),
102 }
103 }
104}
105
106pub struct BluetoothRfcommSocket {
114 device_addr: bluer::Address,
116 rfcomm_channel: Option<u8>,
124 l2cap_psm: Option<u16>,
126 is_secure: bool,
128 connection: Option<BluetoothConnection>,
130}
131
132impl BluetoothRfcommSocket {
133 fn new_rfcomm(device_addr: bluer::Address, channel: u8, is_secure: bool) -> Self {
135 Self {
136 device_addr,
137 rfcomm_channel: Some(channel),
138 l2cap_psm: None,
139 is_secure,
140 connection: None,
141 }
142 }
143
144 fn new_l2cap(device_addr: bluer::Address, psm: u16, is_secure: bool) -> Self {
146 Self {
147 device_addr,
148 rfcomm_channel: None,
149 l2cap_psm: Some(psm),
150 is_secure,
151 connection: None,
152 }
153 }
154}
155
156impl tokio::io::AsyncRead for BluetoothRfcommSocket {
157 fn poll_read(
158 self: std::pin::Pin<&mut Self>,
159 cx: &mut std::task::Context<'_>,
160 buf: &mut tokio::io::ReadBuf<'_>,
161 ) -> std::task::Poll<std::io::Result<()>> {
162 if let Some(conn) = &mut self.get_mut().connection {
163 tokio::io::AsyncRead::poll_read(std::pin::Pin::new(conn), cx, buf)
164 } else {
165 std::task::Poll::Ready(Err(std::io::Error::new(
166 std::io::ErrorKind::NotConnected,
167 "not connected",
168 )))
169 }
170 }
171}
172
173impl tokio::io::AsyncWrite for BluetoothRfcommSocket {
174 fn poll_write(
175 self: std::pin::Pin<&mut Self>,
176 cx: &mut std::task::Context<'_>,
177 buf: &[u8],
178 ) -> std::task::Poll<std::io::Result<usize>> {
179 if let Some(conn) = &mut self.get_mut().connection {
180 tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(conn), cx, buf)
181 } else {
182 std::task::Poll::Ready(Err(std::io::Error::new(
183 std::io::ErrorKind::NotConnected,
184 "not connected",
185 )))
186 }
187 }
188
189 fn poll_flush(
190 self: std::pin::Pin<&mut Self>,
191 cx: &mut std::task::Context<'_>,
192 ) -> std::task::Poll<std::io::Result<()>> {
193 if let Some(conn) = &mut self.get_mut().connection {
194 tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(conn), cx)
195 } else {
196 std::task::Poll::Ready(Ok(()))
197 }
198 }
199
200 fn poll_shutdown(
201 self: std::pin::Pin<&mut Self>,
202 cx: &mut std::task::Context<'_>,
203 ) -> std::task::Poll<std::io::Result<()>> {
204 if let Some(conn) = &mut self.get_mut().connection {
205 tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(conn), cx)
206 } else {
207 std::task::Poll::Ready(Ok(()))
208 }
209 }
210}
211
212#[async_trait::async_trait]
213impl crate::BluetoothSocketTrait for BluetoothRfcommSocket {
214 fn supports_async(&mut self) -> Option<&mut (dyn super::AsyncReadWrite)> {
215 Some(self)
216 }
217
218 fn is_connected(&self) -> Result<bool, std::io::Error> {
219 Ok(self.connection.is_some())
220 }
221
222 fn sync_connect(&mut self) -> Result<(), std::io::Error> {
223 Err(std::io::Error::new(std::io::ErrorKind::Other, "sync not supported"))
224 }
225
226 async fn async_connect(&mut self) -> Result<(), std::io::Error> {
227 if self.connection.is_some() {
228 return Ok(());
229 }
230 if let Some(channel) = self.rfcomm_channel {
231 let addr = bluer::rfcomm::SocketAddr::new(self.device_addr, channel);
232 let socket = bluer::rfcomm::Socket::new()
233 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
234 if self.is_secure {
235 socket
236 .set_security(bluer::rfcomm::Security {
237 level: bluer::rfcomm::SecurityLevel::Medium,
238 key_size: 0,
239 })
240 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
241 }
242 let stream = socket
243 .connect(addr)
244 .await
245 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
246 log::info!("STREAM {:?} to {:?}", stream.as_ref().local_addr(), stream.peer_addr());
247 self.connection = Some(BluetoothConnection::Rfcomm(stream));
248 log::info!("Got an rfcomm stream");
249 } else if let Some(psm) = self.l2cap_psm {
250 let addr = bluer::l2cap::SocketAddr::new(
251 self.device_addr,
252 bluer::AddressType::BrEdr,
253 psm,
254 );
255 let socket = bluer::l2cap::Socket::<bluer::l2cap::Stream>::new_stream()
256 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
257 if self.is_secure {
258 socket
259 .set_security(bluer::l2cap::Security {
260 level: bluer::l2cap::SecurityLevel::Medium,
261 key_size: 0,
262 })
263 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
264 }
265 let stream = socket
266 .connect(addr)
267 .await
268 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
269 self.connection = Some(BluetoothConnection::L2cap(stream));
270 } else {
271 return Err(std::io::Error::new(
272 std::io::ErrorKind::InvalidInput,
273 "BluetoothRfcommSocket has neither an RFCOMM channel nor an L2CAP PSM configured",
274 ));
275 }
276 Ok(())
277 }
278}
279
280pub struct LinuxBluetoothDevice {
288 device: bluer::Device,
290}
291
292impl LinuxBluetoothDevice {
293 pub fn new(device: bluer::Device) -> Self {
295 Self { device }
296 }
297}
298
299#[async_trait::async_trait]
300impl super::BluetoothDeviceAsyncTrait for LinuxBluetoothDevice {
301 async fn get_uuids(&mut self) -> Result<Vec<crate::BluetoothUuid>, std::io::Error> {
302 let uuids =
303 self.device.uuids().await.map_err(|e| {
304 std::io::Error::new(std::io::ErrorKind::Other, e.to_string())
305 })?;
306 Ok(uuids
307 .unwrap_or_default()
308 .into_iter()
309 .map(|u| {
310 use std::str::FromStr;
311 crate::BluetoothUuid::from_str(&u.to_string())
312 .unwrap_or_else(|_| crate::BluetoothUuid::Unknown(u.to_string()))
313 })
314 .collect())
315 }
316
317 async fn get_name(&self) -> Result<String, std::io::Error> {
320 let device = self.device.clone();
321 device
322 .alias()
323 .await
324 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
325 }
326
327 async fn get_pair_state(&self) -> Result<crate::PairingStatus, std::io::Error> {
328 let device = self.device.clone();
329 let paired = device
330 .is_paired()
331 .await
332 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
333 Ok(if paired {
334 crate::PairingStatus::Paired
335 } else {
336 crate::PairingStatus::NotPaired
337 })
338 }
339}
340
341impl super::BluetoothDeviceTrait for LinuxBluetoothDevice {
342 fn supports_async(&mut self) -> Option<&mut dyn super::BluetoothDeviceAsyncTrait> {
343 Some(self)
344 }
345
346 fn supports_sync(&mut self) -> Option<&mut dyn super::BluetoothDeviceSyncTrait> {
347 None
348 }
349
350 fn get_address(&mut self) -> Result<String, std::io::Error> {
351 Ok(self.device.address().to_string())
352 }
353
354 fn get_l2cap_socket(
363 &mut self,
364 psm: u16,
365 is_secure: bool,
366 ) -> Result<crate::BluetoothSocket, String> {
367 let addr = self.device.address();
368 let socket = BluetoothRfcommSocket::new_l2cap(addr, psm, is_secure);
369 Ok(crate::BluetoothSocket::Bluez(socket))
370 }
371
372 fn get_rfcomm_socket(
381 &mut self,
382 channel: u8,
383 is_secure: bool,
384 ) -> Result<crate::BluetoothSocket, String> {
385 let addr = self.device.address();
386 let socket = BluetoothRfcommSocket::new_rfcomm(addr, channel, is_secure);
387 Ok(crate::BluetoothSocket::Bluez(socket))
388 }
389}
390
391pub struct BluetoothDiscovery {}
397
398impl BluetoothDiscovery {
399 fn new() -> Self {
401 Self {}
402 }
403}
404
405impl super::BluetoothDiscoveryTrait for BluetoothDiscovery {}
406
407impl Drop for BluetoothDiscovery {
408 fn drop(&mut self) {}
409}
410
411impl TryFrom<super::BluetoothRfcommProfileSettings> for bluer::rfcomm::Profile {
416 type Error = String;
417 fn try_from(value: super::BluetoothRfcommProfileSettings) -> Result<Self, Self::Error> {
418 let service = if let Some(v) = value.service_uuid {
419 Some(bluer::Uuid::parse_str(&v).map_err(|e| e.to_string())?)
420 } else {
421 None
422 };
423 Ok(Self {
424 uuid: bluer::Uuid::parse_str(&value.uuid).map_err(|e| e.to_string())?,
425 name: value.name,
426 service,
427 role: if value.channel.is_some() {
428 Some(bluer::rfcomm::Role::Server)
429 } else {
430 None
431 },
432 channel: value.channel,
433 psm: value.psm,
434 require_authentication: value.authenticate,
435 require_authorization: value.authorize,
436 auto_connect: value.auto_connect,
437 service_record: value.sdp_record,
438 version: value.sdp_version,
439 features: value.sdp_features,
440 ..Default::default()
441 })
442 }
443}
444
445impl TryFrom<super::BluetoothL2capProfileSettings> for bluer::rfcomm::Profile {
449 type Error = String;
450 fn try_from(value: super::BluetoothL2capProfileSettings) -> Result<Self, Self::Error> {
451 let service = if let Some(v) = value.service_uuid {
452 Some(bluer::Uuid::parse_str(&v).map_err(|e| e.to_string())?)
453 } else {
454 None
455 };
456 Ok(Self {
457 uuid: bluer::Uuid::parse_str(&value.uuid).map_err(|e| e.to_string())?,
458 name: value.name,
459 service,
460 role: None,
461 channel: None,
462 psm: value.psm,
463 require_authentication: value.authenticate,
464 require_authorization: value.authorize,
465 auto_connect: value.auto_connect,
466 service_record: value.sdp_record,
467 version: value.sdp_version,
468 features: value.sdp_features,
469 ..Default::default()
470 })
471 }
472}
473
474pub struct BluetoothHandler {
480 session: bluer::Session,
482 adapters: Vec<bluer::Adapter>,
484 _blue_agent_handle: bluer::agent::AgentHandle,
486}
487
488impl super::BluetoothAdapterTrait for BluetoothHandler {
489 fn supports_async(&self) -> Option<&dyn super::AsyncBluetoothAdapterTrait> {
490 Some(self)
491 }
492
493 fn supports_sync(&self) -> Option<&dyn super::SyncBluetoothAdapterTrait> {
494 None
495 }
496}
497
498#[async_trait::async_trait]
499impl super::AsyncBluetoothAdapterTrait for BluetoothHandler {
500 async fn register_rfcomm_profile(
501 &self,
502 settings: super::BluetoothRfcommProfileSettings,
503 ) -> Result<crate::BluetoothRfcommProfileAsync, String> {
504 self.session
505 .register_profile(settings.try_into()?)
506 .await
507 .map(|a| super::BluetoothRfcommProfileAsync::Bluez(a.into()))
508 .map_err(|e| e.to_string())
509 }
510
511 async fn register_l2cap_profile(
517 &self,
518 settings: super::BluetoothL2capProfileSettings,
519 ) -> Result<crate::BluetoothL2capProfileAsync, String> {
520 self.session
521 .register_profile(settings.try_into()?)
522 .await
523 .map(|a| super::BluetoothL2capProfileAsync::Bluez(a.into()))
524 .map_err(|e| e.to_string())
525 }
526
527 fn start_discovery(&self) -> crate::BluetoothDiscovery {
528 BluetoothDiscovery::new().into()
529 }
530
531 async fn get_paired_devices(&self) -> Option<Vec<crate::BluetoothDevice>> {
533 let mut list = Vec::new();
534 for adapter in &self.adapters {
535 let result = {
536 let addrs = adapter.device_addresses().await.ok()?;
537 let mut paired = Vec::new();
538 for addr in addrs {
539 if let Ok(dev) = adapter.device(addr) {
540 if dev.is_paired().await.unwrap_or(false) {
541 paired.push(dev);
542 }
543 }
544 }
545 Ok::<Vec<bluer::Device>, bluer::Error>(paired)
546 };
547 if let Ok(devices) = result {
548 for dev in devices {
549 list.push(crate::BluetoothDevice::Bluez(LinuxBluetoothDevice::new(
550 dev,
551 )));
552 }
553 }
554 }
555 Some(list)
556 }
557
558 async fn addresses(&self) -> Vec<super::BluetoothAdapterAddress> {
559 let mut a = Vec::new();
560 for adapter in &self.adapters {
561 if let Ok(adr) = adapter.address().await {
562 a.push(super::BluetoothAdapterAddress::Byte(adr.0));
563 }
564 }
565 a
566 }
567
568 async fn set_discoverable(&self, d: bool) -> Result<(), ()> {
569 for adapter in &self.adapters {
570 adapter.set_discoverable(d).await.map_err(|_| ())?;
571 }
572 Ok(())
573 }
574}
575
576impl BluetoothHandler {
577 pub async fn addresses(&self) -> Vec<bluer::Address> {
579 let mut addrs = Vec::new();
580 for a in &self.adapters {
581 if let Ok(addr) = a.address().await {
582 addrs.push(addr);
583 }
584 }
585 addrs
586 }
587
588 pub async fn new(
590 s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
591 ) -> Result<Self, String> {
592 let session = bluer::Session::new().await.map_err(|e| e.to_string())?;
593
594 let adapter_names = session.adapter_names().await.map_err(|e| e.to_string())?;
595 let adapters: Vec<bluer::Adapter> = adapter_names
596 .iter()
597 .filter_map(|n| session.adapter(n).ok())
598 .collect();
599
600 let blue_agent = Self::build_agent(s);
601 let blue_agent_handle = session.register_agent(blue_agent).await;
602 println!("Registered a bluetooth agent {}", blue_agent_handle.is_ok());
603 Ok(Self {
604 session,
605 adapters,
606 _blue_agent_handle: blue_agent_handle.map_err(|e| e.to_string())?,
607 })
608 }
609
610 async fn enable(&mut self) {
612 for adapter in &self.adapters {
613 adapter.set_powered(true).await.unwrap();
614 adapter.set_pairable(true).await.unwrap();
615 }
616 }
617
618 async fn disable(&mut self) {
620 self.set_discoverable(false).await;
621 for adapter in &self.adapters {
622 adapter.set_powered(false).await.unwrap();
623 adapter.set_pairable(false).await.unwrap();
624 }
625 }
626
627 pub async fn set_discoverable(&mut self, d: bool) {
629 for adapter in &self.adapters {
630 adapter.set_discoverable(d).await.unwrap();
631 }
632 }
633
634 pub async fn register_rfcomm_profile(
636 &mut self,
637 profile: bluer::rfcomm::Profile,
638 ) -> Result<bluer::rfcomm::ProfileHandle, bluer::Error> {
639 self.session.register_profile(profile).await
640 }
641
642 fn build_agent(
644 s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
645 ) -> bluer::agent::Agent {
646 let mut blue_agent = bluer::agent::Agent::default();
647 blue_agent.request_default = true;
648 blue_agent.request_pin_code = None;
649 blue_agent.request_passkey = None;
650 let s2 = s.clone();
651 blue_agent.display_passkey = Some(Box::new(move |mut a| {
652 println!("Running process for display_passkey: {:?}", a);
653 let s3 = s2.clone();
654 async move {
655 let mut chan = tokio::sync::mpsc::channel(5);
656 let _ = s3
657 .send(super::MessageToBluetoothHost::DisplayPasskey(a.passkey, chan.0))
658 .await;
659 loop {
660 let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
661 tokio::select! {
662 asdf = f => {
663 match asdf {
664 Ok(Some(m)) => match m {
665 super::ResponseToPasskey::Yes => {
666 let _ = s3
667 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
668 .await;
669 return Ok(());
670 }
671 super::ResponseToPasskey::No => {
672 let _ = s3
673 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
674 .await;
675 return Err(bluer::agent::ReqError::Rejected);
676 }
677 super::ResponseToPasskey::Cancel => {
678 let _ = s3
679 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
680 .await;
681 return Err(bluer::agent::ReqError::Canceled);
682 }
683 super::ResponseToPasskey::Waiting => {}
684 },
685 Ok(None) => {}
686 _ => {
687 let _ = s3
688 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
689 .await;
690 return Err(bluer::agent::ReqError::Canceled);
691 }
692 }
693 }
694 _ = &mut a.cancel => {
695 let _ = s3
696 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
697 .await;
698 break Err(bluer::agent::ReqError::Canceled);
699 }
700 }
701 }
702 }
703 .boxed()
704 }));
705 blue_agent.display_pin_code = Some(Box::new(|a| {
706 async move {
707 println!("Need to display pin code {:?}", a);
708 a.cancel.await.unwrap();
709 Ok(())
710 }
711 .boxed()
712 }));
713 let s2 = s.clone();
714 blue_agent.request_confirmation = Some(Box::new(move |a| {
715 println!("Need to confirm {:?}", a);
716 let s3 = s2.clone();
717 async move {
718 let mut chan = tokio::sync::mpsc::channel(5);
719 let _ = s3
720 .send(super::MessageToBluetoothHost::ConfirmPasskey(
721 a.passkey, chan.0,
722 ))
723 .await;
724 loop {
725 let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
726 let asdf = f.await;
727 match asdf {
728 Ok(Some(m)) => match m {
729 super::ResponseToPasskey::Yes => {
730 let _ = s3
731 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
732 .await;
733 return Ok(());
734 }
735 super::ResponseToPasskey::No => {
736 let _ = s3
737 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
738 .await;
739 return Err(bluer::agent::ReqError::Rejected);
740 }
741 super::ResponseToPasskey::Cancel => {
742 let _ = s3
743 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
744 .await;
745 return Err(bluer::agent::ReqError::Canceled);
746 }
747 super::ResponseToPasskey::Waiting => {}
748 },
749 Ok(None) => {}
750 _ => {
751 let _ = s3
752 .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
753 .await;
754 return Err(bluer::agent::ReqError::Canceled);
755 }
756 }
757 }
758 }
759 .boxed()
760 }));
761 blue_agent.request_authorization = Some(Box::new(|a| {
762 async move {
763 println!("Need to authorize {:?}", a);
764 Ok(())
765 }
766 .boxed()
767 }));
768 blue_agent.authorize_service = Some(Box::new(|a| {
769 async move {
770 println!("Need to authorize service {:?}", a);
771 Ok(())
772 }
773 .boxed()
774 }));
775 blue_agent
776 }
777
778 pub async fn issue_command(
780 &mut self,
781 cmd: super::BluetoothCommand,
782 ) -> Option<super::BluetoothResponse> {
783 match cmd {
784 super::BluetoothCommand::QueryNumAdapters => {
785 Some(super::BluetoothResponse::Adapters(self.adapters.len()))
786 }
787 _ => None,
788 }
789 }
790
791 pub async fn scan<'a>(
794 &'a mut self,
795 bluetooth_devices: &mut HashMap<
796 bluer::Address,
797 (&'a bluer::Adapter, Option<bluer::Device>),
798 >,
799 ) {
800 let mut adapter_scanner = Vec::new();
801 for a in &self.adapters {
802 let da = a.discover_devices_with_changes().await.unwrap();
803 adapter_scanner.push((a, da));
804 }
805
806 for (adapt, da) in &mut adapter_scanner {
807 if let Some(e) = da.next().await {
808 match e {
809 AdapterEvent::DeviceAdded(addr) => {
810 log::debug!("Device added {:?}", addr);
811 bluetooth_devices.insert(addr, (adapt, None));
812 }
813 AdapterEvent::DeviceRemoved(addr) => {
814 log::debug!("Device removed {:?}", addr);
815 bluetooth_devices.remove_entry(&addr);
816 }
817 AdapterEvent::PropertyChanged(prop) => {
818 log::debug!("Adapter property changed {:?}", prop);
819 }
820 }
821 }
822 }
823
824 for (addr, (adapter, dev)) in bluetooth_devices.iter_mut() {
825 if dev.is_none() {
826 if let Ok(d) = adapter.device(*addr) {
827 if let Ok(ps) = d.all_properties().await {
828 for p in ps {
829 log::debug!("Device {:?} property: {:?}", addr, p);
830 }
831 }
832 *dev = Some(d);
833 }
834 }
835 }
836 }
837}