bumble 0.0.198__py3-none-any.whl → 0.0.199__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/apps/pair.py +32 -5
- bumble/att.py +56 -40
- bumble/avdtp.py +2 -2
- bumble/decoder.py +14 -10
- bumble/drivers/rtk.py +19 -5
- bumble/gatt.py +24 -19
- bumble/gatt_client.py +5 -25
- bumble/gatt_server.py +14 -6
- bumble/hci.py +272 -7
- bumble/host.py +16 -6
- bumble/pandora/__init__.py +3 -0
- bumble/pandora/l2cap.py +310 -0
- bumble/profiles/aics.py +520 -0
- bumble/profiles/asha.py +295 -0
- bumble/profiles/hap.py +665 -0
- bumble/profiles/vcp.py +5 -3
- bumble/smp.py +23 -4
- bumble/transport/pyusb.py +19 -2
- {bumble-0.0.198.dist-info → bumble-0.0.199.dist-info}/METADATA +1 -1
- {bumble-0.0.198.dist-info → bumble-0.0.199.dist-info}/RECORD +25 -22
- {bumble-0.0.198.dist-info → bumble-0.0.199.dist-info}/WHEEL +1 -1
- bumble/profiles/asha_service.py +0 -193
- {bumble-0.0.198.dist-info → bumble-0.0.199.dist-info}/LICENSE +0 -0
- {bumble-0.0.198.dist-info → bumble-0.0.199.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.198.dist-info → bumble-0.0.199.dist-info}/top_level.txt +0 -0
bumble/_version.py
CHANGED
bumble/apps/pair.py
CHANGED
|
@@ -46,6 +46,12 @@ from bumble.att import (
|
|
|
46
46
|
ATT_INSUFFICIENT_AUTHENTICATION_ERROR,
|
|
47
47
|
ATT_INSUFFICIENT_ENCRYPTION_ERROR,
|
|
48
48
|
)
|
|
49
|
+
from bumble.utils import AsyncRunner
|
|
50
|
+
|
|
51
|
+
# -----------------------------------------------------------------------------
|
|
52
|
+
# Constants
|
|
53
|
+
# -----------------------------------------------------------------------------
|
|
54
|
+
POST_PAIRING_DELAY = 1
|
|
49
55
|
|
|
50
56
|
|
|
51
57
|
# -----------------------------------------------------------------------------
|
|
@@ -235,8 +241,10 @@ def on_connection(connection, request):
|
|
|
235
241
|
|
|
236
242
|
# Listen for pairing events
|
|
237
243
|
connection.on('pairing_start', on_pairing_start)
|
|
238
|
-
connection.on('pairing', lambda keys: on_pairing(connection
|
|
239
|
-
connection.on(
|
|
244
|
+
connection.on('pairing', lambda keys: on_pairing(connection, keys))
|
|
245
|
+
connection.on(
|
|
246
|
+
'pairing_failure', lambda reason: on_pairing_failure(connection, reason)
|
|
247
|
+
)
|
|
240
248
|
|
|
241
249
|
# Listen for encryption changes
|
|
242
250
|
connection.on(
|
|
@@ -270,19 +278,24 @@ def on_pairing_start():
|
|
|
270
278
|
|
|
271
279
|
|
|
272
280
|
# -----------------------------------------------------------------------------
|
|
273
|
-
|
|
281
|
+
@AsyncRunner.run_in_task()
|
|
282
|
+
async def on_pairing(connection, keys):
|
|
274
283
|
print(color('***-----------------------------------', 'cyan'))
|
|
275
|
-
print(color(f'*** Paired! (peer identity={
|
|
284
|
+
print(color(f'*** Paired! (peer identity={connection.peer_address})', 'cyan'))
|
|
276
285
|
keys.print(prefix=color('*** ', 'cyan'))
|
|
277
286
|
print(color('***-----------------------------------', 'cyan'))
|
|
287
|
+
await asyncio.sleep(POST_PAIRING_DELAY)
|
|
288
|
+
await connection.disconnect()
|
|
278
289
|
Waiter.instance.terminate()
|
|
279
290
|
|
|
280
291
|
|
|
281
292
|
# -----------------------------------------------------------------------------
|
|
282
|
-
|
|
293
|
+
@AsyncRunner.run_in_task()
|
|
294
|
+
async def on_pairing_failure(connection, reason):
|
|
283
295
|
print(color('***-----------------------------------', 'red'))
|
|
284
296
|
print(color(f'*** Pairing failed: {smp_error_name(reason)}', 'red'))
|
|
285
297
|
print(color('***-----------------------------------', 'red'))
|
|
298
|
+
await connection.disconnect()
|
|
286
299
|
Waiter.instance.terminate()
|
|
287
300
|
|
|
288
301
|
|
|
@@ -293,6 +306,7 @@ async def pair(
|
|
|
293
306
|
mitm,
|
|
294
307
|
bond,
|
|
295
308
|
ctkd,
|
|
309
|
+
identity_address,
|
|
296
310
|
linger,
|
|
297
311
|
io,
|
|
298
312
|
oob,
|
|
@@ -382,11 +396,18 @@ async def pair(
|
|
|
382
396
|
oob_contexts = None
|
|
383
397
|
|
|
384
398
|
# Set up a pairing config factory
|
|
399
|
+
if identity_address == 'public':
|
|
400
|
+
identity_address_type = PairingConfig.AddressType.PUBLIC
|
|
401
|
+
elif identity_address == 'random':
|
|
402
|
+
identity_address_type = PairingConfig.AddressType.RANDOM
|
|
403
|
+
else:
|
|
404
|
+
identity_address_type = None
|
|
385
405
|
device.pairing_config_factory = lambda connection: PairingConfig(
|
|
386
406
|
sc=sc,
|
|
387
407
|
mitm=mitm,
|
|
388
408
|
bonding=bond,
|
|
389
409
|
oob=oob_contexts,
|
|
410
|
+
identity_address_type=identity_address_type,
|
|
390
411
|
delegate=Delegate(mode, connection, io, prompt),
|
|
391
412
|
)
|
|
392
413
|
|
|
@@ -457,6 +478,10 @@ class LogHandler(logging.Handler):
|
|
|
457
478
|
help='Enable CTKD',
|
|
458
479
|
show_default=True,
|
|
459
480
|
)
|
|
481
|
+
@click.option(
|
|
482
|
+
'--identity-address',
|
|
483
|
+
type=click.Choice(['random', 'public']),
|
|
484
|
+
)
|
|
460
485
|
@click.option('--linger', default=False, is_flag=True, help='Linger after pairing')
|
|
461
486
|
@click.option(
|
|
462
487
|
'--io',
|
|
@@ -493,6 +518,7 @@ def main(
|
|
|
493
518
|
mitm,
|
|
494
519
|
bond,
|
|
495
520
|
ctkd,
|
|
521
|
+
identity_address,
|
|
496
522
|
linger,
|
|
497
523
|
io,
|
|
498
524
|
oob,
|
|
@@ -518,6 +544,7 @@ def main(
|
|
|
518
544
|
mitm,
|
|
519
545
|
bond,
|
|
520
546
|
ctkd,
|
|
547
|
+
identity_address,
|
|
521
548
|
linger,
|
|
522
549
|
io,
|
|
523
550
|
oob,
|
bumble/att.py
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
# Imports
|
|
24
24
|
# -----------------------------------------------------------------------------
|
|
25
25
|
from __future__ import annotations
|
|
26
|
+
|
|
26
27
|
import enum
|
|
27
28
|
import functools
|
|
28
29
|
import inspect
|
|
@@ -41,6 +42,7 @@ from typing import (
|
|
|
41
42
|
|
|
42
43
|
from pyee import EventEmitter
|
|
43
44
|
|
|
45
|
+
from bumble import utils
|
|
44
46
|
from bumble.core import UUID, name_or_number, ProtocolError
|
|
45
47
|
from bumble.hci import HCI_Object, key_with_value
|
|
46
48
|
from bumble.colors import color
|
|
@@ -145,43 +147,57 @@ ATT_RESPONSES = [
|
|
|
145
147
|
ATT_EXECUTE_WRITE_RESPONSE
|
|
146
148
|
]
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
150
|
+
class ErrorCode(utils.OpenIntEnum):
|
|
151
|
+
'''
|
|
152
|
+
See
|
|
153
|
+
|
|
154
|
+
* Bluetooth spec @ Vol 3, Part F - 3.4.1.1 Error Response
|
|
155
|
+
* Core Specification Supplement: Common Profile And Service Error Codes
|
|
156
|
+
'''
|
|
157
|
+
INVALID_HANDLE = 0x01
|
|
158
|
+
READ_NOT_PERMITTED = 0x02
|
|
159
|
+
WRITE_NOT_PERMITTED = 0x03
|
|
160
|
+
INVALID_PDU = 0x04
|
|
161
|
+
INSUFFICIENT_AUTHENTICATION = 0x05
|
|
162
|
+
REQUEST_NOT_SUPPORTED = 0x06
|
|
163
|
+
INVALID_OFFSET = 0x07
|
|
164
|
+
INSUFFICIENT_AUTHORIZATION = 0x08
|
|
165
|
+
PREPARE_QUEUE_FULL = 0x09
|
|
166
|
+
ATTRIBUTE_NOT_FOUND = 0x0A
|
|
167
|
+
ATTRIBUTE_NOT_LONG = 0x0B
|
|
168
|
+
INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0C
|
|
169
|
+
INVALID_ATTRIBUTE_LENGTH = 0x0D
|
|
170
|
+
UNLIKELY_ERROR = 0x0E
|
|
171
|
+
INSUFFICIENT_ENCRYPTION = 0x0F
|
|
172
|
+
UNSUPPORTED_GROUP_TYPE = 0x10
|
|
173
|
+
INSUFFICIENT_RESOURCES = 0x11
|
|
174
|
+
DATABASE_OUT_OF_SYNC = 0x12
|
|
175
|
+
VALUE_NOT_ALLOWED = 0x13
|
|
176
|
+
# 0x80 – 0x9F: Application Error
|
|
177
|
+
# 0xE0 – 0xFF: Common Profile and Service Error Codes
|
|
178
|
+
WRITE_REQUEST_REJECTED = 0xFC
|
|
179
|
+
CCCD_IMPROPERLY_CONFIGURED = 0xFD
|
|
180
|
+
PROCEDURE_ALREADY_IN_PROGRESS = 0xFE
|
|
181
|
+
OUT_OF_RANGE = 0xFF
|
|
182
|
+
|
|
183
|
+
# Backward Compatible Constants
|
|
184
|
+
ATT_INVALID_HANDLE_ERROR = ErrorCode.INVALID_HANDLE
|
|
185
|
+
ATT_READ_NOT_PERMITTED_ERROR = ErrorCode.READ_NOT_PERMITTED
|
|
186
|
+
ATT_WRITE_NOT_PERMITTED_ERROR = ErrorCode.WRITE_NOT_PERMITTED
|
|
187
|
+
ATT_INVALID_PDU_ERROR = ErrorCode.INVALID_PDU
|
|
188
|
+
ATT_INSUFFICIENT_AUTHENTICATION_ERROR = ErrorCode.INSUFFICIENT_AUTHENTICATION
|
|
189
|
+
ATT_REQUEST_NOT_SUPPORTED_ERROR = ErrorCode.REQUEST_NOT_SUPPORTED
|
|
190
|
+
ATT_INVALID_OFFSET_ERROR = ErrorCode.INVALID_OFFSET
|
|
191
|
+
ATT_INSUFFICIENT_AUTHORIZATION_ERROR = ErrorCode.INSUFFICIENT_AUTHORIZATION
|
|
192
|
+
ATT_PREPARE_QUEUE_FULL_ERROR = ErrorCode.PREPARE_QUEUE_FULL
|
|
193
|
+
ATT_ATTRIBUTE_NOT_FOUND_ERROR = ErrorCode.ATTRIBUTE_NOT_FOUND
|
|
194
|
+
ATT_ATTRIBUTE_NOT_LONG_ERROR = ErrorCode.ATTRIBUTE_NOT_LONG
|
|
195
|
+
ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE_ERROR = ErrorCode.INSUFFICIENT_ENCRYPTION_KEY_SIZE
|
|
196
|
+
ATT_INVALID_ATTRIBUTE_LENGTH_ERROR = ErrorCode.INVALID_ATTRIBUTE_LENGTH
|
|
197
|
+
ATT_UNLIKELY_ERROR_ERROR = ErrorCode.UNLIKELY_ERROR
|
|
198
|
+
ATT_INSUFFICIENT_ENCRYPTION_ERROR = ErrorCode.INSUFFICIENT_ENCRYPTION
|
|
199
|
+
ATT_UNSUPPORTED_GROUP_TYPE_ERROR = ErrorCode.UNSUPPORTED_GROUP_TYPE
|
|
200
|
+
ATT_INSUFFICIENT_RESOURCES_ERROR = ErrorCode.INSUFFICIENT_RESOURCES
|
|
185
201
|
|
|
186
202
|
ATT_DEFAULT_MTU = 23
|
|
187
203
|
|
|
@@ -245,9 +261,9 @@ class ATT_PDU:
|
|
|
245
261
|
def pdu_name(op_code):
|
|
246
262
|
return name_or_number(ATT_PDU_NAMES, op_code, 2)
|
|
247
263
|
|
|
248
|
-
@
|
|
249
|
-
def error_name(error_code):
|
|
250
|
-
return
|
|
264
|
+
@classmethod
|
|
265
|
+
def error_name(cls, error_code: int) -> str:
|
|
266
|
+
return ErrorCode(error_code).name
|
|
251
267
|
|
|
252
268
|
@staticmethod
|
|
253
269
|
def subclass(fields):
|
bumble/avdtp.py
CHANGED
|
@@ -580,10 +580,10 @@ class ServiceCapabilities:
|
|
|
580
580
|
self.service_category = service_category
|
|
581
581
|
self.service_capabilities_bytes = service_capabilities_bytes
|
|
582
582
|
|
|
583
|
-
def to_string(self, details: List[str] =
|
|
583
|
+
def to_string(self, details: Optional[List[str]] = None) -> str:
|
|
584
584
|
attributes = ','.join(
|
|
585
585
|
[name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)]
|
|
586
|
-
+ details
|
|
586
|
+
+ (details or [])
|
|
587
587
|
)
|
|
588
588
|
return f'ServiceCapabilities({attributes})'
|
|
589
589
|
|
bumble/decoder.py
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
from typing import Union
|
|
16
|
+
|
|
15
17
|
# -----------------------------------------------------------------------------
|
|
16
18
|
# Constants
|
|
17
19
|
# -----------------------------------------------------------------------------
|
|
@@ -149,7 +151,7 @@ QMF_COEFFS = [3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11]
|
|
|
149
151
|
# -----------------------------------------------------------------------------
|
|
150
152
|
# Classes
|
|
151
153
|
# -----------------------------------------------------------------------------
|
|
152
|
-
class G722Decoder
|
|
154
|
+
class G722Decoder:
|
|
153
155
|
"""G.722 decoder with bitrate 64kbit/s.
|
|
154
156
|
|
|
155
157
|
For the Blocks in the sub-band decoders, please refer to the G.722
|
|
@@ -157,7 +159,7 @@ class G722Decoder(object):
|
|
|
157
159
|
https://www.itu.int/rec/T-REC-G.722-201209-I
|
|
158
160
|
"""
|
|
159
161
|
|
|
160
|
-
def __init__(self):
|
|
162
|
+
def __init__(self) -> None:
|
|
161
163
|
self._x = [0] * 24
|
|
162
164
|
self._band = [Band(), Band()]
|
|
163
165
|
# The initial value in BLOCK 3L
|
|
@@ -165,12 +167,12 @@ class G722Decoder(object):
|
|
|
165
167
|
# The initial value in BLOCK 3H
|
|
166
168
|
self._band[1].det = 8
|
|
167
169
|
|
|
168
|
-
def decode_frame(self, encoded_data) -> bytearray:
|
|
170
|
+
def decode_frame(self, encoded_data: Union[bytes, bytearray]) -> bytearray:
|
|
169
171
|
result_array = bytearray(len(encoded_data) * 4)
|
|
170
172
|
self.g722_decode(result_array, encoded_data)
|
|
171
173
|
return result_array
|
|
172
174
|
|
|
173
|
-
def g722_decode(self, result_array, encoded_data) -> int:
|
|
175
|
+
def g722_decode(self, result_array, encoded_data: Union[bytes, bytearray]) -> int:
|
|
174
176
|
"""Decode the data frame using g722 decoder."""
|
|
175
177
|
result_length = 0
|
|
176
178
|
|
|
@@ -198,14 +200,16 @@ class G722Decoder(object):
|
|
|
198
200
|
|
|
199
201
|
return result_length
|
|
200
202
|
|
|
201
|
-
def update_decoded_result(
|
|
203
|
+
def update_decoded_result(
|
|
204
|
+
self, xout: int, byte_length: int, byte_array: bytearray
|
|
205
|
+
) -> int:
|
|
202
206
|
result = (int)(xout >> 11)
|
|
203
207
|
bytes_result = result.to_bytes(2, 'little', signed=True)
|
|
204
208
|
byte_array[byte_length] = bytes_result[0]
|
|
205
209
|
byte_array[byte_length + 1] = bytes_result[1]
|
|
206
210
|
return byte_length + 2
|
|
207
211
|
|
|
208
|
-
def lower_sub_band_decoder(self, lower_bits) -> int:
|
|
212
|
+
def lower_sub_band_decoder(self, lower_bits: int) -> int:
|
|
209
213
|
"""Lower sub-band decoder for last six bits."""
|
|
210
214
|
|
|
211
215
|
# Block 5L
|
|
@@ -258,7 +262,7 @@ class G722Decoder(object):
|
|
|
258
262
|
|
|
259
263
|
return rlow
|
|
260
264
|
|
|
261
|
-
def higher_sub_band_decoder(self, higher_bits) -> int:
|
|
265
|
+
def higher_sub_band_decoder(self, higher_bits: int) -> int:
|
|
262
266
|
"""Higher sub-band decoder for first two bits."""
|
|
263
267
|
|
|
264
268
|
# Block 2H
|
|
@@ -306,14 +310,14 @@ class G722Decoder(object):
|
|
|
306
310
|
|
|
307
311
|
|
|
308
312
|
# -----------------------------------------------------------------------------
|
|
309
|
-
class Band
|
|
310
|
-
"""Structure for G722 decode
|
|
313
|
+
class Band:
|
|
314
|
+
"""Structure for G722 decode processing."""
|
|
311
315
|
|
|
312
316
|
s: int = 0
|
|
313
317
|
nb: int = 0
|
|
314
318
|
det: int = 0
|
|
315
319
|
|
|
316
|
-
def __init__(self):
|
|
320
|
+
def __init__(self) -> None:
|
|
317
321
|
self._sp = 0
|
|
318
322
|
self._sz = 0
|
|
319
323
|
self._r = [0] * 3
|
bumble/drivers/rtk.py
CHANGED
|
@@ -301,6 +301,8 @@ class Driver(common.Driver):
|
|
|
301
301
|
fw_name: str = ""
|
|
302
302
|
config_name: str = ""
|
|
303
303
|
|
|
304
|
+
POST_RESET_DELAY: float = 0.2
|
|
305
|
+
|
|
304
306
|
DRIVER_INFOS = [
|
|
305
307
|
# 8723A
|
|
306
308
|
DriverInfo(
|
|
@@ -495,12 +497,24 @@ class Driver(common.Driver):
|
|
|
495
497
|
|
|
496
498
|
@classmethod
|
|
497
499
|
async def driver_info_for_host(cls, host):
|
|
498
|
-
|
|
499
|
-
|
|
500
|
+
try:
|
|
501
|
+
await host.send_command(
|
|
502
|
+
HCI_Reset_Command(),
|
|
503
|
+
check_result=True,
|
|
504
|
+
response_timeout=cls.POST_RESET_DELAY,
|
|
505
|
+
)
|
|
506
|
+
host.ready = True # Needed to let the host know the controller is ready.
|
|
507
|
+
except asyncio.exceptions.TimeoutError:
|
|
508
|
+
logger.warning("timeout waiting for hci reset, retrying")
|
|
509
|
+
await host.send_command(HCI_Reset_Command(), check_result=True)
|
|
510
|
+
host.ready = True
|
|
511
|
+
|
|
512
|
+
command = HCI_Read_Local_Version_Information_Command()
|
|
513
|
+
response = await host.send_command(command, check_result=True)
|
|
514
|
+
if response.command_opcode != command.op_code:
|
|
515
|
+
logger.error("failed to probe local version information")
|
|
516
|
+
return None
|
|
500
517
|
|
|
501
|
-
response = await host.send_command(
|
|
502
|
-
HCI_Read_Local_Version_Information_Command(), check_result=True
|
|
503
|
-
)
|
|
504
518
|
local_version = response.return_parameters
|
|
505
519
|
|
|
506
520
|
logger.debug(
|
bumble/gatt.py
CHANGED
|
@@ -238,22 +238,22 @@ GATT_SEARCH_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x
|
|
|
238
238
|
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Content Control Id')
|
|
239
239
|
|
|
240
240
|
# Telephone Bearer Service (TBS)
|
|
241
|
-
GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC = UUID.from_16_bits(
|
|
242
|
-
GATT_BEARER_UCI_CHARACTERISTIC = UUID.from_16_bits(
|
|
243
|
-
GATT_BEARER_TECHNOLOGY_CHARACTERISTIC = UUID.from_16_bits(
|
|
244
|
-
GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC = UUID.from_16_bits(
|
|
245
|
-
GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC = UUID.from_16_bits(
|
|
246
|
-
GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(
|
|
247
|
-
GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC = UUID.from_16_bits(
|
|
248
|
-
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(
|
|
249
|
-
GATT_STATUS_FLAGS_CHARACTERISTIC = UUID.from_16_bits(
|
|
250
|
-
GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC = UUID.from_16_bits(
|
|
251
|
-
GATT_CALL_STATE_CHARACTERISTIC = UUID.from_16_bits(
|
|
252
|
-
GATT_CALL_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(
|
|
253
|
-
GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC = UUID.from_16_bits(
|
|
254
|
-
GATT_TERMINATION_REASON_CHARACTERISTIC = UUID.from_16_bits(
|
|
255
|
-
GATT_INCOMING_CALL_CHARACTERISTIC = UUID.from_16_bits(
|
|
256
|
-
GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC = UUID.from_16_bits(
|
|
241
|
+
GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BB3, 'Bearer Provider Name')
|
|
242
|
+
GATT_BEARER_UCI_CHARACTERISTIC = UUID.from_16_bits(0x2BB4, 'Bearer UCI')
|
|
243
|
+
GATT_BEARER_TECHNOLOGY_CHARACTERISTIC = UUID.from_16_bits(0x2BB5, 'Bearer Technology')
|
|
244
|
+
GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2BB6, 'Bearer URI Schemes Supported List')
|
|
245
|
+
GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC = UUID.from_16_bits(0x2BB7, 'Bearer Signal Strength')
|
|
246
|
+
GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(0x2BB8, 'Bearer Signal Strength Reporting Interval')
|
|
247
|
+
GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC = UUID.from_16_bits(0x2BB9, 'Bearer List Current Calls')
|
|
248
|
+
GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Content Control ID')
|
|
249
|
+
GATT_STATUS_FLAGS_CHARACTERISTIC = UUID.from_16_bits(0x2BBB, 'Status Flags')
|
|
250
|
+
GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC = UUID.from_16_bits(0x2BBC, 'Incoming Call Target Bearer URI')
|
|
251
|
+
GATT_CALL_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BBD, 'Call State')
|
|
252
|
+
GATT_CALL_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BBE, 'Call Control Point')
|
|
253
|
+
GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC = UUID.from_16_bits(0x2BBF, 'Call Control Point Optional Opcodes')
|
|
254
|
+
GATT_TERMINATION_REASON_CHARACTERISTIC = UUID.from_16_bits(0x2BC0, 'Termination Reason')
|
|
255
|
+
GATT_INCOMING_CALL_CHARACTERISTIC = UUID.from_16_bits(0x2BC1, 'Incoming Call')
|
|
256
|
+
GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BC2, 'Call Friendly Name')
|
|
257
257
|
|
|
258
258
|
# Microphone Control Service (MICS)
|
|
259
259
|
GATT_MUTE_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Mute')
|
|
@@ -275,6 +275,11 @@ GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCC, 'Sou
|
|
|
275
275
|
GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
|
|
276
276
|
GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
|
|
277
277
|
|
|
278
|
+
# Hearing Access Service
|
|
279
|
+
GATT_HEARING_AID_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2BDA, 'Hearing Aid Features')
|
|
280
|
+
GATT_HEARING_AID_PRESET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BDB, 'Hearing Aid Preset Control Point')
|
|
281
|
+
GATT_ACTIVE_PRESET_INDEX_CHARACTERISTIC = UUID.from_16_bits(0x2BDC, 'Active Preset Index')
|
|
282
|
+
|
|
278
283
|
# ASHA Service
|
|
279
284
|
GATT_ASHA_SERVICE = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid')
|
|
280
285
|
GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID('6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties')
|
|
@@ -340,7 +345,7 @@ class Service(Attribute):
|
|
|
340
345
|
uuid: Union[str, UUID],
|
|
341
346
|
characteristics: List[Characteristic],
|
|
342
347
|
primary=True,
|
|
343
|
-
included_services:
|
|
348
|
+
included_services: Iterable[Service] = (),
|
|
344
349
|
) -> None:
|
|
345
350
|
# Convert the uuid to a UUID object if it isn't already
|
|
346
351
|
if isinstance(uuid, str):
|
|
@@ -356,7 +361,7 @@ class Service(Attribute):
|
|
|
356
361
|
uuid.to_pdu_bytes(),
|
|
357
362
|
)
|
|
358
363
|
self.uuid = uuid
|
|
359
|
-
self.included_services = included_services
|
|
364
|
+
self.included_services = list(included_services)
|
|
360
365
|
self.characteristics = characteristics[:]
|
|
361
366
|
self.primary = primary
|
|
362
367
|
|
|
@@ -390,7 +395,7 @@ class TemplateService(Service):
|
|
|
390
395
|
self,
|
|
391
396
|
characteristics: List[Characteristic],
|
|
392
397
|
primary: bool = True,
|
|
393
|
-
included_services:
|
|
398
|
+
included_services: Iterable[Service] = (),
|
|
394
399
|
) -> None:
|
|
395
400
|
super().__init__(self.UUID, characteristics, primary, included_services)
|
|
396
401
|
|
bumble/gatt_client.py
CHANGED
|
@@ -68,7 +68,7 @@ from .att import (
|
|
|
68
68
|
ATT_Error,
|
|
69
69
|
)
|
|
70
70
|
from . import core
|
|
71
|
-
from .core import UUID, InvalidStateError
|
|
71
|
+
from .core import UUID, InvalidStateError
|
|
72
72
|
from .gatt import (
|
|
73
73
|
GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
|
|
74
74
|
GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
|
|
@@ -345,12 +345,7 @@ class Client:
|
|
|
345
345
|
self.mtu_exchange_done = True
|
|
346
346
|
response = await self.send_request(ATT_Exchange_MTU_Request(client_rx_mtu=mtu))
|
|
347
347
|
if response.op_code == ATT_ERROR_RESPONSE:
|
|
348
|
-
raise
|
|
349
|
-
response.error_code,
|
|
350
|
-
'att',
|
|
351
|
-
ATT_PDU.error_name(response.error_code),
|
|
352
|
-
response,
|
|
353
|
-
)
|
|
348
|
+
raise ATT_Error(error_code=response.error_code, message=response)
|
|
354
349
|
|
|
355
350
|
# Compute the final MTU
|
|
356
351
|
self.connection.att_mtu = min(mtu, response.server_rx_mtu)
|
|
@@ -936,12 +931,7 @@ class Client:
|
|
|
936
931
|
if response is None:
|
|
937
932
|
raise TimeoutError('read timeout')
|
|
938
933
|
if response.op_code == ATT_ERROR_RESPONSE:
|
|
939
|
-
raise
|
|
940
|
-
response.error_code,
|
|
941
|
-
'att',
|
|
942
|
-
ATT_PDU.error_name(response.error_code),
|
|
943
|
-
response,
|
|
944
|
-
)
|
|
934
|
+
raise ATT_Error(error_code=response.error_code, message=response)
|
|
945
935
|
|
|
946
936
|
# If the value is the max size for the MTU, try to read more unless the caller
|
|
947
937
|
# specifically asked not to do that
|
|
@@ -963,12 +953,7 @@ class Client:
|
|
|
963
953
|
ATT_INVALID_OFFSET_ERROR,
|
|
964
954
|
):
|
|
965
955
|
break
|
|
966
|
-
raise
|
|
967
|
-
response.error_code,
|
|
968
|
-
'att',
|
|
969
|
-
ATT_PDU.error_name(response.error_code),
|
|
970
|
-
response,
|
|
971
|
-
)
|
|
956
|
+
raise ATT_Error(error_code=response.error_code, message=response)
|
|
972
957
|
|
|
973
958
|
part = response.part_attribute_value
|
|
974
959
|
attribute_value += part
|
|
@@ -1061,12 +1046,7 @@ class Client:
|
|
|
1061
1046
|
)
|
|
1062
1047
|
)
|
|
1063
1048
|
if response.op_code == ATT_ERROR_RESPONSE:
|
|
1064
|
-
raise
|
|
1065
|
-
response.error_code,
|
|
1066
|
-
'att',
|
|
1067
|
-
ATT_PDU.error_name(response.error_code),
|
|
1068
|
-
response,
|
|
1069
|
-
)
|
|
1049
|
+
raise ATT_Error(error_code=response.error_code, message=response)
|
|
1070
1050
|
else:
|
|
1071
1051
|
await self.send_command(
|
|
1072
1052
|
ATT_Write_Command(
|
bumble/gatt_server.py
CHANGED
|
@@ -915,7 +915,7 @@ class Server(EventEmitter):
|
|
|
915
915
|
See Bluetooth spec Vol 3, Part F - 3.4.5.1 Write Request
|
|
916
916
|
'''
|
|
917
917
|
|
|
918
|
-
# Check
|
|
918
|
+
# Check that the attribute exists
|
|
919
919
|
attribute = self.get_attribute(request.attribute_handle)
|
|
920
920
|
if attribute is None:
|
|
921
921
|
self.send_response(
|
|
@@ -942,11 +942,19 @@ class Server(EventEmitter):
|
|
|
942
942
|
)
|
|
943
943
|
return
|
|
944
944
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
945
|
+
try:
|
|
946
|
+
# Accept the value
|
|
947
|
+
await attribute.write_value(connection, request.attribute_value)
|
|
948
|
+
except ATT_Error as error:
|
|
949
|
+
response = ATT_Error_Response(
|
|
950
|
+
request_opcode_in_error=request.op_code,
|
|
951
|
+
attribute_handle_in_error=request.attribute_handle,
|
|
952
|
+
error_code=error.error_code,
|
|
953
|
+
)
|
|
954
|
+
else:
|
|
955
|
+
# Done
|
|
956
|
+
response = ATT_Write_Response()
|
|
957
|
+
self.send_response(connection, response)
|
|
950
958
|
|
|
951
959
|
@AsyncRunner.run_in_task()
|
|
952
960
|
async def on_att_write_command(self, connection, request):
|