btle 0.1.4

Lightweight Bluetooth Low Energy Drivers. WIP and very not stable yet!! Designed for https://github.com/AndrewGi/BluetoothMeshRust
Documentation
use crate::le::scan;
use crate::le::scan::ScanType;
use crate::{
    asyncs::{self, future::ready, stream::StreamExt, sync::mpsc::TrySendError},
    bytes::Storage,
    bytes::ToFromBytesEndian,
    error::IOError,
    le::adapter::Error,
    le::advertisement::{
        AdType, RawAdStructureBuffer, RawAdvertisement, StaticAdvBuffer, StaticAdvStructBuf,
    },
    le::report::{AddressType, EventType, ReportInfo},
    le::scan::{Observer, ScanParameters},
    BTAddress, BoxFuture, BoxStream, RSSI,
};
use core::{
    convert::{TryFrom, TryInto},
    ops::Deref,
    pin::Pin,
    task::{Context, Poll},
};
use futures_core::Stream;
use winrt::{
    windows::{
        devices::bluetooth::advertisement::{
            BluetoothLEAdvertisementDataSection, BluetoothLEAdvertisementFilter,
            BluetoothLEAdvertisementReceivedEventArgs, BluetoothLEAdvertisementType,
            BluetoothLEAdvertisementWatcher, BluetoothLEScanningMode,
        },
        storage::streams::DataReader,
    },
    ComPtr, RtDefaultConstructible,
};

/// Wrapper around `winrt`'s `BluetoothLEAdvertisementWatcher`.
pub struct Watcher {
    watcher: ComPtr<BluetoothLEAdvertisementWatcher>,
    output: asyncs::sync::mpsc::Receiver<ReportInfo>,
}
impl Watcher {
    const DEFAULT_CAPACITY: usize = 16;
    fn data_section_to_raw_ad_struct(
        data_sec: &BluetoothLEAdvertisementDataSection,
    ) -> Result<RawAdStructureBuffer, winrt::Error> {
        let ad_type = AdType::try_from(data_sec.get_data_type()?).expect("bad advertisement part");
        let reader = DataReader::from_buffer(&data_sec.get_data()?.expect("missing data"))?
            .expect("reader should exist from IBuffer");
        let len: u32 = reader.deref().deref().get_unconsumed_buffer_length()?;
        let mut buf = StaticAdvStructBuf::with_size(len as usize);
        reader.read_bytes(buf.as_mut())?;
        Ok(RawAdStructureBuffer::new(ad_type, buf))
    }
    fn advertisement_type_to_event_type(t: BluetoothLEAdvertisementType) -> EventType {
        match t {
            BluetoothLEAdvertisementType::ConnectableUndirected => EventType::AdvInd,
            BluetoothLEAdvertisementType::ConnectableDirected => EventType::AdvDirectInd,
            BluetoothLEAdvertisementType::ScannableUndirected => EventType::AdvScanInd,
            BluetoothLEAdvertisementType::NonConnectableUndirected => EventType::AdvNonconnInd,
            BluetoothLEAdvertisementType::ScanResponse => EventType::ScanRsp,
            _ => unreachable!("all bluetooth le advertisements types"),
        }
    }
    fn u64_to_bluetooth_address(u: u64) -> BTAddress {
        BTAddress::new(&u.to_bytes_le()[..BTAddress::LEN])
    }
    fn event_args_to_report_info(
        args: &BluetoothLEAdvertisementReceivedEventArgs,
    ) -> Result<ReportInfo, winrt::Error> {
        Ok(ReportInfo {
            event_type: Self::advertisement_type_to_event_type(args.get_advertisement_type()?),
            address_type: AddressType::PublicDevice,
            address: Self::u64_to_bluetooth_address(args.get_bluetooth_address()?),
            data: {
                let mut out = RawAdvertisement::default();
                for data_sec in args
                    .get_advertisement()?
                    .expect("missing advertisement")
                    .get_data_sections()?
                    .expect("there should be data sections")
                    .into_iter()
                {
                    if let Some(data_sec) = &data_sec {
                        out.insert(&Self::data_section_to_raw_ad_struct(&data_sec)?)
                            .map_err(|_| winrt::Error::OutOfMemory)?;
                    }
                }
                out
            },
            rssi: Some(RSSI::new(
                args.get_raw_signal_strength_in_dbm()?
                    .try_into()
                    .expect("invalid rssi"),
            )),
        })
    }
    pub fn new() -> Result<Watcher, IOError> {
        Self::with_capacity(Self::DEFAULT_CAPACITY)
    }
    pub fn with_capacity(capacity: usize) -> Result<Watcher, IOError> {
        let filter = BluetoothLEAdvertisementFilter::new();
        let watcher = BluetoothLEAdvertisementWatcher::create(&filter)?;
        let (mut tx, rx) = asyncs::sync::mpsc::channel(capacity);
        watcher.add_received(&winrt::windows::foundation::TypedEventHandler::new(
            move |_sender, args: *mut BluetoothLEAdvertisementReceivedEventArgs| {
                // Unsafe just to dereference the Event Pointer.
                let args = unsafe { &*args };
                match tx.try_send(Self::event_args_to_report_info(args)?) {
                    Ok(_) => {}
                    Err(e) => match e {
                        TrySendError::Full(_) => {}
                        TrySendError::Closed(_) => return Err(winrt::Error::ObjectClosed),
                    },
                }
                Ok(())
            },
        ))?;
        Ok(Watcher {
            watcher,
            output: rx,
        })
    }
    pub fn set_scan_enable(&mut self, is_enabled: bool) -> Result<(), IOError> {
        if is_enabled {
            self.watcher.start()?;
        } else {
            self.watcher.stop()?;
        }
        Ok(())
    }
    pub fn set_scanning_mode(&mut self, scanning_mode: scan::ScanType) -> Result<(), IOError> {
        let mode = match scanning_mode {
            ScanType::Passive => BluetoothLEScanningMode::Passive,
            ScanType::Active => BluetoothLEScanningMode::Active,
        };
        self.watcher.set_scanning_mode(mode)?;
        Ok(())
    }
    pub fn advertisement_stream(&mut self) -> AdvertisementStream<'_> {
        AdvertisementStream::new(self)
    }
}
impl Observer for Watcher {
    fn set_scan_parameters<'a>(
        &'a mut self,
        scan_parameters: ScanParameters,
    ) -> BoxFuture<'a, Result<(), Error>> {
        Box::pin(ready(
            self.set_scanning_mode(scan_parameters.scan_type)
                .map_err(Error::IOError),
        ))
    }

    fn set_scan_enable<'a>(
        &'a mut self,
        is_enabled: bool,
        _filter_duplicates: bool,
    ) -> BoxFuture<'a, Result<(), Error>> {
        Box::pin(ready(
            self.set_scan_enable(is_enabled).map_err(Error::IOError),
        ))
    }

    fn advertisement_stream<'a>(
        &'a mut self,
    ) -> BoxStream<'a, Result<ReportInfo<StaticAdvBuffer>, Error>> {
        Box::pin(self.advertisement_stream().map(|r| Ok(r)))
    }
}
pub struct AdvertisementStream<'a>(&'a mut Watcher);
impl<'a> AdvertisementStream<'a> {
    pub fn new(watcher: &'a mut Watcher) -> Self {
        Self(watcher)
    }
}
impl<'a> Stream for AdvertisementStream<'a> {
    type Item = ReportInfo;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.0.output).poll_next(cx)
    }
}