use crate::{Error, Result, api::ScanFilter, winrtble::utils};
use windows::{Devices::Bluetooth::Advertisement::*, Foundation::TypedEventHandler, core::Ref};
pub type AdvertisementEventHandler =
Box<dyn Fn(&BluetoothLEAdvertisementReceivedEventArgs) -> windows::core::Result<()> + Send>;
#[derive(Debug)]
pub struct BLEWatcher {
watcher: BluetoothLEAdvertisementWatcher,
}
impl From<windows::core::Error> for Error {
fn from(err: windows::core::Error) -> Error {
Error::Other(format!("{:?}", err).into())
}
}
impl BLEWatcher {
pub fn new() -> Result<Self> {
let ad = BluetoothLEAdvertisementFilter::new()?;
let watcher = BluetoothLEAdvertisementWatcher::Create(&ad)?;
Ok(BLEWatcher { watcher })
}
pub fn start(&self, filter: ScanFilter, on_received: AdvertisementEventHandler) -> Result<()> {
let ScanFilter { services } = filter;
let ad = self.watcher.AdvertisementFilter()?.Advertisement()?;
ad.ServiceUuids()?.Clear()?;
self.watcher
.SetScanningMode(BluetoothLEScanningMode::Active)?;
let _ = self.watcher.SetAllowExtendedAdvertisements(true);
let filter_guids: Vec<windows::core::GUID> = services.iter().map(utils::to_guid).collect();
let handler: TypedEventHandler<
BluetoothLEAdvertisementWatcher,
BluetoothLEAdvertisementReceivedEventArgs,
> = TypedEventHandler::new(
move |_sender, args: Ref<BluetoothLEAdvertisementReceivedEventArgs>| {
if let Ok(args) = args.ok() {
if !filter_guids.is_empty() {
if let Ok(ad) = args.Advertisement() {
if let Ok(ad_uuids) = ad.ServiceUuids() {
let count = ad_uuids.Size().unwrap_or(0);
let advertised: Vec<windows::core::GUID> =
(0..count).filter_map(|i| ad_uuids.GetAt(i).ok()).collect();
let all_present =
filter_guids.iter().all(|g| advertised.contains(g));
if !all_present {
return Ok(());
}
}
}
}
on_received(args)?;
}
Ok(())
},
);
self.watcher.Received(&handler)?;
self.watcher.Start()?;
Ok(())
}
pub fn stop(&self) -> Result<()> {
self.watcher.Stop()?;
Ok(())
}
}