bumble 0.0.213__py3-none-any.whl → 0.0.215__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.
Files changed (123) hide show
  1. bumble/_version.py +16 -3
  2. bumble/a2dp.py +15 -16
  3. bumble/apps/auracast.py +14 -38
  4. bumble/apps/bench.py +10 -15
  5. bumble/apps/ble_rpa_tool.py +1 -0
  6. bumble/apps/console.py +22 -25
  7. bumble/apps/controller_info.py +20 -25
  8. bumble/apps/controller_loopback.py +6 -10
  9. bumble/apps/controllers.py +2 -3
  10. bumble/apps/device_info.py +4 -5
  11. bumble/apps/gatt_dump.py +3 -3
  12. bumble/apps/gg_bridge.py +7 -8
  13. bumble/apps/hci_bridge.py +4 -3
  14. bumble/apps/l2cap_bridge.py +5 -5
  15. bumble/apps/lea_unicast/app.py +16 -26
  16. bumble/apps/pair.py +30 -43
  17. bumble/apps/pandora_server.py +5 -4
  18. bumble/apps/player/player.py +20 -24
  19. bumble/apps/rfcomm_bridge.py +4 -10
  20. bumble/apps/scan.py +17 -8
  21. bumble/apps/show.py +4 -5
  22. bumble/apps/speaker/speaker.py +23 -27
  23. bumble/apps/unbond.py +3 -3
  24. bumble/apps/usb_probe.py +2 -4
  25. bumble/att.py +241 -246
  26. bumble/audio/io.py +5 -9
  27. bumble/avc.py +2 -2
  28. bumble/avctp.py +6 -7
  29. bumble/avdtp.py +19 -22
  30. bumble/avrcp.py +1097 -589
  31. bumble/codecs.py +2 -0
  32. bumble/controller.py +142 -35
  33. bumble/core.py +567 -248
  34. bumble/crypto/__init__.py +2 -2
  35. bumble/crypto/builtin.py +1 -1
  36. bumble/crypto/cryptography.py +2 -4
  37. bumble/data_types.py +1025 -0
  38. bumble/device.py +319 -267
  39. bumble/drivers/__init__.py +3 -2
  40. bumble/drivers/intel.py +3 -4
  41. bumble/drivers/rtk.py +26 -9
  42. bumble/gap.py +4 -4
  43. bumble/gatt.py +3 -2
  44. bumble/gatt_adapters.py +3 -11
  45. bumble/gatt_client.py +69 -81
  46. bumble/gatt_server.py +124 -124
  47. bumble/hci.py +114 -18
  48. bumble/helpers.py +19 -26
  49. bumble/hfp.py +10 -21
  50. bumble/hid.py +22 -16
  51. bumble/host.py +191 -103
  52. bumble/keys.py +5 -3
  53. bumble/l2cap.py +138 -104
  54. bumble/link.py +18 -19
  55. bumble/logging.py +65 -0
  56. bumble/pairing.py +7 -6
  57. bumble/pandora/__init__.py +9 -8
  58. bumble/pandora/config.py +3 -1
  59. bumble/pandora/device.py +3 -2
  60. bumble/pandora/host.py +38 -36
  61. bumble/pandora/l2cap.py +22 -21
  62. bumble/pandora/security.py +15 -15
  63. bumble/pandora/utils.py +5 -3
  64. bumble/profiles/aics.py +11 -11
  65. bumble/profiles/ams.py +403 -0
  66. bumble/profiles/ancs.py +6 -7
  67. bumble/profiles/ascs.py +14 -9
  68. bumble/profiles/asha.py +8 -12
  69. bumble/profiles/bap.py +11 -23
  70. bumble/profiles/bass.py +2 -7
  71. bumble/profiles/battery_service.py +3 -4
  72. bumble/profiles/cap.py +1 -2
  73. bumble/profiles/csip.py +2 -6
  74. bumble/profiles/device_information_service.py +2 -2
  75. bumble/profiles/gap.py +4 -4
  76. bumble/profiles/gatt_service.py +1 -4
  77. bumble/profiles/gmap.py +5 -5
  78. bumble/profiles/hap.py +62 -59
  79. bumble/profiles/heart_rate_service.py +5 -4
  80. bumble/profiles/le_audio.py +3 -1
  81. bumble/profiles/mcp.py +3 -7
  82. bumble/profiles/pacs.py +3 -6
  83. bumble/profiles/pbp.py +2 -0
  84. bumble/profiles/tmap.py +2 -3
  85. bumble/profiles/vcs.py +2 -8
  86. bumble/profiles/vocs.py +8 -8
  87. bumble/rfcomm.py +11 -14
  88. bumble/rtp.py +1 -0
  89. bumble/sdp.py +10 -8
  90. bumble/smp.py +151 -159
  91. bumble/snoop.py +5 -5
  92. bumble/tools/generate_company_id_list.py +1 -0
  93. bumble/tools/intel_fw_download.py +3 -3
  94. bumble/tools/intel_util.py +5 -4
  95. bumble/tools/rtk_fw_download.py +6 -3
  96. bumble/tools/rtk_util.py +26 -8
  97. bumble/transport/__init__.py +19 -15
  98. bumble/transport/android_emulator.py +8 -13
  99. bumble/transport/android_netsim.py +19 -18
  100. bumble/transport/common.py +12 -15
  101. bumble/transport/file.py +1 -1
  102. bumble/transport/hci_socket.py +4 -6
  103. bumble/transport/pty.py +5 -6
  104. bumble/transport/pyusb.py +7 -10
  105. bumble/transport/serial.py +2 -1
  106. bumble/transport/tcp_client.py +2 -2
  107. bumble/transport/tcp_server.py +11 -14
  108. bumble/transport/udp.py +3 -3
  109. bumble/transport/unix.py +67 -1
  110. bumble/transport/usb.py +6 -6
  111. bumble/transport/vhci.py +0 -1
  112. bumble/transport/ws_client.py +2 -1
  113. bumble/transport/ws_server.py +3 -2
  114. bumble/utils.py +20 -5
  115. bumble/vendor/android/hci.py +1 -2
  116. bumble/vendor/zephyr/hci.py +0 -1
  117. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/METADATA +4 -2
  118. bumble-0.0.215.dist-info/RECORD +183 -0
  119. bumble-0.0.213.dist-info/RECORD +0 -180
  120. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
  121. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
  122. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
  123. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/top_level.txt +0 -0
