19
19
Awaitable ,
20
20
Callable ,
21
21
Dict ,
22
+ Iterable ,
22
23
List ,
23
24
Optional ,
24
25
Tuple ,
28
29
)
29
30
from warnings import warn
30
31
31
- import async_timeout
32
+ if sys .version_info < (3 , 11 ):
33
+ from async_timeout import timeout as async_timeout
34
+ else :
35
+ from asyncio import timeout as async_timeout
32
36
33
37
if sys .version_info [:2 ] < (3 , 8 ):
34
38
from typing_extensions import Literal
47
51
)
48
52
from .backends .service import BleakGATTServiceCollection
49
53
from .exc import BleakError
54
+ from .uuids import normalize_uuid_str
50
55
51
56
if TYPE_CHECKING :
52
57
from .backends .bluezdbus .scanner import BlueZScannerArgs
58
+ from .backends .corebluetooth .scanner import CBScannerArgs
53
59
from .backends .winrt .client import WinRTClientArgs
54
60
55
61
56
62
_logger = logging .getLogger (__name__ )
57
63
_logger .addHandler (logging .NullHandler ())
58
64
if bool (os .environ .get ("BLEAK_LOGGING" , False )):
59
- FORMAT = "%(asctime)-15s %(name)-8s %(levelname)s: %(message)s"
65
+ FORMAT = "%(asctime)-15s %(name)-8s %(threadName)s %( levelname)s: %(message)s"
60
66
handler = logging .StreamHandler (sys .stdout )
61
67
handler .setLevel (logging .DEBUG )
62
68
handler .setFormatter (logging .Formatter (fmt = FORMAT ))
@@ -89,12 +95,21 @@ class BleakScanner:
89
95
:class:`BleakError` if set to ``"passive"`` on macOS.
90
96
bluez:
91
97
Dictionary of arguments specific to the BlueZ backend.
98
+ cb:
99
+ Dictionary of arguments specific to the CoreBluetooth backend.
92
100
backend:
93
101
Used to override the automatically selected backend (i.e. for a
94
102
custom backend).
95
103
**kwargs:
96
104
Additional args for backwards compatibility.
97
105
106
+ .. tip:: The first received advertisement in ``detection_callback`` may or
107
+ may not include scan response data if the remote device supports it.
108
+ Be sure to take this into account when handing the callback. For example,
109
+ the scan response often contains the local name of the device so if you
110
+ are matching a device based on other data but want to display the local
111
+ name to the user, be sure to wait for ``adv_data.local_name is not None``.
112
+
98
113
.. versionchanged:: 0.15.0
99
114
``detection_callback``, ``service_uuids`` and ``scanning_mode`` are no longer keyword-only.
100
115
Added ``bluez`` parameter.
@@ -111,6 +126,7 @@ def __init__(
111
126
scanning_mode : Literal ["active" , "passive" ] = "active" ,
112
127
* ,
113
128
bluez : BlueZScannerArgs = {},
129
+ cb : CBScannerArgs = {},
114
130
backend : Optional [Type [BaseBleakScanner ]] = None ,
115
131
** kwargs ,
116
132
):
@@ -119,7 +135,12 @@ def __init__(
119
135
)
120
136
121
137
self ._backend = PlatformBleakScanner (
122
- detection_callback , service_uuids , scanning_mode , bluez = bluez , ** kwargs
138
+ detection_callback ,
139
+ service_uuids ,
140
+ scanning_mode ,
141
+ bluez = bluez ,
142
+ cb = cb ,
143
+ ** kwargs ,
123
144
)
124
145
125
146
async def __aenter__ (self ):
@@ -188,7 +209,7 @@ async def discover(
188
209
@overload
189
210
@classmethod
190
211
async def discover (
191
- cls , timeout : float = 5.0 , * , return_adv : Literal [True ], ** kwargs
212
+ cls , timeout : float = 5.0 , * , return_adv : Literal [True ] = True , ** kwargs
192
213
) -> Dict [str , Tuple [BLEDevice , AdvertisementData ]]:
193
214
...
194
215
@@ -272,11 +293,9 @@ async def find_device_by_address(
272
293
"""Obtain a ``BLEDevice`` for a BLE server specified by Bluetooth address or (macOS) UUID address.
273
294
274
295
Args:
275
- device_identifier (str): The Bluetooth/UUID address of the Bluetooth peripheral sought.
276
- timeout (float): Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
277
-
278
- Keyword Args:
279
- adapter (str): Bluetooth adapter to use for discovery.
296
+ device_identifier: The Bluetooth/UUID address of the Bluetooth peripheral sought.
297
+ timeout: Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
298
+ **kwargs: additional args passed to the :class:`BleakScanner` constructor.
280
299
281
300
Returns:
282
301
The ``BLEDevice`` sought or ``None`` if not detected.
@@ -289,6 +308,28 @@ async def find_device_by_address(
289
308
** kwargs ,
290
309
)
291
310
311
+ @classmethod
312
+ async def find_device_by_name (
313
+ cls , name : str , timeout : float = 10.0 , ** kwargs
314
+ ) -> Optional [BLEDevice ]:
315
+ """Obtain a ``BLEDevice`` for a BLE server specified by the local name in the advertising data.
316
+
317
+ Args:
318
+ name: The name sought.
319
+ timeout: Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
320
+ **kwargs: additional args passed to the :class:`BleakScanner` constructor.
321
+
322
+ Returns:
323
+ The ``BLEDevice`` sought or ``None`` if not detected.
324
+
325
+ .. versionadded:: 0.20.0
326
+ """
327
+ return await cls .find_device_by_filter (
328
+ lambda d , ad : ad .local_name == name ,
329
+ timeout = timeout ,
330
+ ** kwargs ,
331
+ )
332
+
292
333
@classmethod
293
334
async def find_device_by_filter (
294
335
cls , filterfunc : AdvertisementDataFilter , timeout : float = 10.0 , ** kwargs
@@ -322,7 +363,7 @@ def apply_filter(d: BLEDevice, ad: AdvertisementData):
322
363
323
364
async with cls (detection_callback = apply_filter , ** kwargs ):
324
365
try :
325
- async with async_timeout . timeout (timeout ):
366
+ async with async_timeout (timeout ):
326
367
return await found_device_queue .get ()
327
368
except asyncio .TimeoutError :
328
369
return None
@@ -345,6 +386,12 @@ class BleakClient:
345
386
Callback that will be scheduled in the event loop when the client is
346
387
disconnected. The callable must take one argument, which will be
347
388
this client object.
389
+ services:
390
+ Optional list of services to filter. If provided, only these services
391
+ will be resolved. This may or may not reduce the time needed to
392
+ enumerate the services depending on if the OS supports such filtering
393
+ in the Bluetooth stack or not (should affect Windows and Mac).
394
+ These can be 16-bit or 128-bit UUIDs.
348
395
timeout:
349
396
Timeout in seconds passed to the implicit ``discover`` call when
350
397
``address_or_ble_device`` is not a :class:`BLEDevice`. Defaults to 10.0.
@@ -381,6 +428,7 @@ def __init__(
381
428
self ,
382
429
address_or_ble_device : Union [BLEDevice , str ],
383
430
disconnected_callback : Optional [Callable [[BleakClient ], None ]] = None ,
431
+ services : Optional [Iterable [str ]] = None ,
384
432
* ,
385
433
timeout : float = 10.0 ,
386
434
winrt : WinRTClientArgs = {},
@@ -393,7 +441,12 @@ def __init__(
393
441
394
442
self ._backend = PlatformBleakClient (
395
443
address_or_ble_device ,
396
- disconnected_callback = disconnected_callback ,
444
+ disconnected_callback = None
445
+ if disconnected_callback is None
446
+ else functools .partial (disconnected_callback , self ),
447
+ services = None
448
+ if services is None
449
+ else set (map (normalize_uuid_str , services )),
397
450
timeout = timeout ,
398
451
winrt = winrt ,
399
452
** kwargs ,
@@ -456,7 +509,9 @@ def set_disconnected_callback(
456
509
FutureWarning ,
457
510
stacklevel = 2 ,
458
511
)
459
- self ._backend .set_disconnected_callback (callback , ** kwargs )
512
+ self ._backend .set_disconnected_callback (
513
+ None if callback is None else functools .partial (callback , self ), ** kwargs
514
+ )
460
515
461
516
async def connect (self , ** kwargs ) -> bool :
462
517
"""Connect to the specified GATT server.
@@ -545,7 +600,13 @@ def services(self) -> BleakGATTServiceCollection:
545
600
Gets the collection of GATT services available on the device.
546
601
547
602
The returned value is only valid as long as the device is connected.
603
+
604
+ Raises:
605
+ BleakError: if service discovery has not been performed yet during this connection.
548
606
"""
607
+ if not self ._backend .services :
608
+ raise BleakError ("Service Discovery has not been performed yet" )
609
+
549
610
return self ._backend .services
550
611
551
612
# I/O methods
@@ -608,7 +669,7 @@ async def start_notify(
608
669
609
670
.. code-block:: python
610
671
611
- def callback(sender: int , data: bytearray):
672
+ def callback(sender: BleakGATTCharacteristic , data: bytearray):
612
673
print(f"{sender}: {data}")
613
674
614
675
client.start_notify(char_uuid, callback)
0 commit comments