bumble 0.0.213__py3-none-any.whl → 0.0.215__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 (123) hide show
  1. bumble/_version.py +16 -3
  2. bumble/a2dp.py +15 -16
  3. bumble/apps/auracast.py +14 -38
  4. bumble/apps/bench.py +10 -15
  5. bumble/apps/ble_rpa_tool.py +1 -0
  6. bumble/apps/console.py +22 -25
  7. bumble/apps/controller_info.py +20 -25
  8. bumble/apps/controller_loopback.py +6 -10
  9. bumble/apps/controllers.py +2 -3
  10. bumble/apps/device_info.py +4 -5
  11. bumble/apps/gatt_dump.py +3 -3
  12. bumble/apps/gg_bridge.py +7 -8
  13. bumble/apps/hci_bridge.py +4 -3
  14. bumble/apps/l2cap_bridge.py +5 -5
  15. bumble/apps/lea_unicast/app.py +16 -26
  16. bumble/apps/pair.py +30 -43
  17. bumble/apps/pandora_server.py +5 -4
  18. bumble/apps/player/player.py +20 -24
  19. bumble/apps/rfcomm_bridge.py +4 -10
  20. bumble/apps/scan.py +17 -8
  21. bumble/apps/show.py +4 -5
  22. bumble/apps/speaker/speaker.py +23 -27
  23. bumble/apps/unbond.py +3 -3
  24. bumble/apps/usb_probe.py +2 -4
  25. bumble/att.py +241 -246
  26. bumble/audio/io.py +5 -9
  27. bumble/avc.py +2 -2
  28. bumble/avctp.py +6 -7
  29. bumble/avdtp.py +19 -22
  30. bumble/avrcp.py +1097 -589
  31. bumble/codecs.py +2 -0
  32. bumble/controller.py +142 -35
  33. bumble/core.py +567 -248
  34. bumble/crypto/__init__.py +2 -2
  35. bumble/crypto/builtin.py +1 -1
  36. bumble/crypto/cryptography.py +2 -4
  37. bumble/data_types.py +1025 -0
  38. bumble/device.py +319 -267
  39. bumble/drivers/__init__.py +3 -2
  40. bumble/drivers/intel.py +3 -4
  41. bumble/drivers/rtk.py +26 -9
  42. bumble/gap.py +4 -4
  43. bumble/gatt.py +3 -2
  44. bumble/gatt_adapters.py +3 -11
  45. bumble/gatt_client.py +69 -81
  46. bumble/gatt_server.py +124 -124
  47. bumble/hci.py +114 -18
  48. bumble/helpers.py +19 -26
  49. bumble/hfp.py +10 -21
  50. bumble/hid.py +22 -16
  51. bumble/host.py +191 -103
  52. bumble/keys.py +5 -3
  53. bumble/l2cap.py +138 -104
  54. bumble/link.py +18 -19
  55. bumble/logging.py +65 -0
  56. bumble/pairing.py +7 -6
  57. bumble/pandora/__init__.py +9 -8
  58. bumble/pandora/config.py +3 -1
  59. bumble/pandora/device.py +3 -2
  60. bumble/pandora/host.py +38 -36
  61. bumble/pandora/l2cap.py +22 -21
  62. bumble/pandora/security.py +15 -15
  63. bumble/pandora/utils.py +5 -3
  64. bumble/profiles/aics.py +11 -11
  65. bumble/profiles/ams.py +403 -0
  66. bumble/profiles/ancs.py +6 -7
  67. bumble/profiles/ascs.py +14 -9
  68. bumble/profiles/asha.py +8 -12
  69. bumble/profiles/bap.py +11 -23
  70. bumble/profiles/bass.py +2 -7
  71. bumble/profiles/battery_service.py +3 -4
  72. bumble/profiles/cap.py +1 -2
  73. bumble/profiles/csip.py +2 -6
  74. bumble/profiles/device_information_service.py +2 -2
  75. bumble/profiles/gap.py +4 -4
  76. bumble/profiles/gatt_service.py +1 -4
  77. bumble/profiles/gmap.py +5 -5
  78. bumble/profiles/hap.py +62 -59
  79. bumble/profiles/heart_rate_service.py +5 -4
  80. bumble/profiles/le_audio.py +3 -1
  81. bumble/profiles/mcp.py +3 -7
  82. bumble/profiles/pacs.py +3 -6
  83. bumble/profiles/pbp.py +2 -0
  84. bumble/profiles/tmap.py +2 -3
  85. bumble/profiles/vcs.py +2 -8
  86. bumble/profiles/vocs.py +8 -8
  87. bumble/rfcomm.py +11 -14
  88. bumble/rtp.py +1 -0
  89. bumble/sdp.py +10 -8
  90. bumble/smp.py +151 -159
  91. bumble/snoop.py +5 -5
  92. bumble/tools/generate_company_id_list.py +1 -0
  93. bumble/tools/intel_fw_download.py +3 -3
  94. bumble/tools/intel_util.py +5 -4
  95. bumble/tools/rtk_fw_download.py +6 -3
  96. bumble/tools/rtk_util.py +26 -8
  97. bumble/transport/__init__.py +19 -15
  98. bumble/transport/android_emulator.py +8 -13
  99. bumble/transport/android_netsim.py +19 -18
  100. bumble/transport/common.py +12 -15
  101. bumble/transport/file.py +1 -1
  102. bumble/transport/hci_socket.py +4 -6
  103. bumble/transport/pty.py +5 -6
  104. bumble/transport/pyusb.py +7 -10
  105. bumble/transport/serial.py +2 -1
  106. bumble/transport/tcp_client.py +2 -2
  107. bumble/transport/tcp_server.py +11 -14
  108. bumble/transport/udp.py +3 -3
  109. bumble/transport/unix.py +67 -1
  110. bumble/transport/usb.py +6 -6
  111. bumble/transport/vhci.py +0 -1
  112. bumble/transport/ws_client.py +2 -1
  113. bumble/transport/ws_server.py +3 -2
  114. bumble/utils.py +20 -5
  115. bumble/vendor/android/hci.py +1 -2
  116. bumble/vendor/zephyr/hci.py +0 -1
  117. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/METADATA +4 -2
  118. bumble-0.0.215.dist-info/RECORD +183 -0
  119. bumble-0.0.213.dist-info/RECORD +0 -180
  120. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
  121. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
  122. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
  123. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/top_level.txt +0 -0