@@ -20,12 +20,13 @@ like loading firmware after a cold start.
20
20
  # Imports
21
21
  # -----------------------------------------------------------------------------
22
22
  from __future__ import annotations
23
+
23
24
  import logging
24
25
  import pathlib
25
26
  import platform
26
- from typing import Iterable, Optional, TYPE_CHECKING
27
+ from typing import TYPE_CHECKING, Iterable, Optional
27
28
 
28
- from bumble.drivers import rtk, intel
29
+ from bumble.drivers import intel, rtk
29
30
  from bumble.drivers.common import Driver
30
31
 
31
32
  if TYPE_CHECKING:
bumble/drivers/intel.py CHANGED
@@ -20,6 +20,7 @@ Loosely based on the Fuchsia OS implementation.
20
20
  # Imports
21
21
  # -----------------------------------------------------------------------------
22
22
  from __future__ import annotations
23
+
23
24
  import asyncio
24
25
  import collections
25
26
  import dataclasses
@@ -28,12 +29,10 @@ import os
28
29
  import pathlib
29
30
  import platform
30
31
  import struct
31
- from typing import Any, Optional, TYPE_CHECKING
32
+ from typing import TYPE_CHECKING, Any, Optional
32
33
 
33
- from bumble import core
34
+ from bumble import core, hci, utils
34
35
  from bumble.drivers import common
35
- from bumble import hci
36
- from bumble import utils
37
36
 
38
37
  if TYPE_CHECKING:
39
38
  from bumble.host import Host
