bumble 0.0.209__py3-none-any.whl → 0.0.211__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 (74) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +7 -7
  3. bumble/apps/auracast.py +37 -29
  4. bumble/apps/bench.py +13 -9
  5. bumble/apps/console.py +1 -1
  6. bumble/apps/lea_unicast/app.py +6 -2
  7. bumble/apps/pair.py +4 -3
  8. bumble/apps/player/player.py +3 -3
  9. bumble/apps/rfcomm_bridge.py +1 -1
  10. bumble/apps/speaker/speaker.py +4 -2
  11. bumble/att.py +2 -3
  12. bumble/avc.py +5 -5
  13. bumble/avdtp.py +9 -10
  14. bumble/avrcp.py +18 -19
  15. bumble/bridge.py +2 -2
  16. bumble/controller.py +6 -7
  17. bumble/core.py +56 -56
  18. bumble/device.py +172 -137
  19. bumble/drivers/__init__.py +2 -2
  20. bumble/gap.py +1 -1
  21. bumble/gatt_adapters.py +3 -3
  22. bumble/gatt_client.py +27 -21
  23. bumble/gatt_server.py +9 -10
  24. bumble/hci.py +48 -22
  25. bumble/hfp.py +3 -3
  26. bumble/hid.py +4 -3
  27. bumble/host.py +22 -16
  28. bumble/keys.py +3 -3
  29. bumble/l2cap.py +19 -17
  30. bumble/link.py +3 -4
  31. bumble/pairing.py +3 -3
  32. bumble/pandora/__init__.py +5 -5
  33. bumble/pandora/host.py +18 -12
  34. bumble/pandora/l2cap.py +2 -2
  35. bumble/pandora/security.py +15 -16
  36. bumble/profiles/aics.py +6 -6
  37. bumble/profiles/ancs.py +9 -10
  38. bumble/profiles/ascs.py +17 -10
  39. bumble/profiles/asha.py +5 -5
  40. bumble/profiles/bass.py +1 -1
  41. bumble/profiles/csip.py +10 -10
  42. bumble/profiles/gatt_service.py +12 -12
  43. bumble/profiles/hap.py +16 -16
  44. bumble/profiles/mcp.py +26 -24
  45. bumble/profiles/pacs.py +6 -6
  46. bumble/profiles/pbp.py +1 -1
  47. bumble/profiles/vcs.py +6 -4
  48. bumble/profiles/vocs.py +3 -3
  49. bumble/rfcomm.py +8 -8
  50. bumble/sdp.py +1 -1
  51. bumble/smp.py +36 -30
  52. bumble/transport/__init__.py +24 -19
  53. bumble/transport/android_emulator.py +8 -4
  54. bumble/transport/android_netsim.py +8 -5
  55. bumble/transport/common.py +5 -1
  56. bumble/transport/file.py +1 -1
  57. bumble/transport/hci_socket.py +1 -1
  58. bumble/transport/pty.py +1 -1
  59. bumble/transport/pyusb.py +3 -3
  60. bumble/transport/serial.py +1 -1
  61. bumble/transport/tcp_client.py +1 -1
  62. bumble/transport/tcp_server.py +1 -1
  63. bumble/transport/udp.py +1 -1
  64. bumble/transport/unix.py +1 -1
  65. bumble/transport/vhci.py +2 -2
  66. bumble/transport/ws_client.py +6 -1
  67. bumble/transport/ws_server.py +1 -1
  68. bumble/utils.py +89 -76
  69. {bumble-0.0.209.dist-info → bumble-0.0.211.dist-info}/METADATA +3 -2
  70. {bumble-0.0.209.dist-info → bumble-0.0.211.dist-info}/RECORD +74 -74
  71. {bumble-0.0.209.dist-info → bumble-0.0.211.dist-info}/WHEEL +1 -1
  72. {bumble-0.0.209.dist-info → bumble-0.0.211.dist-info}/entry_points.txt +0 -0
  73. {bumble-0.0.209.dist-info → bumble-0.0.211.dist-info/licenses}/LICENSE +0 -0
  74. {bumble-0.0.209.dist-info → bumble-0.0.211.dist-info}/top_level.txt +0 -0
bumble/keys.py CHANGED
@@ -28,11 +28,11 @@ import json
28
28
  from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type
29
29
  from typing_extensions import Self
30
30
 
31
- from .colors import color
32
- from .hci import Address
31
+ from bumble.colors import color
32
+ from bumble.hci import Address
33
33
 
34
34
  if TYPE_CHECKING:
35
- from .device import Device
35
+ from bumble.device import Device
36
36
 
