Ejemplo n.º 1
0
async def discover(timeout: float = 5.0, **kwargs) -> List[BLEDevice]:
    """Perform a Bluetooth LE Scan using Windows.Devices.Bluetooth.Advertisement

    Args:
        timeout (float): Time to scan for.

    Keyword Args:
        SignalStrengthFilter (Windows.Devices.Bluetooth.BluetoothSignalStrengthFilter): A
          BluetoothSignalStrengthFilter object used for configuration of Bluetooth
          LE advertisement filtering that uses signal strength-based filtering.
        AdvertisementFilter (Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFilter): A
          BluetoothLEAdvertisementFilter object used for configuration of Bluetooth LE
          advertisement filtering that uses payload section-based filtering.
        string_output (bool): If set to false, ``discover`` returns .NET
            device objects instead.

    Returns:
        List of strings or objects found.

    """
    signal_strength_filter = kwargs.get("SignalStrengthFilter", None)
    advertisement_filter = kwargs.get("AdvertisementFilter", None)

    watcher = BluetoothLEAdvertisementWatcher()

    devices = {}
    scan_responses = {}

    def _format_bdaddr(a):
        return ":".join("{:02X}".format(x)
                        for x in a.to_bytes(6, byteorder="big"))

    def _format_event_args(e):
        try:
            return "{0}: {1}".format(
                _format_bdaddr(e.BluetoothAddress),
                e.Advertisement.LocalName or "Unknown",
            )
        except Exception:
            return e.BluetoothAddress

    def AdvertisementWatcher_Received(sender, e):
        if sender == watcher:
            logger.debug("Received {0}.".format(_format_event_args(e)))
            if e.AdvertisementType == BluetoothLEAdvertisementType.ScanResponse:
                if e.BluetoothAddress not in scan_responses:
                    scan_responses[e.BluetoothAddress] = e
            else:
                if e.BluetoothAddress not in devices:
                    devices[e.BluetoothAddress] = e

    def AdvertisementWatcher_Stopped(sender, e):
        if sender == watcher:
            logger.debug("{0} devices found. Watcher status: {1}.".format(
                len(devices), watcher.Status))

    watcher.Received += AdvertisementWatcher_Received
    watcher.Stopped += AdvertisementWatcher_Stopped

    watcher.ScanningMode = BluetoothLEScanningMode.Active

    if signal_strength_filter is not None:
        watcher.SignalStrengthFilter = signal_strength_filter
    if advertisement_filter is not None:
        watcher.AdvertisementFilter = advertisement_filter

    # Watcher works outside of the Python process.
    watcher.Start()
    await asyncio.sleep(timeout)
    watcher.Stop()

    try:
        watcher.Received -= AdvertisementWatcher_Received
        watcher.Stopped -= AdvertisementWatcher_Stopped
    except Exception as e:
        logger.debug("Could not remove event handlers: {0}...".format(e))

    found = []
    for d in list(devices.values()):
        bdaddr = _format_bdaddr(d.BluetoothAddress)
        uuids = []
        for u in d.Advertisement.ServiceUuids:
            uuids.append(u.ToString())
        data = {}
        for m in d.Advertisement.ManufacturerData:
            with BleakDataReader(m.Data) as reader:
                data[m.CompanyId] = reader.read()
        local_name = d.Advertisement.LocalName
        if not local_name and d.BluetoothAddress in scan_responses:
            local_name = scan_responses[
                d.BluetoothAddress].Advertisement.LocalName
        found.append(
            BLEDevice(
                bdaddr,
                local_name,
                d,
                uuids=uuids,
                manufacturer_data=data,
            ))

    return found
Ejemplo n.º 2
0
async def discover_single(target: str = None,
                          callback: callable = None,
                          loop: AbstractEventLoop = None) -> BLEDevice:
    """ Perform a Bluetooth LE Scan, and update the advertisement information for a specific target

    Args:
        target (str): Target device Bluetooth MAC address
        queue (AsyncIO Queue): The queue to write updates to
        loop (Event Loop): The event loop to use
    
    Returns:
        Nothing
    """
    loop = loop if loop else asyncio.get_event_loop()

    watcher = BluetoothLEAdvertisementWatcher()

    update_count = 0

    def _format_bdaddr(a):
        return ":".join("{:02X}".format(x)
                        for x in a.to_bytes(6, byteorder="big"))

    def _format_event_args(e):
        try:
            return "{0}: {1}".format(
                _format_bdaddr(e.BluetoothAddress),
                e.Advertisement.LocalName or "Unknown",
            )
        except Exception:
            return e.BluetoothAddress

    def AdvertisementWatcher_Received(sender, e):
        if sender == watcher:
            if _format_bdaddr(e.BluetoothAddress) == target:
                device = format_advertisement(e)
                callback(device)

    def AdvertisementWatcher_Stopped(sender, e):
        if sender == watcher:
            logger.debug("{0} updates received. Watcher status: {1}.".format(
                update_count, watcher.Status))

    watcher.Received += AdvertisementWatcher_Received
    watcher.Stopped += AdvertisementWatcher_Stopped

    watcher.ScanningMode = BluetoothLEScanningMode.Active

    # Watcher works outside of the Python process.
    watcher.Start()

    #await asyncio.sleep(timeout, loop=loop)
    #watcher.Stop()

    def format_advertisement(d):
        bdaddr = _format_bdaddr(d.BluetoothAddress)
        uuids = []
        for u in d.Advertisement.ServiceUuids:
            uuids.append(u.ToString())
        data = {}
        for m in d.Advertisement.ManufacturerData:
            md = IBuffer(m.Data)
            b = Array.CreateInstance(Byte, md.Length)
            reader = DataReader.FromBuffer(md)
            reader.ReadBytes(b)
            data[m.CompanyId] = bytes(b)
        local_name = d.Advertisement.LocalName
        return BLEDevice(
            bdaddr,
            local_name,
            d,
            uuids=uuids,
            manufacturer_data=data,
        )
Ejemplo n.º 3
0
async def discover(timeout: float = 5.0,
                   loop: AbstractEventLoop = None,
                   **kwargs) -> List[BLEDevice]:
    """Perform a Bluetooth LE Scan using Windows.Devices.Bluetooth.Advertisement

    Args:
        timeout (float): Time to scan for.
        loop (Event Loop): The event loop to use.

    Keyword Args:
        string_output (bool): If set to false, ``discover`` returns .NET
            device objects instead.

    Returns:
        List of strings or objects found.

    """
    loop = loop if loop else asyncio.get_event_loop()

    watcher = BluetoothLEAdvertisementWatcher()

    devices = {}
    scan_responses = {}

    def _format_bdaddr(a):
        return ":".join("{:02X}".format(x)
                        for x in a.to_bytes(6, byteorder="big"))

    def _format_event_args(e):
        try:
            return "{0}: {1}".format(
                _format_bdaddr(e.BluetoothAddress),
                e.Advertisement.LocalName or "Unknown",
            )
        except Exception:
            return e.BluetoothAddress

    def AdvertisementWatcher_Received(sender, e):
        if sender == watcher:
            logger.debug("Received {0}.".format(_format_event_args(e)))
            if e.AdvertisementType == BluetoothLEAdvertisementType.ScanResponse:
                if e.BluetoothAddress not in scan_responses:
                    scan_responses[e.BluetoothAddress] = e
            else:
                if e.BluetoothAddress not in devices:
                    devices[e.BluetoothAddress] = e

    def AdvertisementWatcher_Stopped(sender, e):
        if sender == watcher:
            logger.debug("{0} devices found. Watcher status: {1}.".format(
                len(devices), watcher.Status))

    watcher.Received += AdvertisementWatcher_Received
    watcher.Stopped += AdvertisementWatcher_Stopped

    watcher.ScanningMode = BluetoothLEScanningMode.Active

    # Watcher works outside of the Python process.
    watcher.Start()
    await asyncio.sleep(timeout, loop=loop)
    watcher.Stop()

    try:
        watcher.Received -= AdvertisementWatcher_Received
        watcher.Stopped -= AdvertisementWatcher_Stopped
    except Exception as e:
        logger.debug("Could not remove event handlers: {0}...".format(e))

    found = []
    for d in devices.values():
        bdaddr = _format_bdaddr(d.BluetoothAddress)
        uuids = []
        for u in d.Advertisement.ServiceUuids:
            uuids.append(u.ToString())
        data = {}
        for m in d.Advertisement.ManufacturerData:
            md = IBuffer(m.Data)
            b = Array.CreateInstance(Byte, md.Length)
            reader = DataReader.FromBuffer(md)
            reader.ReadBytes(b)
            data[m.CompanyId] = bytes(b)
        local_name = d.Advertisement.LocalName
        if not local_name and d.BluetoothAddress in scan_responses:
            local_name = scan_responses[
                d.BluetoothAddress].Advertisement.LocalName
        found.append(
            BLEDevice(
                bdaddr,
                local_name,
                d,
                uuids=uuids,
                manufacturer_data=data,
            ))

    return found
