From 912cc15e1e1f82fbdde926f99ab3b96a30839ab3 Mon Sep 17 00:00:00 2001 From: Corbin Halliwill Date: Fri, 30 May 2025 13:11:15 -0700 Subject: [PATCH 01/12] Retrieve connected devices on macOS --- src/api/mod.rs | 4 +++ src/corebluetooth/adapter.rs | 8 ++++++ src/corebluetooth/internal.rs | 53 ++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index bb3acf68..c364320d 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -359,6 +359,10 @@ pub trait Central: Send + Sync + Clone { /// Stops scanning for BLE devices. async fn stop_scan(&self) -> Result<()>; + /// Retrieve connected peripherals matching the given filter. Same filter and discovery rules + /// apply as for start_scan. + async fn connected_peripherals(&self, filter: ScanFilter) -> Result<()>; + /// Returns the list of [`Peripheral`]s that have been discovered so far. Note that this list /// may contain peripherals that are no longer available. async fn peripherals(&self) -> Result>; diff --git a/src/corebluetooth/adapter.rs b/src/corebluetooth/adapter.rs index b8626bd1..ab10a0ed 100644 --- a/src/corebluetooth/adapter.rs +++ b/src/corebluetooth/adapter.rs @@ -118,6 +118,14 @@ impl Central for Adapter { Ok(()) } + async fn connected_peripherals(&self, filter: ScanFilter) -> Result<()> { + self.sender + .to_owned() + .send(CoreBluetoothMessage::RetrieveConnectedPeripherals { filter }) + .await?; + Ok(()) + } + async fn peripherals(&self) -> Result> { Ok(self.manager.peripherals()) } diff --git a/src/corebluetooth/internal.rs b/src/corebluetooth/internal.rs index 93597bc7..a6778c16 100644 --- a/src/corebluetooth/internal.rs +++ b/src/corebluetooth/internal.rs @@ -14,7 +14,7 @@ use super::{ future::{BtlePlugFuture, BtlePlugFutureStateShared}, utils::{ core_bluetooth::{cbuuid_to_uuid, uuid_to_cbuuid}, - nsuuid_to_uuid, + nsstring_to_string, nsuuid_to_uuid, }, }; use crate::api::{CharPropFlags, Characteristic, Descriptor, ScanFilter, Service, WriteType}; @@ -399,6 +399,9 @@ pub enum CoreBluetoothMessage { filter: ScanFilter, }, StopScanning, + RetrieveConnectedPeripherals { + filter: ScanFilter, + }, ConnectDevice { peripheral_uuid: Uuid, future: CoreBluetoothReplyStateShared, @@ -1169,6 +1172,9 @@ impl CoreBluetoothInternal { }, CoreBluetoothMessage::StartScanning{filter} => self.start_discovery(filter), CoreBluetoothMessage::StopScanning => self.stop_discovery(), + CoreBluetoothMessage::RetrieveConnectedPeripherals{filter} => { + self.retrieve_connected_peripherals(filter); + }, CoreBluetoothMessage::ConnectDevice{peripheral_uuid, future} => { trace!("got connectdevice msg!"); self.connect_peripheral(peripheral_uuid, future); @@ -1239,6 +1245,51 @@ impl CoreBluetoothInternal { trace!("BluetoothAdapter::stop_discovery"); unsafe { self.manager.stopScan() }; } + + fn retrieve_connected_peripherals(&mut self, filter: ScanFilter) { + trace!("BluetoothAdapter::retrieve_connected_peripherals"); + let service_uuids = scan_filter_to_service_uuids(filter); + if service_uuids.is_none() { + warn!("MacOS requires a filter of services to be provided, so we cannot continue."); + return; + } + let peripherals = unsafe { + self.manager + .retrieveConnectedPeripheralsWithServices(service_uuids.as_deref().unwrap()) + }; + + for peripheral in peripherals { + let uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + if self.peripherals.contains_key(&uuid) { + trace!("Peripheral {} already exists, skipping.", uuid); + continue; + } + trace!("Discovered connected peripheral: {}", uuid); + let (event_sender, event_receiver) = mpsc::channel(256); + let name: Option = unsafe { + match peripheral.name() { + Some(ns_name) => nsstring_to_string(ns_name.as_ref()), + None => None, + } + }; + self.peripherals.insert( + uuid, + PeripheralInternal::new(Retained::from(peripheral), event_sender), + ); + let discovered_device = CoreBluetoothEvent::DeviceDiscovered { + uuid, + name, + event_receiver, + }; + // Must use a synchronous sender. + match self.event_sender.try_send(discovered_device) { + Ok(_) => (), + Err(e) => { + error!("Error sending discovered device event: {}", e); + } + } + } + } } /// Convert a `ScanFilter` to the appropriate `NSArray *` to use for discovery. If the From f13f19ac219953c5344c7516db71d8c2c3063f56 Mon Sep 17 00:00:00 2001 From: Corbin Halliwill Date: Mon, 2 Jun 2025 13:43:43 -0700 Subject: [PATCH 02/12] Retrieve connected peripherals on Windows --- Cargo.toml | 2 +- src/winrtble/adapter.rs | 74 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ed77ba06..4ec4a882 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ objc2-core-bluetooth = { version = "0.2.2", default-features = false, features = ] } [target.'cfg(target_os = "windows")'.dependencies] -windows = { version = "0.61", features = ["Devices_Bluetooth", "Devices_Bluetooth_GenericAttributeProfile", "Devices_Bluetooth_Advertisement", "Devices_Radios", "Foundation_Collections", "Foundation", "Storage_Streams"] } +windows = { version = "0.61", features = ["Devices_Bluetooth", "Devices_Bluetooth_GenericAttributeProfile", "Devices_Bluetooth_Advertisement", "Devices_Enumeration", "Devices_Radios", "Foundation_Collections", "Foundation", "Storage_Streams"] } windows-future = "0.2.0" [dev-dependencies] diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index d840c403..a984418a 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -19,11 +19,15 @@ use crate::{ }; use async_trait::async_trait; use futures::stream::Stream; +use log::trace; use std::convert::TryInto; use std::fmt::{self, Debug, Formatter}; use std::pin::Pin; use std::sync::{Arc, Mutex}; +use uuid::Uuid; use windows::{ + Devices::Bluetooth::BluetoothLEDevice, + Devices::Enumeration::DeviceInformation, Devices::Radios::{Radio, RadioState}, Foundation::TypedEventHandler, }; @@ -114,6 +118,76 @@ impl Central for Adapter { Ok(()) } + async fn connected_peripherals(&self, filter: ScanFilter) -> Result<()> { + let device_selector = BluetoothLEDevice::GetDeviceSelector()?; + let get_devices = match DeviceInformation::FindAllAsyncAqsFilter(&device_selector) { + Ok(devices) => devices, + Err(e) => { + return Err(Error::Other(format!("{:?}", e).into())); + } + }; + let devices = match get_devices.get() { + Ok(devices) => devices, + Err(e) => { + return Err(Error::Other(format!("{:?}", e).into())); + } + }; + let manager = self.manager.clone(); + + for device in devices { + let Ok(device_id) = device.Id() else { + continue; + }; + let Ok(ble_device) = BluetoothLEDevice::FromIdAsync(&device_id) else { + continue; + }; + let Ok(ble_device) = ble_device.get() else { + continue; + }; + let Ok(services_result_op) = ble_device.GetGattServicesAsync() else { + continue; + }; + let Ok(services_result) = services_result_op.get() else { + continue; + }; + let Ok(services) = services_result.Services() else { + continue; + }; + + for service in services { + let Ok(s_uuid) = service.Uuid() else { + continue; + }; + let service_uuid = Uuid::from_u128(s_uuid.to_u128()); + if !filter.services.is_empty() && !filter.services.contains(&service_uuid) { + // Skip if services filter is provided, but it does not contain service_uuid. + continue; + } + let Ok(bluetooth_address) = ble_device.BluetoothAddress() else { + continue; + }; + let address: BDAddr = match bluetooth_address.try_into() { + Ok(address) => address, + Err(_) => { + continue; + } + }; + match manager.peripheral_mut(&address.into()) { + Some(_) => { + trace!("Skipping over existing peripheral: {:?}", address); + } + None => { + let peripheral = Peripheral::new(Arc::downgrade(&manager), address); + manager.add_peripheral(peripheral); + manager.emit(CentralEvent::DeviceDiscovered(address.into())); + } + } + return Ok(()); + } + } + Ok(()) + } + async fn peripherals(&self) -> Result> { Ok(self.manager.peripherals()) } From e417dd942ccff7ceb87d8509db6e93a2a7b0905d Mon Sep 17 00:00:00 2001 From: Corbin Halliwill Date: Mon, 2 Jun 2025 15:06:59 -0700 Subject: [PATCH 03/12] Make sure service filter is satisfied on Windows --- src/winrtble/adapter.rs | 63 ++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index a984418a..d2b8e39d 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -133,7 +133,8 @@ impl Central for Adapter { } }; let manager = self.manager.clone(); - + let mut required_services = filter.clone().services; + required_services.sort(); for device in devices { let Ok(device_id) = device.Id() else { continue; @@ -153,37 +154,53 @@ impl Central for Adapter { let Ok(services) = services_result.Services() else { continue; }; - + // Verify all services in filter exist on the device. + let mut found_services: Vec = Vec::new(); for service in services { let Ok(s_uuid) = service.Uuid() else { continue; }; let service_uuid = Uuid::from_u128(s_uuid.to_u128()); - if !filter.services.is_empty() && !filter.services.contains(&service_uuid) { - // Skip if services filter is provided, but it does not contain service_uuid. - continue; + if filter.services.contains(&service_uuid) { + found_services.push(service_uuid); } - let Ok(bluetooth_address) = ble_device.BluetoothAddress() else { + } + found_services.sort(); + trace!( + "Found required services: {:?} of {:?}", + found_services.len(), + required_services.len() + ); + if (required_services.len() != found_services.len()) + || !(required_services + .iter() + .zip(found_services.iter()) + .all(|(l, r)| l == r)) + { + trace!("Not all required services are accounted for, continuing..."); + continue; + } + + let Ok(bluetooth_address) = ble_device.BluetoothAddress() else { + continue; + }; + let address: BDAddr = match bluetooth_address.try_into() { + Ok(address) => address, + Err(_) => { continue; - }; - let address: BDAddr = match bluetooth_address.try_into() { - Ok(address) => address, - Err(_) => { - continue; - } - }; - match manager.peripheral_mut(&address.into()) { - Some(_) => { - trace!("Skipping over existing peripheral: {:?}", address); - } - None => { - let peripheral = Peripheral::new(Arc::downgrade(&manager), address); - manager.add_peripheral(peripheral); - manager.emit(CentralEvent::DeviceDiscovered(address.into())); - } } - return Ok(()); + }; + match manager.peripheral_mut(&address.into()) { + Some(_) => { + trace!("Skipping over existing peripheral: {:?}", address); + } + None => { + let peripheral = Peripheral::new(Arc::downgrade(&manager), address); + manager.add_peripheral(peripheral); + manager.emit(CentralEvent::DeviceDiscovered(address.into())); + } } + return Ok(()); } Ok(()) } From 4a12ed6a617c0104396d3448610dd6d60c23a76f Mon Sep 17 00:00:00 2001 From: Corbin Halliwill Date: Mon, 2 Jun 2025 16:29:51 -0700 Subject: [PATCH 04/12] Re-emit already discovered devices when asking for connected devices --- src/winrtble/adapter.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index d2b8e39d..4f77a634 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -193,6 +193,7 @@ impl Central for Adapter { match manager.peripheral_mut(&address.into()) { Some(_) => { trace!("Skipping over existing peripheral: {:?}", address); + manager.emit(CentralEvent::DeviceDiscovered(address.into())); } None => { let peripheral = Peripheral::new(Arc::downgrade(&manager), address); From 8fb582d8bf7b8d6b41796ca4fe539945b343a280 Mon Sep 17 00:00:00 2001 From: Corbin Halliwill Date: Mon, 2 Jun 2025 16:32:43 -0700 Subject: [PATCH 05/12] Also on Mac, emit already discovered peripherals. This feels more expected to me, otherwise if you ask for connected peripherals a second time you will not get alerts for the previously found peripherals. --- src/corebluetooth/internal.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/corebluetooth/internal.rs b/src/corebluetooth/internal.rs index a6778c16..9ac28439 100644 --- a/src/corebluetooth/internal.rs +++ b/src/corebluetooth/internal.rs @@ -1260,10 +1260,6 @@ impl CoreBluetoothInternal { for peripheral in peripherals { let uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); - if self.peripherals.contains_key(&uuid) { - trace!("Peripheral {} already exists, skipping.", uuid); - continue; - } trace!("Discovered connected peripheral: {}", uuid); let (event_sender, event_receiver) = mpsc::channel(256); let name: Option = unsafe { @@ -1272,10 +1268,12 @@ impl CoreBluetoothInternal { None => None, } }; - self.peripherals.insert( - uuid, - PeripheralInternal::new(Retained::from(peripheral), event_sender), - ); + if !self.peripherals.contains_key(&uuid) { + self.peripherals.insert( + uuid, + PeripheralInternal::new(Retained::from(peripheral), event_sender), + ); + } let discovered_device = CoreBluetoothEvent::DeviceDiscovered { uuid, name, From bc7fdbfc2c2cf075177d55b940e138f9259ab15e Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Fri, 31 Oct 2025 01:11:09 -0700 Subject: [PATCH 06/12] Fix connected_peripherals() to fetch more than just first match --- src/winrtble/adapter.rs | 186 +++++++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 67 deletions(-) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index 4f77a634..11cc3b11 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -19,18 +19,14 @@ use crate::{ }; use async_trait::async_trait; use futures::stream::Stream; -use log::trace; +use log::{debug, trace, warn}; use std::convert::TryInto; use std::fmt::{self, Debug, Formatter}; use std::pin::Pin; use std::sync::{Arc, Mutex}; use uuid::Uuid; -use windows::{ - Devices::Bluetooth::BluetoothLEDevice, - Devices::Enumeration::DeviceInformation, - Devices::Radios::{Radio, RadioState}, - Foundation::TypedEventHandler, -}; +use windows::Devices::Bluetooth::BluetoothLEDevice; +use windows::Devices::Enumeration::DeviceInformation; /// Implementation of [api::Central](crate::api::Central). #[derive(Clone)] @@ -119,90 +115,146 @@ impl Central for Adapter { } async fn connected_peripherals(&self, filter: ScanFilter) -> Result<()> { - let device_selector = BluetoothLEDevice::GetDeviceSelector()?; - let get_devices = match DeviceInformation::FindAllAsyncAqsFilter(&device_selector) { - Ok(devices) => devices, - Err(e) => { - return Err(Error::Other(format!("{:?}", e).into())); - } - }; - let devices = match get_devices.get() { - Ok(devices) => devices, - Err(e) => { - return Err(Error::Other(format!("{:?}", e).into())); - } - }; + let base_selector = BluetoothLEDevice::GetDeviceSelector() + .map_err(|e| Error::Other(format!("GetDeviceSelector failed: {:?}", e).into()))?; + let aqs = format!( + "{} AND System.Devices.Aep.IsConnected:=System.StructuredQueryType.Boolean#True", + base_selector.to_string() + ); + + // Query all BLE devices that are currently connected to the system + let devices = DeviceInformation::FindAllAsyncAqsFilter(&windows::core::HSTRING::from(aqs)) + .map_err(|e| Error::Other(format!("FindAllAsyncAqsFilter failed: {:?}", e).into()))? + .get() + .map_err(|e| Error::Other(format!("FindAllAsync().get() failed: {:?}", e).into()))?; + let manager = self.manager.clone(); - let mut required_services = filter.clone().services; - required_services.sort(); + let required_services: Vec = filter.services.clone(); + + debug!( + "Scanning for connected peripherals with {} service filters", + required_services.len() + ); + + // Iterate through each connected device for device in devices { - let Ok(device_id) = device.Id() else { - continue; - }; - let Ok(ble_device) = BluetoothLEDevice::FromIdAsync(&device_id) else { - continue; - }; - let Ok(ble_device) = ble_device.get() else { - continue; - }; - let Ok(services_result_op) = ble_device.GetGattServicesAsync() else { - continue; - }; - let Ok(services_result) = services_result_op.get() else { - continue; + let device_id = match device.Id() { + Ok(id) => id, + Err(e) => { + warn!("Failed to get device ID: {:?}", e); + continue; + } }; - let Ok(services) = services_result.Services() else { - continue; + debug!("Checking connected device: {:?}", device_id); + + // BluetoothLEDevice from the device ID + let ble_device = match BluetoothLEDevice::FromIdAsync(&device_id) { + Ok(async_op) => match async_op.get() { + Ok(dev) => dev, + Err(e) => { + warn!("FromIdAsync.get() failed for {:?}: {:?}", device_id, e); + continue; + } + }, + Err(e) => { + warn!("FromIdAsync failed for {:?}: {:?}", device_id, e); + continue; + } }; - // Verify all services in filter exist on the device. - let mut found_services: Vec = Vec::new(); - for service in services { - let Ok(s_uuid) = service.Uuid() else { + + // Double-check the connection status + match ble_device.ConnectionStatus() { + Ok(status) + if status + == windows::Devices::Bluetooth::BluetoothConnectionStatus::Connected => {} + Ok(_) => { + trace!("Device {:?} not connected, skipping", device_id); + continue; + } + Err(e) => { + warn!("Failed to get connection status: {:?}", e); continue; + } + } + + // Service filtering logic: + // - If no services specified in filter, accept all connected devices + // - Otherwise, accept only if the device has at least one matching service + let mut accept_device = required_services.is_empty(); + + if !accept_device { + // Query the device's GATT services to check for matches + let services_result = match ble_device.GetGattServicesAsync() { + Ok(async_op) => async_op.get(), + Err(e) => { + warn!("GetGattServicesAsync failed: {:?}", e); + continue; + } }; - let service_uuid = Uuid::from_u128(s_uuid.to_u128()); - if filter.services.contains(&service_uuid) { - found_services.push(service_uuid); + + let services = match services_result { + Ok(gatt_services) => match gatt_services.Services() { + Ok(service_list) => service_list, + Err(e) => { + warn!("Failed to get Services list: {:?}", e); + continue; + } + }, + Err(e) => { + warn!("GetGattServicesAsync.get() failed: {:?}", e); + continue; + } + }; + + // Check if any of the device's services match the filter + for service in &services { + if let Ok(guid) = service.Uuid() { + let service_uuid = Uuid::from_u128(guid.to_u128()); + if required_services.contains(&service_uuid) { + debug!("Found matching service: {:?}", service_uuid); + accept_device = true; + break; + } + } } } - found_services.sort(); - trace!( - "Found required services: {:?} of {:?}", - found_services.len(), - required_services.len() - ); - if (required_services.len() != found_services.len()) - || !(required_services - .iter() - .zip(found_services.iter()) - .all(|(l, r)| l == r)) - { - trace!("Not all required services are accounted for, continuing..."); + + if !accept_device { + debug!("Device does not match service filter, skipping"); continue; } - let Ok(bluetooth_address) = ble_device.BluetoothAddress() else { - continue; - }; - let address: BDAddr = match bluetooth_address.try_into() { - Ok(address) => address, - Err(_) => { + // Convert Bluetooth address to BDAddr + let address = match ble_device.BluetoothAddress() { + Ok(addr) => match (addr as u64).try_into() { + Ok(bd_addr) => bd_addr, + Err(_) => { + warn!("Failed to convert Bluetooth address: {}", addr); + continue; + } + }, + Err(e) => { + warn!("BluetoothAddress() failed: {:?}", e); continue; } }; + + // Update the peripheral in the manager match manager.peripheral_mut(&address.into()) { Some(_) => { - trace!("Skipping over existing peripheral: {:?}", address); - manager.emit(CentralEvent::DeviceDiscovered(address.into())); + debug!("Peripheral already exists in manager: {:?}", address); + // manager.emit(CentralEvent::DeviceUpdated(address.into())); } None => { + debug!("Adding new peripheral: {:?}", address); let peripheral = Peripheral::new(Arc::downgrade(&manager), address); manager.add_peripheral(peripheral); manager.emit(CentralEvent::DeviceDiscovered(address.into())); } } - return Ok(()); } + + debug!("Finished scanning for connected peripherals"); Ok(()) } From 057025ffd95ccb2373293c32a0071ee2d045d7eb Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Sat, 1 Nov 2025 14:31:04 -0700 Subject: [PATCH 07/12] Add new AdapterManager support to clear peripheral cache --- src/common/adapter_manager.rs | 4 ++++ src/winrtble/adapter.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/common/adapter_manager.rs b/src/common/adapter_manager.rs index d50c660e..1058861a 100644 --- a/src/common/adapter_manager.rs +++ b/src/common/adapter_manager.rs @@ -73,6 +73,10 @@ where .collect() } + pub fn clear_peripherals(&self) { + self.peripherals.clear(); + } + // Only used on windows and macOS/iOS, so turn off deadcode so we don't get warnings on android/linux. #[allow(dead_code)] pub fn peripheral_mut( diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index 11cc3b11..1e3eb4f1 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -68,6 +68,10 @@ impl Adapter { radio, }) } + + pub fn clear_cache(&self) { + self.manager.clear_peripherals(); + } } impl Debug for Adapter { From 0e94f4ec4744d6beeec55f1e41de65f169002ff7 Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Sat, 1 Nov 2025 14:39:00 -0700 Subject: [PATCH 08/12] Add peripheral cache clearing support for MacOS imlpementation --- src/corebluetooth/adapter.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/corebluetooth/adapter.rs b/src/corebluetooth/adapter.rs index ab10a0ed..f3220e00 100644 --- a/src/corebluetooth/adapter.rs +++ b/src/corebluetooth/adapter.rs @@ -92,6 +92,10 @@ impl Adapter { sender: adapter_sender, }) } + + pub fn clear_cache(&self) { + self.manager.clear_peripherals(); + } } #[async_trait] From e8a4728dab858dce3bd118300e39c647d7bb8094 Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Sat, 1 Nov 2025 15:00:33 -0700 Subject: [PATCH 09/12] Re-emit already discovered devices when asking for connected peripherals --- src/winrtble/adapter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index 1e3eb4f1..187b927a 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -247,7 +247,7 @@ impl Central for Adapter { match manager.peripheral_mut(&address.into()) { Some(_) => { debug!("Peripheral already exists in manager: {:?}", address); - // manager.emit(CentralEvent::DeviceUpdated(address.into())); + manager.emit(CentralEvent::DeviceDiscovered(address.into())); } None => { debug!("Adding new peripheral: {:?}", address); From 407ae2b5119d60e01b06cef27b4d518b56002fb0 Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Sat, 1 Nov 2025 21:35:47 -0700 Subject: [PATCH 10/12] Re-add necessary library imports -- removed by accident --- src/winrtble/adapter.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index 187b927a..d887f791 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -25,8 +25,12 @@ use std::fmt::{self, Debug, Formatter}; use std::pin::Pin; use std::sync::{Arc, Mutex}; use uuid::Uuid; -use windows::Devices::Bluetooth::BluetoothLEDevice; -use windows::Devices::Enumeration::DeviceInformation; +use windows::{ + Devices::Bluetooth::BluetoothLEDevice, + Devices::Enumeration::DeviceInformation, + Devices::Radios::{Radio, RadioState}, + Foundation::TypedEventHandler, +}; /// Implementation of [api::Central](crate::api::Central). #[derive(Clone)] From 59f180aaa40059874decac723826719acc7f97f3 Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Sat, 1 Nov 2025 21:39:25 -0700 Subject: [PATCH 11/12] Explicitly set address variable type to BDAddr --- src/winrtble/adapter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index d887f791..366d8d32 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -233,7 +233,7 @@ impl Central for Adapter { } // Convert Bluetooth address to BDAddr - let address = match ble_device.BluetoothAddress() { + let address: BDAddr = match ble_device.BluetoothAddress() { Ok(addr) => match (addr as u64).try_into() { Ok(bd_addr) => bd_addr, Err(_) => { From 699b63c45d7d8a6e026f20d421a1afc1736138c5 Mon Sep 17 00:00:00 2001 From: Corten Singer Date: Sat, 1 Nov 2025 22:04:57 -0700 Subject: [PATCH 12/12] Use log::trace instead of log::debug/warn for quietness --- src/winrtble/adapter.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index 366d8d32..465d06cf 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -19,7 +19,7 @@ use crate::{ }; use async_trait::async_trait; use futures::stream::Stream; -use log::{debug, trace, warn}; +use log::trace; use std::convert::TryInto; use std::fmt::{self, Debug, Formatter}; use std::pin::Pin; @@ -139,7 +139,7 @@ impl Central for Adapter { let manager = self.manager.clone(); let required_services: Vec = filter.services.clone(); - debug!( + trace!( "Scanning for connected peripherals with {} service filters", required_services.len() ); @@ -149,23 +149,23 @@ impl Central for Adapter { let device_id = match device.Id() { Ok(id) => id, Err(e) => { - warn!("Failed to get device ID: {:?}", e); + trace!("Failed to get device ID: {:?}", e); continue; } }; - debug!("Checking connected device: {:?}", device_id); + trace!("Checking connected device: {:?}", device_id); // BluetoothLEDevice from the device ID let ble_device = match BluetoothLEDevice::FromIdAsync(&device_id) { Ok(async_op) => match async_op.get() { Ok(dev) => dev, Err(e) => { - warn!("FromIdAsync.get() failed for {:?}: {:?}", device_id, e); + trace!("FromIdAsync.get() failed for {:?}: {:?}", device_id, e); continue; } }, Err(e) => { - warn!("FromIdAsync failed for {:?}: {:?}", device_id, e); + trace!("FromIdAsync failed for {:?}: {:?}", device_id, e); continue; } }; @@ -180,7 +180,7 @@ impl Central for Adapter { continue; } Err(e) => { - warn!("Failed to get connection status: {:?}", e); + trace!("Failed to get connection status: {:?}", e); continue; } } @@ -195,7 +195,7 @@ impl Central for Adapter { let services_result = match ble_device.GetGattServicesAsync() { Ok(async_op) => async_op.get(), Err(e) => { - warn!("GetGattServicesAsync failed: {:?}", e); + trace!("GetGattServicesAsync failed: {:?}", e); continue; } }; @@ -204,12 +204,12 @@ impl Central for Adapter { Ok(gatt_services) => match gatt_services.Services() { Ok(service_list) => service_list, Err(e) => { - warn!("Failed to get Services list: {:?}", e); + trace!("Failed to get Services list: {:?}", e); continue; } }, Err(e) => { - warn!("GetGattServicesAsync.get() failed: {:?}", e); + trace!("GetGattServicesAsync.get() failed: {:?}", e); continue; } }; @@ -219,7 +219,7 @@ impl Central for Adapter { if let Ok(guid) = service.Uuid() { let service_uuid = Uuid::from_u128(guid.to_u128()); if required_services.contains(&service_uuid) { - debug!("Found matching service: {:?}", service_uuid); + trace!("Found matching service: {:?}", service_uuid); accept_device = true; break; } @@ -228,7 +228,7 @@ impl Central for Adapter { } if !accept_device { - debug!("Device does not match service filter, skipping"); + trace!("Device does not match service filter, skipping"); continue; } @@ -237,12 +237,12 @@ impl Central for Adapter { Ok(addr) => match (addr as u64).try_into() { Ok(bd_addr) => bd_addr, Err(_) => { - warn!("Failed to convert Bluetooth address: {}", addr); + trace!("Failed to convert Bluetooth address: {}", addr); continue; } }, Err(e) => { - warn!("BluetoothAddress() failed: {:?}", e); + trace!("BluetoothAddress() failed: {:?}", e); continue; } }; @@ -250,11 +250,11 @@ impl Central for Adapter { // Update the peripheral in the manager match manager.peripheral_mut(&address.into()) { Some(_) => { - debug!("Peripheral already exists in manager: {:?}", address); + trace!("Peripheral already exists in manager: {:?}", address); manager.emit(CentralEvent::DeviceDiscovered(address.into())); } None => { - debug!("Adding new peripheral: {:?}", address); + trace!("Adding new peripheral: {:?}", address); let peripheral = Peripheral::new(Arc::downgrade(&manager), address); manager.add_peripheral(peripheral); manager.emit(CentralEvent::DeviceDiscovered(address.into())); @@ -262,7 +262,7 @@ impl Central for Adapter { } } - debug!("Finished scanning for connected peripherals"); + trace!("Finished scanning for connected peripherals"); Ok(()) }