macOS backend
The macOS backend of Bleak is written with pyobjc directives for interfacing with Foundation and CoreBluetooth APIs.
Specific features for the macOS backend
The most noticeable difference between the other backends of bleak and this backend, is that CoreBluetooth doesn’t scan for other devices via Bluetooth address. Instead, UUIDs are utilized that are often unique between the device that is scanning and the device that is being scanned.
In the example files, this is handled in this fashion:
mac_addr = (
"24:71:89:cc:09:05"
if platform.system() != "Darwin"
else "243E23AE-4A99-406C-B317-18F1BD7B4CBE"
)
As stated above, this will however only work the macOS machine that performed
the scan and thus cached the device as 243E23AE-4A99-406C-B317-18F1BD7B4CBE.
Pairing
There is no pairing functionality implemented in macOS right now, since it does not seem to be any explicit pairing methods in CoreBluetooth.
Instead, macOS will prompt the user the first time a characteristic that requires authorization/authentication is accessed. This means that a GATT read or write operation could block for a long time waiting for the user to responsed. So timeouts should be set accordingly.
Calling the bleak.BleakClient.pair() method will raise a NotImplementedError
on macOS. But setting pair=True in bleak.BleakClient will be silently ignored.
Permissions
To use Bluetooth in an bundled application you have to make sure, that NSBluetoothAlwaysUsageDescription
is set in your Info.plist. For example this has to look like:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Some description why your app needs Bluetooth access</string>
When using briefcase to package your application,
you have to add the following to your pyproject.toml to create that entry in the Info.plist:
[tool.briefcase.app.YOUR_APP_NAME.macOS]
info."NSBluetoothAlwaysUsageDescription" = "Some description why your app needs Bluetooth access"
In addition to this, the user has to grant the Bluetooth permission to the application. When the application currently has no permission the OS will automatically create a popup window and ask the user for permission. This happens when Bluetooth is used by the application for the first time.
When the users allow the the Bluetooth access, all bleak features can be used normally.
If the user doesn’t allow it, a BleakBluetoothNotAvailableError will raised every time
Bluetooth is accessed. An re-request of the Bluetooth permissions is not possible programmatically.
This is only possible via the macOS system settings. To create a nice user experience the application
can catch the BleakBluetoothNotAvailableError and guide the user to System Settings → Privacy & Security →
Bluetooth.
Notifications
CoreBluetooth does not differentiate between data from a notification and data from a read. This can cause confusion in cases where a device may send a notification message on a characteristic as a signal that the characteristic needs to be read again.
Bleak can accept a notification_discriminator callback in the cb dict parameter that is
passed to the bleak.BleakClient.start_notify() method that can differentiate between these types of data.
event = asyncio.Event()
async def notification_handler(char, data):
event.set()
def notification_check_handler(data):
# We can identify notifications on this characteristic because they
# only contain 1 byte of data. Read responses will have more than
# 1 byte.
return len(data) == 1
await client.start_notify(
char,
notification_handler,
cb={"notification_discriminator": notification_check_handler},
)
while True:
await event.wait()
# We received a notification - prepare to receive another
event.clear()
# Then read the characteristic to get the full value
data = await client.read_gatt_char(char)
# Do stuff with data
API
Scanner
- class bleak.backends.corebluetooth.scanner.BleakScannerCoreBluetooth(detection_callback: Callable[[BLEDevice, AdvertisementData], Coroutine[Any, Any, None] | None] | None, service_uuids: list[str] | None, scanning_mode: Literal['active', 'passive'], *, cb: CBScannerArgs, **kwargs: Any)[source]
The native macOS Bleak BLE Scanner.
Documentation: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager
CoreBluetooth doesn’t explicitly use Bluetooth addresses to identify peripheral devices because private devices may obscure their Bluetooth addresses. To cope with this, CoreBluetooth utilizes UUIDs for each peripheral. Bleak uses this for the BLEDevice address on macOS.
- Parameters:
detection_callback – Optional function that will be called each time a device is discovered or advertising data has changed.
service_uuids – Optional list of service UUIDs to filter on. Only advertisements containing this advertising data will be received. Required on macOS >= 12.0, < 12.3 (unless you create an app with
py2app).scanning_mode – Set to
"passive"to avoid the"active"scanning mode. Not supported on macOS! Will raiseBleakErrorif set to"passive"**timeout (float) – The scanning timeout to be used, in case of missing
stopScan_method.
Client
BLE Client for CoreBluetooth on macOS
- class bleak.backends.corebluetooth.client.BleakClientCoreBluetooth(address_or_ble_device: BLEDevice | str, services: set[str] | None = None, **kwargs: Any)[source]
CoreBluetooth class interface for BleakClient
- Parameters:
address_or_ble_device (BLEDevice or str) – The Bluetooth address of the BLE peripheral to connect to or the BLEDevice object representing it.
services – Optional set of service UUIDs that will be used.
- async connect(pair: bool, **kwargs: Any) None[source]
Connect to a specified Peripheral
- Keyword Arguments:
timeout (float) – Timeout for required
BleakScanner.find_device_by_addresscall.
- property is_connected: bool
Checks for current active connection
- property mtu_size: int
Get ATT MTU size for active connection
- property name: str
Get the name of the connected peripheral
- async pair(*args: Any, **kwargs: Any) None[source]
Attempt to pair with a peripheral.
- Raises:
NotImplementedError – This is not available on macOS since there is not explicit API to do a pairing. Instead, the docs state that it “auto-pairs”, when trying to read a characteristic that requires encryption.
Reference:
- async read_gatt_char(characteristic: BleakGATTCharacteristic, *, use_cached: bool = False, **kwargs: Any) bytearray[source]
Perform read operation on the specified GATT characteristic.
- Parameters:
characteristic (BleakGATTCharacteristic) – The characteristic to read from.
- Returns:
(bytearray) The read data.
- async read_gatt_descriptor(descriptor: BleakGATTDescriptor, *, use_cached: bool = False, **kwargs: Any) bytearray[source]
Perform read operation on the specified GATT descriptor.
- Parameters:
handle (int) – The handle of the descriptor to read from.
use_cached (bool) – False forces Windows to read the value from the device again and not use its own cached value. Defaults to False.
- Returns:
(bytearray) The read data.
- async start_notify(characteristic: BleakGATTCharacteristic, callback: Callable[[bytearray], None], **kwargs: Any) None[source]
Activate notifications/indications on a characteristic.
- async stop_notify(characteristic: BleakGATTCharacteristic) None[source]
Deactivate notification/indication on a specified characteristic.
- Parameters:
(BleakGATTCharacteristic (characteristic) – The characteristic to deactivate notification/indication on.
- async unpair() None[source]
Remove pairing information for a peripheral.
- Raises:
NotImplementedError – This is not available on macOS since there is not explicit API to do a pairing.
- async write_gatt_char(characteristic: BleakGATTCharacteristic, data: SizedBuffer, response: bool) None[source]
Perform a write operation on the specified GATT characteristic.
- Parameters:
characteristic – The characteristic to write to.
data – The data to send.
response – If write-with-response operation should be done.
- async write_gatt_descriptor(descriptor: BleakGATTDescriptor, data: SizedBuffer) None[source]
Perform a write operation on the specified GATT descriptor.
- Parameters:
descriptor – The descriptor to read from.
data – The data to send (any bytes-like object).