Exemple #1
0
    def did_discover_peripheral(
        self,
        central: CBCentralManager,
        peripheral: CBPeripheral,
        advertisementData: NSDictionary,
        RSSI: NSNumber,
    ) -> None:
        # Note: this function might be called several times for same device.
        # This can happen for instance when an active scan is done, and the
        # second call with contain the data from the BLE scan response.
        # Example a first time with the following keys in advertisementData:
        # ['kCBAdvDataLocalName', 'kCBAdvDataIsConnectable', 'kCBAdvDataChannel']
        # ... and later a second time with other keys (and values) such as:
        # ['kCBAdvDataServiceUUIDs', 'kCBAdvDataIsConnectable', 'kCBAdvDataChannel']
        #
        # i.e it is best not to trust advertisementData for later use and data
        # from it should be copied.
        #
        # This behaviour could be affected by the
        # CBCentralManagerScanOptionAllowDuplicatesKey global setting.

        uuid_string = peripheral.identifier().UUIDString()

        if uuid_string in self.devices:
            device = self.devices[uuid_string]
            # It could be the device did not have a name previously but now it does.
            if peripheral.name():
                device.name = peripheral.name()
        else:
            address = uuid_string
            name = peripheral.name() or None
            details = peripheral
            device = BLEDeviceCoreBluetooth(address,
                                            name,
                                            details,
                                            delegate=self)
            self.devices[uuid_string] = device

        device._update(advertisementData)
        device._update_rssi(RSSI)

        for callback in self.callbacks.values():
            if callback:
                callback(peripheral, advertisementData, RSSI)

        logger.debug(
            "Discovered device {}: {} @ RSSI: {} (kCBAdvData {}) and Central: {}"
            .format(uuid_string, device.name, RSSI, advertisementData.keys(),
                    central))
Exemple #2
0
        def callback(p: CBPeripheral, a: Dict[str, Any], r: int) -> None:
            # update identifiers for scanned device
            self._identifiers.setdefault(p.identifier(), {}).update(a)

            if not self._callback:
                return

            # Process service data
            service_data_dict_raw = a.get("kCBAdvDataServiceData", {})
            service_data = {
                cb_uuid_to_str(k): bytes(v) for k, v in service_data_dict_raw.items()
            }

            # Process manufacturer data into a more friendly format
            manufacturer_binary_data = a.get("kCBAdvDataManufacturerData")
            manufacturer_data = {}
            if manufacturer_binary_data:
                manufacturer_id = int.from_bytes(
                    manufacturer_binary_data[0:2], byteorder="little"
                )
                manufacturer_value = bytes(manufacturer_binary_data[2:])
                manufacturer_data[manufacturer_id] = manufacturer_value

            service_uuids = [
                cb_uuid_to_str(u) for u in a.get("kCBAdvDataServiceUUIDs", [])
            ]

            advertisement_data = AdvertisementData(
                local_name=p.name(),
                manufacturer_data=manufacturer_data,
                service_data=service_data,
                service_uuids=service_uuids,
                platform_data=(p, a, r),
            )

            device = BLEDevice(
                p.identifier().UUIDString(),
                p.name(),
                p,
                r,
                uuids=service_uuids,
                manufacturer_data=manufacturer_data,
                service_data=service_data,
                delegate=self._manager.central_manager.delegate(),
            )

            self._callback(device, advertisement_data)
Exemple #3
0
    def did_disconnect_peripheral(
        self,
        central: CBCentralManager,
        peripheral: CBPeripheral,
        error: Optional[NSError],
    ) -> None:
        logger.debug("Peripheral Device disconnected!")

        future = self._disconnect_futures.pop(peripheral.identifier(), None)
        if future is not None:
            if error is not None:
                future.set_exception(BleakError(f"disconnect failed: {error}"))
            else:
                future.set_result(None)

        callback = self._disconnect_callbacks.get(peripheral.identifier())
        if callback is not None:
            callback()
Exemple #4
0
 async def _notify_device_found(self, peripheral : CoreBluetooth.CBPeripheral):
     async with self._lock:
         if self._device_found_callback is not None:
             device = self.devices.get(peripheral.identifier().UUIDString())
             if device is None:
                 device = CoreBluetoothDevice(manager=self, cbperipheral=peripheral, queue=self._queue)
                 self.devices[device.identifier] = device
                 if asyncio.iscoroutinefunction(self._device_found_callback):
                     await self._device_found_callback(device)
                 else:
                     self._device_found_callback(device)
Exemple #5
0
 def did_fail_to_connect_peripheral(
     self,
     centralManager: CBCentralManager,
     peripheral: CBPeripheral,
     error: Optional[NSError],
 ) -> None:
     future = self._connect_futures.pop(peripheral.identifier(), None)
     if future is not None:
         if error is not None:
             future.set_exception(BleakError(f"failed to connect: {error}"))
         else:
             future.set_result(False)
Exemple #6
0
 async def connect(
     self,
     peripheral: CBPeripheral,
     disconnect_callback: DisconnectCallback,
     timeout=10.0,
 ) -> None:
     try:
         self._disconnect_callbacks[
             peripheral.identifier()] = disconnect_callback
         future = self.event_loop.create_future()
         self._connect_futures[peripheral.identifier()] = future
         self.central_manager.connectPeripheral_options_(peripheral, None)
         await asyncio.wait_for(future, timeout=timeout)
     except asyncio.TimeoutError:
         logger.debug(f"Connection timed out after {timeout} seconds.")
         del self._disconnect_callbacks[peripheral.identifier()]
         future = self.event_loop.create_future()
         self._disconnect_futures[peripheral.identifier()] = future
         self.central_manager.cancelPeripheralConnection_(peripheral)
         await future
         raise
Exemple #7
0
    def did_read_rssi(self, peripheral: CBPeripheral, rssi: NSNumber,
                      error: Optional[NSError]) -> None:
        future = self._read_rssi_futures.pop(peripheral.identifier(), None)

        if not future:
            logger.warning("Unexpected event did_read_rssi")
            return

        if error is not None:
            exception = BleakError(f"Failed to read RSSI: {error}")
            future.set_exception(exception)
        else:
            future.set_result(rssi)
Exemple #8
0
 def peripheralDidUpdateRSSI_error_(self: PeripheralDelegate,
                                    peripheral: CBPeripheral,
                                    error: Optional[NSError]) -> None:
     logger.debug("peripheralDidUpdateRSSI_error_")
     self._event_loop.call_soon_threadsafe(self.did_read_rssi, peripheral,
                                           peripheral.RSSI(), error)
Exemple #9
0
 async def _peripheral_did_disconnect(
         self, peripheral: CoreBluetooth.CBPeripheral):
     device = self.devices.get(peripheral.identifier().UUIDString())
     if device:
         device._did_disconnect()
Exemple #10
0
 def peripheralDidUpdateName_(self, peripheral: CBPeripheral) -> None:
     logger.debug("peripheralDidUpdateName_")
     self._event_loop.call_soon_threadsafe(self.did_update_name, peripheral,
                                           peripheral.name())
Exemple #11
0
 def did_connect_peripheral(self, central: CBCentralManager,
                            peripheral: CBPeripheral) -> None:
     future = self._connect_futures.pop(peripheral.identifier(), None)
     if future is not None:
         future.set_result(True)
Exemple #12
0
 async def disconnect(self, peripheral: CBPeripheral) -> None:
     future = self.event_loop.create_future()
     self._disconnect_futures[peripheral.identifier()] = future
     self.central_manager.cancelPeripheralConnection_(peripheral)
     await future
     del self._disconnect_callbacks[peripheral.identifier()]