Skip to content

Retrieve connected devices #437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
4 changes: 4 additions & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<Self::Peripheral>>;
Expand Down
8 changes: 8 additions & 0 deletions src/corebluetooth/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<Peripheral>> {
Ok(self.manager.peripherals())
}
Expand Down
51 changes: 50 additions & 1 deletion src/corebluetooth/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -399,6 +399,9 @@ pub enum CoreBluetoothMessage {
filter: ScanFilter,
},
StopScanning,
RetrieveConnectedPeripherals {
filter: ScanFilter,
},
ConnectDevice {
peripheral_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1239,6 +1245,49 @@ 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() });
trace!("Discovered connected peripheral: {}", uuid);
let (event_sender, event_receiver) = mpsc::channel(256);
let name: Option<String> = unsafe {
match peripheral.name() {
Some(ns_name) => nsstring_to_string(ns_name.as_ref()),
None => None,
}
};
if !self.peripherals.contains_key(&uuid) {
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<CBUUID *> *` to use for discovery. If the
Expand Down
92 changes: 92 additions & 0 deletions src/winrtble/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -114,6 +118,94 @@ 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();
let mut required_services = filter.clone().services;
required_services.sort();
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;
};
// Verify all services in filter exist on the device.
let mut found_services: Vec<Uuid> = 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.contains(&service_uuid) {
found_services.push(service_uuid);
}
}
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;
}
};
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);
manager.add_peripheral(peripheral);
manager.emit(CentralEvent::DeviceDiscovered(address.into()));
}
}
return Ok(());
}
Ok(())
}

async fn peripherals(&self) -> Result<Vec<Peripheral>> {
Ok(self.manager.peripherals())
}
Expand Down