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))
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)
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()
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)
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)
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
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)
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)
async def _peripheral_did_disconnect( self, peripheral: CoreBluetooth.CBPeripheral): device = self.devices.get(peripheral.identifier().UUIDString()) if device: device._did_disconnect()
def peripheralDidUpdateName_(self, peripheral: CBPeripheral) -> None: logger.debug("peripheralDidUpdateName_") self._event_loop.call_soon_threadsafe(self.did_update_name, peripheral, peripheral.name())
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)
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()]