bumble 0.0.212__py3-none-any.whl → 0.0.213__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 (86) 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 +11 -9
  5. bumble/apps/bench.py +480 -31
  6. bumble/apps/console.py +3 -3
  7. bumble/apps/controller_info.py +47 -10
  8. bumble/apps/controller_loopback.py +7 -3
  9. bumble/apps/controllers.py +2 -2
  10. bumble/apps/device_info.py +2 -2
  11. bumble/apps/gatt_dump.py +2 -2
  12. bumble/apps/gg_bridge.py +2 -2
  13. bumble/apps/hci_bridge.py +2 -2
  14. bumble/apps/l2cap_bridge.py +2 -2
  15. bumble/apps/lea_unicast/app.py +6 -1
  16. bumble/apps/pair.py +19 -11
  17. bumble/apps/pandora_server.py +2 -2
  18. bumble/apps/rfcomm_bridge.py +1 -1
  19. bumble/apps/scan.py +2 -2
  20. bumble/apps/show.py +4 -2
  21. bumble/apps/speaker/speaker.html +1 -0
  22. bumble/apps/speaker/speaker.js +113 -62
  23. bumble/apps/speaker/speaker.py +126 -18
  24. bumble/at.py +4 -4
  25. bumble/att.py +2 -6
  26. bumble/avc.py +7 -7
  27. bumble/avctp.py +3 -3
  28. bumble/avdtp.py +16 -20
  29. bumble/avrcp.py +41 -53
  30. bumble/colors.py +2 -2
  31. bumble/controller.py +84 -23
  32. bumble/device.py +348 -182
  33. bumble/drivers/__init__.py +2 -2
  34. bumble/drivers/common.py +0 -2
  35. bumble/drivers/intel.py +37 -40
  36. bumble/drivers/rtk.py +28 -35
  37. bumble/gatt.py +4 -4
  38. bumble/gatt_adapters.py +4 -5
  39. bumble/gatt_client.py +26 -31
  40. bumble/gatt_server.py +7 -11
  41. bumble/hci.py +2601 -2909
  42. bumble/helpers.py +4 -5
  43. bumble/hfp.py +32 -37
  44. bumble/host.py +94 -35
  45. bumble/keys.py +5 -5
  46. bumble/l2cap.py +310 -394
  47. bumble/link.py +6 -270
  48. bumble/pairing.py +23 -20
  49. bumble/pandora/__init__.py +1 -1
  50. bumble/pandora/config.py +2 -2
  51. bumble/pandora/device.py +6 -6
  52. bumble/pandora/host.py +27 -28
  53. bumble/pandora/l2cap.py +2 -2
  54. bumble/pandora/security.py +6 -6
  55. bumble/pandora/utils.py +3 -3
  56. bumble/profiles/ascs.py +132 -131
  57. bumble/profiles/asha.py +2 -2
  58. bumble/profiles/bap.py +3 -4
  59. bumble/profiles/csip.py +2 -2
  60. bumble/profiles/device_information_service.py +2 -2
  61. bumble/profiles/gap.py +2 -2
  62. bumble/profiles/hap.py +34 -33
  63. bumble/profiles/le_audio.py +4 -4
  64. bumble/profiles/mcp.py +4 -4
  65. bumble/profiles/vcs.py +3 -5
  66. bumble/rfcomm.py +10 -10
  67. bumble/rtp.py +1 -2
  68. bumble/sdp.py +2 -2
  69. bumble/smp.py +57 -61
  70. bumble/tools/rtk_util.py +2 -2
  71. bumble/transport/__init__.py +2 -16
  72. bumble/transport/android_netsim.py +5 -5
  73. bumble/transport/common.py +4 -4
  74. bumble/transport/pyusb.py +2 -2
  75. bumble/utils.py +2 -5
  76. bumble/vendor/android/hci.py +118 -200
  77. bumble/vendor/zephyr/hci.py +32 -27
  78. {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/METADATA +2 -2
  79. {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/RECORD +83 -86
  80. {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
  81. {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/entry_points.txt +0 -1
  82. bumble/apps/link_relay/__init__.py +0 -0
  83. bumble/apps/link_relay/link_relay.py +0 -289
  84. bumble/apps/link_relay/logging.yml +0 -21
  85. {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
  86. {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/top_level.txt +0 -0
@@ -23,7 +23,7 @@ from __future__ import annotations
23
23
  import logging
24
24
  import pathlib
25
25
  import platform
26
- from typing import Dict, Iterable, Optional, Type, TYPE_CHECKING
26
+ from typing import Iterable, Optional, TYPE_CHECKING
27
27
 
28
28
  from bumble.drivers import rtk, intel
29
29
  from bumble.drivers.common import Driver
@@ -45,7 +45,7 @@ async def get_driver_for_host(host: Host) -> Optional[Driver]:
45
45
  found.
46
46
  If a "driver" HCI metadata entry is present, only that driver class will be probed.
47
47
  """
48
- driver_classes: Dict[str, Type[Driver]] = {"rtk": rtk.Driver, "intel": intel.Driver}
48
+ driver_classes: dict[str, type[Driver]] = {"rtk": rtk.Driver, "intel": intel.Driver}
49
49
  probe_list: Iterable[str]
50
50
  if driver_name := host.hci_metadata.get("driver"):
51
51
  # Only probe a single driver
bumble/drivers/common.py CHANGED
@@ -20,8 +20,6 @@ Common types for drivers.
20
20
  # -----------------------------------------------------------------------------
21
21
  import abc
22
22
 
23
- from bumble import core
24
-
25
23
 
26
24
  # -----------------------------------------------------------------------------
27
25
  # Classes
bumble/drivers/intel.py CHANGED
@@ -28,7 +28,7 @@ import os
28
28
  import pathlib
29
29
  import platform
30
30
  import struct
31
- from typing import Any, Deque, Optional, TYPE_CHECKING
31
+ from typing import Any, Optional, TYPE_CHECKING
32
32
 
33
33
  from bumble import core
34
34
  from bumble.drivers import common
@@ -90,54 +90,51 @@ HCI_INTEL_WRITE_BOOT_PARAMS_COMMAND = hci.hci_vendor_command_op_code(0x000E)
90
90
  hci.HCI_Command.register_commands(globals())
91
91
 
92
92
 
93
- @hci.HCI_Command.command(
94
- fields=[
95
- ("param0", 1),
96
- ],
97
- return_parameters_fields=[
93
+ @hci.HCI_Command.command
94
+ @dataclasses.dataclass
95
+ class HCI_Intel_Read_Version_Command(hci.HCI_Command):
96
+ param0: int = dataclasses.field(metadata=hci.metadata(1))
97
+
98
+ return_parameters_fields = [
98
99
  ("status", hci.STATUS_SPEC),
99
100
  ("tlv", "*"),
100
- ],
101
- )
102
- class HCI_Intel_Read_Version_Command(hci.HCI_Command):
103
- pass
101
+ ]
104
102
 
105
103
 
106
- @hci.HCI_Command.command(
107
- fields=[("data_type", 1), ("data", "*")],
108
- return_parameters_fields=[
109
- ("status", 1),
110
- ],
111
- )
104
+ @hci.HCI_Command.command
105
+ @dataclasses.dataclass
112
106
  class Hci_Intel_Secure_Send_Command(hci.HCI_Command):
113
- pass
114
-
115
-
116
- @hci.HCI_Command.command(
117
- fields=[
118
- ("reset_type", 1),
119
- ("patch_enable", 1),
120
- ("ddc_reload", 1),
121
- ("boot_option", 1),
122
- ("boot_address", 4),
123
- ],
124
- return_parameters_fields=[
125
- ("data", "*"),
126
- ],
127
- )
107
+ data_type: int = dataclasses.field(metadata=hci.metadata(1))
108
+ data: bytes = dataclasses.field(metadata=hci.metadata("*"))
109
+
110
+ return_parameters_fields = [
111
+ ("status", 1),
112
+ ]
113
+
114
+
115
+ @hci.HCI_Command.command
116
+ @dataclasses.dataclass
128
117
  class HCI_Intel_Reset_Command(hci.HCI_Command):
129
- pass
118
+ reset_type: int = dataclasses.field(metadata=hci.metadata(1))
119
+ patch_enable: int = dataclasses.field(metadata=hci.metadata(1))
120
+ ddc_reload: int = dataclasses.field(metadata=hci.metadata(1))
121
+ boot_option: int = dataclasses.field(metadata=hci.metadata(1))
122
+ boot_address: int = dataclasses.field(metadata=hci.metadata(4))
123
+
124
+ return_parameters_fields = [
125
+ ("data", "*"),
126
+ ]
130
127
 
131
128
 
132
- @hci.HCI_Command.command(
133
- fields=[("data", "*")],
134
- return_parameters_fields=[
129
+ @hci.HCI_Command.command
130
+ @dataclasses.dataclass
131
+ class Hci_Intel_Write_Device_Config_Command(hci.HCI_Command):
132
+ data: bytes = dataclasses.field(metadata=hci.metadata("*"))
133
+
134
+ return_parameters_fields = [
135
135
  ("status", hci.STATUS_SPEC),
136
136
  ("params", "*"),
137
- ],
138
- )
139
- class Hci_Intel_Write_Device_Config_Command(hci.HCI_Command):
140
- pass
137
+ ]
141
138
 
142
139
 
143
140
  # -----------------------------------------------------------------------------
@@ -348,7 +345,7 @@ class Driver(common.Driver):
348
345
  def __init__(self, host: Host) -> None:
349
346
  self.host = host
350
347
  self.max_in_flight_firmware_load_commands = 1
351
- self.pending_firmware_load_commands: Deque[hci.HCI_Command] = (
348
+ self.pending_firmware_load_commands: collections.deque[hci.HCI_Command] = (
352
349
  collections.deque()
353
350
  )
354
351
  self.can_send_firmware_load_command = asyncio.Event()
bumble/drivers/rtk.py CHANGED
@@ -20,7 +20,7 @@ Based on various online bits of information, including the Linux kernel.
20
20
  # -----------------------------------------------------------------------------
21
21
  # Imports
22
22
  # -----------------------------------------------------------------------------
23
- from dataclasses import dataclass
23
+ from dataclasses import dataclass, field
24
24
  import asyncio
25
25
  import enum
26
26
  import logging
@@ -29,19 +29,11 @@ import os
29
29
  import pathlib
30
30
  import platform
31
31
  import struct
32
- from typing import Tuple
33
32
  import weakref
34
33
 
35
34
 
36
35
  from bumble import core
37
- from bumble.hci import (
38
- hci_vendor_command_op_code,
39
- STATUS_SPEC,
40
- HCI_SUCCESS,
41
- HCI_Command,
42
- HCI_Reset_Command,
43
- HCI_Read_Local_Version_Information_Command,
44
- )
36
+ from bumble import hci
45
37
  from bumble.drivers import common
46
38
 
47
39
  # -----------------------------------------------------------------------------
@@ -183,27 +175,29 @@ RTK_USB_PRODUCTS = {
183
175
  # -----------------------------------------------------------------------------
184
176
  # HCI Commands
185
177
  # -----------------------------------------------------------------------------
186
- HCI_RTK_READ_ROM_VERSION_COMMAND = hci_vendor_command_op_code(0x6D)
187
- HCI_RTK_DOWNLOAD_COMMAND = hci_vendor_command_op_code(0x20)
188
- HCI_RTK_DROP_FIRMWARE_COMMAND = hci_vendor_command_op_code(0x66)
189
- HCI_Command.register_commands(globals())
178
+ HCI_RTK_READ_ROM_VERSION_COMMAND = hci.hci_vendor_command_op_code(0x6D)
179
+ HCI_RTK_DOWNLOAD_COMMAND = hci.hci_vendor_command_op_code(0x20)
180
+ HCI_RTK_DROP_FIRMWARE_COMMAND = hci.hci_vendor_command_op_code(0x66)
181
+ hci.HCI_Command.register_commands(globals())
190
182
 
191
183
 
192
- @HCI_Command.command(return_parameters_fields=[("status", STATUS_SPEC), ("version", 1)])
193
- class HCI_RTK_Read_ROM_Version_Command(HCI_Command):
194
- pass
184
+ @hci.HCI_Command.command
185
+ @dataclass
186
+ class HCI_RTK_Read_ROM_Version_Command(hci.HCI_Command):
187
+ return_parameters_fields = [("status", hci.STATUS_SPEC), ("version", 1)]
195
188
 
196
189
 
197
- @HCI_Command.command(
198
- fields=[("index", 1), ("payload", RTK_FRAGMENT_LENGTH)],
199
- return_parameters_fields=[("status", STATUS_SPEC), ("index", 1)],
200
- )
201
- class HCI_RTK_Download_Command(HCI_Command):
202
- pass
190
+ @hci.HCI_Command.command
191
+ @dataclass
192
+ class HCI_RTK_Download_Command(hci.HCI_Command):
193
+ index: int = field(metadata=hci.metadata(1))
194
+ payload: bytes = field(metadata=hci.metadata(RTK_FRAGMENT_LENGTH))
195
+ return_parameters_fields = [("status", hci.STATUS_SPEC), ("index", 1)]
203
196
 
204
197
 
205
- @HCI_Command.command()
206
- class HCI_RTK_Drop_Firmware_Command(HCI_Command):
198
+ @hci.HCI_Command.command
199
+ @dataclass
200
+ class HCI_RTK_Drop_Firmware_Command(hci.HCI_Command):
207
201
  pass
208
202
 
209
203
 
@@ -294,7 +288,7 @@ class Driver(common.Driver):
294
288
  @dataclass
295
289
  class DriverInfo:
296
290
  rom: int
297
- hci: Tuple[int, int]
291
+ hci: tuple[int, int]
298
292
  config_needed: bool
299
293
  has_rom_version: bool
300
294
  has_msft_ext: bool = False
@@ -499,17 +493,17 @@ class Driver(common.Driver):
499
493
  async def driver_info_for_host(cls, host):
500
494
  try:
501
495
  await host.send_command(
502
- HCI_Reset_Command(),
496
+ hci.HCI_Reset_Command(),
503
497
  check_result=True,
504
498
  response_timeout=cls.POST_RESET_DELAY,
505
499
  )
506
500
  host.ready = True # Needed to let the host know the controller is ready.
507
501
  except asyncio.exceptions.TimeoutError:
508
502
  logger.warning("timeout waiting for hci reset, retrying")
509
- await host.send_command(HCI_Reset_Command(), check_result=True)
503
+ await host.send_command(hci.HCI_Reset_Command(), check_result=True)
510
504
  host.ready = True
511
505
 
512
- command = HCI_Read_Local_Version_Information_Command()
506
+ command = hci.HCI_Read_Local_Version_Information_Command()
513
507
  response = await host.send_command(command, check_result=True)
514
508
  if response.command_opcode != command.op_code:
515
509
  logger.error("failed to probe local version information")
@@ -596,7 +590,7 @@ class Driver(common.Driver):
596
590
  response = await self.host.send_command(
597
591
  HCI_RTK_Read_ROM_Version_Command(), check_result=True
598
592
  )
599
- if response.return_parameters.status != HCI_SUCCESS:
593
+ if response.return_parameters.status != hci.HCI_SUCCESS:
600
594
  logger.warning("can't get ROM version")
601
595
  return
602
596
  rom_version = response.return_parameters.version
@@ -634,9 +628,8 @@ class Driver(common.Driver):
634
628
  fragment = payload[fragment_offset : fragment_offset + RTK_FRAGMENT_LENGTH]
635
629
  logger.debug(f"downloading fragment {fragment_index}")
636
630
  await self.host.send_command(
637
- HCI_RTK_Download_Command(
638
- index=download_index, payload=fragment, check_result=True
639
- )
631
+ HCI_RTK_Download_Command(index=download_index, payload=fragment),
632
+ check_result=True,
640
633
  )
641
634
 
642
635
  logger.debug("download complete!")
@@ -645,7 +638,7 @@ class Driver(common.Driver):
645
638
  response = await self.host.send_command(
646
639
  HCI_RTK_Read_ROM_Version_Command(), check_result=True
647
640
  )
648
- if response.return_parameters.status != HCI_SUCCESS:
641
+ if response.return_parameters.status != hci.HCI_SUCCESS:
649
642
  logger.warning("can't get ROM version")
650
643
  else:
651
644
  rom_version = response.return_parameters.version
@@ -668,7 +661,7 @@ class Driver(common.Driver):
668
661
 
669
662
  async def init_controller(self):
670
663
  await self.download_firmware()
671
- await self.host.send_command(HCI_Reset_Command(), check_result=True)
664
+ await self.host.send_command(hci.HCI_Reset_Command(), check_result=True)
672
665
  logger.info(f"loaded FW image {self.driver_info.fw_name}")
673
666
 
674
667
 
bumble/gatt.py CHANGED
@@ -27,7 +27,7 @@ import enum
27
27
  import functools
28
28
  import logging
29
29
  import struct
30
- from typing import Iterable, List, Optional, Sequence, TypeVar, Union
30
+ from typing import Iterable, Optional, Sequence, TypeVar, Union
31
31
 
32
32
  from bumble.colors import color
33
33
  from bumble.core import BaseBumbleError, UUID
@@ -350,8 +350,8 @@ class Service(Attribute):
350
350
  '''
351
351
 
352
352
  uuid: UUID
353
- characteristics: List[Characteristic]
354
- included_services: List[Service]
353
+ characteristics: list[Characteristic]
354
+ included_services: list[Service]
355
355
 
356
356
  def __init__(
357
357
  self,
@@ -474,7 +474,7 @@ class Characteristic(Attribute[_T]):
474
474
  # The check for `p.name is not None` here is needed because for InFlag
475
475
  # enums, the .name property can be None, when the enum value is 0,
476
476
  # so the type hint for .name is Optional[str].
477
- enum_list: List[str] = [p.name for p in cls if p.name is not None]
477
+ enum_list: list[str] = [p.name for p in cls if p.name is not None]
478
478
  enum_list_str = ",".join(enum_list)
479
479
  raise TypeError(
480
480
  f"Characteristic.Properties::from_string() error:\nExpected a string containing any of the keys, separated by , or |: {enum_list_str}\nGot: {properties_str}"
bumble/gatt_adapters.py CHANGED
@@ -28,7 +28,6 @@ from typing import (
28
28
  Iterable,
29
29
  Literal,
30
30
  Optional,
31
- Type,
32
31
  TypeVar,
33
32
  )
34
33
 
@@ -270,7 +269,7 @@ class SerializableCharacteristicAdapter(CharacteristicAdapter[_T2]):
270
269
  `to_bytes` and `__bytes__` methods, respectively.
271
270
  '''
272
271
 
273
- def __init__(self, characteristic: Characteristic, cls: Type[_T2]) -> None:
272
+ def __init__(self, characteristic: Characteristic, cls: type[_T2]) -> None:
274
273
  super().__init__(characteristic)
275
274
  self.cls = cls
276
275
 
@@ -289,7 +288,7 @@ class SerializableCharacteristicProxyAdapter(CharacteristicProxyAdapter[_T2]):
289
288
  '''
290
289
 
291
290
  def __init__(
292
- self, characteristic_proxy: CharacteristicProxy, cls: Type[_T2]
291
+ self, characteristic_proxy: CharacteristicProxy, cls: type[_T2]
293
292
  ) -> None:
294
293
  super().__init__(characteristic_proxy)
295
294
  self.cls = cls
@@ -311,7 +310,7 @@ class EnumCharacteristicAdapter(CharacteristicAdapter[_T3]):
311
310
  def __init__(
312
311
  self,
313
312
  characteristic: Characteristic,
314
- cls: Type[_T3],
313
+ cls: type[_T3],
315
314
  length: int,
316
315
  byteorder: Literal['little', 'big'] = 'little',
317
316
  ):
@@ -347,7 +346,7 @@ class EnumCharacteristicProxyAdapter(CharacteristicProxyAdapter[_T3]):
347
346
  def __init__(
348
347
  self,
349
348
  characteristic_proxy: CharacteristicProxy,
350
- cls: Type[_T3],
349
+ cls: type[_T3],
351
350
  length: int,
352
351
  byteorder: Literal['little', 'big'] = 'little',
353
352
  ):
bumble/gatt_client.py CHANGED
@@ -31,15 +31,10 @@ from datetime import datetime
31
31
  from typing import (
32
32
  Any,
33
33
  Callable,
34
- Dict,
35
34
  Generic,
36
35
  Iterable,
37
- List,
38
36
  Optional,
39
- Set,
40
- Tuple,
41
37
  Union,
42
- Type,
43
38
  TypeVar,
44
39
  TYPE_CHECKING,
45
40
  )
@@ -149,8 +144,8 @@ class AttributeProxy(utils.EventEmitter, Generic[_T]):
149
144
 
150
145
  class ServiceProxy(AttributeProxy):
151
146
  uuid: UUID
152
- characteristics: List[CharacteristicProxy[bytes]]
153
- included_services: List[ServiceProxy]
147
+ characteristics: list[CharacteristicProxy[bytes]]
148
+ included_services: list[ServiceProxy]
154
149
 
155
150
  @staticmethod
156
151
  def from_client(service_class, client: Client, service_uuid: UUID):
@@ -199,8 +194,8 @@ class ServiceProxy(AttributeProxy):
199
194
 
200
195
  class CharacteristicProxy(AttributeProxy[_T]):
201
196
  properties: Characteristic.Properties
202
- descriptors: List[DescriptorProxy]
203
- subscribers: Dict[Any, Callable[[_T], Any]]
197
+ descriptors: list[DescriptorProxy]
198
+ subscribers: dict[Any, Callable[[_T], Any]]
204
199
 
205
200
  EVENT_UPDATE = "update"
206
201
 
@@ -277,7 +272,7 @@ class ProfileServiceProxy:
277
272
  Base class for profile-specific service proxies
278
273
  '''
279
274
 
280
- SERVICE_CLASS: Type[TemplateService]
275
+ SERVICE_CLASS: type[TemplateService]
281
276
 
282
277
  @classmethod
283
278
  def from_client(cls, client: Client) -> Optional[ProfileServiceProxy]:
@@ -288,13 +283,13 @@ class ProfileServiceProxy:
288
283
  # GATT Client
289
284
  # -----------------------------------------------------------------------------
290
285
  class Client:
291
- services: List[ServiceProxy]
292
- cached_values: Dict[int, Tuple[datetime, bytes]]
293
- notification_subscribers: Dict[
294
- int, Set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
286
+ services: list[ServiceProxy]
287
+ cached_values: dict[int, tuple[datetime, bytes]]
288
+ notification_subscribers: dict[
289
+ int, set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
295
290
  ]
296
- indication_subscribers: Dict[
297
- int, Set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
291
+ indication_subscribers: dict[
292
+ int, set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
298
293
  ]
299
294
  pending_response: Optional[asyncio.futures.Future[ATT_PDU]]
300
295
  pending_request: Optional[ATT_PDU]
@@ -379,12 +374,12 @@ class Client:
379
374
 
380
375
  return self.connection.att_mtu
381
376
 
382
- def get_services_by_uuid(self, uuid: UUID) -> List[ServiceProxy]:
377
+ def get_services_by_uuid(self, uuid: UUID) -> list[ServiceProxy]:
383
378
  return [service for service in self.services if service.uuid == uuid]
384
379
 
385
380
  def get_characteristics_by_uuid(
386
381
  self, uuid: UUID, service: Optional[ServiceProxy] = None
387
- ) -> List[CharacteristicProxy[bytes]]:
382
+ ) -> list[CharacteristicProxy[bytes]]:
388
383
  services = [service] if service else self.services
389
384
  return [
390
385
  c
@@ -395,8 +390,8 @@ class Client:
395
390
  def get_attribute_grouping(self, attribute_handle: int) -> Optional[
396
391
  Union[
397
392
  ServiceProxy,
398
- Tuple[ServiceProxy, CharacteristicProxy],
399
- Tuple[ServiceProxy, CharacteristicProxy, DescriptorProxy],
393
+ tuple[ServiceProxy, CharacteristicProxy],
394
+ tuple[ServiceProxy, CharacteristicProxy, DescriptorProxy],
400
395
  ]
401
396
  ]:
402
397
  """
@@ -429,7 +424,7 @@ class Client:
429
424
  if not already_known:
430
425
  self.services.append(service)
431
426
 
432
- async def discover_services(self, uuids: Iterable[UUID] = ()) -> List[ServiceProxy]:
427
+ async def discover_services(self, uuids: Iterable[UUID] = ()) -> list[ServiceProxy]:
433
428
  '''
434
429
  See Vol 3, Part G - 4.4.1 Discover All Primary Services
435
430
  '''
@@ -501,7 +496,7 @@ class Client:
501
496
 
502
497
  return services
503
498
 
504
- async def discover_service(self, uuid: Union[str, UUID]) -> List[ServiceProxy]:
499
+ async def discover_service(self, uuid: Union[str, UUID]) -> list[ServiceProxy]:
505
500
  '''
506
501
  See Vol 3, Part G - 4.4.2 Discover Primary Service by Service UUID
507
502
  '''
@@ -572,7 +567,7 @@ class Client:
572
567
 
573
568
  async def discover_included_services(
574
569
  self, service: ServiceProxy
575
- ) -> List[ServiceProxy]:
570
+ ) -> list[ServiceProxy]:
576
571
  '''
577
572
  See Vol 3, Part G - 4.5.1 Find Included Services
578
573
  '''
@@ -580,7 +575,7 @@ class Client:
580
575
  starting_handle = service.handle
581
576
  ending_handle = service.end_group_handle
582
577
 
583
- included_services: List[ServiceProxy] = []
578
+ included_services: list[ServiceProxy] = []
584
579
  while starting_handle <= ending_handle:
585
580
  response = await self.send_request(
586
581
  ATT_Read_By_Type_Request(
@@ -636,7 +631,7 @@ class Client:
636
631
 
637
632
  async def discover_characteristics(
638
633
  self, uuids, service: Optional[ServiceProxy]
639
- ) -> List[CharacteristicProxy[bytes]]:
634
+ ) -> list[CharacteristicProxy[bytes]]:
640
635
  '''
641
636
  See Vol 3, Part G - 4.6.1 Discover All Characteristics of a Service and 4.6.2
642
637
  Discover Characteristics by UUID
@@ -649,12 +644,12 @@ class Client:
649
644
  services = [service] if service else self.services
650
645
 
651
646
  # Perform characteristic discovery for each service
652
- discovered_characteristics: List[CharacteristicProxy[bytes]] = []
647
+ discovered_characteristics: list[CharacteristicProxy[bytes]] = []
653
648
  for service in services:
654
649
  starting_handle = service.handle
655
650
  ending_handle = service.end_group_handle
656
651
 
657
- characteristics: List[CharacteristicProxy[bytes]] = []
652
+ characteristics: list[CharacteristicProxy[bytes]] = []
658
653
  while starting_handle <= ending_handle:
659
654
  response = await self.send_request(
660
655
  ATT_Read_By_Type_Request(
@@ -725,7 +720,7 @@ class Client:
725
720
  characteristic: Optional[CharacteristicProxy] = None,
726
721
  start_handle: Optional[int] = None,
727
722
  end_handle: Optional[int] = None,
728
- ) -> List[DescriptorProxy]:
723
+ ) -> list[DescriptorProxy]:
729
724
  '''
730
725
  See Vol 3, Part G - 4.7.1 Discover All Characteristic Descriptors
731
726
  '''
@@ -738,7 +733,7 @@ class Client:
738
733
  else:
739
734
  return []
740
735
 
741
- descriptors: List[DescriptorProxy] = []
736
+ descriptors: list[DescriptorProxy] = []
742
737
  while starting_handle <= ending_handle:
743
738
  response = await self.send_request(
744
739
  ATT_Find_Information_Request(
@@ -787,7 +782,7 @@ class Client:
787
782
 
788
783
  return descriptors
789
784
 
790
- async def discover_attributes(self) -> List[AttributeProxy[bytes]]:
785
+ async def discover_attributes(self) -> list[AttributeProxy[bytes]]:
791
786
  '''
792
787
  Discover all attributes, regardless of type
793
788
  '''
@@ -1002,7 +997,7 @@ class Client:
1002
997
 
1003
998
  async def read_characteristics_by_uuid(
1004
999
  self, uuid: UUID, service: Optional[ServiceProxy]
1005
- ) -> List[bytes]:
1000
+ ) -> list[bytes]:
1006
1001
  '''
1007
1002
  See Vol 3, Part G - 4.8.2 Read Using Characteristic UUID
1008
1003
  '''
bumble/gatt_server.py CHANGED
@@ -29,13 +29,9 @@ import logging
29
29
  from collections import defaultdict
30
30
  import struct
31
31
  from typing import (
32
- Dict,
33
32
  Iterable,
34
- List,
35
33
  Optional,
36
- Tuple,
37
34
  TypeVar,
38
- Type,
39
35
  TYPE_CHECKING,
40
36
  )
41
37
 
@@ -103,10 +99,10 @@ GATT_SERVER_DEFAULT_MAX_MTU = 517
103
99
  # GATT Server
104
100
  # -----------------------------------------------------------------------------
105
101
  class Server(utils.EventEmitter):
106
- attributes: List[Attribute]
107
- services: List[Service]
108
- attributes_by_handle: Dict[int, Attribute]
109
- subscribers: Dict[int, Dict[int, bytes]]
102
+ attributes: list[Attribute]
103
+ services: list[Service]
104
+ attributes_by_handle: dict[int, Attribute]
105
+ subscribers: dict[int, dict[int, bytes]]
110
106
  indication_semaphores: defaultdict[int, asyncio.Semaphore]
111
107
  pending_confirmations: defaultdict[int, Optional[asyncio.futures.Future]]
112
108
 
@@ -136,7 +132,7 @@ class Server(utils.EventEmitter):
136
132
  def next_handle(self) -> int:
137
133
  return 1 + len(self.attributes)
138
134
 
139
- def get_advertising_service_data(self) -> Dict[Attribute, bytes]:
135
+ def get_advertising_service_data(self) -> dict[Attribute, bytes]:
140
136
  return {
141
137
  attribute: data
142
138
  for attribute in self.attributes
@@ -160,7 +156,7 @@ class Server(utils.EventEmitter):
160
156
  AttributeGroupType = TypeVar('AttributeGroupType', Service, Characteristic)
161
157
 
162
158
  def get_attribute_group(
163
- self, handle: int, group_type: Type[AttributeGroupType]
159
+ self, handle: int, group_type: type[AttributeGroupType]
164
160
  ) -> Optional[AttributeGroupType]:
165
161
  return next(
166
162
  (
@@ -186,7 +182,7 @@ class Server(utils.EventEmitter):
186
182
 
187
183
  def get_characteristic_attributes(
188
184
  self, service_uuid: UUID, characteristic_uuid: UUID
189
- ) -> Optional[Tuple[CharacteristicDeclaration, Characteristic]]:
185
+ ) -> Optional[tuple[CharacteristicDeclaration, Characteristic]]:
190
186
  service_handle = self.get_service_attribute(service_uuid)
191
187
  if not service_handle:
192
188
  return None