37
37
 
38
38
  # -----------------------------------------------------------------------------
bumble/l2cap.py CHANGED
@@ -23,7 +23,6 @@ import logging
23
23
  import struct
24
24
 
25
25
  from collections import deque
26
- from pyee import EventEmitter
27
26
  from typing import (
28
27
  Dict,
29
28
  Type,
@@ -39,16 +38,16 @@ from typing import (
39
38
  TYPE_CHECKING,
40
39
  )
41
40
 
42
- from .utils import deprecated
43
- from .colors import color
44
- from .core import (
41
+ from bumble import utils
42
+ from bumble.colors import color
43
+ from bumble.core import (
45
44
  InvalidStateError,
46
45
  InvalidArgumentError,
47
46
  InvalidPacketError,
48
47
  OutOfResourcesError,
49
48
  ProtocolError,
50
49
  )
51
- from .hci import (
50
+ from bumble.hci import (
52
51
  HCI_LE_Connection_Update_Command,
53
52
  HCI_Object,
54
53
  Role,
@@ -720,7 +719,7 @@ class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
720
719
 
721
720
 
722
721
  # -----------------------------------------------------------------------------
723
- class ClassicChannel(EventEmitter):
722
+ class ClassicChannel(utils.EventEmitter):
724
723
  class State(enum.IntEnum):
725
724
  # States
726
725
  CLOSED = 0x00
@@ -821,8 +820,8 @@ class ClassicChannel(EventEmitter):
821
820
 
822
821
  # Wait for the connection to succeed or fail
823
822
  try:
824
- return await self.connection.abort_on(
825
- 'disconnection', self.connection_result
823
+ return await utils.cancel_on_event(
824
+ self.connection, 'disconnection', self.connection_result
826
825
  )
827
826
  finally:
828
827
  self.connection_result = None
@@ -1026,7 +1025,7 @@ class ClassicChannel(EventEmitter):
1026
1025
 
1027
1026
 
1028
1027
  # -----------------------------------------------------------------------------
1029
- class LeCreditBasedChannel(EventEmitter):
1028
+ class LeCreditBasedChannel(utils.EventEmitter):
1030
1029
  """
1031
1030
  LE Credit-based Connection Oriented Channel
1032
1031
  """
@@ -1381,7 +1380,7 @@ class LeCreditBasedChannel(EventEmitter):
1381
1380
 
1382
1381
 
1383
1382
  # -----------------------------------------------------------------------------
1384
- class ClassicChannelServer(EventEmitter):
1383
+ class ClassicChannelServer(utils.EventEmitter):
1385
1384
  def __init__(
1386
1385
  self,
1387
1386
  manager: ChannelManager,
@@ -1406,7 +1405,7 @@ class ClassicChannelServer(EventEmitter):
1406
1405
 
1407
1406
 
1408
1407
  # -----------------------------------------------------------------------------
1409
- class LeCreditBasedChannelServer(EventEmitter):
1408
+ class LeCreditBasedChannelServer(utils.EventEmitter):
1410
1409
  def __init__(
1411
1410
  self,
1412
1411
  manager: ChannelManager,
@@ -1521,6 +1520,9 @@ class ChannelManager:
1521
1520
 
1522
1521
  def next_identifier(self, connection: Connection) -> int:
1523
1522
  identifier = (self.identifiers.setdefault(connection.handle, 0) + 1) % 256
1523
+ # 0x00 is an invalid ID (BT Core Spec, Vol 3, Part A, Sect 4
1524
+ if identifier == 0:
1525
+ identifier = 1
1524
1526
  self.identifiers[connection.handle] = identifier
1525
1527
  return identifier
1526
1528
 
@@ -1533,7 +1535,7 @@ class ChannelManager:
1533
1535
  if cid in self.fixed_channels:
1534
1536
  del self.fixed_channels[cid]
1535
1537
 
1536
- @deprecated("Please use create_classic_server")
1538
+ @utils.deprecated("Please use create_classic_server")
1537
1539
  def register_server(
1538
1540
  self,
1539
1541
  psm: int,
@@ -1579,7 +1581,7 @@ class ChannelManager:
1579
1581
 
1580
1582
  return self.servers[spec.psm]
1581
1583
 
1582
- @deprecated("Please use create_le_credit_based_server()")
1584
+ @utils.deprecated("Please use create_le_credit_based_server()")
1583
1585
  def register_le_coc_server(
1584
1586
  self,
1585
1587
  psm: int,
@@ -2123,7 +2125,7 @@ class ChannelManager:
2123
2125
  if channel.source_cid in connection_channels:
2124
2126
  del connection_channels[channel.source_cid]
2125
2127
 
2126
- @deprecated("Please use create_le_credit_based_channel()")
2128
+ @utils.deprecated("Please use create_le_credit_based_channel()")
2127
2129
  async def open_le_coc(
2128
2130
  self, connection: Connection, psm: int, max_credits: int, mtu: int, mps: int
2129
2131
  ) -> LeCreditBasedChannel:
@@ -2180,7 +2182,7 @@ class ChannelManager:
2180
2182
 
2181
2183
  return channel
2182
2184
 
2183
- @deprecated("Please use create_classic_channel()")
2185
+ @utils.deprecated("Please use create_classic_channel()")
2184
2186
  async def connect(self, connection: Connection, psm: int) -> ClassicChannel:
2185
2187
  return await self.create_classic_channel(
2186
2188
  connection=connection, spec=ClassicChannelSpec(psm=psm)
@@ -2230,12 +2232,12 @@ class ChannelManager:
2230
2232
 
2231
2233
 
2232
2234
  class Channel(ClassicChannel):
2233
- @deprecated("Please use ClassicChannel")
2235
+ @utils.deprecated("Please use ClassicChannel")
2234
2236
  def __init__(self, *args, **kwargs) -> None:
2235
2237
  super().__init__(*args, **kwargs)
2236
2238
 
2237
2239
 
2238
2240
  class LeConnectionOrientedChannel(LeCreditBasedChannel):
2239
- @deprecated("Please use LeCreditBasedChannel")
2241
+ @utils.deprecated("Please use LeCreditBasedChannel")
2240
2242
  def __init__(self, *args, **kwargs) -> None:
2241
2243
  super().__init__(*args, **kwargs)
bumble/link.py CHANGED
@@ -20,8 +20,7 @@ import asyncio
20
20
  from functools import partial
21
21
 
22
22
  from bumble.core import (
23
- BT_BR_EDR_TRANSPORT,
24
- BT_LE_TRANSPORT,
23
+ PhysicalTransport,
25
24
  InvalidStateError,
26
25
  )
27
26
  from bumble.colors import color
@@ -116,10 +115,10 @@ class LocalLink:
116
115
 
117
116
  def send_acl_data(self, sender_controller, destination_address, transport, data):
118
117
  # Send the data to the first controller with a matching address
119
- if transport == BT_LE_TRANSPORT:
118
+ if transport == PhysicalTransport.LE:
120
119
  destination_controller = self.find_controller(destination_address)
121
120
  source_address = sender_controller.random_address
122
- elif transport == BT_BR_EDR_TRANSPORT:
121
+ elif transport == PhysicalTransport.BR_EDR:
123
122
  destination_controller = self.find_classic_controller(destination_address)
124
123
  source_address = sender_controller.public_address
125
124
  else:
bumble/pairing.py CHANGED
@@ -20,14 +20,14 @@ import enum
20
20
  from dataclasses import dataclass
21
21
  from typing import Optional, Tuple
22
22
 
23
- from .hci import (
23
+ from bumble.hci import (
24
24
  Address,
25
25
  HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
26
26
  HCI_DISPLAY_ONLY_IO_CAPABILITY,
27
27
  HCI_DISPLAY_YES_NO_IO_CAPABILITY,
28
28
  HCI_KEYBOARD_ONLY_IO_CAPABILITY,
29
29
  )
30
- from .smp import (
30
+ from bumble.smp import (
31
31
  SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
32
32
  SMP_KEYBOARD_ONLY_IO_CAPABILITY,
33
33
  SMP_DISPLAY_ONLY_IO_CAPABILITY,
@@ -41,7 +41,7 @@ from .smp import (
41
41
  OobLegacyContext,
42
42
  OobSharedData,
43
43
  )
44
- from .core import AdvertisingData, LeRole
44
+ from bumble.core import AdvertisingData, LeRole
45
45
 
46
46
 
47
47
  # -----------------------------------------------------------------------------
@@ -22,11 +22,11 @@ __version__ = "0.0.1"
22
22
  import grpc
23
23
  import grpc.aio
24
24
 
25
- from .config import Config
26
- from .device import PandoraDevice
27
- from .host import HostService
28
- from .l2cap import L2CAPService
29
- from .security import SecurityService, SecurityStorageService
25
+ from bumble.pandora.config import Config
26
+ from bumble.pandora.device import PandoraDevice
27
+ from bumble.pandora.host import HostService
28
+ from bumble.pandora.l2cap import L2CAPService
29
+ from bumble.pandora.security import SecurityService, SecurityStorageService
30
30
  from pandora.host_grpc_aio import add_HostServicer_to_server
31
31
  from pandora.l2cap_grpc_aio import add_L2CAPServicer_to_server
32
32
  from pandora.security_grpc_aio import (
bumble/pandora/host.py CHANGED
@@ -20,11 +20,11 @@ import grpc.aio
20
20
  import logging
21
21
  import struct
22
22
 
23
- from . import utils
24
- from .config import Config
23
+ import bumble.utils
24
+ from bumble.pandora import utils
25
+ from bumble.pandora.config import Config
25
26
  from bumble.core import (
26
- BT_BR_EDR_TRANSPORT,
27
- BT_LE_TRANSPORT,
27
+ PhysicalTransport,
28
28
  UUID,
29
29
  AdvertisingData,
30
30
  Appearance,
@@ -185,7 +185,7 @@ class HostService(HostServicer):
185
185
 
186
186
  try:
187
187
  connection = await self.device.connect(
188
- address, transport=BT_BR_EDR_TRANSPORT
188
+ address, transport=PhysicalTransport.BR_EDR
189
189
  )
190
190
  except ConnectionError as e:
191
191
  if e.error_code == HCI_PAGE_TIMEOUT_ERROR:
@@ -218,7 +218,7 @@ class HostService(HostServicer):
218
218
  self.log.debug(f"WaitConnection from {address}...")
219
219
 
220
220
  connection = self.device.find_connection_by_bd_addr(
221
- address, transport=BT_BR_EDR_TRANSPORT
221
+ address, transport=PhysicalTransport.BR_EDR
222
222
  )
223
223
  if connection and id(connection) in self.waited_connections:
224
224
  # this connection was already returned: wait for a new one.
@@ -250,7 +250,7 @@ class HostService(HostServicer):
250
250
  try:
251
251
  connection = await self.device.connect(
252
252
  address,
253
- transport=BT_LE_TRANSPORT,
253
+ transport=PhysicalTransport.LE,
254
254
  own_address_type=OwnAddressType(request.own_address_type),
255
255
  )
256
256
  except ConnectionError as e:
@@ -378,7 +378,7 @@ class HostService(HostServicer):
378
378
 
379
379
  def on_connection(connection: bumble.device.Connection) -> None:
380
380
  if (
381
- connection.transport == BT_LE_TRANSPORT
381
+ connection.transport == PhysicalTransport.LE
382
382
  and connection.role == Role.PERIPHERAL
383
383
  ):
384
384
  connections.put_nowait(connection)
@@ -496,7 +496,7 @@ class HostService(HostServicer):
496
496
 
497
497
  def on_connection(connection: bumble.device.Connection) -> None:
498
498
  if (
499
- connection.transport == BT_LE_TRANSPORT
499
+ connection.transport == PhysicalTransport.LE
500
500
  and connection.role == Role.PERIPHERAL
501
501
  ):
502
502
  connections.put_nowait(connection)
@@ -535,7 +535,9 @@ class HostService(HostServicer):
535
535
 
536
536
  try:
537
537
  self.log.debug('Stop advertising')
538
- await self.device.abort_on('flush', self.device.stop_advertising())
538
+ await bumble.utils.cancel_on_event(
539
+ self.device, 'flush', self.device.stop_advertising()
540
+ )
539
541
  except:
540
542
  pass
541
543
 
@@ -603,7 +605,9 @@ class HostService(HostServicer):
603
605
  self.device.remove_listener('advertisement', handler) # type: ignore
604
606
  try:
605
607
  self.log.debug('Stop scanning')
606
- await self.device.abort_on('flush', self.device.stop_scanning())
608
+ await bumble.utils.cancel_on_event(
609
+ self.device, 'flush', self.device.stop_scanning()
610
+ )
607
611
  except:
608
612
  pass
609
613
 
@@ -643,7 +647,9 @@ class HostService(HostServicer):
643
647
  self.device.remove_listener('inquiry_result', result_handler) # type: ignore
644
648
  try:
645
649
  self.log.debug('Stop inquiry')
646
- await self.device.abort_on('flush', self.device.stop_discovery())
650
+ await bumble.utils.cancel_on_event(
651
+ self.device, 'flush', self.device.stop_discovery()
652
+ )
647
653
  except:
648
654
  pass
649
655
 
bumble/pandora/l2cap.py CHANGED
@@ -19,8 +19,8 @@ import logging
19
19
 
20
20
  from asyncio import Queue as AsyncQueue, Future
21
21
 
22
- from . import utils
23
- from .config import Config
22
+ from bumble.pandora import utils
23
+ from bumble.pandora.config import Config
24
24
  from bumble.core import OutOfResourcesError, InvalidArgumentError
25
25
  from bumble.device import Device
26
26
  from bumble.l2cap import (
@@ -18,17 +18,16 @@ import contextlib
18
18
  import grpc
19
19
  import logging
20
20
 
21
- from . import utils
22
- from .config import Config
21
+ from bumble.pandora import utils
22
+ from bumble.pandora.config import Config
23
23
  from bumble import hci
24
24
  from bumble.core import (
25
- BT_BR_EDR_TRANSPORT,
26
- BT_LE_TRANSPORT,
25
+ PhysicalTransport,
27
26
  ProtocolError,
28
27
  )
28
+ import bumble.utils
29
29
  from bumble.device import Connection as BumbleConnection, Device
30
30
  from bumble.hci import HCI_Error, Role
31
- from bumble.utils import EventWatcher
32
31
  from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate
33
32
  from google.protobuf import any_pb2 # pytype: disable=pyi-error
34
33
  from google.protobuf import empty_pb2 # pytype: disable=pyi-error
@@ -94,7 +93,7 @@ class PairingDelegate(BasePairingDelegate):
94
93
  else:
95
94
  # In BR/EDR, connection may not be complete,
96
95
  # use address instead
97
- assert self.connection.transport == BT_BR_EDR_TRANSPORT
96
+ assert self.connection.transport == PhysicalTransport.BR_EDR
98
97
  ev.address = bytes(reversed(bytes(self.connection.peer_address)))
99
98
 
100
99
  return ev
@@ -173,7 +172,7 @@ class PairingDelegate(BasePairingDelegate):
173
172
 
174
173
  async def display_number(self, number: int, digits: int = 6) -> None:
175
174
  if (
176
- self.connection.transport == BT_BR_EDR_TRANSPORT
175
+ self.connection.transport == PhysicalTransport.BR_EDR
177
176
  and self.io_capability == BasePairingDelegate.DISPLAY_OUTPUT_ONLY
178
177
  ):
179
178
  return
@@ -286,7 +285,7 @@ class SecurityService(SecurityServicer):
286
285
 
287
286
  oneof = request.WhichOneof('level')
288
287
  level = getattr(request, oneof)
289
- assert {BT_BR_EDR_TRANSPORT: 'classic', BT_LE_TRANSPORT: 'le'}[
288
+ assert {PhysicalTransport.BR_EDR: 'classic', PhysicalTransport.LE: 'le'}[
290
289
  connection.transport
291
290
  ] == oneof
292
291
 
@@ -301,7 +300,7 @@ class SecurityService(SecurityServicer):
301
300
 
302
301
  security_result = asyncio.get_running_loop().create_future()
303
302
 
304
- with contextlib.closing(EventWatcher()) as watcher:
303
+ with contextlib.closing(bumble.utils.EventWatcher()) as watcher:
305
304
 
306
305
  @watcher.on(connection, 'pairing')
307
306
  def on_pairing(*_: Any) -> None:
@@ -316,7 +315,7 @@ class SecurityService(SecurityServicer):
316
315
  security_result.set_result('connection_died')
317
316
 
318
317
  if (
319
- connection.transport == BT_LE_TRANSPORT
318
+ connection.transport == PhysicalTransport.LE
320
319
  and connection.role == Role.PERIPHERAL
321
320
  ):
322
321
  connection.request_pairing()
@@ -378,7 +377,7 @@ class SecurityService(SecurityServicer):
378
377
 
379
378
  assert request.level
380
379
  level = request.level
381
- assert {BT_BR_EDR_TRANSPORT: 'classic', BT_LE_TRANSPORT: 'le'}[
380
+ assert {PhysicalTransport.BR_EDR: 'classic', PhysicalTransport.LE: 'le'}[
382
381
  connection.transport
383
382
  ] == request.level_variant()
384
383
 
@@ -426,7 +425,7 @@ class SecurityService(SecurityServicer):
426
425
  self.log.debug('Wait for security: done')
427
426
  wait_for_security.set_result('success')
428
427
  elif (
429
- connection.transport == BT_BR_EDR_TRANSPORT
428
+ connection.transport == PhysicalTransport.BR_EDR
430
429
  and self.need_authentication(connection, level)
431
430
  ):
432
431
  nonlocal authenticate_task
@@ -450,7 +449,7 @@ class SecurityService(SecurityServicer):
450
449
  'security_request': pair,
451
450
  }
452
451
 
453
- with contextlib.closing(EventWatcher()) as watcher:
452
+ with contextlib.closing(bumble.utils.EventWatcher()) as watcher:
454
453
  # register event handlers
455
454
  for event, listener in listeners.items():
456
455
  watcher.on(connection, event, listener)
@@ -504,12 +503,12 @@ class SecurityService(SecurityServicer):
504
503
  return BR_LEVEL_REACHED[level](connection)
505
504
 
506
505
  def need_pairing(self, connection: BumbleConnection, level: int) -> bool:
507
- if connection.transport == BT_LE_TRANSPORT:
506
+ if connection.transport == PhysicalTransport.LE:
508
507
  return level >= LE_LEVEL3 and not connection.authenticated
509
508
  return False
510
509
 
511
510
  def need_authentication(self, connection: BumbleConnection, level: int) -> bool:
512
- if connection.transport == BT_LE_TRANSPORT:
511
+ if connection.transport == PhysicalTransport.LE:
513
512
  return False
514
513
  if level == LEVEL2 and connection.encryption != 0:
515
514
  return not connection.authenticated
@@ -517,7 +516,7 @@ class SecurityService(SecurityServicer):
517
516
 
518
517
  def need_encryption(self, connection: BumbleConnection, level: int) -> bool:
519
518
  # TODO(abel): need to support MITM
520
- if connection.transport == BT_LE_TRANSPORT:
519
+ if connection.transport == PhysicalTransport.LE:
521
520
  return level == LE_LEVEL2 and not connection.encryption
522
521
  return level >= LEVEL2 and not connection.encryption
523
522
 
bumble/profiles/aics.py CHANGED
@@ -48,7 +48,7 @@ from bumble.gatt_adapters import (
48
48
  UTF8CharacteristicProxyAdapter,
49
49
  )
50
50
  from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
51
- from bumble.utils import OpenIntEnum
51
+ from bumble import utils
52
52
 
53
53
  # -----------------------------------------------------------------------------
54
54
  # Logging
@@ -64,7 +64,7 @@ GAIN_SETTINGS_MIN_VALUE = 0
64
64
  GAIN_SETTINGS_MAX_VALUE = 255
65
65
 
66
66
 
67
- class ErrorCode(OpenIntEnum):
67
+ class ErrorCode(utils.OpenIntEnum):
68
68
  '''
69
69
  Cf. 1.6 Application error codes
70
70
  '''
@@ -76,7 +76,7 @@ class ErrorCode(OpenIntEnum):
76
76
  GAIN_MODE_CHANGE_NOT_ALLOWED = 0x84
77
77
 
78
78
 
79
- class Mute(OpenIntEnum):
79
+ class Mute(utils.OpenIntEnum):
80
80
  '''
81
81
  Cf. 2.2.1.2 Mute Field
82
82
  '''
@@ -86,7 +86,7 @@ class Mute(OpenIntEnum):
86
86
  DISABLED = 0x02
87
87
 
88
88
 
89
- class GainMode(OpenIntEnum):
89
+ class GainMode(utils.OpenIntEnum):
90
90
  '''
91
91
  Cf. 2.2.1.3 Gain Mode
92
92
  '''
@@ -97,7 +97,7 @@ class GainMode(OpenIntEnum):
97
97
  AUTOMATIC = 0x03
98
98
 
99
99
 
100
- class AudioInputStatus(OpenIntEnum):
100
+ class AudioInputStatus(utils.OpenIntEnum):
101
101
  '''
102
102
  Cf. 3.4 Audio Input Status
103
103
  '''
@@ -106,7 +106,7 @@ class AudioInputStatus(OpenIntEnum):
106
106
  ACTIVE = 0x01
107
107
 
108
108
 
109
- class AudioInputControlPointOpCode(OpenIntEnum):
109
+ class AudioInputControlPointOpCode(utils.OpenIntEnum):
110
110
  '''
111
111
  Cf. 3.5.1 Audio Input Control Point procedure requirements
112
112
  '''
bumble/profiles/ancs.py CHANGED
@@ -28,7 +28,6 @@ import logging
28
28
  import struct
29
29
  from typing import Optional, Sequence, Union
30
30
 
31
- from pyee import EventEmitter
32
31
 
33
32
  from bumble.att import ATT_Error
34
33
  from bumble.device import Peer
@@ -42,7 +41,7 @@ from bumble.gatt import (
42
41
  )
43
42
  from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
44
43
  from bumble.gatt_adapters import SerializableCharacteristicProxyAdapter
45
- from bumble.utils import OpenIntEnum
44
+ from bumble import utils
46
45
 
47
46
 
48
47
  # -----------------------------------------------------------------------------
@@ -60,16 +59,16 @@ logger = logging.getLogger(__name__)
60
59
  # -----------------------------------------------------------------------------
61
60
  # Protocol
62
61
  # -----------------------------------------------------------------------------
63
- class ActionId(OpenIntEnum):
62
+ class ActionId(utils.OpenIntEnum):
64
63
  POSITIVE = 0
65
64
  NEGATIVE = 1
66
65
 
67
66
 
68
- class AppAttributeId(OpenIntEnum):
67
+ class AppAttributeId(utils.OpenIntEnum):
69
68
  DISPLAY_NAME = 0
70
69
 
71
70
 
72
- class CategoryId(OpenIntEnum):
71
+ class CategoryId(utils.OpenIntEnum):
73
72
  OTHER = 0
74
73
  INCOMING_CALL = 1
75
74
  MISSED_CALL = 2
@@ -84,13 +83,13 @@ class CategoryId(OpenIntEnum):
84
83
  ENTERTAINMENT = 11
85
84
 
86
85
 
87
- class CommandId(OpenIntEnum):
86
+ class CommandId(utils.OpenIntEnum):
88
87
  GET_NOTIFICATION_ATTRIBUTES = 0
89
88
  GET_APP_ATTRIBUTES = 1
90
89
  PERFORM_NOTIFICATION_ACTION = 2
91
90
 
92
91
 
93
- class EventId(OpenIntEnum):
92
+ class EventId(utils.OpenIntEnum):
94
93
  NOTIFICATION_ADDED = 0
95
94
  NOTIFICATION_MODIFIED = 1
96
95
  NOTIFICATION_REMOVED = 2
@@ -104,7 +103,7 @@ class EventFlags(enum.IntFlag):
104
103
  NEGATIVE_ACTION = 1 << 4
105
104
 
106
105
 
107
- class NotificationAttributeId(OpenIntEnum):
106
+ class NotificationAttributeId(utils.OpenIntEnum):
108
107
  APP_IDENTIFIER = 0
109
108
  TITLE = 1
110
109
  SUBTITLE = 2
@@ -156,7 +155,7 @@ class Notification:
156
155
  )
157
156
 
158
157
 
159
- class ErrorCode(OpenIntEnum):
158
+ class ErrorCode(utils.OpenIntEnum):
160
159
  UNKNOWN_COMMAND = 0xA0
161
160
  INVALID_COMMAND = 0xA1
162
161
  INVALID_PARAMETER = 0xA2
@@ -243,7 +242,7 @@ class AncsProxy(ProfileServiceProxy):
243
242
  )
244
243
 
245
244
 
246
- class AncsClient(EventEmitter):
245
+ class AncsClient(utils.EventEmitter):
247
246
  _expected_response_command_id: Optional[CommandId]
248
247
  _expected_response_notification_uid: Optional[int]
249
248
  _expected_response_app_identifier: Optional[str]
bumble/profiles/ascs.py CHANGED
@@ -23,6 +23,7 @@ import logging
23
23
  import struct
24
24
  from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
25
25
 
26
+ from bumble import utils
26
27
  from bumble import colors
27
28
  from bumble.profiles.bap import CodecSpecificConfiguration
28
29
  from bumble.profiles import le_audio
@@ -343,8 +344,10 @@ class AseStateMachine(gatt.Characteristic):
343
344
  and cis_id == self.cis_id
344
345
  and self.state == self.State.ENABLING
345
346
  ):
346
- acl_connection.abort_on(
347
- 'flush', self.service.device.accept_cis_request(cis_handle)
347
+ utils.cancel_on_event(
348
+ acl_connection,
349
+ 'flush',
350
+ self.service.device.accept_cis_request(cis_handle),
348
351
  )
349
352
 
350
353
  def on_cis_establishment(self, cis_link: device.CisLink) -> None:
@@ -361,7 +364,9 @@ class AseStateMachine(gatt.Characteristic):
361
364
  self.state = self.State.STREAMING
362
365
  await self.service.device.notify_subscribers(self, self.value)
363
366
 
364
- cis_link.acl_connection.abort_on('flush', post_cis_established())
367
+ utils.cancel_on_event(
368
+ cis_link.acl_connection, 'flush', post_cis_established()
369
+ )
365
370
  self.cis_link = cis_link
366
371
 
367
372
  def on_cis_disconnection(self, _reason) -> None:
@@ -509,7 +514,7 @@ class AseStateMachine(gatt.Characteristic):
509
514
  self.state = self.State.IDLE
510
515
  await self.service.device.notify_subscribers(self, self.value)
511
516
 
512
- self.service.device.abort_on('flush', remove_cis_async())
517
+ utils.cancel_on_event(self.service.device, 'flush', remove_cis_async())
513
518
  return (AseResponseCode.SUCCESS, AseReasonCode.NONE)
514
519
 
515
520
  @property
@@ -594,7 +599,7 @@ class AudioStreamControlService(gatt.TemplateService):
594
599
  UUID = gatt.GATT_AUDIO_STREAM_CONTROL_SERVICE
595
600
 
596
601
  ase_state_machines: Dict[int, AseStateMachine]
597
- ase_control_point: gatt.Characteristic
602
+ ase_control_point: gatt.Characteristic[bytes]
598
603
  _active_client: Optional[device.Connection] = None
599
604
 
600
605
  def __init__(
@@ -691,7 +696,8 @@ class AudioStreamControlService(gatt.TemplateService):
691
696
  control_point_notification = bytes(
692
697
  [operation.op_code, len(responses)]
693
698
  ) + b''.join(map(bytes, responses))
694
- self.device.abort_on(
699
+ utils.cancel_on_event(
700
+ self.device,
695
701
  'flush',
696
702
  self.device.notify_subscribers(
697
703
  self.ase_control_point, control_point_notification
@@ -700,7 +706,8 @@ class AudioStreamControlService(gatt.TemplateService):
700
706
 
701
707
  for ase_id, *_ in responses:
702
708
  if ase := self.ase_state_machines.get(ase_id):
703
- self.device.abort_on(
709
+ utils.cancel_on_event(
710
+ self.device,
704
711
  'flush',
705
712
  self.device.notify_subscribers(ase, ase.value),
706
713
  )
@@ -710,9 +717,9 @@ class AudioStreamControlService(gatt.TemplateService):
710
717
  class AudioStreamControlServiceProxy(gatt_client.ProfileServiceProxy):
711
718
  SERVICE_CLASS = AudioStreamControlService
712
719
 
713
- sink_ase: List[gatt_client.CharacteristicProxy]
714
- source_ase: List[gatt_client.CharacteristicProxy]
715
- ase_control_point: gatt_client.CharacteristicProxy
720
+ sink_ase: List[gatt_client.CharacteristicProxy[bytes]]
721
+ source_ase: List[gatt_client.CharacteristicProxy[bytes]]
722
+ ase_control_point: gatt_client.CharacteristicProxy[bytes]
716
723
 
717
724
  def __init__(self, service_proxy: gatt_client.ServiceProxy):
718
725
  self.service_proxy = service_proxy
bumble/profiles/asha.py CHANGED
@@ -259,11 +259,11 @@ class AshaService(gatt.TemplateService):
259
259
  # -----------------------------------------------------------------------------
260
260
  class AshaServiceProxy(gatt_client.ProfileServiceProxy):
261
261
  SERVICE_CLASS = AshaService
262
- read_only_properties_characteristic: gatt_client.CharacteristicProxy
263
- audio_control_point_characteristic: gatt_client.CharacteristicProxy
264
- audio_status_point_characteristic: gatt_client.CharacteristicProxy
265
- volume_characteristic: gatt_client.CharacteristicProxy
266
- psm_characteristic: gatt_client.CharacteristicProxy
262
+ read_only_properties_characteristic: gatt_client.CharacteristicProxy[bytes]
263
+ audio_control_point_characteristic: gatt_client.CharacteristicProxy[bytes]
264
+ audio_status_point_characteristic: gatt_client.CharacteristicProxy[bytes]
265
+ volume_characteristic: gatt_client.CharacteristicProxy[bytes]
266
+ psm_characteristic: gatt_client.CharacteristicProxy[bytes]
267
267
 
268
268
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
269
269
  self.service_proxy = service_proxy
bumble/profiles/bass.py CHANGED
@@ -354,7 +354,7 @@ class BroadcastAudioScanService(gatt.TemplateService):
354
354
  class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
355
355
  SERVICE_CLASS = BroadcastAudioScanService
356
356
 
357
- broadcast_audio_scan_control_point: gatt_client.CharacteristicProxy
357
+ broadcast_audio_scan_control_point: gatt_client.CharacteristicProxy[bytes]
358
358
  broadcast_receive_states: list[
359
359
  gatt_client.CharacteristicProxy[Optional[BroadcastReceiveState]]
360
360
  ]