bumble 0.0.212__py3-none-any.whl → 0.0.214__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 (92) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +6 -0
  3. bumble/apps/README.md +0 -3
  4. bumble/apps/auracast.py +14 -11
  5. bumble/apps/bench.py +482 -37
  6. bumble/apps/console.py +3 -3
  7. bumble/apps/controller_info.py +44 -12
  8. bumble/apps/controller_loopback.py +7 -7
  9. bumble/apps/controllers.py +4 -5
  10. bumble/apps/device_info.py +4 -5
  11. bumble/apps/gatt_dump.py +5 -5
  12. bumble/apps/gg_bridge.py +5 -5
  13. bumble/apps/hci_bridge.py +5 -4
  14. bumble/apps/l2cap_bridge.py +5 -5
  15. bumble/apps/lea_unicast/app.py +8 -3
  16. bumble/apps/pair.py +19 -11
  17. bumble/apps/pandora_server.py +2 -2
  18. bumble/apps/player/player.py +2 -3
  19. bumble/apps/rfcomm_bridge.py +3 -4
  20. bumble/apps/scan.py +4 -5
  21. bumble/apps/show.py +6 -4
  22. bumble/apps/speaker/speaker.html +1 -0
  23. bumble/apps/speaker/speaker.js +113 -62
  24. bumble/apps/speaker/speaker.py +123 -19
  25. bumble/apps/unbond.py +2 -3
  26. bumble/apps/usb_probe.py +2 -3
  27. bumble/at.py +4 -4
  28. bumble/att.py +2 -6
  29. bumble/avc.py +7 -7
  30. bumble/avctp.py +3 -3
  31. bumble/avdtp.py +16 -20
  32. bumble/avrcp.py +42 -54
  33. bumble/colors.py +2 -2
  34. bumble/controller.py +174 -45
  35. bumble/device.py +398 -182
  36. bumble/drivers/__init__.py +2 -2
  37. bumble/drivers/common.py +0 -2
  38. bumble/drivers/intel.py +37 -40
  39. bumble/drivers/rtk.py +28 -35
  40. bumble/gatt.py +4 -4
  41. bumble/gatt_adapters.py +4 -5
  42. bumble/gatt_client.py +26 -31
  43. bumble/gatt_server.py +7 -11
  44. bumble/hci.py +2648 -2909
  45. bumble/helpers.py +4 -5
  46. bumble/hfp.py +32 -37
  47. bumble/host.py +104 -35
  48. bumble/keys.py +5 -5
  49. bumble/l2cap.py +312 -409
  50. bumble/link.py +16 -280
  51. bumble/logging.py +65 -0
  52. bumble/pairing.py +23 -20
  53. bumble/pandora/__init__.py +2 -2
  54. bumble/pandora/config.py +2 -2
  55. bumble/pandora/device.py +6 -6
  56. bumble/pandora/host.py +27 -28
  57. bumble/pandora/l2cap.py +2 -2
  58. bumble/pandora/security.py +6 -6
  59. bumble/pandora/utils.py +3 -3
  60. bumble/profiles/ams.py +404 -0
  61. bumble/profiles/ascs.py +142 -131
  62. bumble/profiles/asha.py +2 -2
  63. bumble/profiles/bap.py +3 -4
  64. bumble/profiles/csip.py +2 -2
  65. bumble/profiles/device_information_service.py +2 -2
  66. bumble/profiles/gap.py +2 -2
  67. bumble/profiles/hap.py +34 -33
  68. bumble/profiles/le_audio.py +4 -4
  69. bumble/profiles/mcp.py +4 -4
  70. bumble/profiles/vcs.py +3 -5
  71. bumble/rfcomm.py +10 -10
  72. bumble/rtp.py +1 -2
  73. bumble/sdp.py +2 -2
  74. bumble/smp.py +62 -63
  75. bumble/tools/intel_util.py +3 -2
  76. bumble/tools/rtk_util.py +6 -5
  77. bumble/transport/__init__.py +2 -16
  78. bumble/transport/android_netsim.py +5 -5
  79. bumble/transport/common.py +4 -4
  80. bumble/transport/pyusb.py +2 -2
  81. bumble/utils.py +2 -5
  82. bumble/vendor/android/hci.py +118 -200
  83. bumble/vendor/zephyr/hci.py +32 -27
  84. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/METADATA +4 -3
  85. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/RECORD +89 -90
  86. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/WHEEL +1 -1
  87. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/entry_points.txt +0 -1
  88. bumble/apps/link_relay/__init__.py +0 -0
  89. bumble/apps/link_relay/link_relay.py +0 -289
  90. bumble/apps/link_relay/logging.yml +0 -21
  91. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/licenses/LICENSE +0 -0
  92. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/top_level.txt +0 -0
