def test_macos_available(self): self.assertFalse(objc.macos_available(11, 20, 20)) with open("/System/Library/CoreServices/SystemVersion.plist", "rb") as fp: if hasattr(plistlib, "load"): pl = plistlib.load(fp) else: pl = plistlib.readPlist(fp) version = (list(map(int, pl["ProductVersion"].split("."))) + [0])[:3] self.assertTrue(objc.macos_available(*version)) self.assertTrue(objc.macos_available(*version[:2])) self.assertFalse( objc.macos_available(version[0] + 1, version[1], version[2])) self.assertFalse( objc.macos_available(version[0], version[1] + 1, version[2])) self.assertFalse( objc.macos_available(version[0], version[1], version[2] + 1)) self.assertTrue( objc.macos_available(version[0], version[1] - 1, version[2])) self.assertTrue(objc.macos_available(version[0], version[1] - 1, 0)) self.assertTrue(objc.macos_available(version[0], version[1] - 1)) if version[2]: self.assertTrue( objc.macos_available(version[0], version[1], version[2] - 1))
async def stop_scan(self) -> None: self.central_manager.stopScan() # The `isScanning` property was added in macOS 10.13, so before that # just waiting some will have to do. if objc.macos_available(10, 13): event = asyncio.Event() self._did_stop_scanning_event = event if self.central_manager.isScanning(): await event.wait() else: await asyncio.sleep(0.1)
def init(self) -> Optional["CentralManagerDelegate"]: """macOS init function for NSObject""" self = objc.super(CentralManagerDelegate, self).init() if self is None: return None self.event_loop = asyncio.get_event_loop() self._connect_futures: Dict[NSUUID, asyncio.Future] = {} self.devices: Dict[str, BLEDeviceCoreBluetooth] = {} self.callbacks: Dict[int, Callable[[CBPeripheral, Dict[str, Any], int], None]] = {} self._disconnect_callbacks: Dict[NSUUID, DisconnectCallback] = {} self._disconnect_futures: Dict[NSUUID, asyncio.Future] = {} self._did_update_state_event = threading.Event() self.central_manager = CBCentralManager.alloc( ).initWithDelegate_queue_( self, dispatch_queue_create(b"bleak.corebluetooth", DISPATCH_QUEUE_SERIAL)) # according to CoreBluetooth docs, it is not valid to call CBCentral # methods until the centralManagerDidUpdateState_() delegate method # is called and the current state is CBManagerStatePoweredOn. # It doesn't take long for the callback to occur, so we should be able # to do a blocking wait here without anyone complaining. self._did_update_state_event.wait(1) if self.central_manager.state() == CBManagerStateUnsupported: raise BleakError("BLE is unsupported") if self.central_manager.state() != CBManagerStatePoweredOn: raise BleakError("Bluetooth device is turned off") # isScanning property was added in 10.13 if objc.macos_available(10, 13): self.central_manager.addObserver_forKeyPath_options_context_( self, "isScanning", NSKeyValueObservingOptionNew, 0) self._did_start_scanning_event: Optional[asyncio.Event] = None self._did_stop_scanning_event: Optional[asyncio.Event] = None return self
async def start_scan(self, scan_options) -> None: # remove old self.devices = {} service_uuids = None if "service_uuids" in scan_options: service_uuids_str = scan_options["service_uuids"] service_uuids = NSArray.alloc().initWithArray_( list(map(CBUUID.UUIDWithString_, service_uuids_str))) self.central_manager.scanForPeripheralsWithServices_options_( service_uuids, None) # The `isScanning` property was added in macOS 10.13, so before that # just waiting some will have to do. if objc.macos_available(10, 13): event = asyncio.Event() self._did_start_scanning_event = event if not self.central_manager.isScanning(): await event.wait() else: await asyncio.sleep(0.1)
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) # peripheralDidUpdateRSSI:error: was deprecated and replaced with # peripheral:didReadRSSI:error: in macOS 10.13 if objc.macos_available(10, 13): def peripheral_didReadRSSI_error_( self: PeripheralDelegate, peripheral: CBPeripheral, rssi: NSNumber, error: Optional[NSError], ) -> None: logger.debug("peripheral_didReadRSSI_error_") self._event_loop.call_soon_threadsafe(self.did_read_rssi, peripheral, rssi, error) objc.classAddMethod( PeripheralDelegate, b"peripheral:didReadRSSI:error:", peripheral_didReadRSSI_error_,
def __del__(self): if objc.macos_available(10, 13): self.central_manager.removeObserver_forKeyPath_(self, "isScanning")