diff --git a/Cargo.toml b/Cargo.toml index ed77ba06..b2f467d4 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_Enumeration", "Devices_Bluetooth", "Devices_Bluetooth_GenericAttributeProfile", "Devices_Bluetooth_Advertisement", "Devices_Radios", "Foundation_Collections", "Foundation", "Storage_Streams"] } windows-future = "0.2.0" [dev-dependencies] diff --git a/examples/adapters.rs b/examples/adapters.rs new file mode 100644 index 00000000..a6dfee76 --- /dev/null +++ b/examples/adapters.rs @@ -0,0 +1,22 @@ +// See the "macOS permissions note" in README.md before running this on macOS +// Big Sur or later. + +use btleplug::api::{ + Central, Manager as _, +}; +use btleplug::platform::Manager; + + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + pretty_env_logger::init(); + + let manager = Manager::new().await?; + + for adapter in manager.adapters().await.unwrap() { + println!("Info: {:?}", adapter.adapter_info().await.unwrap()); + println!("Mac: {:?}", adapter.adapter_mac().await.unwrap()); + } + + Ok(()) +} diff --git a/src/api/mod.rs b/src/api/mod.rs index bb3acf68..ad2ce3d4 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -375,6 +375,9 @@ pub trait Central: Send + Sync + Clone { /// be useful for debug logs. async fn adapter_info(&self) -> Result; + /// Get information about the Bluetooth adapter mac address. + async fn adapter_mac(&self) -> Result; + /// Get information about the Bluetooth adapter state. async fn adapter_state(&self) -> Result; } diff --git a/src/bluez/adapter.rs b/src/bluez/adapter.rs index c2f67582..8a11e322 100644 --- a/src/bluez/adapter.rs +++ b/src/bluez/adapter.rs @@ -1,5 +1,5 @@ use super::peripheral::{Peripheral, PeripheralId}; -use crate::api::{Central, CentralEvent, CentralState, ScanFilter}; +use crate::api::{BDAddr, Central, CentralEvent, CentralState, ScanFilter}; use crate::{Error, Result}; use async_trait::async_trait; use bluez_async::{ @@ -113,6 +113,11 @@ impl Central for Adapter { Ok(format!("{} ({})", adapter_info.id, adapter_info.modalias)) } + async fn adapter_mac(&self) -> Result { + let adapter_info = self.session.get_adapter_info(&self.adapter).await?; + Ok(adapter_info.mac_address.into()) + } + async fn adapter_state(&self) -> Result { let mut powered = false; if let Ok(info) = self.session.get_adapter_info(&self.adapter).await { diff --git a/src/bluez/mod.rs b/src/bluez/mod.rs index 88efffa9..842262b9 100644 --- a/src/bluez/mod.rs +++ b/src/bluez/mod.rs @@ -1,3 +1,4 @@ pub mod adapter; pub mod manager; pub mod peripheral; +pub mod utils; \ No newline at end of file diff --git a/src/bluez/peripheral.rs b/src/bluez/peripheral.rs index d2803bd7..aff5fa92 100644 --- a/src/bluez/peripheral.rs +++ b/src/bluez/peripheral.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use bluez_async::{ BluetoothEvent, BluetoothSession, CharacteristicEvent, CharacteristicFlags, CharacteristicId, - CharacteristicInfo, DescriptorInfo, DeviceId, DeviceInfo, MacAddress, ServiceInfo, + CharacteristicInfo, DescriptorInfo, DeviceId, DeviceInfo, ServiceInfo, WriteOptions, }; use futures::future::{join_all, ready}; @@ -328,12 +328,6 @@ impl From for bluez_async::WriteType { } } -impl From for BDAddr { - fn from(mac_address: MacAddress) -> Self { - <[u8; 6]>::into(mac_address.into()) - } -} - impl From for PeripheralId { fn from(device_id: DeviceId) -> Self { PeripheralId(device_id) diff --git a/src/bluez/utils.rs b/src/bluez/utils.rs new file mode 100644 index 00000000..2fd9e2e6 --- /dev/null +++ b/src/bluez/utils.rs @@ -0,0 +1,9 @@ +use bluez_async::MacAddress; + +use crate::api::BDAddr; + +impl From for BDAddr { + fn from(mac_address: MacAddress) -> Self { + <[u8; 6]>::into(mac_address.into()) + } +} \ No newline at end of file diff --git a/src/corebluetooth/adapter.rs b/src/corebluetooth/adapter.rs index b8626bd1..00646e57 100644 --- a/src/corebluetooth/adapter.rs +++ b/src/corebluetooth/adapter.rs @@ -6,6 +6,7 @@ use super::peripheral::{Peripheral, PeripheralId}; use crate::api::{Central, CentralEvent, CentralState, ScanFilter}; use crate::common::adapter_manager::AdapterManager; use crate::{Error, Result}; +use crate::api::BDAddr; use async_trait::async_trait; use futures::channel::mpsc::{self, Sender}; use futures::sink::SinkExt; @@ -137,6 +138,10 @@ impl Central for Adapter { Ok("CoreBluetooth".to_string()) } + async fn adapter_mac(&self) -> Result { + todo!(); + } + async fn adapter_state(&self) -> Result { let fut = CoreBluetoothReplyFuture::default(); self.sender diff --git a/src/droidplug/adapter.rs b/src/droidplug/adapter.rs index f3d10c90..2320fb53 100644 --- a/src/droidplug/adapter.rs +++ b/src/droidplug/adapter.rs @@ -168,6 +168,10 @@ impl Central for Adapter { self.add(address.0) } + async fn adapter_mac(&self) -> Result { + todo!(); + } + async fn adapter_state(&self) -> Result { Ok(CentralState::Unknown) } diff --git a/src/winrtble/adapter.rs b/src/winrtble/adapter.rs index d840c403..77cc50bf 100644 --- a/src/winrtble/adapter.rs +++ b/src/winrtble/adapter.rs @@ -19,12 +19,13 @@ use crate::{ }; use async_trait::async_trait; use futures::stream::Stream; +use log::debug; use std::convert::TryInto; use std::fmt::{self, Debug, Formatter}; use std::pin::Pin; use std::sync::{Arc, Mutex}; use windows::{ - Devices::Radios::{Radio, RadioState}, + Devices::{Bluetooth::BluetoothAdapter, Radios::{Radio, RadioState}}, Foundation::TypedEventHandler, }; @@ -34,6 +35,7 @@ pub struct Adapter { watcher: Arc>, manager: Arc>, radio: Radio, + adapter: BluetoothAdapter, } // https://github.com/microsoft/windows-rs/blob/master/crates/libs/windows/src/Windows/Devices/Radios/mod.rs @@ -47,7 +49,8 @@ fn get_central_state(radio: &Radio) -> CentralState { } impl Adapter { - pub(crate) fn new(radio: Radio) -> Result { + pub(crate) async fn new(adapter: BluetoothAdapter) -> Result { + let radio = adapter.GetRadioAsync()?.await?; let watcher = Arc::new(Mutex::new(BLEWatcher::new()?)); let manager = Arc::new(AdapterManager::default()); @@ -66,6 +69,7 @@ impl Adapter { watcher, manager, radio, + adapter, }) } } @@ -129,8 +133,13 @@ impl Central for Adapter { } async fn adapter_info(&self) -> Result { - // TODO: Get information about the adapter. - Ok("WinRT".to_string()) + Ok(format!("DeviceId: {}\n", self.adapter.DeviceId()?.to_string())) + } + + async fn adapter_mac(&self) -> Result { + let address = self.adapter.BluetoothAddress()?; + let mac = BDAddr::try_from(address)?; + Ok(mac) } async fn adapter_state(&self) -> Result { diff --git a/src/winrtble/manager.rs b/src/winrtble/manager.rs index e4445f71..a0a809c1 100644 --- a/src/winrtble/manager.rs +++ b/src/winrtble/manager.rs @@ -14,9 +14,11 @@ use super::adapter::Adapter; use crate::{api, Result}; use async_trait::async_trait; -use std::future::IntoFuture; -use windows::Devices::Radios::{Radio, RadioKind}; - +use windows::{ + Devices::Bluetooth::BluetoothAdapter, + Devices::Enumeration::DeviceInformation, + core::HSTRING +}; /// Implementation of [api::Manager](crate::api::Manager). #[derive(Clone, Debug)] pub struct Manager {} @@ -32,11 +34,20 @@ impl api::Manager for Manager { type Adapter = Adapter; async fn adapters(&self) -> Result> { - let radios = Radio::GetRadiosAsync()?.into_future().await?; - radios + // Get the selector for Bluetooth adapters + let selector = BluetoothAdapter::GetDeviceSelector()?; + + // Find all devices that match the selector + let devices = DeviceInformation::FindAllAsyncAqsFilter(&HSTRING::from(selector))?.await?; + + let futures = devices .into_iter() - .filter(|radio| radio.Kind() == Ok(RadioKind::Bluetooth)) - .map(|radio| Adapter::new(radio)) - .collect() + .map(|device| async move { + let device_id = device.Id()?; + let bt_adapter = BluetoothAdapter::FromIdAsync(&device_id)?.await?; + Adapter::new(bt_adapter).await + }); + let adapters = futures::future::try_join_all(futures).await?; + Ok(adapters) } }