@@ -17,7 +17,6 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  import logging
19
19
  import asyncio
20
- import os
21
20
  from typing import Any, Optional
22
21
 
23
22
  import click
@@ -26,6 +25,8 @@ from bumble.colors import color
26
25
  from bumble import transport
27
26
  from bumble.drivers import intel
28
27
  from bumble.host import Host
28
+ import bumble.logging
29
+
29
30
 
30
31
  # -----------------------------------------------------------------------------
31
32
  # Logging
@@ -107,7 +108,7 @@ async def do_bootloader(usb_transport: str, force: bool) -> None:
107
108
  # -----------------------------------------------------------------------------
108
109
  @click.group()
109
110
  def main():
110
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
111
+ bumble.logging.setup_basic_logging()
111
112
 
112
113
 
113
114
  @main.command
bumble/tools/rtk_util.py CHANGED
@@ -15,15 +15,16 @@
15
15
  # -----------------------------------------------------------------------------
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
- import logging
19
18
  import asyncio
20
- import os
19
+ import logging
21
20
 
22
21
  import click
23
22
 
24
23
  from bumble import transport
25
24
  from bumble.host import Host
26
25
  from bumble.drivers import rtk
26
+ import bumble.logging
27
+
27
28
 
28
29
  # -----------------------------------------------------------------------------
29
30
  # Logging
@@ -50,7 +51,7 @@ def do_parse(firmware_path):
50
51
 
51
52
  # -----------------------------------------------------------------------------
52
53
  async def do_load(usb_transport, force):
