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/profiles/csip.py CHANGED
@@ -99,10 +99,10 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
99
99
  UUID = gatt.GATT_COORDINATED_SET_IDENTIFICATION_SERVICE
100
100
 
101
101
  set_identity_resolving_key: bytes
102
- set_identity_resolving_key_characteristic: gatt.Characteristic
103
- coordinated_set_size_characteristic: Optional[gatt.Characteristic] = None
104
- set_member_lock_characteristic: Optional[gatt.Characteristic] = None
105
- set_member_rank_characteristic: Optional[gatt.Characteristic] = None
102
+ set_identity_resolving_key_characteristic: gatt.Characteristic[bytes]
103
+ coordinated_set_size_characteristic: Optional[gatt.Characteristic[bytes]] = None
104
+ set_member_lock_characteristic: Optional[gatt.Characteristic[bytes]] = None
105
+ set_member_rank_characteristic: Optional[gatt.Characteristic[bytes]] = None
106
106
 
107
107
  def __init__(
108
108
  self,
@@ -170,7 +170,7 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
170
170
  else:
171
171
  assert connection
172
172
 
173
- if connection.transport == core.BT_LE_TRANSPORT:
173
+ if connection.transport == core.PhysicalTransport.LE:
174
174
  key = await connection.device.get_long_term_key(
175
175
  connection_handle=connection.handle, rand=b'', ediv=0
176
176
  )
@@ -203,10 +203,10 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
203
203
  class CoordinatedSetIdentificationProxy(gatt_client.ProfileServiceProxy):
204
204
  SERVICE_CLASS = CoordinatedSetIdentificationService
205
205
 
206
- set_identity_resolving_key: gatt_client.CharacteristicProxy
207
- coordinated_set_size: Optional[gatt_client.CharacteristicProxy] = None
208
- set_member_lock: Optional[gatt_client.CharacteristicProxy] = None
209
- set_member_rank: Optional[gatt_client.CharacteristicProxy] = None
206
+ set_identity_resolving_key: gatt_client.CharacteristicProxy[bytes]
207
+ coordinated_set_size: Optional[gatt_client.CharacteristicProxy[bytes]] = None
208
+ set_member_lock: Optional[gatt_client.CharacteristicProxy[bytes]] = None
209
+ set_member_rank: Optional[gatt_client.CharacteristicProxy[bytes]] = None
210
210
 
211
211
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
212
212
  self.service_proxy = service_proxy
@@ -242,7 +242,7 @@ class CoordinatedSetIdentificationProxy(gatt_client.ProfileServiceProxy):
242
242
  else:
243
243
  connection = self.service_proxy.client.connection
244
244
  device = connection.device
245
- if connection.transport == core.BT_LE_TRANSPORT:
245
+ if connection.transport == core.PhysicalTransport.LE:
246
246
  key = await device.get_long_term_key(
247
247
  connection_handle=connection.handle, rand=b'', ediv=0
248
248
  )
@@ -32,10 +32,10 @@ class GenericAttributeProfileService(gatt.TemplateService):
32
32
 
33
33
  UUID = gatt.GATT_GENERIC_ATTRIBUTE_SERVICE
34
34
 
35
- client_supported_features_characteristic: gatt.Characteristic | None = None
36
- server_supported_features_characteristic: gatt.Characteristic | None = None
37
- database_hash_characteristic: gatt.Characteristic | None = None
38
- service_changed_characteristic: gatt.Characteristic | None = None
35
+ client_supported_features_characteristic: gatt.Characteristic[bytes] | None = None
36
+ server_supported_features_characteristic: gatt.Characteristic[bytes] | None = None
37
+ database_hash_characteristic: gatt.Characteristic[bytes] | None = None
38
+ service_changed_characteristic: gatt.Characteristic[bytes] | None = None
39
39
 
40
40
  def __init__(
41
41
  self,
@@ -143,14 +143,14 @@ class GenericAttributeProfileService(gatt.TemplateService):
143
143
  class GenericAttributeProfileServiceProxy(gatt_client.ProfileServiceProxy):
144
144
  SERVICE_CLASS = GenericAttributeProfileService
145
145
 
146
- client_supported_features_characteristic: gatt_client.CharacteristicProxy | None = (
147
- None
148
- )
149
- server_supported_features_characteristic: gatt_client.CharacteristicProxy | None = (
150
- None
151
- )
152
- database_hash_characteristic: gatt_client.CharacteristicProxy | None = None
153
- service_changed_characteristic: gatt_client.CharacteristicProxy | None = None
146
+ client_supported_features_characteristic: (
147
+ gatt_client.CharacteristicProxy[bytes] | None
148
+ ) = None
149
+ server_supported_features_characteristic: (
150
+ gatt_client.CharacteristicProxy[bytes] | None
151
+ ) = None
152
+ database_hash_characteristic: gatt_client.CharacteristicProxy[bytes] | None = None
153
+ service_changed_characteristic: gatt_client.CharacteristicProxy[bytes] | None = None
154
154
 
155
155
  _CHARACTERISTICS = {
156
156
  gatt.GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC: 'client_supported_features_characteristic',
bumble/profiles/hap.py CHANGED
@@ -25,14 +25,14 @@ from typing import Any, Dict, List, Optional, Set, Union
25
25
  from bumble import att, gatt, gatt_adapters, gatt_client
26
26
  from bumble.core import InvalidArgumentError, InvalidStateError
27
27
  from bumble.device import Device, Connection
28
- from bumble.utils import AsyncRunner, OpenIntEnum
28
+ from bumble import utils
29
29
  from bumble.hci import Address
30
30
 
31
31
 
32
32
  # -----------------------------------------------------------------------------
33
33
  # Constants
34
34
  # -----------------------------------------------------------------------------
35
- class ErrorCode(OpenIntEnum):
35
+ class ErrorCode(utils.OpenIntEnum):
36
36
  '''See Hearing Access Service 2.4. Attribute Profile error codes.'''
37
37
 
38
38
  INVALID_OPCODE = 0x80
@@ -42,7 +42,7 @@ class ErrorCode(OpenIntEnum):
42
42
  INVALID_PARAMETERS_LENGTH = 0x84
43
43
 
44
44
 
45
- class HearingAidType(OpenIntEnum):
45
+ class HearingAidType(utils.OpenIntEnum):
46
46
  '''See Hearing Access Service 3.1. Hearing Aid Features.'''
47
47
 
48
48
  BINAURAL_HEARING_AID = 0b00
@@ -50,35 +50,35 @@ class HearingAidType(OpenIntEnum):
50
50
  BANDED_HEARING_AID = 0b10
51
51
 
52
52
 
53
- class PresetSynchronizationSupport(OpenIntEnum):
53
+ class PresetSynchronizationSupport(utils.OpenIntEnum):
54
54
  '''See Hearing Access Service 3.1. Hearing Aid Features.'''
55
55
 
56
56
  PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED = 0b0
57
57
  PRESET_SYNCHRONIZATION_IS_SUPPORTED = 0b1
58
58
 
59
59
 
60
- class IndependentPresets(OpenIntEnum):
60
+ class IndependentPresets(utils.OpenIntEnum):
61
61
  '''See Hearing Access Service 3.1. Hearing Aid Features.'''
62
62
 
63
63
  IDENTICAL_PRESET_RECORD = 0b0
64
64
  DIFFERENT_PRESET_RECORD = 0b1
65
65
 
66
66
 
67
- class DynamicPresets(OpenIntEnum):
67
+ class DynamicPresets(utils.OpenIntEnum):
68
68
  '''See Hearing Access Service 3.1. Hearing Aid Features.'''
69
69
 
70
70
  PRESET_RECORDS_DOES_NOT_CHANGE = 0b0
71
71
  PRESET_RECORDS_MAY_CHANGE = 0b1
72
72
 
73
73
 
74
- class WritablePresetsSupport(OpenIntEnum):
74
+ class WritablePresetsSupport(utils.OpenIntEnum):
75
75
  '''See Hearing Access Service 3.1. Hearing Aid Features.'''
76
76
 
77
77
  WRITABLE_PRESET_RECORDS_NOT_SUPPORTED = 0b0
78
78
  WRITABLE_PRESET_RECORDS_SUPPORTED = 0b1
79
79
 
80
80
 
81
- class HearingAidPresetControlPointOpcode(OpenIntEnum):
81
+ class HearingAidPresetControlPointOpcode(utils.OpenIntEnum):
82
82
  '''See Hearing Access Service 3.3.1 Hearing Aid Preset Control Point operation requirements.'''
83
83
 
84
84
  # fmt: off
@@ -130,7 +130,7 @@ def HearingAidFeatures_from_bytes(data: int) -> HearingAidFeatures:
130
130
  class PresetChangedOperation:
131
131
  '''See Hearing Access Service 3.2.2.2. Preset Changed operation.'''
132
132
 
133
- class ChangeId(OpenIntEnum):
133
+ class ChangeId(utils.OpenIntEnum):
134
134
  # fmt: off
135
135
  GENERIC_UPDATE = 0x00
136
136
  PRESET_RECORD_DELETED = 0x01
@@ -190,11 +190,11 @@ class PresetRecord:
190
190
 
191
191
  @dataclass
192
192
  class Property:
193
- class Writable(OpenIntEnum):
193
+ class Writable(utils.OpenIntEnum):
194
194
  CANNOT_BE_WRITTEN = 0b0
195
195
  CAN_BE_WRITTEN = 0b1
196
196
 
197
- class IsAvailable(OpenIntEnum):
197
+ class IsAvailable(utils.OpenIntEnum):
198
198
  IS_UNAVAILABLE = 0b0
199
199
  IS_AVAILABLE = 0b1
200
200
 
@@ -224,9 +224,9 @@ class PresetRecord:
224
224
  class HearingAccessService(gatt.TemplateService):
225
225
  UUID = gatt.GATT_HEARING_ACCESS_SERVICE
226
226
 
227
- hearing_aid_features_characteristic: gatt.Characteristic
228
- hearing_aid_preset_control_point: gatt.Characteristic
229
- active_preset_index_characteristic: gatt.Characteristic
227
+ hearing_aid_features_characteristic: gatt.Characteristic[bytes]
228
+ hearing_aid_preset_control_point: gatt.Characteristic[bytes]
229
+ active_preset_index_characteristic: gatt.Characteristic[bytes]
230
230
  active_preset_index: int
231
231
  active_preset_index_per_device: Dict[Address, int]
232
232
 
@@ -333,7 +333,7 @@ class HearingAccessService(gatt.TemplateService):
333
333
  # Update the active preset index if needed
334
334
  await self.notify_active_preset_for_connection(connection)
335
335
 
336
- connection.abort_on('disconnection', on_connection_async())
336
+ utils.cancel_on_event(connection, 'disconnection', on_connection_async())
337
337
 
338
338
  def _on_read_active_preset_index(
339
339
  self, __connection__: Optional[Connection]
@@ -382,7 +382,7 @@ class HearingAccessService(gatt.TemplateService):
382
382
  if len(presets) == 0:
383
383
  raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
384
384
 
385
- AsyncRunner.spawn(self._read_preset_response(connection, presets))
385
+ utils.AsyncRunner.spawn(self._read_preset_response(connection, presets))
386
386
 
387
387
  async def _read_preset_response(
388
388
  self, connection: Connection, presets: List[PresetRecord]
bumble/profiles/mcp.py CHANGED
@@ -338,30 +338,32 @@ class MediaControlServiceProxy(
338
338
  'content_control_id': gatt.GATT_CONTENT_CONTROL_ID_CHARACTERISTIC,
339
339
  }
340
340
 
341
- media_player_name: Optional[gatt_client.CharacteristicProxy] = None
342
- media_player_icon_object_id: Optional[gatt_client.CharacteristicProxy] = None
343
- media_player_icon_url: Optional[gatt_client.CharacteristicProxy] = None
344
- track_changed: Optional[gatt_client.CharacteristicProxy] = None
345
- track_title: Optional[gatt_client.CharacteristicProxy] = None
346
- track_duration: Optional[gatt_client.CharacteristicProxy] = None
347
- track_position: Optional[gatt_client.CharacteristicProxy] = None
348
- playback_speed: Optional[gatt_client.CharacteristicProxy] = None
349
- seeking_speed: Optional[gatt_client.CharacteristicProxy] = None
350
- current_track_segments_object_id: Optional[gatt_client.CharacteristicProxy] = None
351
- current_track_object_id: Optional[gatt_client.CharacteristicProxy] = None
352
- next_track_object_id: Optional[gatt_client.CharacteristicProxy] = None
353
- parent_group_object_id: Optional[gatt_client.CharacteristicProxy] = None
354
- current_group_object_id: Optional[gatt_client.CharacteristicProxy] = None
355
- playing_order: Optional[gatt_client.CharacteristicProxy] = None
356
- playing_orders_supported: Optional[gatt_client.CharacteristicProxy] = None
357
- media_state: Optional[gatt_client.CharacteristicProxy] = None
358
- media_control_point: Optional[gatt_client.CharacteristicProxy] = None
359
- media_control_point_opcodes_supported: Optional[gatt_client.CharacteristicProxy] = (
360
- None
361
- )
362
- search_control_point: Optional[gatt_client.CharacteristicProxy] = None
363
- search_results_object_id: Optional[gatt_client.CharacteristicProxy] = None
364
- content_control_id: Optional[gatt_client.CharacteristicProxy] = None
341
+ media_player_name: Optional[gatt_client.CharacteristicProxy[bytes]] = None
342
+ media_player_icon_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
343
+ media_player_icon_url: Optional[gatt_client.CharacteristicProxy[bytes]] = None
344
+ track_changed: Optional[gatt_client.CharacteristicProxy[bytes]] = None
345
+ track_title: Optional[gatt_client.CharacteristicProxy[bytes]] = None
346
+ track_duration: Optional[gatt_client.CharacteristicProxy[bytes]] = None
347
+ track_position: Optional[gatt_client.CharacteristicProxy[bytes]] = None
348
+ playback_speed: Optional[gatt_client.CharacteristicProxy[bytes]] = None
349
+ seeking_speed: Optional[gatt_client.CharacteristicProxy[bytes]] = None
350
+ current_track_segments_object_id: Optional[
351
+ gatt_client.CharacteristicProxy[bytes]
352
+ ] = None
353
+ current_track_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
354
+ next_track_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
355
+ parent_group_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
356
+ current_group_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
357
+ playing_order: Optional[gatt_client.CharacteristicProxy[bytes]] = None
358
+ playing_orders_supported: Optional[gatt_client.CharacteristicProxy[bytes]] = None
359
+ media_state: Optional[gatt_client.CharacteristicProxy[bytes]] = None
360
+ media_control_point: Optional[gatt_client.CharacteristicProxy[bytes]] = None
361
+ media_control_point_opcodes_supported: Optional[
362
+ gatt_client.CharacteristicProxy[bytes]
363
+ ] = None
364
+ search_control_point: Optional[gatt_client.CharacteristicProxy[bytes]] = None
365
+ search_results_object_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
366
+ content_control_id: Optional[gatt_client.CharacteristicProxy[bytes]] = None
365
367
 
366
368
  if TYPE_CHECKING:
367
369
  media_control_point_notifications: asyncio.Queue[bytes]
bumble/profiles/pacs.py CHANGED
@@ -104,12 +104,12 @@ class PacRecord:
104
104
  class PublishedAudioCapabilitiesService(gatt.TemplateService):
105
105
  UUID = gatt.GATT_PUBLISHED_AUDIO_CAPABILITIES_SERVICE
106
106
 
107
- sink_pac: Optional[gatt.Characteristic]
108
- sink_audio_locations: Optional[gatt.Characteristic]
109
- source_pac: Optional[gatt.Characteristic]
110
- source_audio_locations: Optional[gatt.Characteristic]
111
- available_audio_contexts: gatt.Characteristic
112
- supported_audio_contexts: gatt.Characteristic
107
+ sink_pac: Optional[gatt.Characteristic[bytes]]
108
+ sink_audio_locations: Optional[gatt.Characteristic[bytes]]
109
+ source_pac: Optional[gatt.Characteristic[bytes]]
110
+ source_audio_locations: Optional[gatt.Characteristic[bytes]]
111
+ available_audio_contexts: gatt.Characteristic[bytes]
112
+ supported_audio_contexts: gatt.Characteristic[bytes]
113
113
 
114
114
  def __init__(
115
115
  self,
bumble/profiles/pbp.py CHANGED
@@ -40,7 +40,7 @@ class PublicBroadcastAnnouncement:
40
40
  def from_bytes(cls, data: bytes) -> Self:
41
41
  features = cls.Features(data[0])
42
42
  metadata_length = data[1]
43
- metadata_ltv = data[1 : 1 + metadata_length]
43
+ metadata_ltv = data[2 : 2 + metadata_length]
44
44
  return cls(
45
45
  features=features, metadata=le_audio.Metadata.from_bytes(metadata_ltv)
46
46
  )
bumble/profiles/vcs.py CHANGED
@@ -23,6 +23,7 @@ import enum
23
23
  from typing import Optional, Sequence
24
24
 
25
25
  from bumble import att
26
+ from bumble import utils
26
27
  from bumble import device
27
28
  from bumble import gatt
28
29
  from bumble import gatt_adapters
@@ -90,9 +91,9 @@ class VolumeState:
90
91
  class VolumeControlService(gatt.TemplateService):
91
92
  UUID = gatt.GATT_VOLUME_CONTROL_SERVICE
92
93
 
93
- volume_state: gatt.Characteristic
94
- volume_control_point: gatt.Characteristic
95
- volume_flags: gatt.Characteristic
94
+ volume_state: gatt.Characteristic[bytes]
95
+ volume_control_point: gatt.Characteristic[bytes]
96
+ volume_flags: gatt.Characteristic[bytes]
96
97
 
97
98
  volume_setting: int
98
99
  muted: int
@@ -160,7 +161,8 @@ class VolumeControlService(gatt.TemplateService):
160
161
  handler = getattr(self, '_on_' + opcode.name.lower())
161
162
  if handler(*value[2:]):
162
163
  self.change_counter = (self.change_counter + 1) % 256
163
- connection.abort_on(
164
+ utils.cancel_on_event(
165
+ connection,
164
166
  'disconnection',
165
167
  connection.device.notify_subscribers(attribute=self.volume_state),
166
168
  )
bumble/profiles/vocs.py CHANGED
@@ -38,7 +38,7 @@ from bumble.gatt_adapters import (
38
38
  UTF8CharacteristicProxyAdapter,
39
39
  )
40
40
  from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
41
- from bumble.utils import OpenIntEnum
41
+ from bumble import utils
42
42
  from bumble.profiles.bap import AudioLocation
43
43
 
44
44
  # -----------------------------------------------------------------------------
@@ -50,11 +50,11 @@ MAX_VOLUME_OFFSET = 255
50
50
  CHANGE_COUNTER_MAX_VALUE = 0xFF
51
51
 
52
52
 
53
- class SetVolumeOffsetOpCode(OpenIntEnum):
53
+ class SetVolumeOffsetOpCode(utils.OpenIntEnum):
54
54
  SET_VOLUME_OFFSET = 0x01
55
55
 
56
56
 
57
- class ErrorCode(OpenIntEnum):
57
+ class ErrorCode(utils.OpenIntEnum):
58
58
  """
59
59
  See Volume Offset Control Service 1.6. Application error codes.
60
60
  """
bumble/rfcomm.py CHANGED
@@ -25,16 +25,16 @@ import enum
25
25
  from typing import Callable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
26
26
  from typing_extensions import Self
27
27
 
28
- from pyee import EventEmitter
29
28
 
30
29
  from bumble import core
31
30
  from bumble import l2cap
32
31
  from bumble import sdp
33
- from .colors import color
34
- from .core import (
32
+ from bumble import utils
33
+ from bumble.colors import color
34
+ from bumble.core import (
35
35
  UUID,
36
36
  BT_RFCOMM_PROTOCOL_ID,
37
- BT_BR_EDR_TRANSPORT,
37
+ PhysicalTransport,
38
38
  BT_L2CAP_PROTOCOL_ID,
39
39
  InvalidArgumentError,
40
40
  InvalidStateError,
@@ -441,7 +441,7 @@ class RFCOMM_MCC_MSC:
441
441
 
442
442
 
443
443
  # -----------------------------------------------------------------------------
444
- class DLC(EventEmitter):
444
+ class DLC(utils.EventEmitter):
445
445
  class State(enum.IntEnum):
446
446
  INIT = 0x00
447
447
  CONNECTING = 0x01
@@ -749,7 +749,7 @@ class DLC(EventEmitter):
749
749
 
750
750
 
751
751
  # -----------------------------------------------------------------------------
752
- class Multiplexer(EventEmitter):
752
+ class Multiplexer(utils.EventEmitter):
753
753
  class Role(enum.IntEnum):
754
754
  INITIATOR = 0x00
755
755
  RESPONDER = 0x01
@@ -845,7 +845,7 @@ class Multiplexer(EventEmitter):
845
845
  self.open_result.set_exception(
846
846
  core.ConnectionError(
847
847
  core.ConnectionError.CONNECTION_REFUSED,
848
- BT_BR_EDR_TRANSPORT,
848
+ PhysicalTransport.BR_EDR,
849
849
  self.l2cap_channel.connection.peer_address,
850
850
  'rfcomm',
851
851
  )
@@ -1075,7 +1075,7 @@ class Client:
1075
1075
 
1076
1076
 
1077
1077
  # -----------------------------------------------------------------------------
1078
- class Server(EventEmitter):
1078
+ class Server(utils.EventEmitter):
1079
1079
  def __init__(
1080
1080
  self, device: Device, l2cap_mtu: int = RFCOMM_DEFAULT_L2CAP_MTU
1081
1081
  ) -> None:
bumble/sdp.py CHANGED
@@ -33,7 +33,7 @@ from bumble.core import (
33
33
  from bumble.hci import HCI_Object, name_or_number, key_with_value
34
34
 
35
35
  if TYPE_CHECKING:
36
- from .device import Device, Connection
36
+ from bumble.device import Device, Connection
37
37
 
38
38
  # -----------------------------------------------------------------------------
39
39
  # Logging
bumble/smp.py CHANGED
@@ -41,26 +41,25 @@ from typing import (
41
41
  cast,
42
42
  )
43
43
 
44
- from pyee import EventEmitter
45
44
 
46
- from .colors import color
47
- from .hci import (
45
+ from bumble.colors import color
46
+ from bumble.hci import (
48
47
  Address,
49
48
  Role,
50
49
  HCI_LE_Enable_Encryption_Command,
51
50
  HCI_Object,
52
51
  key_with_value,
53
52
  )
54
- from .core import (
55
- BT_BR_EDR_TRANSPORT,
56
- BT_LE_TRANSPORT,
53
+ from bumble.core import (
54
+ PhysicalTransport,
57
55
  AdvertisingData,
58
56
  InvalidArgumentError,
59
57
  ProtocolError,
60
58
  name_or_number,
61
59
  )
62
- from .keys import PairingKeys
63
- from . import crypto
60
+ from bumble.keys import PairingKeys
61
+ from bumble import crypto
62
+ from bumble import utils
64
63
 
65
64
  if TYPE_CHECKING:
66
65
  from bumble.device import Connection, Device
@@ -857,7 +856,7 @@ class Session:
857
856
  initiator_io_capability: int,
858
857
  responder_io_capability: int,
859
858
  ) -> None:
860
- if self.connection.transport == BT_BR_EDR_TRANSPORT:
859
+ if self.connection.transport == PhysicalTransport.BR_EDR:
861
860
  self.pairing_method = PairingMethod.CTKD_OVER_CLASSIC
862
861
  return
863
862
  if (not self.mitm) and (auth_req & SMP_MITM_AUTHREQ == 0):
@@ -900,7 +899,7 @@ class Session:
900
899
 
901
900
  self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
902
901
 
903
- self.connection.abort_on('disconnection', prompt())
902
+ utils.cancel_on_event(self.connection, 'disconnection', prompt())
904
903
 
905
904
  def prompt_user_for_numeric_comparison(
906
905
  self, code: int, next_steps: Callable[[], None]
@@ -919,7 +918,7 @@ class Session:
919
918
 
920
919
  self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
921
920
 
922
- self.connection.abort_on('disconnection', prompt())
921
+ utils.cancel_on_event(self.connection, 'disconnection', prompt())
923
922
 
924
923
  def prompt_user_for_number(self, next_steps: Callable[[int], None]) -> None:
925
924
  async def prompt() -> None:
@@ -936,7 +935,7 @@ class Session:
936
935
  logger.warning(f'exception while prompting: {error}')
937
936
  self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR)
938
937
 
939
- self.connection.abort_on('disconnection', prompt())
938
+ utils.cancel_on_event(self.connection, 'disconnection', prompt())
940
939
 
941
940
  def display_passkey(self) -> None:
942
941
  # Generate random Passkey/PIN code
@@ -951,7 +950,8 @@ class Session:
951
950
  logger.debug(f'TK from passkey = {self.tk.hex()}')
952
951
 
953
952
  try:
954
- self.connection.abort_on(
953
+ utils.cancel_on_event(
954
+ self.connection,
955
955
  'disconnection',
956
956
  self.pairing_config.delegate.display_number(self.passkey, digits=6),
957
957
  )
@@ -1050,7 +1050,7 @@ class Session:
1050
1050
  )
1051
1051
 
1052
1052
  # Perform the next steps asynchronously in case we need to wait for input
1053
- self.connection.abort_on('disconnection', next_steps())
1053
+ utils.cancel_on_event(self.connection, 'disconnection', next_steps())
1054
1054
  else:
1055
1055
  confirm_value = crypto.c1(
1056
1056
  self.tk,
@@ -1170,11 +1170,11 @@ class Session:
1170
1170
  if self.is_initiator:
1171
1171
  # CTKD: Derive LTK from LinkKey
1172
1172
  if (
1173
- self.connection.transport == BT_BR_EDR_TRANSPORT
1173
+ self.connection.transport == PhysicalTransport.BR_EDR
1174
1174
  and self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
1175
1175
  ):
1176
- self.ctkd_task = self.connection.abort_on(
1177
- 'disconnection', self.get_link_key_and_derive_ltk()
1176
+ self.ctkd_task = utils.cancel_on_event(
1177
+ self.connection, 'disconnection', self.get_link_key_and_derive_ltk()
1178
1178
  )
1179
1179
  elif not self.sc:
1180
1180
  # Distribute the LTK, EDIV and RAND
@@ -1209,11 +1209,11 @@ class Session:
1209
1209
  else:
1210
1210
  # CTKD: Derive LTK from LinkKey
1211
1211
  if (
1212
- self.connection.transport == BT_BR_EDR_TRANSPORT
1212
+ self.connection.transport == PhysicalTransport.BR_EDR
1213
1213
  and self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
1214
1214
  ):
1215
- self.ctkd_task = self.connection.abort_on(
1216
- 'disconnection', self.get_link_key_and_derive_ltk()
1215
+ self.ctkd_task = utils.cancel_on_event(
1216
+ self.connection, 'disconnection', self.get_link_key_and_derive_ltk()
1217
1217
  )
1218
1218
  # Distribute the LTK, EDIV and RAND
1219
1219
  elif not self.sc:
@@ -1248,7 +1248,7 @@ class Session:
1248
1248
  def compute_peer_expected_distributions(self, key_distribution_flags: int) -> None:
1249
1249
  # Set our expectations for what to wait for in the key distribution phase
1250
1250
  self.peer_expected_distributions = []
1251
- if not self.sc and self.connection.transport == BT_LE_TRANSPORT:
1251
+ if not self.sc and self.connection.transport == PhysicalTransport.LE:
1252
1252
  if key_distribution_flags & SMP_ENC_KEY_DISTRIBUTION_FLAG != 0:
1253
1253
  self.peer_expected_distributions.append(
1254
1254
  SMP_Encryption_Information_Command
@@ -1305,7 +1305,9 @@ class Session:
1305
1305
 
1306
1306
  # Wait for the pairing process to finish
1307
1307
  assert self.pairing_result
1308
- await self.connection.abort_on('disconnection', self.pairing_result)
1308
+ await utils.cancel_on_event(
1309
+ self.connection, 'disconnection', self.pairing_result
1310
+ )
1309
1311
 
1310
1312
  def on_disconnection(self, _: int) -> None:
1311
1313
  self.connection.remove_listener('disconnection', self.on_disconnection)
@@ -1323,7 +1325,7 @@ class Session:
1323
1325
  if self.is_initiator:
1324
1326
  self.distribute_keys()
1325
1327
 
1326
- self.connection.abort_on('disconnection', self.on_pairing())
1328
+ utils.cancel_on_event(self.connection, 'disconnection', self.on_pairing())
1327
1329
 
1328
1330
  def on_connection_encryption_change(self) -> None:
1329
1331
  if self.connection.is_encrypted and not self.completed:
@@ -1365,7 +1367,7 @@ class Session:
1365
1367
  keys = PairingKeys()
1366
1368
  keys.address_type = peer_address.address_type
1367
1369
  authenticated = self.pairing_method != PairingMethod.JUST_WORKS
1368
- if self.sc or self.connection.transport == BT_BR_EDR_TRANSPORT:
1370
+ if self.sc or self.connection.transport == PhysicalTransport.BR_EDR:
1369
1371
  keys.ltk = PairingKeys.Key(value=self.ltk, authenticated=authenticated)
1370
1372
  else:
1371
1373
  our_ltk_key = PairingKeys.Key(
@@ -1432,8 +1434,10 @@ class Session:
1432
1434
  def on_smp_pairing_request_command(
1433
1435
  self, command: SMP_Pairing_Request_Command
1434
1436
  ) -> None:
1435
- self.connection.abort_on(
1436
- 'disconnection', self.on_smp_pairing_request_command_async(command)
1437
+ utils.cancel_on_event(
1438
+ self.connection,
1439
+ 'disconnection',
1440
+ self.on_smp_pairing_request_command_async(command),
1437
1441
  )
1438
1442
 
1439
1443
  async def on_smp_pairing_request_command_async(
@@ -1506,7 +1510,7 @@ class Session:
1506
1510
  # CTKD over BR/EDR should happen after the connection has been encrypted,
1507
1511
  # so when receiving pairing requests, responder should start distributing keys
1508
1512
  if (
1509
- self.connection.transport == BT_BR_EDR_TRANSPORT
1513
+ self.connection.transport == PhysicalTransport.BR_EDR
1510
1514
  and self.connection.is_encrypted
1511
1515
  and self.is_responder
1512
1516
  and accepted
@@ -1878,7 +1882,7 @@ class Session:
1878
1882
  self.wait_before_continuing = None
1879
1883
  self.send_pairing_dhkey_check_command()
1880
1884
 
1881
- self.connection.abort_on('disconnection', next_steps())
1885
+ utils.cancel_on_event(self.connection, 'disconnection', next_steps())
1882
1886
  else:
1883
1887
  self.send_pairing_dhkey_check_command()
1884
1888
  else:
@@ -1922,7 +1926,7 @@ class Session:
1922
1926
 
1923
1927
 
1924
1928
  # -----------------------------------------------------------------------------
1925
- class Manager(EventEmitter):
1929
+ class Manager(utils.EventEmitter):
1926
1930
  '''
1927
1931
  Implements the Initiator and Responder roles of the Security Manager Protocol
1928
1932
  '''
@@ -1950,7 +1954,9 @@ class Manager(EventEmitter):
1950
1954
  f'>>> Sending SMP Command on connection [0x{connection.handle:04X}] '
1951
1955
  f'{connection.peer_address}: {command}'
1952
1956
  )
1953
- cid = SMP_BR_CID if connection.transport == BT_BR_EDR_TRANSPORT else SMP_CID
1957
+ cid = (
1958
+ SMP_BR_CID if connection.transport == PhysicalTransport.BR_EDR else SMP_CID
1959
+ )
1954
1960
  connection.send_l2cap_pdu(cid, bytes(command))
1955
1961
 
1956
1962
  def on_smp_security_request_command(