Skip to content

Commit 2cb6cb2

Browse files
committed
WASM32
1 parent 7cb12a1 commit 2cb6cb2

File tree

9 files changed

+695
-0
lines changed

9 files changed

+695
-0
lines changed

Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,28 @@ windows = "0.18.0"
5656
[target.'cfg(target_os = "windows")'.build-dependencies]
5757
windows = "0.18.0"
5858

59+
[target.'cfg(target_arch = "wasm32")'.dependencies]
60+
js-sys = "0.3.53"
61+
wasm-bindgen = "0.2.76"
62+
wasm-bindgen-futures = "0.4.18"
63+
64+
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
65+
version = "0.3.4"
66+
features = [
67+
'Navigator',
68+
'Bluetooth',
69+
'BluetoothCharacteristicProperties',
70+
'BluetoothDevice',
71+
'BluetoothLeScanFilterInit',
72+
'BluetoothRemoteGattCharacteristic',
73+
'BluetoothRemoteGattServer',
74+
'BluetoothRemoteGattService',
75+
'console',
76+
'Event',
77+
'RequestDeviceOptions',
78+
'Window',
79+
]
80+
5981
[dev-dependencies]
6082
rand = "0.8.4"
6183
pretty_env_logger = "0.4.0"

src/api/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ pub enum WriteType {
174174
WithoutResponse,
175175
}
176176

177+
#[cfg(not(target_arch = "wasm32"))]
177178
pub type PeripheralId = BDAddr;
179+
#[cfg(target_arch = "wasm32")]
180+
pub type PeripheralId = String;
178181

179182
/// Peripheral is the device that you would like to communicate with (the "server" of BLE). This
180183
/// struct contains both the current state of the device (its properties, characteristics, etc.)

src/common/adapter_manager.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ where
6161
Box::pin(receiver)
6262
}
6363