bumble/drivers/rtk.py CHANGED
@@ -17,10 +17,6 @@ Based on various online bits of information, including the Linux kernel.
17
17
  (see `drivers/bluetooth/btrtl.c`)
18
18
  """
19
19
 
20
- # -----------------------------------------------------------------------------
21
- # Imports
22
- # -----------------------------------------------------------------------------
23
- from dataclasses import dataclass, field
24
20
  import asyncio
25
21
  import enum
26
22
  import logging
@@ -31,9 +27,12 @@ import platform
31
27
  import struct
32
28
  import weakref
33
29
 
30
+ # -----------------------------------------------------------------------------
31
+ # Imports
32
+ # -----------------------------------------------------------------------------
33
+ from dataclasses import dataclass, field
34
34
 
35
- from bumble import core
36
- from bumble import hci
35
+ from bumble import core, hci
37
36
  from bumble.drivers import common
38
37
 
39
38
  # -----------------------------------------------------------------------------
@@ -489,6 +488,21 @@ class Driver(common.Driver):
489
488
 
490
489
  return True
491
490
 
491
+ @staticmethod
492
+ async def get_loaded_firmware_version(host):
493
+ response = await host.send_command(HCI_RTK_Read_ROM_Version_Command())
494
+
495
+ if response.return_parameters.status != hci.HCI_SUCCESS:
496
+ return None
497
+
498
+ response = await host.send_command(
499
+ hci.HCI_Read_Local_Version_Information_Command(), check_result=True
500
+ )
501
+ return (
502
+ response.return_parameters.hci_subversion << 16
503
+ | response.return_parameters.lmp_subversion
504
+ )
505
+
492
506
  @classmethod
493
507
  async def driver_info_for_host(cls, host):
494
508
  try:
@@ -592,7 +606,7 @@ class Driver(common.Driver):
592
606
  )
593
607
  if response.return_parameters.status != hci.HCI_SUCCESS:
594
608
  logger.warning("can't get ROM version")
595
- return
609
+ return None
596
610
  rom_version = response.return_parameters.version
597
611
  logger.debug(f"ROM version before download: {rom_version:04X}")
598
612
  else:
@@ -600,13 +614,14 @@ class Driver(common.Driver):
600
614
 
601
615
  firmware = Firmware(self.firmware)
602
616
  logger.debug(f"firmware: project_id=0x{firmware.project_id:04X}")
617
+ logger.debug(f"firmware: version=0x{firmware.version:04X}")
603
618
  for patch in firmware.patches:
604
619
  if patch[0] == rom_version + 1:
605
620
  logger.debug(f"using patch {patch[0]}")
606
621
  break
607
622
  else:
608
623
  logger.warning("no valid patch found for rom version {rom_version}")
609
- return
624
+ return None
610
625
 
611
626
  # Append the config if there is one.
612
627
  if self.config:
@@ -642,7 +657,9 @@ class Driver(common.Driver):
642
657
  logger.warning("can't get ROM version")
643
658
  else:
644
659
  rom_version = response.return_parameters.version
645
- logger.debug(f"ROM version after download: {rom_version:04X}")
660
+ logger.debug(f"ROM version after download: {rom_version:02X}")
661
+
662
+ return firmware.version
646
663
 
647
664
  async def download_firmware(self):
648
665
  if self.driver_info.rom == RTK_ROM_LMP_8723A:
bumble/gap.py CHANGED
@@ -19,11 +19,11 @@ import logging
19
19
  import struct
20
20
 
21
21
  from bumble.gatt import (
22
- Service,
23
- Characteristic,
24
- GATT_GENERIC_ACCESS_SERVICE,
25
- GATT_DEVICE_NAME_CHARACTERISTIC,
26
22
  GATT_APPEARANCE_CHARACTERISTIC,
23
+ GATT_DEVICE_NAME_CHARACTERISTIC,
24
+ GATT_GENERIC_ACCESS_SERVICE,
25
+ Characteristic,
26
+ Service,
27
27
  )
28
28
 
29
29
  # -----------------------------------------------------------------------------
bumble/gatt.py CHANGED
@@ -23,15 +23,16 @@
23
23
  # Imports
24
24
  # -----------------------------------------------------------------------------
25
25
  from __future__ import annotations
26
+
26
27
  import enum
27
28
  import functools
28
29
  import logging
29
30
  import struct
30
31
  from typing import Iterable, Optional, Sequence, TypeVar, Union
31
32
 
32
- from bumble.colors import color
33
- from bumble.core import BaseBumbleError, UUID
34
33
  from bumble.att import Attribute, AttributeValue
34
+ from bumble.colors import color
35
+ from bumble.core import UUID, BaseBumbleError
35
36
 
36
37
  # -----------------------------------------------------------------------------
37
38
  # Typing
bumble/gatt_adapters.py CHANGED
@@ -20,22 +20,14 @@
20
20
  # Imports
21
21
  # -----------------------------------------------------------------------------
22
22
  from __future__ import annotations
23
+
23
24
  import struct
24
- from typing import (
25
- Any,
26
- Callable,
27
- Generic,
28
- Iterable,
29
- Literal,
30
- Optional,
31
- TypeVar,
32
- )
25
+ from typing import Any, Callable, Generic, Iterable, Literal, Optional, TypeVar
33
26
 
27
+ from bumble import utils
34
28
  from bumble.core import InvalidOperationError
35
29
  from bumble.gatt import Characteristic
36
30
  from bumble.gatt_client import CharacteristicProxy
37
- from bumble import utils
38
-
39
31
 
40
32
  # -----------------------------------------------------------------------------
41
33
  # Typing
bumble/gatt_client.py CHANGED
@@ -24,60 +24,38 @@
24
24
  # Imports
25
25
  # -----------------------------------------------------------------------------
26
26
  from __future__ import annotations
27
+
27
28
  import asyncio
28
29
  import logging
29
30
  import struct
30
31
  from datetime import datetime
31
32
  from typing import (
33
+ TYPE_CHECKING,
32
34
  Any,
33
35
  Callable,
34
36
  Generic,
35
37
  Iterable,
36
38
  Optional,
37
- Union,
38
39
  TypeVar,
39
- TYPE_CHECKING,
40
+ Union,
40
41
  )
41
42
 
42
-
43
+ from bumble import att, core, utils
43
44
  from bumble.colors import color
44
- from bumble.hci import HCI_Constant
45
- from bumble.att import (
46
- ATT_ATTRIBUTE_NOT_FOUND_ERROR,
47
- ATT_ATTRIBUTE_NOT_LONG_ERROR,
48
- ATT_CID,
49
- ATT_DEFAULT_MTU,
50
- ATT_ERROR_RESPONSE,
51
- ATT_INVALID_OFFSET_ERROR,
52
- ATT_PDU,
53
- ATT_RESPONSES,
54
- ATT_Exchange_MTU_Request,
55
- ATT_Find_By_Type_Value_Request,
56
- ATT_Find_Information_Request,
57
- ATT_Handle_Value_Confirmation,
58
- ATT_Read_Blob_Request,
59
- ATT_Read_By_Group_Type_Request,
60
- ATT_Read_By_Type_Request,
61
- ATT_Read_Request,
62
- ATT_Write_Command,
63
- ATT_Write_Request,
64
- ATT_Error,
65
- )
66
- from bumble import utils
67
- from bumble import core
68
45
  from bumble.core import UUID, InvalidStateError
69
46
  from bumble.gatt import (
70
47
  GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
71
48
  GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
49
+ GATT_INCLUDE_ATTRIBUTE_TYPE,
72
50
  GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
73
51
  GATT_REQUEST_TIMEOUT,
74
52
  GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
75
- GATT_INCLUDE_ATTRIBUTE_TYPE,
76
53
  Characteristic,
77
54
  ClientCharacteristicConfigurationBits,
78
55
  InvalidServiceError,
79
56
  TemplateService,
80
57
  )
58
+ from bumble.hci import HCI_Constant
81
59
 
82
60
  # -----------------------------------------------------------------------------
83
61
  # Typing
@@ -291,8 +269,8 @@ class Client:
291
269
  indication_subscribers: dict[
292
270
  int, set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
293
271
  ]
294
- pending_response: Optional[asyncio.futures.Future[ATT_PDU]]
295
- pending_request: Optional[ATT_PDU]
272
+ pending_response: Optional[asyncio.futures.Future[att.ATT_PDU]]
273
+ pending_request: Optional[att.ATT_PDU]
296
274
 
297
275
  def __init__(self, connection: Connection) -> None:
298
276
  self.connection = connection
@@ -308,15 +286,15 @@ class Client:
308
286
  connection.on(connection.EVENT_DISCONNECTION, self.on_disconnection)
309
287
 
310
288
  def send_gatt_pdu(self, pdu: bytes) -> None:
311
- self.connection.send_l2cap_pdu(ATT_CID, pdu)
289
+ self.connection.send_l2cap_pdu(att.ATT_CID, pdu)
312
290
 
313
- async def send_command(self, command: ATT_PDU) -> None:
291
+ async def send_command(self, command: att.ATT_PDU) -> None:
314
292
  logger.debug(
315
293
  f'GATT Command from client: [0x{self.connection.handle:04X}] {command}'
316
294
  )
317
295
  self.send_gatt_pdu(bytes(command))
318
296
 
319
- async def send_request(self, request: ATT_PDU):
297
+ async def send_request(self, request: att.ATT_PDU):
320
298
  logger.debug(
321
299
  f'GATT Request from client: [0x{self.connection.handle:04X}] {request}'
322
300
  )
@@ -345,7 +323,9 @@ class Client:
345
323
 
346
324
  return response
347
325
 
348
- def send_confirmation(self, confirmation: ATT_Handle_Value_Confirmation) -> None:
326
+ def send_confirmation(
327
+ self, confirmation: att.ATT_Handle_Value_Confirmation
328
+ ) -> None:
349
329
  logger.debug(
350
330
  f'GATT Confirmation from client: [0x{self.connection.handle:04X}] '
351
331
  f'{confirmation}'
@@ -354,8 +334,8 @@ class Client:
354
334
 
355
335
  async def request_mtu(self, mtu: int) -> int:
356
336
  # Check the range
357
- if mtu < ATT_DEFAULT_MTU:
358
- raise core.InvalidArgumentError(f'MTU must be >= {ATT_DEFAULT_MTU}')
337
+ if mtu < att.ATT_DEFAULT_MTU:
338
+ raise core.InvalidArgumentError(f'MTU must be >= {att.ATT_DEFAULT_MTU}')
359
339
  if mtu > 0xFFFF:
360
340
  raise core.InvalidArgumentError('MTU must be <= 0xFFFF')
361
341
 
@@ -365,9 +345,11 @@ class Client:
365
345
 
366
346
  # Send the request
367
347
  self.mtu_exchange_done = True
368
- response = await self.send_request(ATT_Exchange_MTU_Request(client_rx_mtu=mtu))
369
- if response.op_code == ATT_ERROR_RESPONSE:
370
- raise ATT_Error(error_code=response.error_code, message=response)
348
+ response = await self.send_request(
349
+ att.ATT_Exchange_MTU_Request(client_rx_mtu=mtu)
350
+ )
351
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
352
+ raise att.ATT_Error(error_code=response.error_code, message=response)
371
353
 
372
354
  # Compute the final MTU
373
355
  self.connection.att_mtu = min(mtu, response.server_rx_mtu)
@@ -432,7 +414,7 @@ class Client:
432
414
  services = []
433
415
  while starting_handle < 0xFFFF:
434
416
  response = await self.send_request(
435
- ATT_Read_By_Group_Type_Request(
417
+ att.ATT_Read_By_Group_Type_Request(
436
418
  starting_handle=starting_handle,
437
419
  ending_handle=0xFFFF,
438
420
  attribute_group_type=GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
@@ -443,14 +425,14 @@ class Client:
443
425
  return []
444
426
 
445
427
  # Check if we reached the end of the iteration
446
- if response.op_code == ATT_ERROR_RESPONSE:
447
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
428
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
429
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
448
430
  # Unexpected end
449
431
  logger.warning(
450
432
  '!!! unexpected error while discovering services: '
451
433
  f'{HCI_Constant.error_name(response.error_code)}'
452
434
  )
453
- raise ATT_Error(
435
+ raise att.ATT_Error(
454
436
  error_code=response.error_code,
455
437
  message='Unexpected error while discovering services',
456
438
  )
@@ -509,7 +491,7 @@ class Client:
509
491
  services = []
510
492
  while starting_handle < 0xFFFF:
511
493
  response = await self.send_request(
512
- ATT_Find_By_Type_Value_Request(
494
+ att.ATT_Find_By_Type_Value_Request(
513
495
  starting_handle=starting_handle,
514
496
  ending_handle=0xFFFF,
515
497
  attribute_type=GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
@@ -521,8 +503,8 @@ class Client:
521
503
  return []
522
504
 
523
505
  # Check if we reached the end of the iteration
524
- if response.op_code == ATT_ERROR_RESPONSE:
525
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
506
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
507
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
526
508
  # Unexpected end
527
509
  logger.warning(
528
510
  '!!! unexpected error while discovering services: '
@@ -578,7 +560,7 @@ class Client:
578
560
  included_services: list[ServiceProxy] = []
579
561
  while starting_handle <= ending_handle:
580
562
  response = await self.send_request(
581
- ATT_Read_By_Type_Request(
563
+ att.ATT_Read_By_Type_Request(
582
564
  starting_handle=starting_handle,
583
565
  ending_handle=ending_handle,
584
566
  attribute_type=GATT_INCLUDE_ATTRIBUTE_TYPE,
@@ -589,14 +571,14 @@ class Client:
589
571
  return []
590
572
 
591
573
  # Check if we reached the end of the iteration
592
- if response.op_code == ATT_ERROR_RESPONSE:
593
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
574
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
575
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
594
576
  # Unexpected end
595
577
  logger.warning(
596
578
  '!!! unexpected error while discovering included services: '
597
579
  f'{HCI_Constant.error_name(response.error_code)}'
598
580
  )
599
- raise ATT_Error(
581
+ raise att.ATT_Error(
600
582
  error_code=response.error_code,
601
583
  message='Unexpected error while discovering included services',
602
584
  )
@@ -652,7 +634,7 @@ class Client:
652
634
  characteristics: list[CharacteristicProxy[bytes]] = []
653
635
  while starting_handle <= ending_handle:
654
636
  response = await self.send_request(
655
- ATT_Read_By_Type_Request(
637
+ att.ATT_Read_By_Type_Request(
656
638
  starting_handle=starting_handle,
657
639
  ending_handle=ending_handle,
658
640
  attribute_type=GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
@@ -663,14 +645,14 @@ class Client:
663
645
  return []
664
646
 
665
647
  # Check if we reached the end of the iteration
666
- if response.op_code == ATT_ERROR_RESPONSE:
667
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
648
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
649
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
668
650
  # Unexpected end
669
651
  logger.warning(
670
652
  '!!! unexpected error while discovering characteristics: '
671
653
  f'{HCI_Constant.error_name(response.error_code)}'
672
654
  )
673
- raise ATT_Error(
655
+ raise att.ATT_Error(
674
656
  error_code=response.error_code,
675
657
  message='Unexpected error while discovering characteristics',
676
658
  )
@@ -736,7 +718,7 @@ class Client:
736
718
  descriptors: list[DescriptorProxy] = []
737
719
  while starting_handle <= ending_handle:
738
720
  response = await self.send_request(
739
- ATT_Find_Information_Request(
721
+ att.ATT_Find_Information_Request(
740
722
  starting_handle=starting_handle, ending_handle=ending_handle
741
723
  )
742
724
  )
@@ -745,8 +727,8 @@ class Client:
745
727
  return []
746
728
 
747
729
  # Check if we reached the end of the iteration
748
- if response.op_code == ATT_ERROR_RESPONSE:
749
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
730
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
731
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
750
732
  # Unexpected end
751
733
  logger.warning(
752
734
  '!!! unexpected error while discovering descriptors: '
@@ -791,7 +773,7 @@ class Client:
791
773
  attributes = []
792
774
  while True:
793
775
  response = await self.send_request(
794
- ATT_Find_Information_Request(
776
+ att.ATT_Find_Information_Request(
795
777
  starting_handle=starting_handle, ending_handle=ending_handle
796
778
  )
797
779
  )
@@ -799,8 +781,8 @@ class Client:
799
781
  return []
800
782
 
801
783
  # Check if we reached the end of the iteration
802
- if response.op_code == ATT_ERROR_RESPONSE:
803
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
784
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
785
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
804
786
  # Unexpected end
805
787
  logger.warning(
806
788
  '!!! unexpected error while discovering attributes: '
@@ -954,12 +936,12 @@ class Client:
954
936
  # Send a request to read
955
937
  attribute_handle = attribute if isinstance(attribute, int) else attribute.handle
956
938
  response = await self.send_request(
957
- ATT_Read_Request(attribute_handle=attribute_handle)
939
+ att.ATT_Read_Request(attribute_handle=attribute_handle)
958
940
  )
959
941
  if response is None:
960
942
  raise TimeoutError('read timeout')
961
- if response.op_code == ATT_ERROR_RESPONSE:
962
- raise ATT_Error(error_code=response.error_code, message=response)
943
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
944
+ raise att.ATT_Error(error_code=response.error_code, message=response)
963
945
 
964
946
  # If the value is the max size for the MTU, try to read more unless the caller
965
947
  # specifically asked not to do that
@@ -969,19 +951,21 @@ class Client:
969
951
  offset = len(attribute_value)
970
952
  while True:
971
953
  response = await self.send_request(
972
- ATT_Read_Blob_Request(
954
+ att.ATT_Read_Blob_Request(
973
955
  attribute_handle=attribute_handle, value_offset=offset
974
956
  )
975
957
  )
976
958
  if response is None:
977
959
  raise TimeoutError('read timeout')
978
- if response.op_code == ATT_ERROR_RESPONSE:
960
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
979
961
  if response.error_code in (
980
- ATT_ATTRIBUTE_NOT_LONG_ERROR,
981
- ATT_INVALID_OFFSET_ERROR,
962
+ att.ATT_ATTRIBUTE_NOT_LONG_ERROR,
963
+ att.ATT_INVALID_OFFSET_ERROR,
982
964
  ):
983
965
  break
984
- raise ATT_Error(error_code=response.error_code, message=response)
966
+ raise att.ATT_Error(
967
+ error_code=response.error_code, message=response
968
+ )
985
969
 
986
970
  part = response.part_attribute_value
987
971
  attribute_value += part
@@ -1012,7 +996,7 @@ class Client:
1012
996
  characteristics_values = []
1013
997
  while starting_handle <= ending_handle:
1014
998
  response = await self.send_request(
1015
- ATT_Read_By_Type_Request(
999
+ att.ATT_Read_By_Type_Request(
1016
1000
  starting_handle=starting_handle,
1017
1001
  ending_handle=ending_handle,
1018
1002
  attribute_type=uuid,
@@ -1023,8 +1007,8 @@ class Client:
1023
1007
  return []
1024
1008
 
1025
1009
  # Check if we reached the end of the iteration
1026
- if response.op_code == ATT_ERROR_RESPONSE:
1027
- if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
1010
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
1011
+ if response.error_code != att.ATT_ATTRIBUTE_NOT_FOUND_ERROR:
1028
1012
  # Unexpected end
1029
1013
  logger.warning(
1030
1014
  '!!! unexpected error while reading characteristics: '
@@ -1069,15 +1053,15 @@ class Client:
1069
1053
  attribute_handle = attribute if isinstance(attribute, int) else attribute.handle
1070
1054
  if with_response:
1071
1055
  response = await self.send_request(
1072
- ATT_Write_Request(
1056
+ att.ATT_Write_Request(
1073
1057
  attribute_handle=attribute_handle, attribute_value=value
1074
1058
  )
1075
1059
  )
1076
- if response.op_code == ATT_ERROR_RESPONSE:
1077
- raise ATT_Error(error_code=response.error_code, message=response)
1060
+ if response.op_code == att.Opcode.ATT_ERROR_RESPONSE:
1061
+ raise att.ATT_Error(error_code=response.error_code, message=response)
1078
1062
  else:
1079
1063
  await self.send_command(
1080
- ATT_Write_Command(
1064
+ att.ATT_Write_Command(
1081
1065
  attribute_handle=attribute_handle, attribute_value=value
1082
1066
  )
1083
1067
  )
@@ -1086,11 +1070,11 @@ class Client:
1086
1070
  if self.pending_response and not self.pending_response.done():
1087
1071
  self.pending_response.cancel()
1088
1072
 
1089
- def on_gatt_pdu(self, att_pdu: ATT_PDU) -> None:
1073
+ def on_gatt_pdu(self, att_pdu: att.ATT_PDU) -> None:
1090
1074
  logger.debug(
1091
1075
  f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}'
1092
1076
  )
1093
- if att_pdu.op_code in ATT_RESPONSES:
1077
+ if att_pdu.op_code in att.ATT_RESPONSES:
1094
1078
  if self.pending_request is None:
1095
1079
  # Not expected!
1096
1080
  logger.warning('!!! unexpected response, there is no pending request')
@@ -1098,7 +1082,7 @@ class Client:
1098
1082
 
1099
1083
  # The response should match the pending request unless it is
1100
1084
  # an error response
1101
- if att_pdu.op_code != ATT_ERROR_RESPONSE:
1085
+ if att_pdu.op_code != att.Opcode.ATT_ERROR_RESPONSE:
1102
1086
  expected_response_name = self.pending_request.name.replace(
1103
1087
  '_REQUEST', '_RESPONSE'
1104
1088
  )
@@ -1126,7 +1110,9 @@ class Client:
1126
1110
  + str(att_pdu)
1127
1111
  )
1128
1112
 
1129
- def on_att_handle_value_notification(self, notification):
1113
+ def on_att_handle_value_notification(
1114
+ self, notification: att.ATT_Handle_Value_Notification
1115
+ ):
1130
1116
  # Call all subscribers
1131
1117
  subscribers = self.notification_subscribers.get(
1132
1118
  notification.attribute_handle, set()
@@ -1141,7 +1127,9 @@ class Client:
1141
1127
  else:
1142
1128
  subscriber.emit(subscriber.EVENT_UPDATE, notification.attribute_value)
1143
1129
 
1144
- def on_att_handle_value_indication(self, indication):
1130
+ def on_att_handle_value_indication(
1131
+ self, indication: att.ATT_Handle_Value_Indication
1132
+ ):
1145
1133
  # Call all subscribers
1146
1134
  subscribers = self.indication_subscribers.get(
1147
1135
  indication.attribute_handle, set()
@@ -1157,7 +1145,7 @@ class Client:
1157
1145
  subscriber.emit(subscriber.EVENT_UPDATE, indication.attribute_value)
1158
1146
 
1159
1147
  # Confirm that we received the indication
1160
- self.send_confirmation(ATT_Handle_Value_Confirmation())
1148
+ self.send_confirmation(att.ATT_Handle_Value_Confirmation())
1161
1149
 
1162
1150
  def cache_value(self, attribute_handle: int, value: bytes) -> None:
1163
1151
  self.cached_values[attribute_handle] = (