|
| 1 | +use super::peripheral::{Peripheral, PeripheralId}; |
| 2 | +use super::utils::wrap_promise; |
| 3 | +use crate::api::{BDAddr, Central, CentralEvent, Peripheral as _, ScanFilter}; |
| 4 | +use crate::common::adapter_manager::AdapterManager; |
| 5 | +use crate::{Error, Result}; |
| 6 | +use async_trait::async_trait; |
| 7 | +use futures::channel::oneshot; |
| 8 | +use futures::stream::Stream; |
| 9 | +use js_sys::Array; |
| 10 | +use std::pin::Pin; |
| 11 | +use std::sync::Arc; |
| 12 | +use wasm_bindgen::prelude::*; |
| 13 | +use wasm_bindgen_futures::spawn_local; |
| 14 | +use web_sys::{BluetoothDevice, BluetoothLeScanFilterInit, RequestDeviceOptions}; |
| 15 | + |
| 16 | +macro_rules! spawn_local { |
| 17 | + ($a:expr) => {{ |
| 18 | + let (sender, receiver) = oneshot::channel(); |
| 19 | + spawn_local(async move { |
| 20 | + let _ = sender.send($a); |
| 21 | + }); |
| 22 | + receiver.await.unwrap() |
| 23 | + }}; |
| 24 | +} |
| 25 | + |
| 26 | +/// Implementation of [api::Central](crate::api::Central). |
| 27 | +#[derive(Clone, Debug)] |
| 28 | +pub struct Adapter { |
| 29 | + manager: Arc<AdapterManager<Peripheral>>, |
| 30 | +} |
| 31 | + |
| 32 | +fn bluetooth() -> Option<web_sys::Bluetooth> { |
| 33 | + web_sys::window().unwrap().navigator().bluetooth() |
| 34 | +} |
| 35 | + |
| 36 | +#[async_trait] |
| 37 | +trait AddPeripheralAndEmit { |
| 38 | + async fn add_inital_periperals(&self) -> Vec<PeripheralId>; |
| 39 | + fn add_device(&self, device: JsValue) -> Option<PeripheralId>; |
| 40 | +} |
| 41 | + |
| 42 | +#[async_trait] |
| 43 | +impl AddPeripheralAndEmit for Arc<AdapterManager<Peripheral>> { |
| 44 | + async fn add_inital_periperals(&self) -> Vec<PeripheralId> { |
| 45 | + if !self.peripherals().is_empty() { |
| 46 | + return vec![]; |
| 47 | + } |
| 48 | + |
| 49 | + let self_clone = self.clone(); |
| 50 | + spawn_local!({ |
| 51 | + wrap_promise::<Array>(bluetooth().unwrap().get_devices()) |
| 52 | + .await |
| 53 | + .map_or(vec![], |devices| { |
| 54 | + devices |
| 55 | + .iter() |
| 56 | + .map(|device| self_clone.add_device(device).unwrap()) |
| 57 | + .collect() |
| 58 | + }) |
| 59 | + }) |
| 60 | + } |
| 61 | + |
| 62 | + fn add_device(&self, device: JsValue) -> Option<PeripheralId> { |
| 63 | + let p = Peripheral::new(Arc::downgrade(self), BluetoothDevice::from(device)); |
| 64 | + let id = p.id(); |
| 65 | + if self.peripheral(&id).is_none() { |
| 66 | + self.add_peripheral(p); |
| 67 | + Some(id) |
| 68 | + } else { |
| 69 | + None |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +impl Adapter { |
| 75 | + pub(crate) fn try_new() -> Option<Self> { |
| 76 | + if let Some(_) = bluetooth() { |
| 77 | + Some(Self { |
| 78 | + manager: Arc::new(AdapterManager::default()), |
| 79 | + }) |
| 80 | + } else { |
| 81 | + None |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +#[async_trait] |
| 87 | +impl Central for Adapter { |
| 88 | + type Peripheral = Peripheral; |
| 89 | + |
| 90 | + async fn events(&self) -> Result<Pin<Box<dyn Stream<Item = CentralEvent> + Send>>> { |
| 91 | + Ok(self.manager.event_stream()) |
| 92 | + } |
| 93 | + |
| 94 | + async fn start_scan(&self, filter: ScanFilter) -> Result<()> { |
| 95 | + let manager = self.manager.clone(); |
| 96 | + spawn_local!({ |
| 97 | + for id in manager.add_inital_periperals().await { |
| 98 | + manager.emit(CentralEvent::DeviceDiscovered(id)); |
| 99 | + } |
| 100 | + |
| 101 | + let mut options = RequestDeviceOptions::new(); |
| 102 | + let optional_services = Array::new(); |
| 103 | + let filters = Array::new(); |
| 104 | + |
| 105 | + for uuid in filter.services.iter() { |
| 106 | + let mut filter = BluetoothLeScanFilterInit::new(); |
| 107 | + let filter_services = Array::new(); |
| 108 | + filter_services.push(&uuid.to_string().into()); |
| 109 | + filter.services(&filter_services.into()); |
| 110 | + filters.push(&filter.into()); |
| 111 | + optional_services.push(&uuid.to_string().into()); |
| 112 | + } |
| 113 | + |
| 114 | + options.filters(&filters.into()); |
| 115 | + options.optional_services(&optional_services.into()); |
| 116 | + |
| 117 | + wrap_promise(bluetooth().unwrap().request_device(&options)) |
| 118 | + .await |
| 119 | + .map(|device| { |
| 120 | + if let Some(id) = manager.add_device(device) { |
| 121 | + manager.emit(CentralEvent::DeviceDiscovered(id)); |
| 122 | + } |
| 123 | + () |
| 124 | + }) |
| 125 | + }) |
| 126 | + } |
| 127 | + |
| 128 | + async fn stop_scan(&self) -> Result<()> { |
| 129 | + Ok(()) |
| 130 | + } |
| 131 | + |
| 132 | + async fn peripherals(&self) -> Result<Vec<Peripheral>> { |
| 133 | + self.manager.add_inital_periperals().await; |
| 134 | + Ok(self.manager.peripherals()) |
| 135 | + } |
| 136 | + |
| 137 | + async fn peripheral(&self, id: &PeripheralId) -> Result<Peripheral> { |
| 138 | + self.manager.add_inital_periperals().await; |
| 139 | + self.manager.peripheral(id).ok_or(Error::DeviceNotFound) |
| 140 | + } |
| 141 | + |
| 142 | + async fn add_peripheral(&self, _address: BDAddr) -> Result<Peripheral> { |
| 143 | + Err(Error::NotSupported( |
| 144 | + "Can't add a Peripheral from a BDAddr".to_string(), |
| 145 | + )) |
| 146 | + } |
| 147 | + |
| 148 | + async fn adapter_info(&self) -> Result<String> { |
| 149 | + Ok("WebBluetooth".to_string()) |
| 150 | + } |
| 151 | +} |
0 commit comments