bumble/hci.py CHANGED
@@ -16,19 +16,31 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
+
19
20
  import collections
20
21
  import dataclasses
21
- from dataclasses import field
22
22
  import enum
23
23
  import functools
24
24
  import logging
25
25
  import secrets
26
26
  import struct
27
27
  from collections.abc import Sequence
28
- from typing import Any, Callable, Iterable, Optional, Union, TypeVar, ClassVar, cast
28
+ from dataclasses import field
29
+ from typing import (
30
+ Any,
31
+ Callable,
32
+ ClassVar,
33
+ Iterable,
34
+ Literal,
35
+ Optional,
36
+ TypeVar,
37
+ Union,
38
+ cast,
39
+ )
40
+
29
41
  from typing_extensions import Self
30
42
 
31
- from bumble import crypto
43
+ from bumble import crypto, utils
32
44
  from bumble.colors import color
33
45
  from bumble.core import (
34
46
  DeviceClass,
@@ -40,8 +52,6 @@ from bumble.core import (
40
52
  name_or_number,
41
53
  padded_bytes,
42
54
  )
43
- from bumble import utils
44
-
45
55
 
46
56
  # -----------------------------------------------------------------------------
47
57
  # Logging
@@ -111,23 +121,57 @@ def phy_list_to_bits(phys: Optional[Iterable[Phy]]) -> int:
111
121
  class SpecableEnum(utils.OpenIntEnum):
112
122
 
113
123
  @classmethod
114
- def type_spec(cls, size: int):
115
- return {'size': size, 'mapper': lambda x: cls(x).name}
124
+ def type_spec(cls, size: int, byteorder: Literal['little', 'big'] = 'little'):
125
+ return {
126
+ 'serializer': lambda x: x.to_bytes(size, byteorder),
127
+ 'parser': lambda data, offset: (
128
+ offset + size,
129
+ cls(int.from_bytes(data[offset : offset + size], byteorder)),
130
+ ),
131
+ 'mapper': lambda x: cls(x).name,
132
+ }
116
133
 
117
134
  @classmethod
118
- def type_metadata(cls, size: int, list_begin: bool = False, list_end: bool = False):
119
- return metadata(cls.type_spec(size), list_begin=list_begin, list_end=list_end)
135
+ def type_metadata(
136
+ cls,
137
+ size: int,
138
+ list_begin: bool = False,
139
+ list_end: bool = False,
140
+ byteorder: Literal['little', 'big'] = 'little',
141
+ ):
142
+ return metadata(
143
+ cls.type_spec(size, byteorder),
144
+ list_begin=list_begin,
145
+ list_end=list_end,
146
+ )
120
147
 
121
148
 
122
149
  class SpecableFlag(enum.IntFlag):
123
150
 
124
151
  @classmethod
125
- def type_spec(cls, size: int):
126
- return {'size': size, 'mapper': lambda x: cls(x).name}
152
+ def type_spec(cls, size: int, byteorder: Literal['little', 'big'] = 'little'):
153
+ return {
154
+ 'serializer': lambda x: x.to_bytes(size, byteorder),
155
+ 'parser': lambda data, offset: (
156
+ offset + size,
157
+ cls(int.from_bytes(data[offset : offset + size], byteorder)),
158
+ ),
159
+ 'mapper': lambda x: cls(x).name,
160
+ }
127
161
 
128
162
  @classmethod
129
- def type_metadata(cls, size: int, list_begin: bool = False, list_end: bool = False):
130
- return metadata(cls.type_spec(size), list_begin=list_begin, list_end=list_end)
163
+ def type_metadata(
164
+ cls,
165
+ size: int,
166
+ list_begin: bool = False,
167
+ list_end: bool = False,
168
+ byteorder: Literal['little', 'big'] = 'little',
169
+ ):
170
+ return metadata(
171
+ cls.type_spec(size, byteorder),
172
+ list_begin=list_begin,
173
+ list_end=list_end,
174
+ )
131
175
 
132
176
 
133
177
  # -----------------------------------------------------------------------------
@@ -1322,7 +1366,7 @@ class LeFeature(SpecableEnum):
1322
1366
  MONITORING_ADVERTISERS = 64
1323
1367
  FRAME_SPACE_UPDATE = 65
1324
1368
 
1325
- class LeFeatureMask(enum.IntFlag):
1369
+ class LeFeatureMask(utils.CompatibleIntFlag):
1326
1370
  LE_ENCRYPTION = 1 << LeFeature.LE_ENCRYPTION
1327
1371
  CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1 << LeFeature.CONNECTION_PARAMETERS_REQUEST_PROCEDURE
1328
1372
  EXTENDED_REJECT_INDICATION = 1 << LeFeature.EXTENDED_REJECT_INDICATION
@@ -1463,7 +1507,7 @@ class LmpFeature(SpecableEnum):
1463
1507
  SLOT_AVAILABILITY_MASK = 138
1464
1508
  TRAIN_NUDGING = 139
1465
1509
 
1466
- class LmpFeatureMask(enum.IntFlag):
1510
+ class LmpFeatureMask(utils.CompatibleIntFlag):
1467
1511
  # Page 0 (Legacy LMP features)
1468
1512
  LMP_3_SLOT_PACKETS = (1 << LmpFeature.LMP_3_SLOT_PACKETS)
1469
1513
  LMP_5_SLOT_PACKETS = (1 << LmpFeature.LMP_5_SLOT_PACKETS)
@@ -2135,6 +2179,7 @@ class Address:
2135
2179
  if len(address) == 12 + 5:
2136
2180
  # Form with ':' separators
2137
2181
  address = address.replace(':', '')
2182
+
2138
2183
  self.address_bytes = bytes(reversed(bytes.fromhex(address)))
2139
2184
 
2140
2185
  if len(self.address_bytes) != 6:
@@ -5257,7 +5302,7 @@ class HCI_LE_BIG_Terminate_Sync_Command(HCI_Command):
5257
5302
 
5258
5303
  return_parameters_fields = [
5259
5304
  ('status', STATUS_SPEC),
5260
- ('big_handle', 2),
5305
+ ('big_handle', 1),
5261
5306
  ]
5262
5307
 
5263
5308
 
@@ -5315,6 +5360,37 @@ class HCI_LE_Set_Host_Feature_Command(HCI_Command):
5315
5360
  bit_value: int = field(metadata=metadata(1))
5316
5361
 
5317
5362
 
5363
+ # -----------------------------------------------------------------------------
5364
+ @HCI_Command.command
5365
+ @dataclasses.dataclass
5366
+ class HCI_LE_Set_Default_Subrate_Command(HCI_Command):
5367
+ '''
5368
+ See Bluetooth spec @ 7.8.123 LE Set Default Subrate command
5369
+ '''
5370
+
5371
+ subrate_min: int = field(metadata=metadata(2))
5372
+ subrate_max: int = field(metadata=metadata(2))
5373
+ max_latency: int = field(metadata=metadata(2))
5374
+ continuation_number: int = field(metadata=metadata(2))
5375
+ supervision_timeout: int = field(metadata=metadata(2))
5376
+
5377
+
5378
+ # -----------------------------------------------------------------------------
5379
+ @HCI_Command.command
5380
+ @dataclasses.dataclass
5381
+ class HCI_LE_Subrate_Request_Command(HCI_Command):
5382
+ '''
5383
+ See Bluetooth spec @ 7.8.124 LE Subrate Request command
5384
+ '''
5385
+
5386
+ connection_handle: int = field(metadata=metadata(2))
5387
+ subrate_min: int = field(metadata=metadata(2))
5388
+ subrate_max: int = field(metadata=metadata(2))
5389
+ max_latency: int = field(metadata=metadata(2))
5390
+ continuation_number: int = field(metadata=metadata(2))
5391
+ supervision_timeout: int = field(metadata=metadata(2))
5392
+
5393
+
5318
5394
  # -----------------------------------------------------------------------------
5319
5395
  @HCI_Command.command
5320
5396
  @dataclasses.dataclass
@@ -6390,7 +6466,9 @@ class HCI_LE_Create_BIG_Complete_Event(HCI_LE_Meta_Event):
6390
6466
  irc: int = field(metadata=metadata(1))
6391
6467
  max_pdu: int = field(metadata=metadata(2))
6392
6468
  iso_interval: int = field(metadata=metadata(2))
6393
- connection_handle: int = field(metadata=metadata(2, list_begin=True, list_end=True))
6469
+ connection_handle: Sequence[int] = field(
6470
+ metadata=metadata(2, list_begin=True, list_end=True)
6471
+ )
6394
6472
 
6395
6473
 
6396
6474
  # -----------------------------------------------------------------------------
@@ -6422,7 +6500,9 @@ class HCI_LE_BIG_Sync_Established_Event(HCI_LE_Meta_Event):
6422
6500
  irc: int = field(metadata=metadata(1))
6423
6501
  max_pdu: int = field(metadata=metadata(2))
6424
6502
  iso_interval: int = field(metadata=metadata(2))
6425
- connection_handle: int = field(metadata=metadata(2, list_begin=True, list_end=True))
6503
+ connection_handle: Sequence[int] = field(
6504
+ metadata=metadata(2, list_begin=True, list_end=True)
6505
+ )
6426
6506
 
6427
6507
 
6428
6508
  # -----------------------------------------------------------------------------
@@ -6460,6 +6540,22 @@ class HCI_LE_BIGInfo_Advertising_Report_Event(HCI_LE_Meta_Event):
6460
6540
  encryption: int = field(metadata=metadata(1))
6461
6541
 
6462
6542
 
6543
+ # -----------------------------------------------------------------------------
6544
+ @HCI_LE_Meta_Event.event
6545
+ @dataclasses.dataclass
6546
+ class HCI_LE_Subrate_Change_Event(HCI_LE_Meta_Event):
6547
+ '''
6548
+ See Bluetooth spec @ 7.7.65.35 LE Subrate Change event
6549
+ '''
6550
+
6551
+ status: int = field(metadata=metadata(STATUS_SPEC))
6552
+ connection_handle: int = field(metadata=metadata(2))
6553
+ subrate_factor: int = field(metadata=metadata(2))
6554
+ peripheral_latency: int = field(metadata=metadata(2))
6555
+ continuation_number: int = field(metadata=metadata(2))
6556
+ supervision_timeout: int = field(metadata=metadata(2))
6557
+
6558
+
6463
6559
  # -----------------------------------------------------------------------------
6464
6560
  @HCI_LE_Meta_Event.event
6465
6561
  @dataclasses.dataclass
bumble/helpers.py CHANGED
@@ -17,43 +17,36 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
 
20
- from collections.abc import Callable, MutableMapping
21
20
  import datetime
22
- from typing import cast, Any, Optional
23
21
  import logging
22
+ from collections.abc import Callable, MutableMapping
23
+ from typing import Any, Optional, cast
24
24
 
25
- from bumble import avc
26
- from bumble import avctp
27
- from bumble import avdtp
28
- from bumble import avrcp
29
- from bumble import crypto
30
- from bumble import rfcomm
31
- from bumble import sdp
32
- from bumble.colors import color
25
+ from bumble import avc, avctp, avdtp, avrcp, crypto, rfcomm, sdp
33
26
  from bumble.att import ATT_CID, ATT_PDU
34
- from bumble.smp import SMP_CID, SMP_Command
27
+ from bumble.colors import color
35
28
  from bumble.core import name_or_number
36
- from bumble.l2cap import (
37
- CommandCode,
38
- L2CAP_PDU,
39
- L2CAP_SIGNALING_CID,
40
- L2CAP_LE_SIGNALING_CID,
41
- L2CAP_Control_Frame,
42
- L2CAP_Connection_Request,
43
- L2CAP_Connection_Response,
44
- )
45
29
  from bumble.hci import (
46
- Address,
47
- HCI_EVENT_PACKET,
48
30
  HCI_ACL_DATA_PACKET,
49
31
  HCI_DISCONNECTION_COMPLETE_EVENT,
50
- HCI_AclDataPacketAssembler,
51
- HCI_Packet,
52
- HCI_Event,
32
+ HCI_EVENT_PACKET,
33
+ Address,
53
34
  HCI_AclDataPacket,
35
+ HCI_AclDataPacketAssembler,
54
36
  HCI_Disconnection_Complete_Event,
37
+ HCI_Event,
38
+ HCI_Packet,
55
39
  )
56
-
40
+ from bumble.l2cap import (
41
+ L2CAP_LE_SIGNALING_CID,
42
+ L2CAP_PDU,
43
+ L2CAP_SIGNALING_CID,
44
+ CommandCode,
45
+ L2CAP_Connection_Request,
46
+ L2CAP_Connection_Response,
47
+ L2CAP_Control_Frame,
48
+ )
49
+ from bumble.smp import SMP_CID, SMP_Command
57
50
 
58
51
  # -----------------------------------------------------------------------------
59
52
  # Logging
bumble/hfp.py CHANGED
@@ -17,45 +17,34 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
 
20
+ import asyncio
20
21
  import collections
21
22
  import collections.abc
22
- import logging
23
- import asyncio
24
23
  import dataclasses
25
24
  import enum
26
- import traceback
25
+ import logging
27
26
  import re
28
- from typing import (
29
- Union,
30
- Any,
31
- Optional,
32
- ClassVar,
33
- Iterable,
34
- TYPE_CHECKING,
35
- )
27
+ import traceback
28
+ from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Optional, Union
29
+
36
30
  from typing_extensions import Self
37
31
 
38
- from bumble import at
39
- from bumble import device
40
- from bumble import rfcomm
41
- from bumble import sdp
42
- from bumble import utils
32
+ from bumble import at, device, rfcomm, sdp, utils
43
33
  from bumble.colors import color
44
34
  from bumble.core import (
45
- ProtocolError,
46
35
  BT_GENERIC_AUDIO_SERVICE,
47
- BT_HANDSFREE_SERVICE,
48
36
  BT_HANDSFREE_AUDIO_GATEWAY_SERVICE,
37
+ BT_HANDSFREE_SERVICE,
49
38
  BT_L2CAP_PROTOCOL_ID,
50
39
  BT_RFCOMM_PROTOCOL_ID,
40
+ ProtocolError,
51
41
  )
52
42
  from bumble.hci import (
53
- HCI_Enhanced_Setup_Synchronous_Connection_Command,
54
- CodingFormat,
55
43
  CodecID,
44
+ CodingFormat,
45
+ HCI_Enhanced_Setup_Synchronous_Connection_Command,
56
46
  )
57
47
 
58
-
59
48
  # -----------------------------------------------------------------------------
60
49
  # Logging
61
50
  # -----------------------------------------------------------------------------
bumble/hid.py CHANGED
@@ -16,22 +16,20 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
- from dataclasses import dataclass
20
- import logging
19
+
21
20
  import enum
21
+ import logging
22
22
  import struct
23
-
24
23
  from abc import ABC, abstractmethod
25
- from typing import Optional, Callable
24
+ from dataclasses import dataclass
25
+ from typing import Callable, Optional
26
+
26
27
  from typing_extensions import override
27
28
 
28
- from bumble import l2cap
29
- from bumble import device
30
- from bumble import utils
29
+ from bumble import device, l2cap, utils
31
30
  from bumble.core import InvalidStateError, ProtocolError
32
31
  from bumble.hci import Address
33
32
 
34
-
35
33
  # -----------------------------------------------------------------------------
36
34
  # Logging
37
35
  # -----------------------------------------------------------------------------
@@ -219,33 +217,41 @@ class HID(ABC, utils.EventEmitter):
219
217
  self.role = role
220
218
 
221
219
  # Register ourselves with the L2CAP channel manager
222
- device.register_l2cap_server(HID_CONTROL_PSM, self.on_l2cap_connection)
223
- device.register_l2cap_server(HID_INTERRUPT_PSM, self.on_l2cap_connection)
220
+ device.create_l2cap_server(
221
+ l2cap.ClassicChannelSpec(HID_CONTROL_PSM), self.on_l2cap_connection
222
+ )
223
+ device.create_l2cap_server(
224
+ l2cap.ClassicChannelSpec(HID_INTERRUPT_PSM), self.on_l2cap_connection
225
+ )
224
226
 
225
227
  device.on(device.EVENT_CONNECTION, self.on_device_connection)
226
228
 
227
229
  async def connect_control_channel(self) -> None:
230
+ if not self.connection:
231
+ raise InvalidStateError("Connection is not established!")
228
232
  # Create a new L2CAP connection - control channel
229
233
  try:
230
- channel = await self.device.l2cap_channel_manager.connect(
231
- self.connection, HID_CONTROL_PSM
234
+ channel = await self.connection.create_l2cap_channel(
235
+ l2cap.ClassicChannelSpec(HID_CONTROL_PSM)
232
236
  )
233
237
  channel.sink = self.on_ctrl_pdu
234
238
  self.l2cap_ctrl_channel = channel
235
239
  except ProtocolError:
236
- logging.exception(f'L2CAP connection failed.')
240
+ logging.exception('L2CAP connection failed.')
237
241
  raise
238
242
 
239
243
  async def connect_interrupt_channel(self) -> None:
244
+ if not self.connection:
245
+ raise InvalidStateError("Connection is not established!")
240
246
  # Create a new L2CAP connection - interrupt channel
241
247
  try:
242
- channel = await self.device.l2cap_channel_manager.connect(
243
- self.connection, HID_INTERRUPT_PSM
248
+ channel = await self.connection.create_l2cap_channel(
249
+ l2cap.ClassicChannelSpec(HID_CONTROL_PSM)
244
250
  )
245
251
  channel.sink = self.on_intr_pdu
246
252
  self.l2cap_intr_channel = channel
247
253
  except ProtocolError:
248
- logging.exception(f'L2CAP connection failed.')
254
+ logging.exception('L2CAP connection failed.')
249
255
  raise
250
256
 
251
257
  async def disconnect_interrupt_channel(self) -> None: