Example #1
0
class CentralManagerDotNet(CentralManager):
    """The Central Manager Dot Net Class"""
    def __init__(self, loop=None, **kwargs):
        super(CentralManagerDotNet, self).__init__(loop)
        self._device_found_callback = None
        self.devices = {}
        self._watcher = None
        self._device_found_callback = None
        self.service_uuids = None

    async def start_scan(self, callback, service_uuids=[]):
        self._device_found_callback = callback
        self.service_uuids = service_uuids
        self._watcher = BluetoothLEAdvertisementWatcher()
        self._watcher.Received += self._advertisement_received
        self._watcher.Start()

    def _advertisement_received(self, watcher, eventargs):
        if self._watcher == watcher:
            s_uuid = [
                str(i) for i in list(eventargs.Advertisement.ServiceUuids)
            ]
            if self.service_uuids:
                if any(x in self.service_uuids for x in s_uuid):
                    if eventargs.BluetoothAddress not in self.devices:
                        self.devices[eventargs.BluetoothAddress] = {
                            "Address",
                            hex(eventargs.BluetoothAddress),
                        }
                        s = (str(hex(eventargs.BluetoothAddress)))[2:].upper()
                        self._device_found_callback(
                            eventargs.BluetoothAddress,
                            ":".join(a + b for a, b in zip(s[::2], s[1::2])),
                            eventargs.Advertisement.LocalName,
                        )
            else:
                if eventargs.BluetoothAddress not in self.devices:
                    self.devices[eventargs.BluetoothAddress] = {
                        "Address",
                        hex(eventargs.BluetoothAddress),
                    }
                    s = (str(hex(eventargs.BluetoothAddress)))[2:].upper()
                    self._device_found_callback(
                        eventargs.BluetoothAddress,
                        ":".join(a + b for a, b in zip(s[::2], s[1::2])),
                        eventargs.Advertisement.LocalName,
                    )

    async def stop_scan(self):
        """Stop Scan with timeout"""
        self._watcher.Stop()

    #
    async def power_on(self):
        """Power on BLE Adapter"""
        raise NotImplementedError()

    async def power_off(self):
        """Power off BLE Adapter"""
        raise NotImplementedError()
Example #2
0
    async def start(self):
        self.watcher = BluetoothLEAdvertisementWatcher()
        self.watcher.ScanningMode = self._scanning_mode

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

        if self._signal_strength_filter is not None:
            self.watcher.SignalStrengthFilter = self._signal_strength_filter
        if self._advertisement_filter is not None:
            self.watcher.AdvertisementFilter = self._advertisement_filter

        self.watcher.Start()
Example #3
0
    async def start(self):
        self.watcher = BluetoothLEAdvertisementWatcher()
        self.watcher.ScanningMode = self._scanning_mode

        self._received_token = self.watcher.add_Received(
            TypedEventHandler[BluetoothLEAdvertisementWatcher,
                              BluetoothLEAdvertisementReceivedEventArgs, ](
                                  self._received_handler))
        self._stopped_token = self.watcher.add_Stopped(TypedEventHandler[
            BluetoothLEAdvertisementWatcher,
            BluetoothLEAdvertisementWatcherStoppedEventArgs, ](
                self._stopped_handler))

        if self._signal_strength_filter is not None:
            self.watcher.SignalStrengthFilter = self._signal_strength_filter
        if self._advertisement_filter is not None:
            self.watcher.AdvertisementFilter = self._advertisement_filter

        self.watcher.Start()
Example #4
0
    async def start(self):
        self.watcher = BluetoothLEAdvertisementWatcher()
        self.watcher.ScanningMode = self._scanning_mode

        event_loop = asyncio.get_event_loop()

        self._received_token = self.watcher.add_Received(
            TypedEventHandler[BluetoothLEAdvertisementWatcher,
                              BluetoothLEAdvertisementReceivedEventArgs, ](
                                  lambda s, e: event_loop.call_soon_threadsafe(
                                      self._received_handler, s, e)))
        self._stopped_token = self.watcher.add_Stopped(TypedEventHandler[
            BluetoothLEAdvertisementWatcher,
            BluetoothLEAdvertisementWatcherStoppedEventArgs, ](
                lambda s, e: event_loop.call_soon_threadsafe(
                    self._stopped_handler, s, e)))

        if self._signal_strength_filter is not None:
            self.watcher.SignalStrengthFilter = self._signal_strength_filter
        if self._advertisement_filter is not None:
            self.watcher.AdvertisementFilter = self._advertisement_filter

        self.watcher.Start()
Example #5
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
Example #6
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
Example #7
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,
        )
Example #8
0
 async def start_scan(self, callback, service_uuids=[]):
     self._device_found_callback = callback
     self.service_uuids = service_uuids
     self._watcher = BluetoothLEAdvertisementWatcher()
     self._watcher.Received += self._advertisement_received
     self._watcher.Start()
Example #9
0
class BleakScannerDotNet(BaseBleakScanner):
    """The native Windows Bleak BLE Scanner.

    Implemented using `pythonnet <https://pythonnet.github.io/>`_, a package that provides an integration to
    the .NET Common Language Runtime (CLR). Therefore, much of the code below has a distinct C# feel.

    Keyword Args:

        scanning mode (str): Set to ``Passive`` to avoid the ``Active`` scanning mode.

        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.

    """
    def __init__(self, **kwargs):
        super(BleakScannerDotNet, self).__init__(**kwargs)

        self.watcher = None
        self._devices = {}
        self._scan_responses = {}

        self._received_token = None
        self._stopped_token = None

        if "scanning_mode" in kwargs and kwargs["scanning_mode"].lower(
        ) == "passive":
            self._scanning_mode = BluetoothLEScanningMode.Passive
        else:
            self._scanning_mode = BluetoothLEScanningMode.Active

        self._signal_strength_filter = kwargs.get("SignalStrengthFilter", None)
        self._advertisement_filter = kwargs.get("AdvertisementFilter", None)

    def _received_handler(
        self,
        sender: BluetoothLEAdvertisementWatcher,
        event_args: BluetoothLEAdvertisementReceivedEventArgs,
    ):
        if sender == self.watcher:
            logger.debug("Received {0}.".format(
                _format_event_args(event_args)))
            if (event_args.AdvertisementType ==
                    BluetoothLEAdvertisementType.ScanResponse):
                if event_args.BluetoothAddress not in self._scan_responses:
                    self._scan_responses[
                        event_args.BluetoothAddress] = event_args
            else:
                if event_args.BluetoothAddress not in self._devices:
                    self._devices[event_args.BluetoothAddress] = event_args

        if self._callback is None:
            return

        # Get a "BLEDevice" from parse_event args
        device = self.parse_eventargs(event_args)

        # Decode service data
        service_data = {}
        # 0x16 is service data with 16-bit UUID
        for section in event_args.Advertisement.GetSectionsByType(0x16):
            with BleakDataReader(section.Data) as reader:
                data = reader.read()
                service_data[
                    f"0000{data[1]:02x}{data[0]:02x}-0000-1000-8000-00805f9b34fb"] = data[
                        2:]
        # 0x20 is service data with 32-bit UUID
        for section in event_args.Advertisement.GetSectionsByType(0x20):
            with BleakDataReader(section.Data) as reader:
                data = reader.read()
                service_data[
                    f"{data[3]:02x}{data[2]:02x}{data[1]:02x}{data[0]:02x}-0000-1000-8000-00805f9b34fb"] = data[
                        4:]
        # 0x21 is service data with 128-bit UUID
        for section in event_args.Advertisement.GetSectionsByType(0x21):
            with BleakDataReader(section.Data) as reader:
                data = reader.read()
                service_data[str(UUID(bytes=data[15::-1]))] = data[16:]

        # Use the BLEDevice to populate all the fields for the advertisement data to return
        advertisement_data = AdvertisementData(
            local_name=event_args.Advertisement.LocalName,
            manufacturer_data=device.metadata["manufacturer_data"],
            service_data=service_data,
            service_uuids=device.metadata["uuids"],
            platform_data=(sender, event_args),
        )

        self._callback(device, advertisement_data)

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

    async def start(self):
        self.watcher = BluetoothLEAdvertisementWatcher()
        self.watcher.ScanningMode = self._scanning_mode

        event_loop = asyncio.get_event_loop()

        self._received_token = self.watcher.add_Received(
            TypedEventHandler[BluetoothLEAdvertisementWatcher,
                              BluetoothLEAdvertisementReceivedEventArgs, ](
                                  lambda s, e: event_loop.call_soon_threadsafe(
                                      self._received_handler, s, e)))
        self._stopped_token = self.watcher.add_Stopped(TypedEventHandler[
            BluetoothLEAdvertisementWatcher,
            BluetoothLEAdvertisementWatcherStoppedEventArgs, ](
                lambda s, e: event_loop.call_soon_threadsafe(
                    self._stopped_handler, s, e)))

        if self._signal_strength_filter is not None:
            self.watcher.SignalStrengthFilter = self._signal_strength_filter
        if self._advertisement_filter is not None:
            self.watcher.AdvertisementFilter = self._advertisement_filter

        self.watcher.Start()

    async def stop(self):
        self.watcher.Stop()

        if self._received_token:
            self.watcher.remove_Received(self._received_token)
            self._received_token = None
        if self._stopped_token:
            self.watcher.remove_Stopped(self._stopped_token)
            self._stopped_token = None

        self.watcher = None

    async def set_scanning_filter(self, **kwargs):
        """Set a scanning filter for the BleakScanner.

        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.

        """
        if "SignalStrengthFilter" in kwargs:
            # TODO: Handle SignalStrengthFilter parameters
            self._signal_strength_filter = kwargs["SignalStrengthFilter"]
        if "AdvertisementFilter" in kwargs:
            # TODO: Handle AdvertisementFilter parameters
            self._advertisement_filter = kwargs["AdvertisementFilter"]

    async def get_discovered_devices(self) -> List[BLEDevice]:
        found = []
        for event_args in list(self._devices.values()):
            new_device = self.parse_eventargs(event_args)
            if (not new_device.name
                    and event_args.BluetoothAddress in self._scan_responses):
                new_device.name = self._scan_responses[
                    event_args.BluetoothAddress].Advertisement.LocalName
            found.append(new_device)

        return found

    @staticmethod
    def parse_eventargs(event_args):
        bdaddr = _format_bdaddr(event_args.BluetoothAddress)
        uuids = []
        for u in event_args.Advertisement.ServiceUuids:
            uuids.append(u.ToString())
        data = {}
        for m in event_args.Advertisement.ManufacturerData:
            with BleakDataReader(m.Data) as reader:
                data[m.CompanyId] = reader.read()
        local_name = event_args.Advertisement.LocalName
        rssi = event_args.RawSignalStrengthInDBm
        return BLEDevice(bdaddr,
                         local_name,
                         event_args,
                         rssi,
                         uuids=uuids,
                         manufacturer_data=data)

    # Windows specific

    @property
    def status(self) -> int:
        """Get status of the Watcher.

        Returns:

            Aborted 4
            An error occurred during transition or scanning that stopped the watcher due to an error.

            Created 0
            The initial status of the watcher.

            Started 1
            The watcher is started.

            Stopped 3
            The watcher is stopped.

            Stopping 2
            The watcher stop command was issued.

        """
        return self.watcher.Status if self.watcher else None
Example #10
0
class BleakScannerDotNet(BaseBleakScanner):
    """The native Windows Bleak BLE Scanner.

    Implemented using `pythonnet <https://pythonnet.github.io/>`_, a package that provides an integration to
    the .NET Common Language Runtime (CLR). Therefore, much of the code below has a distinct C# feel.

    Keyword Args:

        scanning mode (str): Set to ``Passive`` to avoid the ``Active`` scanning mode.

        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.

    """
    def __init__(self, **kwargs):
        super(BleakScannerDotNet, self).__init__()

        self.watcher = None
        self._devices = {}
        self._scan_responses = {}

        self._callback = None

        self._received_token = None
        self._stopped_token = None

        if "scanning_mode" in kwargs and kwargs["scanning_mode"].lower(
        ) == "passive":
            self._scanning_mode = BluetoothLEScanningMode.Passive
        else:
            self._scanning_mode = BluetoothLEScanningMode.Active

        self._signal_strength_filter = kwargs.get("SignalStrengthFilter", None)
        self._advertisement_filter = kwargs.get("AdvertisementFilter", None)

    def _received_handler(self, sender, event_args):
        if sender == self.watcher:
            logger.debug("Received {0}.".format(
                _format_event_args(event_args)))
            if (event_args.AdvertisementType ==
                    BluetoothLEAdvertisementType.ScanResponse):
                if event_args.BluetoothAddress not in self._scan_responses:
                    self._scan_responses[
                        event_args.BluetoothAddress] = event_args
            else:
                if event_args.BluetoothAddress not in self._devices:
                    self._devices[event_args.BluetoothAddress] = event_args
        if self._callback is not None:
            self._callback(sender, event_args)

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

    async def start(self):
        self.watcher = BluetoothLEAdvertisementWatcher()
        self.watcher.ScanningMode = self._scanning_mode

        self._received_token = self.watcher.add_Received(
            TypedEventHandler[BluetoothLEAdvertisementWatcher,
                              BluetoothLEAdvertisementReceivedEventArgs, ](
                                  self._received_handler))
        self._stopped_token = self.watcher.add_Stopped(TypedEventHandler[
            BluetoothLEAdvertisementWatcher,
            BluetoothLEAdvertisementWatcherStoppedEventArgs, ](
                self._stopped_handler))

        if self._signal_strength_filter is not None:
            self.watcher.SignalStrengthFilter = self._signal_strength_filter
        if self._advertisement_filter is not None:
            self.watcher.AdvertisementFilter = self._advertisement_filter

        self.watcher.Start()

    async def stop(self):
        self.watcher.Stop()

        if self._received_token:
            self.watcher.remove_Received(self._received_token)
            self._received_token = None
        if self._stopped_token:
            self.watcher.remove_Stopped(self._stopped_token)
            self._stopped_token = None

        self.watcher = None

    async def set_scanning_filter(self, **kwargs):
        """Set a scanning filter for the BleakScanner.

        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.

        """
        if "SignalStrengthFilter" in kwargs:
            # TODO: Handle SignalStrengthFilter parameters
            self._signal_strength_filter = kwargs["SignalStrengthFilter"]
        if "AdvertisementFilter" in kwargs:
            # TODO: Handle AdvertisementFilter parameters
            self._advertisement_filter = kwargs["AdvertisementFilter"]

    async def get_discovered_devices(self) -> List[BLEDevice]:
        found = []
        for event_args in list(self._devices.values()):
            new_device = self.parse_eventargs(event_args)
            if (not new_device.name
                    and event_args.BluetoothAddress in self._scan_responses):
                new_device.name = self._scan_responses[
                    event_args.BluetoothAddress].Advertisement.LocalName
            found.append(new_device)

        return found

    @staticmethod
    def parse_eventargs(event_args):
        bdaddr = _format_bdaddr(event_args.BluetoothAddress)
        uuids = []
        for u in event_args.Advertisement.ServiceUuids:
            uuids.append(u.ToString())
        data = {}
        for m in event_args.Advertisement.ManufacturerData:
            with BleakDataReader(m.Data) as reader:
                data[m.CompanyId] = reader.read()
        local_name = event_args.Advertisement.LocalName
        return BLEDevice(bdaddr,
                         local_name,
                         event_args,
                         uuids=uuids,
                         manufacturer_data=data)

    def register_detection_callback(self, callback: Callable):
        """Set a function to act as Received Event Handler.

        Documentation for the Event Handler:
        https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcher.received

        Args:
            callback: Function accepting two arguments:
             sender (``Windows.Devices.Bluetooth.AdvertisementBluetoothLEAdvertisementWatcher``) and
             eventargs (``Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs``)

        """
        self._callback = callback

    # Windows specific

    @property
    def status(self) -> int:
        """Get status of the Watcher.

        Returns:

            Aborted 4
            An error occurred during transition or scanning that stopped the watcher due to an error.

            Created 0
            The initial status of the watcher.

            Started 1
            The watcher is started.

            Stopped 3
            The watcher is stopped.

            Stopping 2
            The watcher stop command was issued.

        """
        return self.watcher.Status if self.watcher else None

    @classmethod
    async def find_device_by_address(cls,
                                     device_identifier: str,
                                     timeout: float = 10.0,
                                     **kwargs) -> Union[BLEDevice, None]:
        """A convenience method for obtaining a ``BLEDevice`` object specified by Bluetooth address.

        Args:

            device_identifier (str): The Bluetooth address of the Bluetooth peripheral.

            timeout (float): Optional timeout to wait for detection of specified peripheral
              before giving up. Defaults to 10.0 seconds.

        Keyword Args:

          scanning mode (str): Set to ``Passive`` to avoid the ``Active`` scanning mode.

          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.

        Returns:

            The ``BLEDevice`` sought or ``None`` if not detected.

        """

        ulong_id = int(device_identifier.replace(":", ""), 16)
        loop = asyncio.get_event_loop()
        stop_scanning_event = asyncio.Event()
        scanner = cls(timeout=timeout)

        def stop_if_detected(sender, event_args):
            if event_args.BluetoothAddress == ulong_id:
                loop.call_soon_threadsafe(stop_scanning_event.set)

        return await scanner._find_device_by_address(device_identifier,
                                                     stop_scanning_event,
                                                     stop_if_detected, timeout)
Example #11
0
class BleakScannerDotNet(BaseBleakScanner):
    """The native Windows Bleak BLE Scanner.

    Implemented using `pythonnet <https://pythonnet.github.io/>`_, a package that provides an integration to the .NET
    Common Language Runtime (CLR). Therefore, much of the code below has a distinct C# feel.

    Args:
        loop (asyncio.events.AbstractEventLoop): The event loop to use.

    Keyword Args:
        scanning mode (str): Set to "Passive" to avoid the "Active" scanning mode.
        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.

    """
    def __init__(self, loop: AbstractEventLoop = None, **kwargs):
        super(BleakScannerDotNet, self).__init__(loop, **kwargs)

        self.watcher = None
        self._devices = {}
        self._scan_responses = {}

        self._callback = None

        if "scanning_mode" in kwargs and kwargs["scanning_mode"].lower(
        ) == "passive":
            self._scanning_mode = BluetoothLEScanningMode.Passive
        else:
            self._scanning_mode = BluetoothLEScanningMode.Active

        self._signal_strength_filter = kwargs.get("SignalStrengthFilter", None)
        self._advertisement_filter = kwargs.get("AdvertisementFilter", None)

    def AdvertisementWatcher_Received(self, sender, e):
        if sender == self.watcher:
            logger.debug("Received {0}.".format(_format_event_args(e)))
            if e.AdvertisementType == BluetoothLEAdvertisementType.ScanResponse:
                if e.BluetoothAddress not in self._scan_responses:
                    self._scan_responses[e.BluetoothAddress] = e
            else:
                if e.BluetoothAddress not in self._devices:
                    self._devices[e.BluetoothAddress] = e
        if self._callback is not None:
            self._callback(sender, e)

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

    async def start(self):
        self.watcher = BluetoothLEAdvertisementWatcher()
        self.watcher.ScanningMode = self._scanning_mode

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

        if self._signal_strength_filter is not None:
            self.watcher.SignalStrengthFilter = self._signal_strength_filter
        if self._advertisement_filter is not None:
            self.watcher.AdvertisementFilter = self._advertisement_filter

        self.watcher.Start()

    async def stop(self):
        self.watcher.Stop()

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

    async def set_scanning_filter(self, **kwargs):
        if "SignalStrengthFilter" in kwargs:
            # TODO: Handle SignalStrengthFilter parameters
            self._signal_strength_filter = kwargs["SignalStrengthFilter"]
        if "AdvertisementFilter" in kwargs:
            # TODO: Handle AdvertisementFilter parameters
            self._advertisement_filter = kwargs["AdvertisementFilter"]

    async def get_discovered_devices(self) -> List[BLEDevice]:
        found = []
        for event_args in list(self._devices.values()):
            new_device = self.parse_eventargs(event_args)
            if ((not new_device.name or new_device.name == "Unknown")
                    and event_args.BluetoothAddress in self._scan_responses):
                new_device.name = self._scan_responses[
                    event_args.BluetoothAddress].Advertisement.LocalName
            found.append(new_device)

        return found

    @staticmethod
    def parse_eventargs(event_args):
        bdaddr = _format_bdaddr(event_args.BluetoothAddress)
        uuids = []
        for u in event_args.Advertisement.ServiceUuids:
            uuids.append(u.ToString())
        data = {}
        for m in event_args.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 = event_args.Advertisement.LocalName
        return BLEDevice(bdaddr,
                         local_name,
                         event_args,
                         uuids=uuids,
                         manufacturer_data=data)

    def register_detection_callback(self, callback: Callable):
        """Set a function to act as Received Event Handler.

        Documentation for the Event Handler:
        https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcher.received

        Args:
            callback: Function accepting two arguments:
             sender (Windows.Devices.Bluetooth.AdvertisementBluetoothLEAdvertisementWatcher) and
             eventargs (Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs)

        """
        self._callback = callback

    # Windows specific

    @property
    def status(self) -> int:
        """Get status of the Watcher.

        Returns:

            Aborted 4
            An error occurred during transition or scanning that stopped the watcher due to an error.

            Created 0
            The initial status of the watcher.

            Started 1
            The watcher is started.

            Stopped 3
            The watcher is stopped.

            Stopping 2
            The watcher stop command was issued.

        """
        return self.watcher.Status if self.watcher else None
Example #12
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')