bumble 0.0.208__py3-none-any.whl → 0.0.210__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 (77) 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 +9 -7
  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 +12 -5
  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 +13 -15
  17. bumble/core.py +61 -60
  18. bumble/device.py +193 -162
  19. bumble/drivers/__init__.py +2 -2
  20. bumble/gap.py +1 -1
  21. bumble/gatt.py +16 -0
  22. bumble/gatt_adapters.py +3 -3
  23. bumble/gatt_client.py +27 -21
  24. bumble/gatt_server.py +9 -10
  25. bumble/hci.py +109 -90
  26. bumble/hfp.py +3 -3
  27. bumble/hid.py +4 -3
  28. bumble/host.py +30 -19
  29. bumble/keys.py +3 -3
  30. bumble/l2cap.py +21 -19
  31. bumble/link.py +5 -6
  32. bumble/pairing.py +3 -3
  33. bumble/pandora/__init__.py +5 -5
  34. bumble/pandora/host.py +30 -23
  35. bumble/pandora/l2cap.py +2 -2
  36. bumble/pandora/security.py +17 -19
  37. bumble/pandora/utils.py +2 -2
  38. bumble/profiles/aics.py +6 -6
  39. bumble/profiles/ancs.py +513 -0
  40. bumble/profiles/ascs.py +17 -10
  41. bumble/profiles/asha.py +5 -5
  42. bumble/profiles/bass.py +1 -1
  43. bumble/profiles/csip.py +10 -10
  44. bumble/profiles/gatt_service.py +12 -12
  45. bumble/profiles/hap.py +16 -16
  46. bumble/profiles/mcp.py +26 -24
  47. bumble/profiles/pacs.py +6 -6
  48. bumble/profiles/pbp.py +1 -1
  49. bumble/profiles/vcs.py +6 -4
  50. bumble/profiles/vocs.py +3 -3
  51. bumble/rfcomm.py +8 -8
  52. bumble/sdp.py +1 -1
  53. bumble/smp.py +39 -33
  54. bumble/transport/__init__.py +24 -19
  55. bumble/transport/android_emulator.py +8 -4
  56. bumble/transport/android_netsim.py +8 -5
  57. bumble/transport/common.py +5 -1
  58. bumble/transport/file.py +1 -1
  59. bumble/transport/hci_socket.py +1 -1
  60. bumble/transport/pty.py +1 -1
  61. bumble/transport/pyusb.py +3 -3
  62. bumble/transport/serial.py +1 -1
  63. bumble/transport/tcp_client.py +1 -1
  64. bumble/transport/tcp_server.py +1 -1
  65. bumble/transport/udp.py +1 -1
  66. bumble/transport/unix.py +1 -1
  67. bumble/transport/usb.py +1 -3
  68. bumble/transport/vhci.py +2 -2
  69. bumble/transport/ws_client.py +6 -1
  70. bumble/transport/ws_server.py +1 -1
  71. bumble/utils.py +89 -76
  72. {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/METADATA +3 -2
  73. {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/RECORD +77 -76
  74. {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/WHEEL +1 -1
  75. {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/entry_points.txt +0 -0
  76. {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info/licenses}/LICENSE +0 -0
  77. {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/top_level.txt +0 -0
bumble/hci.py CHANGED
@@ -24,21 +24,23 @@ import logging
24
24
  import secrets
25
25
  import struct
26
26
  from typing import Any, Callable, Dict, Iterable, List, Optional, Type, Union, ClassVar
27
+ from typing_extensions import Self
27
28
 
28
29
  from bumble import crypto
29
30
  from bumble.colors import color
30
31
  from bumble.core import (
31
- BT_BR_EDR_TRANSPORT,
32
+ PhysicalTransport,
32
33
  AdvertisingData,
33
34
  DeviceClass,
34
35
  InvalidArgumentError,
35
36
  InvalidPacketError,
36
37
  ProtocolError,
38
+ PhysicalTransport,
37
39
  bit_flags_to_strings,
38
40
  name_or_number,
39
41
  padded_bytes,
40
42
  )
41
- from bumble.utils import OpenIntEnum
43
+ from bumble import utils
42
44
 
43
45
 
44
46
  # -----------------------------------------------------------------------------
@@ -94,7 +96,7 @@ def map_class_of_device(class_of_device):
94
96
  )
95
97
 
96
98
 
97
- def phy_list_to_bits(phys: Optional[Iterable[int]]) -> int:
99
+ def phy_list_to_bits(phys: Optional[Iterable[Phy]]) -> int:
98
100
  if phys is None:
99
101
  return 0
100
102
 
@@ -700,30 +702,22 @@ HCI_ERROR_NAMES[HCI_SUCCESS] = 'HCI_SUCCESS'
700
702
  HCI_COMMAND_STATUS_PENDING = 0
701
703
 
702
704
 
705
+ class Phy(enum.IntEnum):
706
+ LE_1M = 1
707
+ LE_2M = 2
708
+ LE_CODED = 3
709
+
710
+
703
711
  # ACL
704
712
  HCI_ACL_PB_FIRST_NON_FLUSHABLE = 0
705
713
  HCI_ACL_PB_CONTINUATION = 1
706
714
  HCI_ACL_PB_FIRST_FLUSHABLE = 2
707
715
  HCI_ACK_PB_COMPLETE_L2CAP = 3
708
716
 
709
- # Roles
710
- HCI_CENTRAL_ROLE = 0
711
- HCI_PERIPHERAL_ROLE = 1
712
-
713
- HCI_ROLE_NAMES = {
714
- HCI_CENTRAL_ROLE: 'CENTRAL',
715
- HCI_PERIPHERAL_ROLE: 'PERIPHERAL'
716
- }
717
-
718
- # LE PHY Types
719
- HCI_LE_1M_PHY = 1
720
- HCI_LE_2M_PHY = 2
721
- HCI_LE_CODED_PHY = 3
722
-
723
- HCI_LE_PHY_NAMES = {
724
- HCI_LE_1M_PHY: 'LE 1M',
725
- HCI_LE_2M_PHY: 'LE 2M',
726
- HCI_LE_CODED_PHY: 'LE Coded'
717
+ HCI_LE_PHY_NAMES: dict[int,str] = {
718
+ Phy.LE_1M: 'LE 1M',
719
+ Phy.LE_2M: 'LE 2M',
720
+ Phy.LE_CODED: 'LE Coded'
727
721
  }
728
722
 
729
723
  HCI_LE_1M_PHY_BIT = 0
@@ -732,26 +726,20 @@ HCI_LE_CODED_PHY_BIT = 2
732
726
 
733
727
  HCI_LE_PHY_BIT_NAMES = ['LE_1M_PHY', 'LE_2M_PHY', 'LE_CODED_PHY']
734
728
 
735
- HCI_LE_PHY_TYPE_TO_BIT = {
736
- HCI_LE_1M_PHY: HCI_LE_1M_PHY_BIT,
737
- HCI_LE_2M_PHY: HCI_LE_2M_PHY_BIT,
738
- HCI_LE_CODED_PHY: HCI_LE_CODED_PHY_BIT
729
+ HCI_LE_PHY_TYPE_TO_BIT: dict[Phy, int] = {
730
+ Phy.LE_1M: HCI_LE_1M_PHY_BIT,
731
+ Phy.LE_2M: HCI_LE_2M_PHY_BIT,
732
+ Phy.LE_CODED: HCI_LE_CODED_PHY_BIT,
739
733
  }
740
734
 
741
735
 
742
- class Phy(enum.IntEnum):
743
- LE_1M = HCI_LE_1M_PHY
744
- LE_2M = HCI_LE_2M_PHY
745
- LE_CODED = HCI_LE_CODED_PHY
746
-
747
-
748
736
  class PhyBit(enum.IntFlag):
749
737
  LE_1M = 1 << HCI_LE_1M_PHY_BIT
750
738
  LE_2M = 1 << HCI_LE_2M_PHY_BIT
751
739
  LE_CODED = 1 << HCI_LE_CODED_PHY_BIT
752
740
 
753
741
 
754
- class CsRole(OpenIntEnum):
742
+ class CsRole(utils.OpenIntEnum):
755
743
  INITIATOR = 0x00
756
744
  REFLECTOR = 0x01
757
745
 
@@ -761,7 +749,7 @@ class CsRoleMask(enum.IntFlag):
761
749
  REFLECTOR = 0x02
762
750
 
763
751
 
764
- class CsSyncPhy(OpenIntEnum):
752
+ class CsSyncPhy(utils.OpenIntEnum):
765
753
  LE_1M = 1
766
754
  LE_2M = 2
767
755
  LE_2M_2BT = 3
@@ -772,7 +760,7 @@ class CsSyncPhySupported(enum.IntFlag):
772
760
  LE_2M_2BT = 0x02
773
761
 
774
762
 
775
- class RttType(OpenIntEnum):
763
+ class RttType(utils.OpenIntEnum):
776
764
  AA_ONLY = 0x00
777
765
  SOUNDING_SEQUENCE_32_BIT = 0x01
778
766
  SOUNDING_SEQUENCE_96_BIT = 0x02
@@ -782,7 +770,7 @@ class RttType(OpenIntEnum):
782
770
  RANDOM_SEQUENCE_128_BIT = 0x06
783
771
 
784
772
 
785
- class CsSnr(OpenIntEnum):
773
+ class CsSnr(utils.OpenIntEnum):
786
774
  SNR_18_DB = 0x00
787
775
  SNR_21_DB = 0x01
788
776
  SNR_24_DB = 0x02
@@ -791,26 +779,39 @@ class CsSnr(OpenIntEnum):
791
779
  NOT_APPLIED = 0xFF
792
780
 
793
781
 
794
- class CsDoneStatus(OpenIntEnum):
782
+ class CsDoneStatus(utils.OpenIntEnum):
795
783
  ALL_RESULTS_COMPLETED = 0x00
796
784
  PARTIAL = 0x01
797
785
  ABORTED = 0x0F
798
786
 
799
787
 
800
- class CsProcedureAbortReason(OpenIntEnum):
788
+ class CsProcedureAbortReason(utils.OpenIntEnum):
801
789
  NO_ABORT = 0x00
802
790
  LOCAL_HOST_OR_REMOTE_REQUEST = 0x01
803
791
  CHANNEL_MAP_UPDATE_INSTANT_PASSED = 0x02
804
792
  UNSPECIFIED = 0x0F
805
793
 
806
794
 
807
- class CsSubeventAbortReason(OpenIntEnum):
795
+ class CsSubeventAbortReason(utils.OpenIntEnum):
808
796
  NO_ABORT = 0x00
809
797
  LOCAL_HOST_OR_REMOTE_REQUEST = 0x01
810
798
  NO_CS_SYNC_RECEIVED = 0x02
811
799
  SCHEDULING_CONFLICT_OR_LIMITED_RESOURCES = 0x03
812
800
  UNSPECIFIED = 0x0F
813
801
 
802
+ class Role(enum.IntEnum):
803
+ CENTRAL = 0
804
+ PERIPHERAL = 1
805
+
806
+ # For Backward Compatibility.
807
+ HCI_CENTRAL_ROLE = Role.CENTRAL
808
+ HCI_PERIPHERAL_ROLE = Role.PERIPHERAL
809
+
810
+
811
+ HCI_LE_1M_PHY = Phy.LE_1M
812
+ HCI_LE_2M_PHY = Phy.LE_2M
813
+ HCI_LE_CODED_PHY = Phy.LE_CODED
814
+
814
815
 
815
816
  # Connection Parameters
816
817
  HCI_CONNECTION_INTERVAL_MS_PER_UNIT = 1.25
@@ -889,10 +890,15 @@ HCI_LINK_TYPE_NAMES = {
889
890
  }
890
891
 
891
892
  # Address types
892
- HCI_PUBLIC_DEVICE_ADDRESS_TYPE = 0x00
893
- HCI_RANDOM_DEVICE_ADDRESS_TYPE = 0x01
894
- HCI_PUBLIC_IDENTITY_ADDRESS_TYPE = 0x02
895
- HCI_RANDOM_IDENTITY_ADDRESS_TYPE = 0x03
893
+ class AddressType(utils.OpenIntEnum):
894
+ PUBLIC_DEVICE = 0x00
895
+ RANDOM_DEVICE = 0x01
896
+ PUBLIC_IDENTITY = 0x02
897
+ RANDOM_IDENTITY = 0x03
898
+ # (Directed Only) Address is RPA, but controller cannot resolve.
899
+ UNABLE_TO_RESOLVE = 0xFE
900
+ # (Extended Only) No address.
901
+ ANONYMOUS = 0xFF
896
902
 
897
903
  # Supported Commands Masks
898
904
  # See Bluetooth spec @ 6.27 SUPPORTED COMMANDS
@@ -1233,7 +1239,7 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
1233
1239
 
1234
1240
  # LE Supported Features
1235
1241
  # See Bluetooth spec @ Vol 6, Part B, 4.6 FEATURE SUPPORT
1236
- class LeFeature(OpenIntEnum):
1242
+ class LeFeature(utils.OpenIntEnum):
1237
1243
  LE_ENCRYPTION = 0
1238
1244
  CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1
1239
1245
  EXTENDED_REJECT_INDICATION = 2
@@ -1531,7 +1537,7 @@ RTT_TYPE_SPEC = {'size': 1, 'mapper': lambda x: RttType(x).name}
1531
1537
  CS_SNR_SPEC = {'size': 1, 'mapper': lambda x: CsSnr(x).name}
1532
1538
 
1533
1539
 
1534
- class CodecID(OpenIntEnum):
1540
+ class CodecID(utils.OpenIntEnum):
1535
1541
  # fmt: off
1536
1542
  U_LOG = 0x00
1537
1543
  A_LOG = 0x01
@@ -1582,8 +1588,8 @@ class HCI_Constant:
1582
1588
  return HCI_ERROR_NAMES.get(status, f'0x{status:02X}')
1583
1589
 
1584
1590
  @staticmethod
1585
- def role_name(role):
1586
- return HCI_ROLE_NAMES.get(role, str(role))
1591
+ def role_name(role: int) -> str:
1592
+ return Role(role).name
1587
1593
 
1588
1594
  @staticmethod
1589
1595
  def le_phy_name(phy):
@@ -1949,17 +1955,10 @@ class Address:
1949
1955
  address[0] is the LSB of the address, address[5] is the MSB.
1950
1956
  '''
1951
1957
 
1952
- PUBLIC_DEVICE_ADDRESS = 0x00
1953
- RANDOM_DEVICE_ADDRESS = 0x01
1954
- PUBLIC_IDENTITY_ADDRESS = 0x02
1955
- RANDOM_IDENTITY_ADDRESS = 0x03
1956
-
1957
- ADDRESS_TYPE_NAMES = {
1958
- PUBLIC_DEVICE_ADDRESS: 'PUBLIC_DEVICE_ADDRESS',
1959
- RANDOM_DEVICE_ADDRESS: 'RANDOM_DEVICE_ADDRESS',
1960
- PUBLIC_IDENTITY_ADDRESS: 'PUBLIC_IDENTITY_ADDRESS',
1961
- RANDOM_IDENTITY_ADDRESS: 'RANDOM_IDENTITY_ADDRESS',
1962
- }
1958
+ PUBLIC_DEVICE_ADDRESS = AddressType.PUBLIC_DEVICE
1959
+ RANDOM_DEVICE_ADDRESS = AddressType.RANDOM_DEVICE
1960
+ PUBLIC_IDENTITY_ADDRESS = AddressType.PUBLIC_IDENTITY
1961
+ RANDOM_IDENTITY_ADDRESS = AddressType.RANDOM_IDENTITY
1963
1962
 
1964
1963
  # Type declarations
1965
1964
  NIL: Address
@@ -1969,40 +1968,44 @@ class Address:
1969
1968
  # pylint: disable-next=unnecessary-lambda
1970
1969
  ADDRESS_TYPE_SPEC = {'size': 1, 'mapper': lambda x: Address.address_type_name(x)}
1971
1970
 
1972
- @staticmethod
1973
- def address_type_name(address_type):
1974
- return name_or_number(Address.ADDRESS_TYPE_NAMES, address_type)
1971
+ @classmethod
1972
+ def address_type_name(cls: type[Self], address_type: int) -> str:
1973
+ return AddressType(address_type).name
1975
1974
 
1976
- @staticmethod
1977
- def from_string_for_transport(string, transport):
1978
- if transport == BT_BR_EDR_TRANSPORT:
1975
+ @classmethod
1976
+ def from_string_for_transport(
1977
+ cls: type[Self], string: str, transport: PhysicalTransport
1978
+ ) -> Self:
1979
+ if transport == PhysicalTransport.BR_EDR:
1979
1980
  address_type = Address.PUBLIC_DEVICE_ADDRESS
1980
1981
  else:
1981
1982
  address_type = Address.RANDOM_DEVICE_ADDRESS
1982
- return Address(string, address_type)
1983
+ return cls(string, address_type)
1983
1984
 
1984
- @staticmethod
1985
- def parse_address(data, offset):
1985
+ @classmethod
1986
+ def parse_address(cls: type[Self], data: bytes, offset: int) -> tuple[int, Self]:
1986
1987
  # Fix the type to a default value. This is used for parsing type-less Classic
1987
1988
  # addresses
1988
- return Address.parse_address_with_type(
1989
- data, offset, Address.PUBLIC_DEVICE_ADDRESS
1990
- )
1989
+ return cls.parse_address_with_type(data, offset, Address.PUBLIC_DEVICE_ADDRESS)
1991
1990
 
1992
- @staticmethod
1993
- def parse_random_address(data, offset):
1994
- return Address.parse_address_with_type(
1995
- data, offset, Address.RANDOM_DEVICE_ADDRESS
1996
- )
1991
+ @classmethod
1992
+ def parse_random_address(
1993
+ cls: type[Self], data: bytes, offset: int
1994
+ ) -> tuple[int, Self]:
1995
+ return cls.parse_address_with_type(data, offset, Address.RANDOM_DEVICE_ADDRESS)
1997
1996
 
1998
- @staticmethod
1999
- def parse_address_with_type(data, offset, address_type):
2000
- return offset + 6, Address(data[offset : offset + 6], address_type)
1997
+ @classmethod
1998
+ def parse_address_with_type(
1999
+ cls: type[Self], data: bytes, offset: int, address_type: AddressType
2000
+ ) -> tuple[int, Self]:
2001
+ return offset + 6, cls(data[offset : offset + 6], address_type)
2001
2002
 
2002
- @staticmethod
2003
- def parse_address_preceded_by_type(data, offset):
2004
- address_type = data[offset - 1]
2005
- return Address.parse_address_with_type(data, offset, address_type)
2003
+ @classmethod
2004
+ def parse_address_preceded_by_type(
2005
+ cls: type[Self], data: bytes, offset: int
2006
+ ) -> tuple[int, Self]:
2007
+ address_type = AddressType(data[offset - 1])
2008
+ return cls.parse_address_with_type(data, offset, address_type)
2006
2009
 
2007
2010
  @classmethod
2008
2011
  def generate_static_address(cls) -> Address:
@@ -2042,8 +2045,10 @@ class Address:
2042
2045
  )
2043
2046
 
2044
2047
  def __init__(
2045
- self, address: Union[bytes, str], address_type: int = RANDOM_DEVICE_ADDRESS
2046
- ):
2048
+ self,
2049
+ address: Union[bytes, str],
2050
+ address_type: AddressType = RANDOM_DEVICE_ADDRESS,
2051
+ ) -> None:
2047
2052
  '''
2048
2053
  Initialize an instance. `address` may be a byte array in little-endian
2049
2054
  format, or a hex string in big-endian format (with optional ':'
@@ -4878,6 +4883,20 @@ class HCI_LE_Periodic_Advertising_Sync_Transfer_Command(HCI_Command):
4878
4883
  '''
4879
4884
 
4880
4885
 
4886
+ # -----------------------------------------------------------------------------
4887
+ @HCI_Command.command(
4888
+ fields=[('connection_handle', 2), ('service_data', 2), ('advertising_handle', 1)],
4889
+ return_parameters_fields=[
4890
+ ('status', STATUS_SPEC),
4891
+ ('connection_handle', 2),
4892
+ ],
4893
+ )
4894
+ class HCI_LE_Periodic_Advertising_Set_Info_Transfer_Command(HCI_Command):
4895
+ '''
4896
+ See Bluetooth spec @ 7.8.90 LE Periodic Advertising Set Info Transfer Command
4897
+ '''
4898
+
4899
+
4881
4900
  # -----------------------------------------------------------------------------
4882
4901
  @HCI_Command.command(
4883
4902
  fields=[
@@ -5351,11 +5370,11 @@ class HCI_LE_CS_Create_Config_Command(HCI_Command):
5351
5370
  See Bluetooth spec @ 7.8.137 LE CS Create Config command
5352
5371
  '''
5353
5372
 
5354
- class ChannelSelectionType(OpenIntEnum):
5373
+ class ChannelSelectionType(utils.OpenIntEnum):
5355
5374
  ALGO_3B = 0
5356
5375
  ALGO_3C = 1
5357
5376
 
5358
- class Ch3cShape(OpenIntEnum):
5377
+ class Ch3cShape(utils.OpenIntEnum):
5359
5378
  HAT = 0x00
5360
5379
  X = 0x01
5361
5380
 
@@ -6184,13 +6203,13 @@ class HCI_LE_Periodic_Advertising_Report_Event(HCI_LE_Meta_Event):
6184
6203
  TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F
6185
6204
  RSSI_NOT_AVAILABLE = 0x7F
6186
6205
 
6187
- class CteType(OpenIntEnum):
6206
+ class CteType(utils.OpenIntEnum):
6188
6207
  AOA_CONSTANT_TONE_EXTENSION = 0x00
6189
6208
  AOD_CONSTANT_TONE_EXTENSION_1US = 0x01
6190
6209
  AOD_CONSTANT_TONE_EXTENSION_2US = 0x02
6191
6210
  NO_CONSTANT_TONE_EXTENSION = 0xFF
6192
6211
 
6193
- class DataStatus(OpenIntEnum):
6212
+ class DataStatus(utils.OpenIntEnum):
6194
6213
  DATA_COMPLETE = 0x00
6195
6214
  DATA_INCOMPLETE_MORE_TO_COME = 0x01
6196
6215
  DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02
@@ -6571,7 +6590,7 @@ class HCI_LE_CS_Config_Complete_Event(HCI_LE_Meta_Event):
6571
6590
  See Bluetooth spec @ 7.7.65.42 LE CS Config Complete event
6572
6591
  '''
6573
6592
 
6574
- class Action(OpenIntEnum):
6593
+ class Action(utils.OpenIntEnum):
6575
6594
  REMOVED = 0
6576
6595
  CREATED = 1
6577
6596
 
@@ -6623,7 +6642,7 @@ class HCI_LE_CS_Procedure_Enable_Complete_Event(HCI_LE_Meta_Event):
6623
6642
  See Bluetooth spec @ 7.7.65.43 LE CS Procedure Enable Complete event
6624
6643
  '''
6625
6644
 
6626
- class State(OpenIntEnum):
6645
+ class State(utils.OpenIntEnum):
6627
6646
  DISABLED = 0
6628
6647
  ENABLED = 1
6629
6648
 
@@ -6965,7 +6984,7 @@ class HCI_QOS_Setup_Complete_Event(HCI_Event):
6965
6984
  See Bluetooth spec @ 7.7.13 QoS Setup Complete Event
6966
6985
  '''
6967
6986
 
6968
- class ServiceType(OpenIntEnum):
6987
+ class ServiceType(utils.OpenIntEnum):
6969
6988
  NO_TRAFFIC_AVAILABLE = 0x00
6970
6989
  BEST_EFFORT_AVAILABLE = 0x01
6971
6990
  GUARANTEED_AVAILABLE = 0x02
bumble/hfp.py CHANGED
@@ -24,7 +24,6 @@ import asyncio
24
24
  import dataclasses
25
25
  import enum
26
26
  import traceback
27
- import pyee
28
27
  import re
29
28
  from typing import (
30
29
  Dict,
@@ -45,6 +44,7 @@ from bumble import at
45
44
  from bumble import device
46
45
  from bumble import rfcomm
47
46
  from bumble import sdp
47
+ from bumble import utils
48
48
  from bumble.colors import color
49
49
  from bumble.core import (
50
50
  ProtocolError,
@@ -690,7 +690,7 @@ class HfIndicatorState:
690
690
  current_status: int = 0
691
691
 
692
692
 
693
- class HfProtocol(pyee.EventEmitter):
693
+ class HfProtocol(utils.EventEmitter):
694
694
  """
695
695
  Implementation for the Hands-Free side of the Hands-Free profile.
696
696
 
@@ -1146,7 +1146,7 @@ class HfProtocol(pyee.EventEmitter):
1146
1146
  logger.error(traceback.format_exc())
1147
1147
 
1148
1148
 
1149
- class AgProtocol(pyee.EventEmitter):
1149
+ class AgProtocol(utils.EventEmitter):
1150
1150
  """
1151
1151
  Implementation for the Audio-Gateway side of the Hands-Free profile.
1152
1152
 
bumble/hid.py CHANGED
@@ -22,11 +22,12 @@ import enum
22
22
  import struct
23
23
 
24
24
  from abc import ABC, abstractmethod
25
- from pyee import EventEmitter
26
25
  from typing import Optional, Callable
27
26
  from typing_extensions import override
28
27
 
29
- from bumble import l2cap, device
28
+ from bumble import l2cap
29
+ from bumble import device
30
+ from bumble import utils
30
31
  from bumble.core import InvalidStateError, ProtocolError
31
32
  from bumble.hci import Address
32
33
 
@@ -195,7 +196,7 @@ class SendHandshakeMessage(Message):
195
196
 
196
197
 
197
198
  # -----------------------------------------------------------------------------
198
- class HID(ABC, EventEmitter):
199
+ class HID(ABC, utils.EventEmitter):
199
200
  l2cap_ctrl_channel: Optional[l2cap.ClassicChannel] = None
200
201
  l2cap_intr_channel: Optional[l2cap.ClassicChannel] = None
201
202
  connection: Optional[device.Connection] = None
bumble/host.py CHANGED
@@ -34,7 +34,6 @@ from typing import (
34
34
  TYPE_CHECKING,
35
35
  )
36
36
 
37
- import pyee
38
37
 
39
38
  from bumble.colors import color
40
39
  from bumble.l2cap import L2CAP_PDU
@@ -42,16 +41,16 @@ from bumble.snoop import Snooper
42
41
  from bumble import drivers
43
42
  from bumble import hci
44
43
  from bumble.core import (
45
- BT_BR_EDR_TRANSPORT,
46
- BT_LE_TRANSPORT,
44
+ PhysicalTransport,
45
+ PhysicalTransport,
47
46
  ConnectionPHY,
48
47
  ConnectionParameters,
49
48
  )
50
- from bumble.utils import AbortableEventEmitter
49
+ from bumble import utils
51
50
  from bumble.transport.common import TransportLostError
52
51
 
53
52
  if TYPE_CHECKING:
54
- from .transport.common import TransportSink, TransportSource
53
+ from bumble.transport.common import TransportSink, TransportSource
55
54
 
56
55
 
57
56
  # -----------------------------------------------------------------------------
@@ -61,7 +60,7 @@ logger = logging.getLogger(__name__)
61
60
 
62
61
 
63
62
  # -----------------------------------------------------------------------------
64
- class DataPacketQueue(pyee.EventEmitter):
63
+ class DataPacketQueue(utils.EventEmitter):
65
64
  """
66
65
  Flow-control queue for host->controller data packets (ACL, ISO).
67
66
 
@@ -186,7 +185,11 @@ class DataPacketQueue(pyee.EventEmitter):
186
185
  # -----------------------------------------------------------------------------
187
186
  class Connection:
188
187
  def __init__(
189
- self, host: Host, handle: int, peer_address: hci.Address, transport: int
188
+ self,
189
+ host: Host,
190
+ handle: int,
191
+ peer_address: hci.Address,
192
+ transport: PhysicalTransport,
190
193
  ):
191
194
  self.host = host
192
195
  self.handle = handle
@@ -195,7 +198,7 @@ class Connection:
195
198
  self.transport = transport
196
199
  acl_packet_queue: Optional[DataPacketQueue] = (
197
200
  host.le_acl_packet_queue
198
- if transport == BT_LE_TRANSPORT
201
+ if transport == PhysicalTransport.LE
199
202
  else host.acl_packet_queue
200
203
  )
201
204
  assert acl_packet_queue
@@ -230,7 +233,7 @@ class IsoLink:
230
233
 
231
234
 
232
235
  # -----------------------------------------------------------------------------
233
- class Host(AbortableEventEmitter):
236
+ class Host(utils.EventEmitter):
234
237
  connections: Dict[int, Connection]
235
238
  cis_links: Dict[int, IsoLink]
236
239
  bis_links: Dict[int, IsoLink]
@@ -962,7 +965,7 @@ class Host(AbortableEventEmitter):
962
965
  self,
963
966
  event.connection_handle,
964
967
  event.peer_address,
965
- BT_LE_TRANSPORT,
968
+ PhysicalTransport.LE,
966
969
  )
967
970
  self.connections[event.connection_handle] = connection
968
971
 
@@ -975,11 +978,11 @@ class Host(AbortableEventEmitter):
975
978
  self.emit(
976
979
  'connection',
977
980
  event.connection_handle,
978
- BT_LE_TRANSPORT,
981
+ PhysicalTransport.LE,
979
982
  event.peer_address,
980
983
  getattr(event, 'local_resolvable_private_address', None),
981
984
  getattr(event, 'peer_resolvable_private_address', None),
982
- event.role,
985
+ hci.Role(event.role),
983
986
  connection_parameters,
984
987
  )
985
988
  else:
@@ -987,7 +990,10 @@ class Host(AbortableEventEmitter):
987
990
 
988
991
  # Notify the listeners
989
992
  self.emit(
990
- 'connection_failure', BT_LE_TRANSPORT, event.peer_address, event.status
993
+ 'connection_failure',
994
+ PhysicalTransport.LE,
995
+ event.peer_address,
996
+ event.status,
991
997
  )
992
998
 
993
999
  def on_hci_le_enhanced_connection_complete_event(self, event):
@@ -1012,7 +1018,7 @@ class Host(AbortableEventEmitter):
1012
1018
  self,
1013
1019
  event.connection_handle,
1014
1020
  event.bd_addr,
1015
- BT_BR_EDR_TRANSPORT,
1021
+ PhysicalTransport.BR_EDR,
1016
1022
  )
1017
1023
  self.connections[event.connection_handle] = connection
1018
1024
 
@@ -1020,7 +1026,7 @@ class Host(AbortableEventEmitter):
1020
1026
  self.emit(
1021
1027
  'connection',
1022
1028
  event.connection_handle,
1023
- BT_BR_EDR_TRANSPORT,
1029
+ PhysicalTransport.BR_EDR,
1024
1030
  event.bd_addr,
1025
1031
  None,
1026
1032
  None,
@@ -1032,7 +1038,10 @@ class Host(AbortableEventEmitter):
1032
1038
 
1033
1039
  # Notify the client
1034
1040
  self.emit(
1035
- 'connection_failure', BT_BR_EDR_TRANSPORT, event.bd_addr, event.status
1041
+ 'connection_failure',
1042
+ PhysicalTransport.BR_EDR,
1043
+ event.bd_addr,
1044
+ event.status,
1036
1045
  )
1037
1046
 
1038
1047
  def on_hci_disconnection_complete_event(self, event):
@@ -1279,7 +1288,8 @@ class Host(AbortableEventEmitter):
1279
1288
  logger.debug('no long term key provider')
1280
1289
  long_term_key = None
1281
1290
  else:
1282
- long_term_key = await self.abort_on(
1291
+ long_term_key = await utils.cancel_on_event(
1292
+ self,
1283
1293
  'flush',
1284
1294
  # pylint: disable-next=not-callable
1285
1295
  self.long_term_key_provider(
@@ -1337,7 +1347,7 @@ class Host(AbortableEventEmitter):
1337
1347
  f'role change for {event.bd_addr}: '
1338
1348
  f'{hci.HCI_Constant.role_name(event.new_role)}'
1339
1349
  )
1340
- self.emit('role_change', event.bd_addr, event.new_role)
1350
+ self.emit('role_change', event.bd_addr, hci.Role(event.new_role))
1341
1351
  else:
1342
1352
  logger.debug(
1343
1353
  f'role change for {event.bd_addr} failed: '
@@ -1437,7 +1447,8 @@ class Host(AbortableEventEmitter):
1437
1447
  logger.debug('no link key provider')
1438
1448
  link_key = None
1439
1449
  else:
1440
- link_key = await self.abort_on(
1450
+ link_key = await utils.cancel_on_event(
1451
+ self,
1441
1452
  'flush',
1442
1453
  # pylint: disable-next=not-callable
1443
1454
  self.link_key_provider(event.bd_addr),
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
  # -----------------------------------------------------------------------------