bumble 0.0.220__py3-none-any.whl → 0.0.221__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 (102) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +5 -5
  3. bumble/apps/auracast.py +746 -473
  4. bumble/apps/bench.py +4 -5
  5. bumble/apps/console.py +5 -10
  6. bumble/apps/controller_info.py +12 -7
  7. bumble/apps/controller_loopback.py +1 -2
  8. bumble/apps/device_info.py +2 -3
  9. bumble/apps/gatt_dump.py +0 -1
  10. bumble/apps/lea_unicast/app.py +1 -1
  11. bumble/apps/pair.py +49 -46
  12. bumble/apps/pandora_server.py +2 -2
  13. bumble/apps/player/player.py +10 -12
  14. bumble/apps/rfcomm_bridge.py +10 -11
  15. bumble/apps/scan.py +1 -3
  16. bumble/apps/speaker/speaker.py +3 -4
  17. bumble/at.py +4 -5
  18. bumble/att.py +91 -25
  19. bumble/audio/io.py +5 -3
  20. bumble/avc.py +1 -2
  21. bumble/avctp.py +2 -3
  22. bumble/avdtp.py +53 -57
  23. bumble/avrcp.py +25 -27
  24. bumble/codecs.py +15 -15
  25. bumble/colors.py +7 -8
  26. bumble/controller.py +663 -391
  27. bumble/core.py +41 -49
  28. bumble/crypto/__init__.py +2 -1
  29. bumble/crypto/builtin.py +2 -8
  30. bumble/data_types.py +2 -1
  31. bumble/decoder.py +2 -3
  32. bumble/device.py +171 -142
  33. bumble/drivers/__init__.py +3 -2
  34. bumble/drivers/intel.py +6 -8
  35. bumble/drivers/rtk.py +1 -1
  36. bumble/gatt.py +9 -9
  37. bumble/gatt_adapters.py +6 -6
  38. bumble/gatt_client.py +110 -60
  39. bumble/gatt_server.py +209 -139
  40. bumble/hci.py +87 -74
  41. bumble/helpers.py +5 -5
  42. bumble/hfp.py +27 -26
  43. bumble/hid.py +9 -9
  44. bumble/host.py +44 -50
  45. bumble/keys.py +17 -17
  46. bumble/l2cap.py +1015 -218
  47. bumble/link.py +26 -159
  48. bumble/ll.py +200 -0
  49. bumble/pairing.py +14 -15
  50. bumble/pandora/__init__.py +2 -2
  51. bumble/pandora/device.py +6 -4
  52. bumble/pandora/host.py +19 -10
  53. bumble/pandora/l2cap.py +8 -9
  54. bumble/pandora/security.py +18 -16
  55. bumble/pandora/utils.py +4 -4
  56. bumble/profiles/aics.py +6 -8
  57. bumble/profiles/ams.py +3 -5
  58. bumble/profiles/ancs.py +11 -11
  59. bumble/profiles/ascs.py +5 -5
  60. bumble/profiles/asha.py +10 -9
  61. bumble/profiles/bass.py +9 -3
  62. bumble/profiles/battery_service.py +1 -2
  63. bumble/profiles/csip.py +9 -10
  64. bumble/profiles/device_information_service.py +16 -17
  65. bumble/profiles/gap.py +3 -4
  66. bumble/profiles/gatt_service.py +0 -1
  67. bumble/profiles/gmap.py +12 -13
  68. bumble/profiles/hap.py +3 -3
  69. bumble/profiles/heart_rate_service.py +7 -8
  70. bumble/profiles/le_audio.py +1 -1
  71. bumble/profiles/mcp.py +28 -28
  72. bumble/profiles/pacs.py +13 -17
  73. bumble/profiles/pbp.py +16 -0
  74. bumble/profiles/vcs.py +2 -2
  75. bumble/profiles/vocs.py +6 -9
  76. bumble/rfcomm.py +19 -18
  77. bumble/sdp.py +12 -11
  78. bumble/smp.py +20 -30
  79. bumble/snoop.py +2 -1
  80. bumble/tools/generate_company_id_list.py +1 -1
  81. bumble/tools/intel_util.py +2 -2
  82. bumble/tools/rtk_fw_download.py +1 -1
  83. bumble/tools/rtk_util.py +1 -1
  84. bumble/transport/__init__.py +1 -2
  85. bumble/transport/android_emulator.py +2 -3
  86. bumble/transport/android_netsim.py +49 -40
  87. bumble/transport/common.py +9 -9
  88. bumble/transport/file.py +1 -2
  89. bumble/transport/hci_socket.py +2 -3
  90. bumble/transport/pty.py +3 -5
  91. bumble/transport/pyusb.py +8 -5
  92. bumble/transport/serial.py +1 -2
  93. bumble/transport/vhci.py +1 -2
  94. bumble/transport/ws_server.py +2 -3
  95. bumble/utils.py +22 -9
  96. bumble/vendor/android/hci.py +4 -2
  97. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/METADATA +3 -2
  98. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/RECORD +102 -101
  99. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
  100. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
  101. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
  102. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/pandora/l2cap.py CHANGED
