use super::webbluetooth_hardware::WebBluetoothHardwareConnector; use buttplug_core::ButtplugResultFuture; use buttplug_server_device_config::ProtocolCommunicationSpecifier; use buttplug_server::device::hardware::communication::{ HardwareCommunicationManager, HardwareCommunicationManagerBuilder, HardwareCommunicationManagerEvent, }; use futures::future; use tokio::sync::mpsc::Sender; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::{spawn_local, JsFuture}; use web_sys::BluetoothDevice; use crate::create_test_dcm; #[derive(Default)] pub struct WebBluetoothCommunicationManagerBuilder { } impl HardwareCommunicationManagerBuilder for WebBluetoothCommunicationManagerBuilder { fn finish(&mut self, sender: Sender) -> Box { Box::new(WebBluetoothCommunicationManager { sender, }) } } pub struct WebBluetoothCommunicationManager { sender: Sender, } #[wasm_bindgen] extern "C" { // Use `js_namespace` here to bind `console.log(..)` instead of just // `log(..)` #[wasm_bindgen(js_namespace = console)] fn log(s: &str); } impl HardwareCommunicationManager for WebBluetoothCommunicationManager { fn name(&self) -> &'static str { "WebBluetoothCommunicationManager" } fn can_scan(&self) -> bool { true } fn start_scanning(&mut self) -> ButtplugResultFuture { info!("WebBluetooth manager scanning"); let sender_clone = self.sender.clone(); spawn_local(async move { // Build the filter block let nav = web_sys::window().unwrap().navigator(); if nav.bluetooth().is_none() { error!("WebBluetooth is not supported on this browser"); return; } info!("WebBluetooth supported by browser, continuing with scan."); // HACK: As of buttplug v5, we can't just create a HardwareCommunicationManager anymore. This is // using a test method to create a filled out DCM, which will work for now because there's no // way for anyone to add device configurations through FFI yet anyways. let config_manager = create_test_dcm(false); let options = web_sys::RequestDeviceOptions::new(); let mut filters = Vec::new(); let mut optional_services = Vec::new(); for vals in config_manager.base_communication_specifiers().iter() { for config in vals.1.iter() { if let ProtocolCommunicationSpecifier::BluetoothLE(btle) = &config { for name in btle.names() { let filter = web_sys::BluetoothLeScanFilterInit::new(); if name.contains("*") { let mut name_clone = name.clone(); name_clone.pop(); filter.set_name_prefix(&name_clone); } else { filter.set_name(&name); } filters.push(filter); } for (service, _) in btle.services() { optional_services.push(js_sys::JsString::from(service.to_string())); } } } } options.set_filters(&filters); options.set_optional_services(&optional_services); let nav = web_sys::window().unwrap().navigator(); //nav.bluetooth().get_availability(); //JsFuture::from(nav.bluetooth().request_device()).await; match JsFuture::from(nav.bluetooth().unwrap().request_device(&options)).await { Ok(device) => { let bt_device = BluetoothDevice::from(device); if bt_device.name().is_none() { return; } let name = bt_device.name().unwrap(); let address = bt_device.id(); let device_creator = Box::new(WebBluetoothHardwareConnector::new(bt_device)); if sender_clone .send(HardwareCommunicationManagerEvent::DeviceFound { name, address, creator: device_creator, }) .await .is_err() { error!("Device manager receiver dropped, cannot send device found message."); } else { info!("WebBluetooth device found."); } } Err(e) => { error!("Error while trying to start bluetooth scan: {:?}", e); } }; let _ = sender_clone .send(HardwareCommunicationManagerEvent::ScanningFinished) .await; }); Box::pin(future::ready(Ok(()))) } fn stop_scanning(&mut self) -> ButtplugResultFuture { Box::pin(future::ready(Ok(()))) } }