Skip to content
This repository has been archived by the owner on Mar 29, 2023. It is now read-only.

Commit

Permalink
Remove corebluetooth global central manager.
Browse files Browse the repository at this point in the history
This removes the global Application() class in the corebluetooth
backend. Instead, a new central manager is created for each scanner
object.

Since an instance of the `Application()` was created on module import,
it could interfere with other code like introspection tools that don't
actually want to run `bleak`.

This also fixes running `bleak` in threads (hbldh#206).
  • Loading branch information
dlech committed Jun 26, 2020
1 parent 56ec40e commit 133879b
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 36 deletions.
16 changes: 0 additions & 16 deletions bleak/backends/corebluetooth/__init__.py
Expand Up @@ -6,22 +6,6 @@
"""

import asyncio
from .CentralManagerDelegate import CentralManagerDelegate
import objc

objc.options.verbose = True


class Application:
"""
This is a temporary application class responsible for running the NSRunLoop
so that events within CoreBluetooth are appropriately handled
"""
def __init__(self):
self.central_manager_delegate = CentralManagerDelegate.alloc().init()


# Restructure this later: Global isn't the prettiest way of doing this...
global CBAPP
CBAPP = Application()
38 changes: 24 additions & 14 deletions bleak/backends/corebluetooth/client.py
Expand Up @@ -13,7 +13,6 @@
from CoreBluetooth import CBCharacteristicWriteWithResponse, CBCharacteristicWriteWithoutResponse

from bleak.backends.client import BaseBleakClient
from bleak.backends.corebluetooth import CBAPP as cbapp
from bleak.backends.corebluetooth.characteristic import (
BleakGATTCharacteristicCoreBluetooth
)
Expand Down Expand Up @@ -76,7 +75,8 @@ async def connect(self, **kwargs) -> bool:

logger.debug("Connecting to BLE device @ {}".format(self.address))

await cbapp.central_manager_delegate.connect_(sought_device[0].details)
manager = self._device_info.manager().delegate()
await manager.connect_(sought_device[0].details)

# Now get services
await self.get_services()
Expand All @@ -85,12 +85,14 @@ async def connect(self, **kwargs) -> bool:

async def disconnect(self) -> bool:
"""Disconnect from the peripheral device"""
await cbapp.central_manager_delegate.disconnect()
manager = self._device_info.manager().delegate()
await manager.disconnect()
return True

async def is_connected(self) -> bool:
"""Checks for current active connection"""
return cbapp.central_manager_delegate.isConnected
manager = self._device_info.manager().delegate()
return manager.isConnected

def set_disconnected_callback(
self, callback: Callable[[BaseBleakClient], None], **kwargs
Expand All @@ -100,8 +102,9 @@ def set_disconnected_callback(
callback: callback to be called on disconnection.
"""
manager = self._device_info.manager().delegate()
self._disconnected_callback = callback
cbapp.central_manager_delegate.disconnected_callback = self._disconnect_callback_client
manager.disconnected_callback = self._disconnect_callback_client

def _disconnect_callback_client(self):
"""
Expand All @@ -124,16 +127,17 @@ async def get_services(self) -> BleakGATTServiceCollection:
return self._services

logger.debug("Retrieving services...")
manager = self._device_info.manager().delegate()
services = (
await cbapp.central_manager_delegate.connected_peripheral_delegate.discoverServices()
await manager.connected_peripheral_delegate.discoverServices()
)

for service in services:
serviceUUID = service.UUID().UUIDString()
logger.debug(
"Retrieving characteristics for service {}".format(serviceUUID)
)
characteristics = await cbapp.central_manager_delegate.connected_peripheral_delegate.discoverCharacteristics_(
characteristics = await manager.connected_peripheral_delegate.discoverCharacteristics_(
service
)

Expand All @@ -144,7 +148,7 @@ async def get_services(self) -> BleakGATTServiceCollection:
logger.debug(
"Retrieving descriptors for characteristic {}".format(cUUID)
)
descriptors = await cbapp.central_manager_delegate.connected_peripheral_delegate.discoverDescriptors_(
descriptors = await manager.connected_peripheral_delegate.discoverDescriptors_(
characteristic
)

Expand Down Expand Up @@ -173,12 +177,13 @@ async def read_gatt_char(self, _uuid: Union[str, uuid.UUID], use_cached=False, *
(bytearray) The read data.
"""
manager = self._device_info.manager().delegate()
_uuid = await self.get_appropriate_uuid(str(_uuid))
characteristic = self.services.get_characteristic(str(_uuid))
if not characteristic:
raise BleakError("Characteristic {} was not found!".format(_uuid))

output = await cbapp.central_manager_delegate.connected_peripheral_delegate.readCharacteristic_(
output = await manager.connected_peripheral_delegate.readCharacteristic_(
characteristic.obj, use_cached=use_cached
)
value = bytearray(output)
Expand All @@ -198,11 +203,12 @@ async def read_gatt_descriptor(
Returns:
(bytearray) The read data.
"""
manager = self._device_info.manager().delegate()
descriptor = self.services.get_descriptor(handle)
if not descriptor:
raise BleakError("Descriptor {} was not found!".format(handle))

output = await cbapp.central_manager_delegate.connected_peripheral_delegate.readDescriptor_(
output = await manager.connected_peripheral_delegate.readDescriptor_(
descriptor.obj, use_cached=use_cached
)
if isinstance(
Expand All @@ -225,13 +231,14 @@ async def write_gatt_char(
response (bool): If write-with-response operation should be done. Defaults to `False`.
"""
manager = self._device_info.manager().delegate()
_uuid = await self.get_appropriate_uuid(str(_uuid))
characteristic = self.services.get_characteristic(str(_uuid))
if not characteristic:
raise BleakError("Characteristic {} was not found!".format(_uuid))

value = NSData.alloc().initWithBytes_length_(data, len(data))
success = await cbapp.central_manager_delegate.connected_peripheral_delegate.writeCharacteristic_value_type_(
success = await manager.connected_peripheral_delegate.writeCharacteristic_value_type_(
characteristic.obj,
value,
CBCharacteristicWriteWithResponse if response else CBCharacteristicWriteWithoutResponse
Expand All @@ -253,12 +260,13 @@ async def write_gatt_descriptor(self, handle: int, data: bytearray) -> None:
data (bytes or bytearray): The data to send.
"""
manager = self._device_info.manager().delegate()
descriptor = self.services.get_descriptor(handle)
if not descriptor:
raise BleakError("Descriptor {} was not found!".format(handle))

value = NSData.alloc().initWithBytes_length_(data, len(data))
success = await cbapp.central_manager_delegate.connected_peripheral_delegate.writeDescriptor_value_(
success = await manager.connected_peripheral_delegate.writeDescriptor_value_(
descriptor.obj, value
)
if success:
Expand Down Expand Up @@ -289,12 +297,13 @@ def callback(sender, data):
callback (function): The function to be called on notification.
"""
manager = self._device_info.manager().delegate()
_uuid = await self.get_appropriate_uuid(str(_uuid))
characteristic = self.services.get_characteristic(str(_uuid))
if not characteristic:
raise BleakError("Characteristic {0} not found!".format(_uuid))

success = await cbapp.central_manager_delegate.connected_peripheral_delegate.startNotify_cb_(
success = await manager.connected_peripheral_delegate.startNotify_cb_(
characteristic.obj, callback
)
if not success:
Expand All @@ -311,12 +320,13 @@ async def stop_notify(self, _uuid: Union[str, uuid.UUID]) -> None:
_uuid: The characteristic to stop notifying/indicating on.
"""
manager = self._device_info.manager().delegate()
_uuid = await self.get_appropriate_uuid(str(_uuid))
characteristic = self.services.get_characteristic(str(_uuid))
if not characteristic:
raise BleakError("Characteristic {} not found!".format(_uuid))

success = await cbapp.central_manager_delegate.connected_peripheral_delegate.stopNotify_(
success = await manager.connected_peripheral_delegate.stopNotify_(
characteristic.obj
)
if not success:
Expand Down
10 changes: 5 additions & 5 deletions bleak/backends/corebluetooth/discovery.py
Expand Up @@ -11,7 +11,7 @@
from asyncio.events import AbstractEventLoop
from typing import List

from bleak.backends.corebluetooth import CBAPP as cbapp
from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate
from bleak.backends.device import BLEDevice
from bleak.exc import BleakError

Expand All @@ -27,21 +27,21 @@ async def discover(
"""
loop = loop if loop else asyncio.get_event_loop()

manager = CentralManagerDelegate.alloc().init()
try:
await cbapp.central_manager_delegate.wait_for_powered_on(0.1)
await manager.wait_for_powered_on(0.1)
except asyncio.TimeoutError:
raise BleakError("Bluetooth device is turned off")

scan_options = {"timeout": timeout}

await cbapp.central_manager_delegate.scanForPeripherals_(scan_options)
await manager.scanForPeripherals_(scan_options)

# CoreBluetooth doesn't explicitly use MAC addresses to identify peripheral
# devices because private devices may obscure their MAC addresses. To cope
# with this, CoreBluetooth utilizes UUIDs for each peripheral. We'll use
# this for the BLEDevice address on macOS


devices = cbapp.central_manager_delegate.devices
devices = manager.devices
return list(devices.values())

3 changes: 2 additions & 1 deletion bleak/backends/corebluetooth/scanner.py
Expand Up @@ -5,7 +5,7 @@
from asyncio.events import AbstractEventLoop
from typing import Callable, Any, Union, List

from bleak.backends.corebluetooth import CBAPP as cbapp
from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate
from bleak.backends.device import BLEDevice
from bleak.exc import BleakError
from bleak.backends.scanner import BaseBleakScanner
Expand Down Expand Up @@ -41,6 +41,7 @@ def __init__(self, **kwargs):
super(BleakScannerCoreBluetooth, self).__init__(**kwargs)
self._callback = None
self._identifiers = None
self._manager = CentralManagerDelegate.alloc().init()
self._timeout = kwargs.get("timeout", 5.0)

async def start(self):
Expand Down

0 comments on commit 133879b

Please sign in to comment.