53
- async with await transport.open_transport_or_link(usb_transport) as (
54
+ async with await transport.open_transport(usb_transport) as (
54
55
  hci_source,
55
56
  hci_sink,
56
57
  ):
@@ -69,7 +70,7 @@ async def do_load(usb_transport, force):
69
70
 
70
71
  # -----------------------------------------------------------------------------
71
72
  async def do_drop(usb_transport):
72
- async with await transport.open_transport_or_link(usb_transport) as (
73
+ async with await transport.open_transport(usb_transport) as (
73
74
  hci_source,
74
75
  hci_sink,
75
76
  ):
@@ -112,7 +113,7 @@ async def do_info(usb_transport, force):
112
113
  # -----------------------------------------------------------------------------
113
114
  @click.group()
114
115
  def main():
115
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
116
+ bumble.logging.setup_basic_logging()
116
117
 
117
118
 
118
119
  @main.command
@@ -20,9 +20,9 @@ import logging
20
20
  import os
21
21
  from typing import Optional
22
22
 
23
+ from bumble import utils
23
24
  from bumble.transport.common import (
24
25
  Transport,
25
- AsyncPipeSink,
26
26
  SnoopingTransport,
27
27
  TransportSpecError,
28
28
  )
@@ -195,6 +195,7 @@ async def _open_transport(scheme: str, spec: Optional[str]) -> Transport:
195
195
 
196
196
 
197
197
  # -----------------------------------------------------------------------------
198
+ @utils.deprecated("RemoteLink has been removed. Use open_transport instead.")
198
199
  async def open_transport_or_link(name: str) -> Transport:
199
200
  """
200
201
  Open a transport or a link relay.
@@ -205,21 +206,6 @@ async def open_transport_or_link(name: str) -> Transport:
205
206
  When the name starts with "link-relay:", open a link relay (see RemoteLink
206
207
  for details on what the arguments are).
207
208
  For other namespaces, see `open_transport`.
208
-
209
209
  """
210
- if name.startswith('link-relay:'):
211
- logger.warning('Link Relay has been deprecated.')
212
- from bumble.controller import Controller
213
- from bumble.link import RemoteLink # lazy import
214
-
215
- link = RemoteLink(name[11:])
216
- await link.wait_until_connected()
217
- controller = Controller('remote', link=link) # type:ignore[arg-type]
218
-
219
- class LinkTransport(Transport):
220
- async def close(self):
221
- link.close()
222
-
223
- return _wrap_transport(LinkTransport(controller, AsyncPipeSink(controller)))
224
210
 
225
211
  return await open_transport(name)
@@ -22,7 +22,7 @@ import os
22
22
  import pathlib
23
23
  import platform
24
24
  import sys
25
- from typing import Dict, Optional
25
+ from typing import Optional
26
26
 
27
27
  import grpc.aio
28
28
 
@@ -143,7 +143,7 @@ def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:
143
143
 
144
144
  # -----------------------------------------------------------------------------
145
145
  async def open_android_netsim_controller_transport(
146
- server_host: Optional[str], server_port: int, options: Dict[str, str]
146
+ server_host: Optional[str], server_port: int, options: dict[str, str]
147
147
  ) -> Transport:
148
148
  if not server_port:
149
149
  raise TransportSpecError('invalid port')
@@ -288,7 +288,7 @@ async def open_android_netsim_controller_transport(
288
288
  async def open_android_netsim_host_transport_with_address(
289
289
  server_host: Optional[str],
290
290
  server_port: int,
291
- options: Optional[Dict[str, str]] = None,
291
+ options: Optional[dict[str, str]] = None,
292
292
  ):
293
293
  if server_host == '_' or not server_host:
294
294
  server_host = 'localhost'
@@ -313,7 +313,7 @@ async def open_android_netsim_host_transport_with_address(
313
313
 
314
314
  # -----------------------------------------------------------------------------
315
315
  async def open_android_netsim_host_transport_with_channel(
316
- channel, options: Optional[Dict[str, str]] = None
316
+ channel, options: Optional[dict[str, str]] = None
317
317
  ):
318
318
  # Wrapper for I/O operations
319
319
  class HciDevice:
@@ -451,7 +451,7 @@ async def open_android_netsim_transport(spec: Optional[str]) -> Transport:
451
451
  port = 0
452
452
  params_offset = 0
453
453
 
454
- options: Dict[str, str] = {}
454
+ options: dict[str, str] = {}
455
455
  for param in params[params_offset:]:
456
456
  if '=' not in param:
457
457
  raise TransportSpecError('invalid parameter, expected <name>=<value>')
@@ -21,7 +21,7 @@ import struct
21
21
  import asyncio
22
22
  import logging
23
23
  import io
24
- from typing import Any, ContextManager, Tuple, Optional, Protocol, Dict
24
+ from typing import Any, ContextManager, Optional, Protocol
25
25
 
26
26
  from bumble import core
27
27
  from bumble import hci
@@ -38,7 +38,7 @@ logger = logging.getLogger(__name__)
38
38
  # Information needed to parse HCI packets with a generic parser:
39
39
  # For each packet type, the info represents:
40
40
  # (length-size, length-offset, unpack-type)
41
- HCI_PACKET_INFO: Dict[int, Tuple[int, int, str]] = {
41
+ HCI_PACKET_INFO: dict[int, tuple[int, int, str]] = {
42
42
  hci.HCI_COMMAND_PACKET: (1, 2, 'B'),
43
43
  hci.HCI_ACL_DATA_PACKET: (2, 2, 'H'),
44
44
  hci.HCI_SYNCHRONOUS_DATA_PACKET: (1, 2, 'B'),
@@ -108,8 +108,8 @@ class PacketParser:
108
108
  NEED_BODY = 2
109
109
 
110
110
  sink: Optional[TransportSink]
111
- extended_packet_info: Dict[int, Tuple[int, int, str]]
112
- packet_info: Optional[Tuple[int, int, str]] = None
111
+ extended_packet_info: dict[int, tuple[int, int, str]]
112
+ packet_info: Optional[tuple[int, int, str]] = None
113
113
 
114
114
  def __init__(self, sink: Optional[TransportSink] = None) -> None:
115
115
  self.sink = sink
bumble/transport/pyusb.py CHANGED
@@ -23,7 +23,7 @@ import time
23
23
  import usb.core
24
24
  import usb.util
25
25
 
26
- from typing import Optional, Set
26
+ from typing import Optional
27
27
  from usb.core import Device as UsbDevice
28
28
  from usb.core import USBError
29
29
  from usb.util import CTRL_TYPE_CLASS, CTRL_RECIPIENT_OTHER
@@ -49,7 +49,7 @@ logger = logging.getLogger(__name__)
49
49
  # -----------------------------------------------------------------------------
50
50
  # Global
51
51
  # -----------------------------------------------------------------------------
52
- devices_in_use: Set[int] = set()
52
+ devices_in_use: set[int] = set()
53
53
 
54
54
 
55
55
  # -----------------------------------------------------------------------------
bumble/utils.py CHANGED
@@ -27,11 +27,8 @@ from typing import (
27
27
  Any,
28
28
  Awaitable,
29
29
  Callable,
30
- List,
31
30
  Optional,
32
31
  Protocol,
33
- Set,
34
- Tuple,
35
32
  TypeVar,
36
33
  Union,
37
34
  overload,
@@ -156,7 +153,7 @@ class EventWatcher:
156
153
  ```
157
154
  '''
158
155
 
159
- handlers: List[Tuple[pyee.EventEmitter, str, Callable[..., Any]]]
156
+ handlers: list[tuple[pyee.EventEmitter, str, Callable[..., Any]]]
160
157
 
161
158
  def __init__(self) -> None:
162
159
  self.handlers = []
@@ -329,7 +326,7 @@ class AsyncRunner:
329
326
  default_queue = WorkQueue()
330
327
 
331
328
  # Shared set of running tasks
332
- running_tasks: Set[Awaitable] = set()
329
+ running_tasks: set[Awaitable] = set()
333
330
 
334
331
  @staticmethod
335
332
  def run_in_task(queue=None):
@@ -15,21 +15,12 @@
15
15
  # -----------------------------------------------------------------------------
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
+ import dataclasses
19
+ from dataclasses import field
18
20
  import struct
19
- from typing import Dict, Optional, Type
20
-
21
- from bumble.hci import (
22
- name_or_number,
23
- hci_vendor_command_op_code,
24
- Address,
25
- HCI_Constant,
26
- HCI_Object,
27
- HCI_Command,
28
- HCI_Event,
29
- HCI_Extended_Event,
30
- HCI_VENDOR_EVENT,
31
- STATUS_SPEC,
32
- )
21
+ from typing import Optional
22
+
23
+ from bumble import hci
33
24
 
34
25
 
35
26
  # -----------------------------------------------------------------------------
@@ -41,22 +32,28 @@ from bumble.hci import (
41
32
  #
42
33
  # pylint: disable-next=line-too-long
43
34
  # See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#chip-capabilities-and-configuration
44
- HCI_LE_GET_VENDOR_CAPABILITIES_COMMAND = hci_vendor_command_op_code(0x153)
45
- HCI_LE_APCF_COMMAND = hci_vendor_command_op_code(0x157)
46
- HCI_GET_CONTROLLER_ACTIVITY_ENERGY_INFO_COMMAND = hci_vendor_command_op_code(0x159)
47
- HCI_A2DP_HARDWARE_OFFLOAD_COMMAND = hci_vendor_command_op_code(0x15D)
48
- HCI_BLUETOOTH_QUALITY_REPORT_COMMAND = hci_vendor_command_op_code(0x15E)
49
- HCI_DYNAMIC_AUDIO_BUFFER_COMMAND = hci_vendor_command_op_code(0x15F)
35
+ HCI_LE_GET_VENDOR_CAPABILITIES_COMMAND = hci.hci_vendor_command_op_code(0x153)
36
+ HCI_LE_APCF_COMMAND = hci.hci_vendor_command_op_code(0x157)
37
+ HCI_GET_CONTROLLER_ACTIVITY_ENERGY_INFO_COMMAND = hci.hci_vendor_command_op_code(0x159)
38
+ HCI_A2DP_HARDWARE_OFFLOAD_COMMAND = hci.hci_vendor_command_op_code(0x15D)
39
+ HCI_BLUETOOTH_QUALITY_REPORT_COMMAND = hci.hci_vendor_command_op_code(0x15E)
40
+ HCI_DYNAMIC_AUDIO_BUFFER_COMMAND = hci.hci_vendor_command_op_code(0x15F)
50
41
 
51
42
  HCI_BLUETOOTH_QUALITY_REPORT_EVENT = 0x58
52
43
 
53
- HCI_Command.register_commands(globals())
44
+ hci.HCI_Command.register_commands(globals())
54
45
 
55
46
 
56
47
  # -----------------------------------------------------------------------------
57
- @HCI_Command.command(
58
- return_parameters_fields=[
59
- ('status', STATUS_SPEC),
48
+ @hci.HCI_Command.command
49
+ @dataclasses.dataclass
50
+ class HCI_LE_Get_Vendor_Capabilities_Command(hci.HCI_Command):
51
+ # pylint: disable=line-too-long
52
+ '''
53
+ See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#vendor-specific-capabilities
54
+ '''
55
+ return_parameters_fields = [
56
+ ('status', hci.STATUS_SPEC),
60
57
  ('max_advt_instances', 1),
61
58
  ('offloaded_resolution_of_private_address', 1),
62
59
  ('total_scan_results_storage', 2),
@@ -73,12 +70,6 @@ HCI_Command.register_commands(globals())
73
70
  ('bluetooth_quality_report_support', 1),
74
71
  ('dynamic_audio_buffer_support', 4),
75
72
  ]
76
- )
77
- class HCI_LE_Get_Vendor_Capabilities_Command(HCI_Command):
78
- # pylint: disable=line-too-long
79
- '''
80
- See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#vendor-specific-capabilities
81
- '''
82
73
 
83
74
  @classmethod
84
75
  def parse_return_parameters(cls, parameters):
@@ -86,13 +77,13 @@ class HCI_LE_Get_Vendor_Capabilities_Command(HCI_Command):
86
77
  # there are no more bytes to parse, and leave un-signal parameters set to
87
78
  # None (older versions)
88
79
  nones = {field: None for field, _ in cls.return_parameters_fields}
89
- return_parameters = HCI_Object(cls.return_parameters_fields, **nones)
80
+ return_parameters = hci.HCI_Object(cls.return_parameters_fields, **nones)
90
81
 
91
82
  try:
92
83
  offset = 0
93
84
  for field in cls.return_parameters_fields:
94
85
  field_name, field_type = field
95
- field_value, field_size = HCI_Object.parse_field(
86
+ field_value, field_size = hci.HCI_Object.parse_field(
96
87
  parameters, offset, field_type
97
88
  )
98
89
  setattr(return_parameters, field_name, field_value)
@@ -104,30 +95,9 @@ class HCI_LE_Get_Vendor_Capabilities_Command(HCI_Command):
104
95
 
105
96
 
106
97
  # -----------------------------------------------------------------------------
107
- @HCI_Command.command(
108
- fields=[
109
- (
110
- 'opcode',
111
- {
112
- 'size': 1,
113
- 'mapper': lambda x: HCI_LE_APCF_Command.opcode_name(x),
114
- },
115
- ),
116
- ('payload', '*'),
117
- ],
118
- return_parameters_fields=[
119
- ('status', STATUS_SPEC),
120
- (
121
- 'opcode',
122
- {
123
- 'size': 1,
124
- 'mapper': lambda x: HCI_LE_APCF_Command.opcode_name(x),
125
- },
126
- ),
127
- ('payload', '*'),
128
- ],
129
- )
130
- class HCI_LE_APCF_Command(HCI_Command):
98
+ @hci.HCI_Command.command
99
+ @dataclasses.dataclass
100
+ class HCI_LE_APCF_Command(hci.HCI_Command):
131
101
  # pylint: disable=line-too-long
132
102
  '''
133
103
  See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command
@@ -137,80 +107,50 @@ class HCI_LE_APCF_Command(HCI_Command):
137
107
  '''
138
108
 
139
109
  # APCF Subcommands
140
- # TODO: use the OpenIntEnum class (when upcoming PR is merged)
141
- APCF_ENABLE = 0x00
142
- APCF_SET_FILTERING_PARAMETERS = 0x01
143
- APCF_BROADCASTER_ADDRESS = 0x02
144
- APCF_SERVICE_UUID = 0x03
145
- APCF_SERVICE_SOLICITATION_UUID = 0x04
146
- APCF_LOCAL_NAME = 0x05
147
- APCF_MANUFACTURER_DATA = 0x06
148
- APCF_SERVICE_DATA = 0x07
149
- APCF_TRANSPORT_DISCOVERY_SERVICE = 0x08
150
- APCF_AD_TYPE_FILTER = 0x09
151
- APCF_READ_EXTENDED_FEATURES = 0xFF
152
-
153
- OPCODE_NAMES = {
154
- APCF_ENABLE: 'APCF_ENABLE',
155
- APCF_SET_FILTERING_PARAMETERS: 'APCF_SET_FILTERING_PARAMETERS',
156
- APCF_BROADCASTER_ADDRESS: 'APCF_BROADCASTER_ADDRESS',
157
- APCF_SERVICE_UUID: 'APCF_SERVICE_UUID',
158
- APCF_SERVICE_SOLICITATION_UUID: 'APCF_SERVICE_SOLICITATION_UUID',
159
- APCF_LOCAL_NAME: 'APCF_LOCAL_NAME',
160
- APCF_MANUFACTURER_DATA: 'APCF_MANUFACTURER_DATA',
161
- APCF_SERVICE_DATA: 'APCF_SERVICE_DATA',
162
- APCF_TRANSPORT_DISCOVERY_SERVICE: 'APCF_TRANSPORT_DISCOVERY_SERVICE',
163
- APCF_AD_TYPE_FILTER: 'APCF_AD_TYPE_FILTER',
164
- APCF_READ_EXTENDED_FEATURES: 'APCF_READ_EXTENDED_FEATURES',
165
- }
166
-
167
- @classmethod
168
- def opcode_name(cls, opcode):
169
- return name_or_number(cls.OPCODE_NAMES, opcode)
110
+ class Opcode(hci.SpecableEnum):
111
+ ENABLE = 0x00
112
+ SET_FILTERING_PARAMETERS = 0x01
113
+ BROADCASTER_ADDRESS = 0x02
114
+ SERVICE_UUID = 0x03
115
+ SERVICE_SOLICITATION_UUID = 0x04
116
+ LOCAL_NAME = 0x05
117
+ MANUFACTURER_DATA = 0x06
118
+ SERVICE_DATA = 0x07
119
+ TRANSPORT_DISCOVERY_SERVICE = 0x08
120
+ AD_TYPE_FILTER = 0x09
121
+ READ_EXTENDED_FEATURES = 0xFF
122
+
123
+ opcode: int = dataclasses.field(metadata=Opcode.type_metadata(1))
124
+ payload: bytes = dataclasses.field(metadata=hci.metadata("*"))
125
+
126
+ return_parameters_fields = [
127
+ ('status', hci.STATUS_SPEC),
128
+ ('opcode', Opcode.type_spec(1)),
129
+ ('payload', '*'),
130
+ ]
170
131
 
171
132
 
172
133
  # -----------------------------------------------------------------------------
173
- @HCI_Command.command(
174
- return_parameters_fields=[
175
- ('status', STATUS_SPEC),
176
- ('total_tx_time_ms', 4),
177
- ('total_rx_time_ms', 4),
178
- ('total_idle_time_ms', 4),
179
- ('total_energy_used', 4),
180
- ],
181
- )
182
- class HCI_Get_Controller_Activity_Energy_Info_Command(HCI_Command):
134
+ @hci.HCI_Command.command
135
+ @dataclasses.dataclass
136
+ class HCI_Get_Controller_Activity_Energy_Info_Command(hci.HCI_Command):
183
137
  # pylint: disable=line-too-long
184
138
  '''
185
139
  See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_get_controller_activity_energy_info
186
140
  '''
141
+ return_parameters_fields = [
142
+ ('status', hci.STATUS_SPEC),
143
+ ('total_tx_time_ms', 4),
144
+ ('total_rx_time_ms', 4),
145
+ ('total_idle_time_ms', 4),
146
+ ('total_energy_used', 4),
147
+ ]
187
148
 
188
149
 
189
150
  # -----------------------------------------------------------------------------
190
- @HCI_Command.command(
191
- fields=[
192
- (
193
- 'opcode',
194
- {
195
- 'size': 1,
196
- 'mapper': lambda x: HCI_A2DP_Hardware_Offload_Command.opcode_name(x),
197
- },
198
- ),
199
- ('payload', '*'),
200
- ],
201
- return_parameters_fields=[
202
- ('status', STATUS_SPEC),
203
- (
204
- 'opcode',
205
- {
206
- 'size': 1,
207
- 'mapper': lambda x: HCI_A2DP_Hardware_Offload_Command.opcode_name(x),
208
- },
209
- ),
210
- ('payload', '*'),
211
- ],
212
- )
213
- class HCI_A2DP_Hardware_Offload_Command(HCI_Command):
151
+ @hci.HCI_Command.command
152
+ @dataclasses.dataclass
153
+ class HCI_A2DP_Hardware_Offload_Command(hci.HCI_Command):
214
154
  # pylint: disable=line-too-long
215
155
  '''
216
156
  See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#a2dp-hardware-offload-support
@@ -220,45 +160,24 @@ class HCI_A2DP_Hardware_Offload_Command(HCI_Command):
220
160
  '''
221
161
 
222
162
  # A2DP Hardware Offload Subcommands
223
- # TODO: use the OpenIntEnum class (when upcoming PR is merged)
224
- START_A2DP_OFFLOAD = 0x01
225
- STOP_A2DP_OFFLOAD = 0x02
163
+ class Opcode(hci.SpecableEnum):
164
+ START_A2DP_OFFLOAD = 0x01
165
+ STOP_A2DP_OFFLOAD = 0x02
226
166
 
227
- OPCODE_NAMES = {
228
- START_A2DP_OFFLOAD: 'START_A2DP_OFFLOAD',
229
- STOP_A2DP_OFFLOAD: 'STOP_A2DP_OFFLOAD',
230
- }
167
+ opcode: int = dataclasses.field(metadata=Opcode.type_metadata(1))
168
+ payload: bytes = dataclasses.field(metadata=hci.metadata("*"))
231
169
 
232
- @classmethod
233
- def opcode_name(cls, opcode):
234
- return name_or_number(cls.OPCODE_NAMES, opcode)
170
+ return_parameters_fields = [
171
+ ('status', hci.STATUS_SPEC),
172
+ ('opcode', Opcode.type_spec(1)),
173
+ ('payload', '*'),
174
+ ]
235
175
 
236
176
 
237
177
  # -----------------------------------------------------------------------------
238
- @HCI_Command.command(
239
- fields=[
240
- (
241
- 'opcode',
242
- {
243
- 'size': 1,
244
- 'mapper': lambda x: HCI_Dynamic_Audio_Buffer_Command.opcode_name(x),
245
- },
246
- ),
247
- ('payload', '*'),
248
- ],
249
- return_parameters_fields=[
250
- ('status', STATUS_SPEC),
251
- (
252
- 'opcode',
253
- {
254
- 'size': 1,
255
- 'mapper': lambda x: HCI_Dynamic_Audio_Buffer_Command.opcode_name(x),
256
- },
257
- ),
258
- ('payload', '*'),
259
- ],
260
- )
261
- class HCI_Dynamic_Audio_Buffer_Command(HCI_Command):
178
+ @hci.HCI_Command.command
179
+ @dataclasses.dataclass
180
+ class HCI_Dynamic_Audio_Buffer_Command(hci.HCI_Command):
262
181
  # pylint: disable=line-too-long
263
182
  '''
264
183
  See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#dynamic-audio-buffer-command
@@ -268,27 +187,28 @@ class HCI_Dynamic_Audio_Buffer_Command(HCI_Command):
268
187
  '''
269
188
 
270
189
  # Dynamic Audio Buffer Subcommands
271
- # TODO: use the OpenIntEnum class (when upcoming PR is merged)
272
- GET_AUDIO_BUFFER_TIME_CAPABILITY = 0x01
190
+ class Opcode(hci.SpecableEnum):
191
+ GET_AUDIO_BUFFER_TIME_CAPABILITY = 0x01
273
192
 
274
- OPCODE_NAMES = {
275
- GET_AUDIO_BUFFER_TIME_CAPABILITY: 'GET_AUDIO_BUFFER_TIME_CAPABILITY',
276
- }
193
+ opcode: int = dataclasses.field(metadata=Opcode.type_metadata(1))
194
+ payload: bytes = dataclasses.field(metadata=hci.metadata("*"))
277
195
 
278
- @classmethod
279
- def opcode_name(cls, opcode):
280
- return name_or_number(cls.OPCODE_NAMES, opcode)
196
+ return_parameters_fields = [
197
+ ('status', hci.STATUS_SPEC),
198
+ ('opcode', Opcode.type_spec(1)),
199
+ ('payload', '*'),
200
+ ]
281
201
 
282
202
 
283
203
  # -----------------------------------------------------------------------------
284
- class HCI_Android_Vendor_Event(HCI_Extended_Event):
285
- event_code: int = HCI_VENDOR_EVENT
286
- subevent_classes: Dict[int, Type[HCI_Extended_Event]] = {}
204
+ class HCI_Android_Vendor_Event(hci.HCI_Extended_Event):
205
+ event_code: int = hci.HCI_VENDOR_EVENT
206
+ subevent_classes: dict[int, type[hci.HCI_Extended_Event]] = {}
287
207
 
288
208
  @classmethod
289
209
  def subclass_from_parameters(
290
210
  cls, parameters: bytes
291
- ) -> Optional[HCI_Extended_Event]:
211
+ ) -> Optional[hci.HCI_Extended_Event]:
292
212
  subevent_code = parameters[0]
293
213
  if subevent_code == HCI_BLUETOOTH_QUALITY_REPORT_EVENT:
294
214
  quality_report_id = parameters[1]
@@ -299,45 +219,43 @@ class HCI_Android_Vendor_Event(HCI_Extended_Event):
299
219
 
300
220
 
301
221
  HCI_Android_Vendor_Event.register_subevents(globals())
302
- HCI_Event.add_vendor_factory(HCI_Android_Vendor_Event.subclass_from_parameters)
222
+ hci.HCI_Event.add_vendor_factory(HCI_Android_Vendor_Event.subclass_from_parameters)
303
223
 
304
224
 
305
225
  # -----------------------------------------------------------------------------
306
- @HCI_Extended_Event.event(
307
- fields=[
308
- ('quality_report_id', 1),
309
- ('packet_types', 1),
310
- ('connection_handle', 2),
311
- ('connection_role', {'size': 1, 'mapper': HCI_Constant.role_name}),
312
- ('tx_power_level', -1),
313
- ('rssi', -1),
314
- ('snr', 1),
315
- ('unused_afh_channel_count', 1),
316
- ('afh_select_unideal_channel_count', 1),
317
- ('lsto', 2),
318
- ('connection_piconet_clock', 4),
319
- ('retransmission_count', 4),
320
- ('no_rx_count', 4),
321
- ('nak_count', 4),
322
- ('last_tx_ack_timestamp', 4),
323
- ('flow_off_count', 4),
324
- ('last_flow_on_timestamp', 4),
325
- ('buffer_overflow_bytes', 4),
326
- ('buffer_underflow_bytes', 4),
327
- ('bdaddr', Address.parse_address),
328
- ('cal_failed_item_count', 1),
329
- ('tx_total_packets', 4),
330
- ('tx_unacked_packets', 4),
331
- ('tx_flushed_packets', 4),
332
- ('tx_last_subevent_packets', 4),
333
- ('crc_error_packets', 4),
334
- ('rx_duplicate_packets', 4),
335
- ('rx_unreceived_packets', 4),
336
- ('vendor_specific_parameters', '*'),
337
- ]
338
- )
226
+ @hci.HCI_Extended_Event.event
227
+ @dataclasses.dataclass
339
228
  class HCI_Bluetooth_Quality_Report_Event(HCI_Android_Vendor_Event):
340
229
  # pylint: disable=line-too-long
341
230
  '''
342
231
  See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#bluetooth-quality-report-sub-event
343
232
  '''
233
+ quality_report_id: int = field(metadata=hci.metadata(1))
234
+ packet_types: int = field(metadata=hci.metadata(1))
235
+ connection_handle: int = field(metadata=hci.metadata(2))
236
+ connection_role: int = field(metadata=hci.Role.type_metadata(1))
237
+ tx_power_level: int = field(metadata=hci.metadata(-1))
238
+ rssi: int = field(metadata=hci.metadata(-1))
239
+ snr: int = field(metadata=hci.metadata(1))
240
+ unused_afh_channel_count: int = field(metadata=hci.metadata(1))
241
+ afh_select_unideal_channel_count: int = field(metadata=hci.metadata(1))
242
+ lsto: int = field(metadata=hci.metadata(2))
243
+ connection_piconet_clock: int = field(metadata=hci.metadata(4))
244
+ retransmission_count: int = field(metadata=hci.metadata(4))
245
+ no_rx_count: int = field(metadata=hci.metadata(4))
246
+ nak_count: int = field(metadata=hci.metadata(4))
247
+ last_tx_ack_timestamp: int = field(metadata=hci.metadata(4))
248
+ flow_off_count: int = field(metadata=hci.metadata(4))
249
+ last_flow_on_timestamp: int = field(metadata=hci.metadata(4))
250
+ buffer_overflow_bytes: int = field(metadata=hci.metadata(4))
251
+ buffer_underflow_bytes: int = field(metadata=hci.metadata(4))
252
+ bdaddr: hci.Address = field(metadata=hci.metadata(hci.Address.parse_address))
253
+ cal_failed_item_count: int = field(metadata=hci.metadata(1))
254
+ tx_total_packets: int = field(metadata=hci.metadata(4))
255
+ tx_unacked_packets: int = field(metadata=hci.metadata(4))
256
+ tx_flushed_packets: int = field(metadata=hci.metadata(4))
257
+ tx_last_subevent_packets: int = field(metadata=hci.metadata(4))
258
+ crc_error_packets: int = field(metadata=hci.metadata(4))
259
+ rx_duplicate_packets: int = field(metadata=hci.metadata(4))
260
+ rx_unreceived_packets: int = field(metadata=hci.metadata(4))
261
+ vendor_specific_parameters: bytes = field(metadata=hci.metadata('*'))