@@ -18,15 +18,15 @@ import json
18
18
  import logging
19
19
  from asyncio import Future
20
20
  from asyncio import Queue as AsyncQueue
21
+ from collections.abc import AsyncGenerator
21
22
  from dataclasses import dataclass
22
- from typing import AsyncGenerator, Optional, Union
23
23
 
24
24
  import grpc
25
25
  from google.protobuf import any_pb2, empty_pb2 # pytype: disable=pyi-error
26
26
  from pandora.l2cap_grpc_aio import L2CAPServicer # pytype: disable=pyi-error
27
- from pandora.l2cap_pb2 import COMMAND_NOT_UNDERSTOOD, INVALID_CID_IN_REQUEST
28
- from pandora.l2cap_pb2 import Channel as PandoraChannel # pytype: disable=pyi-error
29
27
  from pandora.l2cap_pb2 import (
28
+ COMMAND_NOT_UNDERSTOOD,
29
+ INVALID_CID_IN_REQUEST,
30
30
  ConnectRequest,
31
31
  ConnectResponse,
32
32
  CreditBasedChannelRequest,
@@ -41,6 +41,7 @@ from pandora.l2cap_pb2 import (
41
41
  WaitDisconnectionRequest,
42
42
  WaitDisconnectionResponse,
43
43
  )
44
+ from pandora.l2cap_pb2 import Channel as PandoraChannel # pytype: disable=pyi-error
44
45
 
45
46
  from bumble.core import InvalidArgumentError, OutOfResourcesError
46
47
  from bumble.device import Device
@@ -55,7 +56,7 @@ from bumble.l2cap import (
55
56
  from bumble.pandora import utils
56
57
  from bumble.pandora.config import Config
57
58
 
58
- L2capChannel = Union[ClassicChannel, LeCreditBasedChannel]
59
+ L2capChannel = ClassicChannel | LeCreditBasedChannel
59
60
 
60
61
 
61
62
  @dataclass
@@ -106,10 +107,8 @@ class L2CAPService(L2CAPServicer):
106
107
  oneof = request.WhichOneof('type')
107
108
  self.log.debug(f'WaitConnection channel request type: {oneof}.')
108
109
  channel_type = getattr(request, oneof)
109
- spec: Optional[Union[ClassicChannelSpec, LeCreditBasedChannelSpec]] = None
110
- l2cap_server: Optional[
111
- Union[ClassicChannelServer, LeCreditBasedChannelServer]
112
- ] = None
110
+ spec: ClassicChannelSpec | LeCreditBasedChannelSpec | None = None
111
+ l2cap_server: ClassicChannelServer | LeCreditBasedChannelServer | None = None
113
112
  if isinstance(channel_type, CreditBasedChannelRequest):
114
113
  spec = LeCreditBasedChannelSpec(
115
114
  psm=channel_type.spsm,
@@ -216,7 +215,7 @@ class L2CAPService(L2CAPServicer):
216
215
  oneof = request.WhichOneof('type')
217
216
  self.log.debug(f'Channel request type: {oneof}.')
218
217
  channel_type = getattr(request, oneof)
219
- spec: Optional[Union[ClassicChannelSpec, LeCreditBasedChannelSpec]] = None
218
+ spec: ClassicChannelSpec | LeCreditBasedChannelSpec | None = None
220
219
  if isinstance(channel_type, CreditBasedChannelRequest):
221
220
  spec = LeCreditBasedChannelSpec(
222
221
  psm=channel_type.spsm,
@@ -17,13 +17,15 @@ from __future__ import annotations
17
17
  import asyncio
18
18
  import contextlib
19
19
  import logging
20
- from collections.abc import Awaitable
21
- from typing import Any, AsyncGenerator, AsyncIterator, Callable, Optional, Union
20
+ from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable
21
+ from typing import Any
22
22
 
23
23
  import grpc
24
- from google.protobuf import any_pb2 # pytype: disable=pyi-error
25
- from google.protobuf import empty_pb2 # pytype: disable=pyi-error
26
- from google.protobuf import wrappers_pb2 # pytype: disable=pyi-error
24
+ from google.protobuf import (
25
+ any_pb2, # pytype: disable=pyi-error
26
+ empty_pb2, # pytype: disable=pyi-error
27
+ wrappers_pb2, # pytype: disable=pyi-error
28
+ )
27
29
  from pandora.host_pb2 import Connection
28
30
  from pandora.security_grpc_aio import SecurityServicer, SecurityStorageServicer
29
31
  from pandora.security_pb2 import (
@@ -64,7 +66,7 @@ class PairingDelegate(BasePairingDelegate):
64
66
  def __init__(
65
67
  self,
66
68
  connection: BumbleConnection,
67
- service: "SecurityService",
69
+ service: SecurityService,
68
70
  io_capability: BasePairingDelegate.IoCapability = BasePairingDelegate.NO_OUTPUT_NO_INPUT,
69
71
  local_initiator_key_distribution: BasePairingDelegate.KeyDistribution = BasePairingDelegate.DEFAULT_KEY_DISTRIBUTION,
70
72
  local_responder_key_distribution: BasePairingDelegate.KeyDistribution = BasePairingDelegate.DEFAULT_KEY_DISTRIBUTION,
@@ -130,7 +132,7 @@ class PairingDelegate(BasePairingDelegate):
130
132
  assert answer.answer_variant() == 'confirm' and answer.confirm is not None
131
133
  return answer.confirm
132
134
 
133
- async def get_number(self) -> Optional[int]:
135
+ async def get_number(self) -> int | None:
134
136
  self.log.debug(
135
137
  f"Pairing event: `passkey_entry_request` (io_capability: {self.io_capability})"
136
138
  )
@@ -147,7 +149,7 @@ class PairingDelegate(BasePairingDelegate):
147
149
  assert answer.answer_variant() == 'passkey'
148
150
  return answer.passkey
149
151
 
150
- async def get_string(self, max_length: int) -> Optional[str]:
152
+ async def get_string(self, max_length: int) -> str | None:
151
153
  self.log.debug(
152
154
  f"Pairing event: `pin_code_request` (io_capability: {self.io_capability})"
153
155
  )
@@ -195,8 +197,8 @@ class SecurityService(SecurityServicer):
195
197
  self.log = utils.BumbleServerLoggerAdapter(
196
198
  logging.getLogger(), {'service_name': 'Security', 'device': device}
197
199
  )
198
- self.event_queue: Optional[asyncio.Queue[PairingEvent]] = None
199
- self.event_answer: Optional[AsyncIterator[PairingEventAnswer]] = None
200
+ self.event_queue: asyncio.Queue[PairingEvent] | None = None
201
+ self.event_answer: AsyncIterator[PairingEventAnswer] | None = None
200
202
  self.device = device
201
203
  self.config = config
202
204
 
@@ -231,7 +233,7 @@ class SecurityService(SecurityServicer):
231
233
  if level == LEVEL2:
232
234
  return connection.encryption != 0 and connection.authenticated
233
235
 
234
- link_key_type: Optional[int] = None
236
+ link_key_type: int | None = None
235
237
  if (keystore := connection.device.keystore) and (
236
238
  keys := await keystore.get(str(connection.peer_address))
237
239
  ):
@@ -410,8 +412,8 @@ class SecurityService(SecurityServicer):
410
412
  wait_for_security: asyncio.Future[str] = (
411
413
  asyncio.get_running_loop().create_future()
412
414
  )
413
- authenticate_task: Optional[asyncio.Future[None]] = None
414
- pair_task: Optional[asyncio.Future[None]] = None
415
+ authenticate_task: asyncio.Future[None] | None = None
416
+ pair_task: asyncio.Future[None] | None = None
415
417
 
416
418
  async def authenticate() -> None:
417
419
  if (encryption := connection.encryption) != 0:
@@ -455,9 +457,9 @@ class SecurityService(SecurityServicer):
455
457
 
456
458
  def pair(*_: Any) -> None:
457
459
  if self.need_pairing(connection, level):
458
- pair_task = asyncio.create_task(connection.pair())
460
+ bumble.utils.AsyncRunner.spawn(connection.pair())
459
461
 
460
- listeners: dict[str, Callable[..., Union[None, Awaitable[None]]]] = {
462
+ listeners: dict[str, Callable[..., None | Awaitable[None]]] = {
461
463
  'disconnection': set_failure('connection_died'),
462
464
  'pairing_failure': set_failure('pairing_failure'),
463
465
  'connection_authentication_failure': set_failure('authentication_failure'),
@@ -500,7 +502,7 @@ class SecurityService(SecurityServicer):
500
502
  return WaitSecurityResponse(**kwargs)
501
503
 
502
504
  async def reached_security_level(
503
- self, connection: BumbleConnection, level: Union[SecurityLevel, LESecurityLevel]
505
+ self, connection: BumbleConnection, level: SecurityLevel | LESecurityLevel
504
506
  ) -> bool:
505
507
  self.log.debug(
506
508
  str(
bumble/pandora/utils.py CHANGED
@@ -18,7 +18,8 @@ import contextlib
18
18
  import functools
19
19
  import inspect
20
20
  import logging
21
- from typing import Any, Generator, MutableMapping, Optional
21
+ from collections.abc import Generator, MutableMapping
22
+ from typing import Any
22
23
 
23
24
  import grpc
24
25
  from google.protobuf.message import Message # pytype: disable=pyi-error
@@ -34,7 +35,7 @@ ADDRESS_TYPES: dict[str, AddressType] = {
34
35
  }
35
36
 
36
37
 
37
- def address_from_request(request: Message, field: Optional[str]) -> Address:
38
+ def address_from_request(request: Message, field: str | None) -> Address:
38
39
  if field is None:
39
40
  return Address.ANY
40
41
  return Address(bytes(reversed(getattr(request, field))), ADDRESS_TYPES[field])
@@ -95,8 +96,7 @@ def rpc(func: Any) -> Any:
95
96
  @functools.wraps(func)
96
97
  def gen_wrapper(self: Any, request: Any, context: grpc.ServicerContext) -> Any:
97
98
  with exception_to_rpc_error(context):
98
- for v in func(self, request, context):
99
- yield v
99
+ yield from func(self, request, context)
100
100
 
101
101
  @functools.wraps(func)
102
102
  def wrapper(self: Any, request: Any, context: grpc.ServicerContext) -> Any:
bumble/profiles/aics.py CHANGED
@@ -22,7 +22,6 @@ from __future__ import annotations
22
22
  import logging
23
23
  import struct
24
24
  from dataclasses import dataclass
25
- from typing import Optional
26
25
 
27
26
  from bumble import utils
28
27
  from bumble.att import ATT_Error
@@ -129,7 +128,7 @@ class AudioInputState:
129
128
  mute: Mute = Mute.NOT_MUTED
130
129
  gain_mode: GainMode = GainMode.MANUAL
131
130
  change_counter: int = 0
132
- attribute: Optional[Attribute] = None
131
+ attribute: Attribute | None = None
133
132
 
134
133
  def __bytes__(self) -> bytes:
135
134
  return bytes(
@@ -199,7 +198,6 @@ class AudioInputControlPoint:
199
198
  gain_settings_properties: GainSettingsProperties
200
199
 
201
200
  async def on_write(self, connection: Connection, value: bytes) -> None:
202
-
203
201
  opcode = AudioInputControlPointOpCode(value[0])
204
202
 
205
203
  if opcode == AudioInputControlPointOpCode.SET_GAIN_SETTING:
@@ -317,7 +315,7 @@ class AudioInputDescription:
317
315
  '''
318
316
 
319
317
  audio_input_description: str = "Bluetooth"
320
- attribute: Optional[Attribute] = None
318
+ attribute: Attribute | None = None
321
319
 
322
320
  def on_read(self, _connection: Connection) -> str:
323
321
  return self.audio_input_description
@@ -340,11 +338,11 @@ class AICSService(TemplateService):
340
338
 
341
339
  def __init__(
342
340
  self,
343
- audio_input_state: Optional[AudioInputState] = None,
344
- gain_settings_properties: Optional[GainSettingsProperties] = None,
341
+ audio_input_state: AudioInputState | None = None,
342
+ gain_settings_properties: GainSettingsProperties | None = None,
345
343
  audio_input_type: str = "local",
346
- audio_input_status: Optional[AudioInputStatus] = None,
347
- audio_input_description: Optional[AudioInputDescription] = None,
344
+ audio_input_status: AudioInputStatus | None = None,
345
+ audio_input_description: AudioInputDescription | None = None,
348
346
  ):
349
347
  self.audio_input_state = (
350
348
  AudioInputState() if audio_input_state is None else audio_input_state
bumble/profiles/ams.py CHANGED
@@ -25,7 +25,7 @@ import asyncio
25
25
  import dataclasses
26
26
  import enum
27
27
  import logging
28
- from typing import Iterable, Optional, Union
28
+ from collections.abc import Iterable
29
29
 
30
30
  from bumble import utils
31
31
  from bumble.device import Peer
@@ -230,7 +230,7 @@ class AmsClient(utils.EventEmitter):
230
230
  self.supported_commands = set()
231
231
 
232
232
  @classmethod
233
- async def for_peer(cls, peer: Peer) -> Optional[AmsClient]:
233
+ async def for_peer(cls, peer: Peer) -> AmsClient | None:
234
234
  ams_proxy = await peer.discover_service_and_create_proxy(AmsProxy)
235
235
  if ams_proxy is None:
236
236
  return None
@@ -263,9 +263,7 @@ class AmsClient(utils.EventEmitter):
263
263
  async def observe(
264
264
  self,
265
265
  entity: EntityId,
266
- attributes: Iterable[
267
- Union[PlayerAttributeId, QueueAttributeId, TrackAttributeId]
268
- ],
266
+ attributes: Iterable[PlayerAttributeId | QueueAttributeId | TrackAttributeId],
269
267
  ) -> None:
270
268
  await self._ams_proxy.entity_update.write_value(
271
269
  bytes([entity] + list(attributes)), with_response=True
bumble/profiles/ancs.py CHANGED
@@ -27,7 +27,7 @@ import datetime
27
27
  import enum
28
28
  import logging
29
29
  import struct
30
- from typing import Optional, Sequence, Union
30
+ from collections.abc import Sequence
31
31
 
32
32
  from bumble import utils
33
33
  from bumble.att import ATT_Error
@@ -116,7 +116,7 @@ class NotificationAttributeId(utils.OpenIntEnum):
116
116
  @dataclasses.dataclass
117
117
  class NotificationAttribute:
118
118
  attribute_id: NotificationAttributeId
119
- value: Union[str, int, datetime.datetime]
119
+ value: str | int | datetime.datetime
120
120
 
121
121
 
122
122
  @dataclasses.dataclass
@@ -242,10 +242,10 @@ class AncsProxy(ProfileServiceProxy):
242
242
 
243
243
 
244
244
  class AncsClient(utils.EventEmitter):
245
- _expected_response_command_id: Optional[CommandId]
246
- _expected_response_notification_uid: Optional[int]
247
- _expected_response_app_identifier: Optional[str]
248
- _expected_app_identifier: Optional[str]
245
+ _expected_response_command_id: CommandId | None
246
+ _expected_response_notification_uid: int | None
247
+ _expected_response_app_identifier: str | None
248
+ _expected_app_identifier: str | None
249
249
  _expected_response_tuples: int
250
250
  _response_accumulator: bytes
251
251
 
@@ -255,12 +255,12 @@ class AncsClient(utils.EventEmitter):
255
255
  super().__init__()
256
256
  self._ancs_proxy = ancs_proxy
257
257
  self._command_semaphore = asyncio.Semaphore()
258
- self._response: Optional[asyncio.Future] = None
258
+ self._response: asyncio.Future | None = None
259
259
  self._reset_response()
260
260
  self._started = False
261
261
 
262
262
  @classmethod
263
- async def for_peer(cls, peer: Peer) -> Optional[AncsClient]:
263
+ async def for_peer(cls, peer: Peer) -> AncsClient | None:
264
264
  ancs_proxy = await peer.discover_service_and_create_proxy(AncsProxy)
265
265
  if ancs_proxy is None:
266
266
  return None
@@ -316,7 +316,7 @@ class AncsClient(utils.EventEmitter):
316
316
  # Not enough data yet.
317
317
  return
318
318
 
319
- attributes: list[Union[NotificationAttribute, AppAttribute]] = []
319
+ attributes: list[NotificationAttribute | AppAttribute] = []
320
320
 
321
321
  if command_id == CommandId.GET_NOTIFICATION_ATTRIBUTES:
322
322
  (notification_uid,) = struct.unpack_from(
@@ -342,7 +342,7 @@ class AncsClient(utils.EventEmitter):
342
342
  str_value = attribute_data[3 : 3 + attribute_data_length].decode(
343
343
  "utf-8"
344
344
  )
345
- value: Union[str, int, datetime.datetime]
345
+ value: str | int | datetime.datetime
346
346
  if attribute_id == NotificationAttributeId.MESSAGE_SIZE:
347
347
  value = int(str_value)
348
348
  elif attribute_id == NotificationAttributeId.DATE:
@@ -415,7 +415,7 @@ class AncsClient(utils.EventEmitter):
415
415
  self,
416
416
  notification_uid: int,
417
417
  attributes: Sequence[
418
- Union[NotificationAttributeId, tuple[NotificationAttributeId, int]]
418
+ NotificationAttributeId | tuple[NotificationAttributeId, int]
419
419
  ],
420
420
  ) -> list[NotificationAttribute]:
421
421
  if not self._started:
bumble/profiles/ascs.py CHANGED
@@ -24,7 +24,7 @@ import logging
24
24
  import struct
25
25
  from collections.abc import Sequence
26
26
  from dataclasses import dataclass, field
27
- from typing import Any, Optional, TypeVar, Union
27
+ from typing import Any, TypeVar
28
28
 
29
29
  from bumble import colors, device, gatt, gatt_client, hci, utils
30
30
  from bumble.profiles import le_audio
@@ -49,7 +49,7 @@ class ASE_Operation:
49
49
  classes: dict[int, type[ASE_Operation]] = {}
50
50
  op_code: Opcode
51
51
  name: str
52
- fields: Optional[Sequence[Any]] = None
52
+ fields: Sequence[Any] | None = None
53
53
  ase_id: Sequence[int]
54
54
 
55
55
  class Opcode(enum.IntEnum):
@@ -278,7 +278,7 @@ class AseStateMachine(gatt.Characteristic):
278
278
 
279
279
  EVENT_STATE_CHANGE = "state_change"
280
280
 
281
- cis_link: Optional[device.CisLink] = None
281
+ cis_link: device.CisLink | None = None
282
282
 
283
283
  # Additional parameters in CODEC_CONFIGURED State
284
284
  preferred_framing = 0 # Unframed PDU supported
@@ -290,7 +290,7 @@ class AseStateMachine(gatt.Characteristic):
290
290
  preferred_presentation_delay_min = 0
291
291
  preferred_presentation_delay_max = 0
292
292
  codec_id = hci.CodingFormat(hci.CodecID.LC3)
293
- codec_specific_configuration: Union[CodecSpecificConfiguration, bytes] = b''
293
+ codec_specific_configuration: CodecSpecificConfiguration | bytes = b''
294
294
 
295
295
  # Additional parameters in QOS_CONFIGURED State
296
296
  cig_id = 0
@@ -610,7 +610,7 @@ class AudioStreamControlService(gatt.TemplateService):
610
610
 
611
611
  ase_state_machines: dict[int, AseStateMachine]
612
612
  ase_control_point: gatt.Characteristic[bytes]
613
- _active_client: Optional[device.Connection] = None
613
+ _active_client: device.Connection | None = None
614
614
 
615
615
  def __init__(
616
616
  self,
bumble/profiles/asha.py CHANGED
@@ -19,7 +19,8 @@
19
19
  import enum
20
20
  import logging
21
21
  import struct
22
- from typing import Any, Callable, Optional, Union
22
+ from collections.abc import Callable
23
+ from typing import Any
23
24
 
24
25
  from bumble import data_types, gatt, gatt_client, l2cap, utils
25
26
  from bumble.core import AdvertisingData
@@ -90,20 +91,20 @@ class AshaService(gatt.TemplateService):
90
91
  EVENT_DISCONNECTED = "disconnected"
91
92
  EVENT_VOLUME_CHANGED = "volume_changed"
92
93
 
93
- audio_sink: Optional[Callable[[bytes], Any]]
94
- active_codec: Optional[Codec] = None
95
- audio_type: Optional[AudioType] = None
96
- volume: Optional[int] = None
97
- other_state: Optional[int] = None
98
- connection: Optional[Connection] = None
94
+ audio_sink: Callable[[bytes], Any] | None
95
+ active_codec: Codec | None = None
96
+ audio_type: AudioType | None = None
97
+ volume: int | None = None
98
+ other_state: int | None = None
99
+ connection: Connection | None = None
99
100
 
100
101
  def __init__(
101
102
  self,
102
103
  capability: int,
103
- hisyncid: Union[list[int], bytes],
104
+ hisyncid: list[int] | bytes,
104
105
  device: Device,
105
106
  psm: int = 0,
106
- audio_sink: Optional[Callable[[bytes], Any]] = None,
107
+ audio_sink: Callable[[bytes], Any] | None = None,
107
108
  feature_map: int = FeatureMap.LE_COC_AUDIO_OUTPUT_STREAMING_SUPPORTED,
108
109
  protocol_version: int = 0x01,
109
110
  render_delay_milliseconds: int = 0,
bumble/profiles/bass.py CHANGED
@@ -21,7 +21,8 @@ from __future__ import annotations
21
21
  import dataclasses
22
22
  import logging
23
23
  import struct
24
- from typing import ClassVar, Optional, Sequence
24
+ from collections.abc import Sequence
25
+ from typing import ClassVar
25
26
 
26
27
  from bumble import core, device, gatt, gatt_adapters, gatt_client, hci, utils
27
28
 
@@ -337,7 +338,12 @@ class BroadcastAudioScanService(gatt.TemplateService):
337
338
  b"12", # TEST
338
339
  )
339
340
 
340
- super().__init__([self.battery_level_characteristic])
341
+ super().__init__(
342
+ [
343
+ self.broadcast_audio_scan_control_point_characteristic,
344
+ self.broadcast_receive_state_characteristic,
345
+ ]
346
+ )
341
347
 
342
348
  def on_broadcast_audio_scan_control_point_write(
343
349
  self, connection: device.Connection, value: bytes
@@ -351,7 +357,7 @@ class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
351
357
 
352
358
  broadcast_audio_scan_control_point: gatt_client.CharacteristicProxy[bytes]
353
359
  broadcast_receive_states: list[
354
- gatt_client.CharacteristicProxy[Optional[BroadcastReceiveState]]
360
+ gatt_client.CharacteristicProxy[BroadcastReceiveState | None]
355
361
  ]
356
362
 
357
363
  def __init__(self, service_proxy: gatt_client.ServiceProxy):
@@ -16,7 +16,6 @@
16
16
  # -----------------------------------------------------------------------------
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
- from typing import Optional
20
19
 
21
20
  from bumble.gatt import (
22
21
  GATT_BATTERY_LEVEL_CHARACTERISTIC,
@@ -56,7 +55,7 @@ class BatteryService(TemplateService):
56
55
  class BatteryServiceProxy(ProfileServiceProxy):
57
56
  SERVICE_CLASS = BatteryService
58
57
 
59
- battery_level: Optional[CharacteristicProxy[int]]
58
+ battery_level: CharacteristicProxy[int] | None
60
59
 
61
60
  def __init__(self, service_proxy):
62
61
  self.service_proxy = service_proxy
bumble/profiles/csip.py CHANGED
@@ -20,7 +20,6 @@ from __future__ import annotations
20
20
 
21
21
  import enum
22
22
  import struct
23
- from typing import Optional
24
23
 
25
24
  from bumble import core, crypto, device, gatt, gatt_client
26
25
 
@@ -96,17 +95,17 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
96
95
 
97
96
  set_identity_resolving_key: bytes
98
97
  set_identity_resolving_key_characteristic: gatt.Characteristic[bytes]
99
- coordinated_set_size_characteristic: Optional[gatt.Characteristic[bytes]] = None
100
- set_member_lock_characteristic: Optional[gatt.Characteristic[bytes]] = None
101
- set_member_rank_characteristic: Optional[gatt.Characteristic[bytes]] = None
98
+ coordinated_set_size_characteristic: gatt.Characteristic[bytes] | None = None
99
+ set_member_lock_characteristic: gatt.Characteristic[bytes] | None = None
100
+ set_member_rank_characteristic: gatt.Characteristic[bytes] | None = None
102
101
 
103
102
  def __init__(
104
103
  self,
105
104
  set_identity_resolving_key: bytes,
106
105
  set_identity_resolving_key_type: SirkType,
107
- coordinated_set_size: Optional[int] = None,
108
- set_member_lock: Optional[MemberLock] = None,
109
- set_member_rank: Optional[int] = None,
106
+ coordinated_set_size: int | None = None,
107
+ set_member_lock: MemberLock | None = None,
108
+ set_member_rank: int | None = None,
110
109
  ) -> None:
111
110
  if len(set_identity_resolving_key) != SET_IDENTITY_RESOLVING_KEY_LENGTH:
112
111
  raise core.InvalidArgumentError(
@@ -198,9 +197,9 @@ class CoordinatedSetIdentificationProxy(gatt_client.ProfileServiceProxy):
198
197
  SERVICE_CLASS = CoordinatedSetIdentificationService
199
198
 
200
199
  set_identity_resolving_key: gatt_client.CharacteristicProxy[bytes]
201
- coordinated_set_size: Optional[gatt_client.CharacteristicProxy[bytes]] = None
202
- set_member_lock: Optional[gatt_client.CharacteristicProxy[bytes]] = None
203
- set_member_rank: Optional[gatt_client.CharacteristicProxy[bytes]] = None
200
+ coordinated_set_size: gatt_client.CharacteristicProxy[bytes] | None = None
201
+ set_member_lock: gatt_client.CharacteristicProxy[bytes] | None = None
202
+ set_member_rank: gatt_client.CharacteristicProxy[bytes] | None = None
204
203
 
205
204
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
206
205
  self.service_proxy = service_proxy
@@ -17,7 +17,6 @@
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
19
  import struct
20
- from typing import Optional
21
20
 
22
21
  from bumble.gatt import (
23
22
  GATT_DEVICE_INFORMATION_SERVICE,
@@ -54,14 +53,14 @@ class DeviceInformationService(TemplateService):
54
53
 
55
54
  def __init__(
56
55
  self,
57
- manufacturer_name: Optional[str] = None,
58
- model_number: Optional[str] = None,
59
- serial_number: Optional[str] = None,
60
- hardware_revision: Optional[str] = None,
61
- firmware_revision: Optional[str] = None,
62
- software_revision: Optional[str] = None,
63
- system_id: Optional[tuple[int, int]] = None, # (OUI, Manufacturer ID)
64
- ieee_regulatory_certification_data_list: Optional[bytes] = None,
56
+ manufacturer_name: str | None = None,
57
+ model_number: str | None = None,
58
+ serial_number: str | None = None,
59
+ hardware_revision: str | None = None,
60
+ firmware_revision: str | None = None,
61
+ software_revision: str | None = None,
62
+ system_id: tuple[int, int] | None = None, # (OUI, Manufacturer ID)
63
+ ieee_regulatory_certification_data_list: bytes | None = None,
65
64
  # TODO: pnp_id
66
65
  ):
67
66
  characteristics: list[Characteristic[bytes]] = [
@@ -109,14 +108,14 @@ class DeviceInformationService(TemplateService):
109
108
  class DeviceInformationServiceProxy(ProfileServiceProxy):
110
109
  SERVICE_CLASS = DeviceInformationService
111
110
 
112
- manufacturer_name: Optional[CharacteristicProxy[str]]
113
- model_number: Optional[CharacteristicProxy[str]]
114
- serial_number: Optional[CharacteristicProxy[str]]
115
- hardware_revision: Optional[CharacteristicProxy[str]]
116
- firmware_revision: Optional[CharacteristicProxy[str]]
117
- software_revision: Optional[CharacteristicProxy[str]]
118
- system_id: Optional[CharacteristicProxy[tuple[int, int]]]
119
- ieee_regulatory_certification_data_list: Optional[CharacteristicProxy[bytes]]
111
+ manufacturer_name: CharacteristicProxy[str] | None
112
+ model_number: CharacteristicProxy[str] | None
113
+ serial_number: CharacteristicProxy[str] | None
114
+ hardware_revision: CharacteristicProxy[str] | None
115
+ firmware_revision: CharacteristicProxy[str] | None
116
+ software_revision: CharacteristicProxy[str] | None
117
+ system_id: CharacteristicProxy[tuple[int, int]] | None
118
+ ieee_regulatory_certification_data_list: CharacteristicProxy[bytes] | None
120
119
 
121
120
  def __init__(self, service_proxy: ServiceProxy):
122
121
  self.service_proxy = service_proxy
bumble/profiles/gap.py CHANGED
@@ -19,7 +19,6 @@
19
19
  # -----------------------------------------------------------------------------
20
20
  import logging
21
21
  import struct
22
- from typing import Optional, Union
23
22
 
24
23
  from bumble.core import Appearance
25
24
  from bumble.gatt import (
@@ -54,7 +53,7 @@ class GenericAccessService(TemplateService):
54
53
  appearance_characteristic: Characteristic[bytes]
55
54
 
56
55
  def __init__(
57
- self, device_name: str, appearance: Union[Appearance, tuple[int, int], int] = 0
56
+ self, device_name: str, appearance: Appearance | tuple[int, int] | int = 0
58
57
  ):
59
58
  if isinstance(appearance, int):
60
59
  appearance_int = appearance
@@ -88,8 +87,8 @@ class GenericAccessService(TemplateService):
88
87
  class GenericAccessServiceProxy(ProfileServiceProxy):
89
88
  SERVICE_CLASS = GenericAccessService
90
89
 
91
- device_name: Optional[CharacteristicProxy[str]]
92
- appearance: Optional[CharacteristicProxy[Appearance]]
90
+ device_name: CharacteristicProxy[str] | None
91
+ appearance: CharacteristicProxy[Appearance] | None
93
92
 
94
93
  def __init__(self, service_proxy: ServiceProxy):
95
94
  self.service_proxy = service_proxy
@@ -40,7 +40,6 @@ class GenericAttributeProfileService(gatt.TemplateService):
40
40
  database_hash_enabled: bool = True,
41
41
  service_change_enabled: bool = True,
42
42
  ) -> None:
43
-
44
43
  if server_supported_features is not None:
45
44
  self.server_supported_features_characteristic = gatt.Characteristic(
46
45
  uuid=gatt.GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC,
bumble/profiles/gmap.py CHANGED
@@ -19,7 +19,6 @@
19
19
  # -----------------------------------------------------------------------------
20
20
  import struct
21
21
  from enum import IntFlag
22
- from typing import Optional
23
22
 
24
23
  from bumble.gatt import (
25
24
  GATT_BGR_FEATURES_CHARACTERISTIC,
@@ -77,18 +76,18 @@ class GamingAudioService(TemplateService):
77
76
  UUID = GATT_GAMING_AUDIO_SERVICE
78
77
 
79
78
  gmap_role: Characteristic
80
- ugg_features: Optional[Characteristic] = None
81
- ugt_features: Optional[Characteristic] = None
82
- bgs_features: Optional[Characteristic] = None
83
- bgr_features: Optional[Characteristic] = None
79
+ ugg_features: Characteristic | None = None
80
+ ugt_features: Characteristic | None = None
81
+ bgs_features: Characteristic | None = None
82
+ bgr_features: Characteristic | None = None
84
83
 
85
84
  def __init__(
86
85
  self,
87
86
  gmap_role: GmapRole,
88
- ugg_features: Optional[UggFeatures] = None,
89
- ugt_features: Optional[UgtFeatures] = None,
90
- bgs_features: Optional[BgsFeatures] = None,
91
- bgr_features: Optional[BgrFeatures] = None,
87
+ ugg_features: UggFeatures | None = None,
88
+ ugt_features: UgtFeatures | None = None,
89
+ bgs_features: BgsFeatures | None = None,
90
+ bgr_features: BgrFeatures | None = None,
92
91
  ) -> None:
93
92
  characteristics = []
94
93
 
@@ -150,10 +149,10 @@ class GamingAudioService(TemplateService):
150
149
  class GamingAudioServiceProxy(ProfileServiceProxy):
151
150
  SERVICE_CLASS = GamingAudioService
152
151
 
153
- ugg_features: Optional[CharacteristicProxy[UggFeatures]] = None
154
- ugt_features: Optional[CharacteristicProxy[UgtFeatures]] = None
155
- bgs_features: Optional[CharacteristicProxy[BgsFeatures]] = None
156
- bgr_features: Optional[CharacteristicProxy[BgrFeatures]] = None
152
+ ugg_features: CharacteristicProxy[UggFeatures] | None = None
153
+ ugt_features: CharacteristicProxy[UgtFeatures] | None = None
154
+ bgs_features: CharacteristicProxy[BgsFeatures] | None = None
155
+ bgr_features: CharacteristicProxy[BgrFeatures] | None = None
157
156
 
158
157
  def __init__(self, service_proxy: ServiceProxy) -> None:
159
158
  self.service_proxy = service_proxy