Ejemplo n.º 4
0
async def scanner(
    outqueue: asyncio.Queue,
    stopevent: asyncio.Event,
    **kwargs
):
    """Perform a continuous Bluetooth LE Scan using Windows.Devices.Bluetooth.Advertisement
    Args:
        outqueue:  outgoing queue
        stopevent: stop event
    """
    logger.info(f'>>> scanner:windows')

    watcher = BluetoothLEAdvertisementWatcher()
    q = queue.Queue(QUEUE_SIZE)

 # -----------------------------------------------------------------------------
    def _format_bdaddr(a):
        return ":".join("{:02X}".format(x) for x in a.to_bytes(6, byteorder="big"))

# -----------------------------------------------------------------------------
    def AdvertisementWatcher_Received(sender, e):
        if sender == watcher:
            # logger.debug("Received {0}.".format(_format_event_args(e)))
            l_bdaddr = _format_bdaddr(e.BluetoothAddress)
            l_uuids = []
            for l_u in e.Advertisement.ServiceUuids:
                l_uuids.append(l_u.ToString())
            l_data = {}
            for l_m in e.Advertisement.ManufacturerData:
                l_md = IBuffer(l_m.Data)
                l_b = Array.CreateInstance(Byte, l_md.Length)
                l_reader = DataReader.FromBuffer(l_md)
                l_reader.ReadBytes(l_b)
                l_data[l_m.CompanyId] = bytes(l_b)
            local_name = e.Advertisement.LocalName
            logger.debug(f'>>> bdaddr:{l_bdaddr} local_name:{local_name} mfdata:{l_data}')
            if q:
                q.put(BLEDevice(
                    l_bdaddr,
                    local_name,
                    e,
                    uuids=l_uuids,
                    manufacturer_data=l_data,
                ))

    def AdvertisementWatcher_Stopped(sender, e):
        if sender == watcher:
            logger.info(f'>>> stopped')

# -----------------------------------------------------------------------------

    watcher.Received += AdvertisementWatcher_Received
    watcher.Stopped += AdvertisementWatcher_Stopped
    watcher.ScanningMode = BluetoothLEScanningMode.Active

    # Watcher works outside of the Python process.
    watcher.Start()
    # communication loop
    while not stopevent.is_set():
        try:
            l_data = q.get_nowait()
            if l_data and outqueue:
                await outqueue.put(l_data)
        except queue.Empty:
            try:
                await asyncio.sleep(0.1)
            except asyncio.CancelledError:
                logger.warning(f'>>> CancelledError')
                break
        except:
            logger.exception(f'>>> exception')
    watcher.Stop()
    await asyncio.sleep(0.1)

    try:
        watcher.Received -= AdvertisementWatcher_Received
        watcher.Stopped -= AdvertisementWatcher_Stopped
        logger.info(f'>>> Event handlers removed')
    except:
        logger.warning(f'>>> Could not remove event handlers')