24
24
)
25
25
from ..uuid import StandardUUID , VendorUUID
26
26
27
+ try :
28
+ from typing import Optional , List , Tuple , Union , Type , Iterator , Iterable , Any
29
+ from typing_extensions import Protocol
30
+ from adafruit_ble .uuid import UUID
31
+ from adafruit_ble .characteristics import Characteristic
32
+ from adafruit_ble .services import Service
33
+ from _bleio import ScanEntry
34
+
35
+ UsesServicesAdvertisement = Union ["ProvideServicesAdvertisement" , "SolicitServicesAdvertisement" ]
36
+
37
+
38
+
39
+ except ImportError :
40
+ pass
41
+
27
42
__version__ = "0.0.0-auto.0"
28
43
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
29
44
30
45
31
46
class BoundServiceList :
32
47
"""Sequence-like object of Service UUID objects. It stores both standard and vendor UUIDs."""
33
48
34
- def __init__ (self , advertisement , * , standard_services , vendor_services ) :
49
+ def __init__ (self , advertisement : UsesServicesAdvertisement , * , standard_services : List [ int ] , vendor_services : List [ int ]) -> None :
35
50
self ._advertisement = advertisement
36
51
self ._standard_service_fields = standard_services
37
52
self ._vendor_service_fields = vendor_services
@@ -50,13 +65,13 @@ def __init__(self, advertisement, *, standard_services, vendor_services):
50
65
uuid = VendorUUID (data [16 * i : 16 * (i + 1 )])
51
66
self ._vendor_services .append (uuid )
52
67
53
- def __contains__ (self , key ) :
68
+ def __contains__ (self , key : Union [ UUID , Characteristic ]) -> bool :
54
69
uuid = key
55
70
if hasattr (key , "uuid" ):
56
71
uuid = key .uuid
57
72
return uuid in self ._vendor_services or uuid in self ._standard_services
58
73
59
- def _update (self , adt , uuids ) :
74
+ def _update (self , adt : int , uuids : List [ UUID ]) -> None :
60
75
if not uuids :
61
76
# uuids is empty
62
77
del self ._advertisement .data_dict [adt ]
@@ -68,13 +83,13 @@ def _update(self, adt, uuids):
68
83
i += uuid_length
69
84
self ._advertisement .data_dict [adt ] = b
70
85
71
- def __iter__ (self ):
86
+ def __iter__ (self ) -> Iterator [ UUID ] :
72
87
all_services = list (self ._standard_services )
73
88
all_services .extend (self ._vendor_services )
74
89
return iter (all_services )
75
90
76
91
# TODO: Differentiate between complete and incomplete lists.
77
- def append (self , service ) :
92
+ def append (self , service : Service ) -> None :
78
93
"""Append a service to the list."""
79
94
if (
80
95
isinstance (service .uuid , StandardUUID )
@@ -90,7 +105,7 @@ def append(self, service):
90
105
self ._update (self ._vendor_service_fields [0 ], self ._vendor_services )
91
106
92
107
# TODO: Differentiate between complete and incomplete lists.
93
- def extend (self , services ) :
108
+ def extend (self , services : Iterable [ Service ]) -> None :
94
109
"""Appends all services in the iterable to the list."""
95
110
standard = False
96
111
vendor = False
@@ -113,7 +128,7 @@ def extend(self, services):
113
128
if vendor :
114
129
self ._update (self ._vendor_service_fields [0 ], self ._vendor_services )
115
130
116
- def __str__ (self ):
131
+ def __str__ (self ) -> str :
117
132
data = []
118
133
for service_uuid in self ._standard_services :
119
134
data .append (str (service_uuid ))
@@ -125,11 +140,11 @@ def __str__(self):
125
140
class ServiceList (AdvertisingDataField ):
126
141
"""Descriptor for a list of Service UUIDs that lazily binds a corresponding BoundServiceList."""
127
142
128
- def __init__ (self , * , standard_services , vendor_services ) :
143
+ def __init__ (self , * , standard_services : List [ int ] , vendor_services : List [ int ]) -> None :
129
144
self .standard_services = standard_services
130
145
self .vendor_services = vendor_services
131
146
132
- def _present (self , obj ) :
147
+ def _present (self , obj : UsesServicesAdvertisement ) -> bool :
133
148
for adt in self .standard_services :
134
149
if adt in obj .data_dict :
135
150
return True
@@ -138,7 +153,7 @@ def _present(self, obj):
138
153
return True
139
154
return False
140
155
141
- def __get__ (self , obj , cls ) :
156
+ def __get__ (self , obj : Optional [ UsesServicesAdvertisement ] , cls : Type [ UsesServicesAdvertisement ]) -> Union [ UsesServicesAdvertisement , Tuple [()], "ServiceList" ] :
142
157
if obj is None :
143
158
return self
144
159
if not self ._present (obj ) and not obj .mutable :
@@ -159,7 +174,7 @@ class ProvideServicesAdvertisement(Advertisement):
159
174
services = ServiceList (standard_services = [0x02 , 0x03 ], vendor_services = [0x06 , 0x07 ])
160
175
"""List of services the device can provide."""
161
176
162
- def __init__ (self , * services , entry = None ):
177
+ def __init__ (self , * services : Service , entry : Optional [ ScanEntry ] = None ) -> None :
163
178
super ().__init__ (entry = entry )
164
179
if entry :
165
180
if services :
@@ -173,7 +188,7 @@ def __init__(self, *services, entry=None):
173
188
self .flags .le_only = True
174
189
175
190
@classmethod
176
- def matches (cls , entry ) :
191
+ def matches (cls , entry : ScanEntry ) -> bool :
177
192
"""Only one kind of service list need be present in a ProvideServicesAdvertisement,
178
193
so override the default behavior and match any prefix, not all.
179
194
"""
@@ -189,7 +204,7 @@ class SolicitServicesAdvertisement(Advertisement):
189
204
solicited_services = ServiceList (standard_services = [0x14 ], vendor_services = [0x15 ])
190
205
"""List of services the device would like to use."""
191
206
192
- def __init__ (self , * services , entry = None ):
207
+ def __init__ (self , * services : Service , entry : Optional [ ScanEntry ] = None ) -> None :
193
208
super ().__init__ (entry = entry )
194
209
if entry :
195
210
if services :
@@ -212,8 +227,8 @@ class ManufacturerData(AdvertisingDataField):
212
227
"""
213
228
214
229
def __init__ (
215
- self , obj , * , advertising_data_type = 0xFF , company_id , key_encoding = "B"
216
- ):
230
+ self , obj : UsesServicesAdvertisement , * , advertising_data_type : int = 0xFF , company_id : int , key_encoding : str = "B"
231
+ ) -> None :
217
232
self ._obj = obj
218
233
self ._company_id = company_id
219
234
self ._adt = advertising_data_type
@@ -231,15 +246,15 @@ def __init__(
231
246
self .data = decode_data (existing_data [2 :], key_encoding = key_encoding )
232
247
self ._key_encoding = key_encoding
233
248
234
- def __len__ (self ):
249
+ def __len__ (self ) -> int :
235
250
return 2 + compute_length (self .data , key_encoding = self ._key_encoding )
236
251
237
- def __bytes__ (self ):
252
+ def __bytes__ (self ) -> bytes :
238
253
return struct .pack ("<H" , self .company_id ) + encode_data (
239
254
self .data , key_encoding = self ._key_encoding
240
255
)
241
256
242
- def __str__ (self ):
257
+ def __str__ (self ) -> str :
243
258
hex_data = to_hex (encode_data (self .data , key_encoding = self ._key_encoding ))
244
259
return "<ManufacturerData company_id={:04x} data={} >" .format (
245
260
self .company_id , hex_data
@@ -249,7 +264,7 @@ def __str__(self):
249
264
class ManufacturerDataField :
250
265
"""A single piece of data within the manufacturer specific data. The format can be repeated."""
251
266
252
- def __init__ (self , key , value_format , field_names = None ):
267
+ def __init__ (self , key : int , value_format : str , field_names : Optional [ Iterable [ str ]] = None ) -> None :
253
268
self ._key = key
254
269
self ._format = value_format
255
270
# TODO: Support format strings that use numbers to repeat a given type. For now, we strip
@@ -267,7 +282,7 @@ def __init__(self, key, value_format, field_names=None):
267
282
# Mostly, this is to raise a ValueError if field_names has invalid entries
268
283
self .mdf_tuple = namedtuple ("mdf_tuple" , self .field_names )
269
284
270
- def __get__ (self , obj , cls ) :
285
+ def __get__ (self , obj : Optional [ Advertisement ] , cls : Type [ Advertisement ]) -> Optional [ Union [ "ManufacturerDataField" , Tuple , namedtuple ]] :
271
286
if obj is None :
272
287
return self
273
288
if self ._key not in obj .manufacturer_data .data :
@@ -293,7 +308,7 @@ def __get__(self, obj, cls):
293
308
unpacked [i ] = unpacked [i ][0 ]
294
309
return tuple (unpacked )
295
310
296
- def __set__ (self , obj , value ) :
311
+ def __set__ (self , obj : "Advertisement" , value : Any ) -> None :
297
312
if not obj .mutable :
298
313
raise AttributeError ()
299
314
if isinstance (value , tuple ) and (
@@ -317,16 +332,16 @@ class ServiceData(AdvertisingDataField):
317
332
"""Encapsulates service data. It is read as a memoryview which can be manipulated or set as a
318
333
bytearray to change the size."""
319
334
320
- def __init__ (self , service ) :
335
+ def __init__ (self , service : Characteristic ) -> None :
321
336
if isinstance (service .uuid , StandardUUID ):
322
337
self ._adt = 0x16
323
338
elif isinstance (service .uuid , VendorUUID ):
324
339
self ._adt = 0x21
325
340
self ._prefix = bytes (service .uuid )
326
341
327
342
def __get__ (
328
- self , obj , cls
329
- ): # pylint: disable=too-many-return-statements,too-many-branches
343
+ self , obj : Optional [ Service ] , cls : Type [ Service ]
344
+ ) -> Optional [ Union [ "ServiceData" , memoryview ]] : # pylint: disable=too-many-return-statements,too-many-branches
330
345
if obj is None :
331
346
return self
332
347
# If not present at all and mutable, then we init it, otherwise None.
@@ -366,7 +381,7 @@ def __get__(
366
381
367
382
return None
368
383
369
- def __set__ (self , obj , value ) :
384
+ def __set__ (self , obj : Advertisement , value : bytearray ) -> None :
370
385
if not obj .mutable :
371
386
raise RuntimeError ("Advertisement immutable" )
372
387
if not isinstance (value , bytearray ):
0 commit comments