64+
#[allow(dead_code)]
65+
pub fn has_peripheral(&self, addr: &PeripheralId) -> bool {
66+
self.peripherals.contains_key(addr)
67+
}
68+
6469
pub fn add_peripheral(&self, addr: PeripheralId, peripheral: PeripheralType) {
6570
assert!(
6671
!self.peripherals.contains_key(&addr),

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ mod corebluetooth;
104104
pub mod platform;
105105
#[cfg(feature = "serde")]
106106
pub mod serde;
107+
#[cfg(target_arch = "wasm32")]
108+
mod wasm;
107109
#[cfg(target_os = "windows")]
108110
mod winrtble;
109111

src/platform.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
pub use crate::bluez::{adapter::Adapter, manager::Manager, peripheral::Peripheral};
66
#[cfg(any(target_os = "macos", target_os = "ios"))]
77
pub use crate::corebluetooth::{adapter::Adapter, manager::Manager, peripheral::Peripheral};
8+
#[cfg(target_arch = "wasm32")]
9+
pub use crate::wasm::{adapter::Adapter, manager::Manager, peripheral::Peripheral};
810
#[cfg(target_os = "windows")]
911
pub use crate::winrtble::{adapter::Adapter, manager::Manager, peripheral::Peripheral};
1012

src/wasm/adapter.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use super::peripheral::Peripheral;
2+
use crate::api::{BDAddr, Central, CentralEvent, Peripheral as _, PeripheralId};
3+
use crate::{Error, Result};
4+
5+
use crate::common::adapter_manager::AdapterManager;
6+
use async_trait::async_trait;
7+
use std::pin::Pin;
8+
9+
use futures::channel::oneshot;
10+
use futures::stream::Stream;
11+
use wasm_bindgen::prelude::*;
12+
13+
use js_sys::Array;
14+
use wasm_bindgen_futures::{spawn_local, JsFuture};
15+
use web_sys::{BluetoothDevice, RequestDeviceOptions};
16+
17+
/// Implementation of [api::Central](crate::api::Central).
18+
#[derive(Clone, Debug)]
19+
pub struct Adapter {
20+
manager: AdapterManager<Peripheral>,
21+
}
22+
23+
fn bluetooth() -> Option<web_sys::Bluetooth> {
24+
web_sys::window().unwrap().navigator().bluetooth()
25+
}
26+
27+
#[macro_export]
28+
macro_rules! spawn_local_and_wait {
29+
($x:expr) => {
30+
let (sender, receiver) = oneshot::channel();
31+
spawn_local(async move {
32+
let _ = sender.send($x);
33+
});
34+
receiver.await.unwrap()
35+
};
36+
}
37+
38+
impl Adapter {
39+
pub(crate) fn new() -> Option<Self> {
40+
if let Some(_) = bluetooth() {
41+
let manager = AdapterManager::default();
42+
Some(Self { manager })
43+
} else {
44+
None
45+
}
46+
}
47+
48+
pub(crate) async fn add_inital_peripherals(&self) {
49+
let manager = self.manager.clone();
50+
spawn_local_and_wait!({
51+
if let Ok(devices) = JsFuture::from(bluetooth().unwrap().get_devices()).await {
52+
let devices = Array::from(&devices);
53+
for device in devices.iter() {
54+
let p = Peripheral::new(BluetoothDevice::from(device), manager.clone());
55+
if !manager.has_peripheral(&p.id()) {
56+
manager.add_peripheral(p.id(), p);
57+
}
58+
}
59+
}
60+
});
61+
}
62+
}
63+
64+
#[async_trait]
65+
impl Central for Adapter {
66+
type Peripheral = Peripheral;
67+
68+
async fn events(&self) -> Result<Pin<Box<dyn Stream<Item = CentralEvent> + Send>>> {
69+
Ok(self.manager.event_stream())
70+
}
71+
72+
async fn start_scan(&self) -> Result<()> {
73+
let manager = self.manager.clone();
74+
75+
spawn_local_and_wait!({
76+
let mut options = RequestDeviceOptions::new();
77+
// let mut filters = Array::new();
78+
let services = Array::new();
79+
// let mut optional_services = Array::new();
80+
// for (protocol_name, configs) in config_manager.config.protocols {
81+
// if let Some(btle) = configs.btle {
82+
// for name in btle.names {
83+
// let mut filter = web_sys::BluetoothLeScanFilterInit::new();
84+
// if name.contains("*") {
85+
// let mut name_clone = name.clone();
86+
// name_clone.pop();
87+
// filter.name_prefix(&name_clone);
88+
// } else {
89+
// filter.name(&name);
90+
// }
91+
// filters.push(&filter.into());
92+
// }
93+
// for (service, _) in btle.services {
94+
// optional_services.push(&service.to_string().into());
95+
// }
96+
// }
97+
// }
98+
99+
for uuid in Peripheral::known_services().iter() {
100+
services.push(&uuid.to_string().into());
101+
}
102+
103+
// services.push(&"00001801-0000-1000-8000-00805f9b34fb".to_string().into());
104+
// services.push(&"000000ff-0000-1000-8000-00805f9b34fb".to_string().into());
105+
// services.push(&"000000ee-0000-1000-8000-00805f9b34fb".to_string().into());
106+
// services.push(&"955a180a-0fe2-f5aa-a094-84b8d4f3e8ad".to_string().into());
107+
// services.push(&"955a180b-0fe2-f5aa-a094-84b8d4f3e8ad".to_string().into());
108+
// services.push(&"f55a180b-0fe2-f5aa-a094-84b8d4f3e8ad".to_string().into());
109+
// services.push(&"0000180d-0000-1000-8000-00805f9b34fb".to_string().into());
110+
111+
// let mut filter = web_sys::BluetoothLeScanFilterInit::new();
112+
// filter.services(&services.into());
113+
// filters.push(&filter.into());
114+
// options.filters(&filters.into());
115+
options.optional_services(&services.into());
116+
options.accept_all_devices(true);
117+
118+
web_sys::console::dir_1(&options);
119+
120+
match JsFuture::from(bluetooth().unwrap().request_device(&options)).await {
121+
Ok(device) => {
122+
println!("GOT DEVICE");
123+
let p = Peripheral::new(BluetoothDevice::from(device), manager.clone());
124+
let id = p.id();
125+
manager.add_peripheral(p.id(), p);
126+
manager.emit(CentralEvent::DeviceDiscovered(id));
127+
}
128+
Err(e) => web_sys::console::error_1(&JsValue::from(e)),
129+
}
130+
});
131+
Ok(())
132+
}
133+
134+
async fn stop_scan(&self) -> Result<()> {
135+
Ok(())
136+
}
137+
138+
async fn peripherals(&self) -> Result<Vec<Peripheral>> {
139+
Ok(self.manager.peripherals())
140+
}
141+
142+
async fn peripheral(&self, address: PeripheralId) -> Result<Peripheral> {
143+
self.manager
144+
.peripheral(address)
145+
.ok_or(Error::DeviceNotFound)
146+
}
147+
148+
async fn add_peripheral(&self, _address: BDAddr) -> Result<Peripheral> {
149+
Err(Error::NotSupported(
150+
"Can't add a Peripheral from a BDAddr".to_string(),
151+
))
152+
}
153+
}

src/wasm/manager.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use super::adapter::Adapter;
2+
use crate::{api, Result};
3+
use async_trait::async_trait;
4+
5+
/// Implementation of [api::Manager](crate::api::Manager).
6+
#[derive(Clone, Debug)]
7+
pub struct Manager {}
8+
9+
impl Manager {
10+
pub async fn new() -> Result<Self> {
11+
Ok(Self {})
12+
}
13+
}
14+
15+
#[async_trait]
16+
impl api::Manager for Manager {
17+
type Adapter = Adapter;
18+
19+
async fn adapters(&self) -> Result<Vec<Adapter>> {
20+
if let Some(adapter) = Adapter::new() {
21+
adapter.add_inital_peripherals().await;
22+
Ok(vec![adapter])
23+
} else {
24+
Ok(vec![])
25+
}
26+
}
27+
}

src/wasm/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod adapter;
2+
pub mod manager;
3+
pub mod peripheral;

0 commit comments

Comments
 (0)