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))
Exemple #2
0
    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)
Exemple #3
0
    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
Exemple #4
0
    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)
Exemple #5
0
        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_,
Exemple #6
0
 def __del__(self):
     if objc.macos_available(10, 13):
         self.central_manager.removeObserver_forKeyPath_(self, "isScanning")