bumble 0.0.181__py3-none-any.whl → 0.0.182__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.
- bumble/_version.py +2 -2
- bumble/apps/bench.py +302 -67
- bumble/apps/controller_info.py +30 -4
- bumble/apps/controller_loopback.py +200 -0
- bumble/apps/l2cap_bridge.py +32 -24
- bumble/controller.py +131 -23
- bumble/device.py +15 -3
- bumble/hci.py +37 -1
- bumble/l2cap.py +10 -5
- bumble/link.py +55 -1
- bumble/profiles/csip.py +60 -8
- bumble/rfcomm.py +7 -3
- bumble/transport/__init__.py +2 -1
- {bumble-0.0.181.dist-info → bumble-0.0.182.dist-info}/METADATA +1 -1
- {bumble-0.0.181.dist-info → bumble-0.0.182.dist-info}/RECORD +19 -18
- {bumble-0.0.181.dist-info → bumble-0.0.182.dist-info}/LICENSE +0 -0
- {bumble-0.0.181.dist-info → bumble-0.0.182.dist-info}/WHEEL +0 -0
- {bumble-0.0.181.dist-info → bumble-0.0.182.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.181.dist-info → bumble-0.0.182.dist-info}/top_level.txt +0 -0
bumble/hci.py
CHANGED
|
@@ -2026,6 +2026,17 @@ class OwnAddressType(enum.IntEnum):
|
|
|
2026
2026
|
return {'size': 1, 'mapper': lambda x: OwnAddressType(x).name}
|
|
2027
2027
|
|
|
2028
2028
|
|
|
2029
|
+
# -----------------------------------------------------------------------------
|
|
2030
|
+
class LoopbackMode(enum.IntEnum):
|
|
2031
|
+
DISABLED = 0
|
|
2032
|
+
LOCAL = 1
|
|
2033
|
+
REMOTE = 2
|
|
2034
|
+
|
|
2035
|
+
@classmethod
|
|
2036
|
+
def type_spec(cls):
|
|
2037
|
+
return {'size': 1, 'mapper': lambda x: LoopbackMode(x).name}
|
|
2038
|
+
|
|
2039
|
+
|
|
2029
2040
|
# -----------------------------------------------------------------------------
|
|
2030
2041
|
class HCI_Packet:
|
|
2031
2042
|
'''
|
|
@@ -3352,6 +3363,27 @@ class HCI_Read_Encryption_Key_Size_Command(HCI_Command):
|
|
|
3352
3363
|
'''
|
|
3353
3364
|
|
|
3354
3365
|
|
|
3366
|
+
# -----------------------------------------------------------------------------
|
|
3367
|
+
@HCI_Command.command(
|
|
3368
|
+
return_parameters_fields=[
|
|
3369
|
+
('status', STATUS_SPEC),
|
|
3370
|
+
('loopback_mode', LoopbackMode.type_spec()),
|
|
3371
|
+
],
|
|
3372
|
+
)
|
|
3373
|
+
class HCI_Read_Loopback_Mode_Command(HCI_Command):
|
|
3374
|
+
'''
|
|
3375
|
+
See Bluetooth spec @ 7.6.1 Read Loopback Mode Command
|
|
3376
|
+
'''
|
|
3377
|
+
|
|
3378
|
+
|
|
3379
|
+
# -----------------------------------------------------------------------------
|
|
3380
|
+
@HCI_Command.command([('loopback_mode', 1)])
|
|
3381
|
+
class HCI_Write_Loopback_Mode_Command(HCI_Command):
|
|
3382
|
+
'''
|
|
3383
|
+
See Bluetooth spec @ 7.6.2 Write Loopback Mode Command
|
|
3384
|
+
'''
|
|
3385
|
+
|
|
3386
|
+
|
|
3355
3387
|
# -----------------------------------------------------------------------------
|
|
3356
3388
|
@HCI_Command.command([('le_event_mask', 8)])
|
|
3357
3389
|
class HCI_LE_Set_Event_Mask_Command(HCI_Command):
|
|
@@ -4733,7 +4765,11 @@ class HCI_Event(HCI_Packet):
|
|
|
4733
4765
|
HCI_Object.init_from_bytes(self, parameters, 0, fields)
|
|
4734
4766
|
return self
|
|
4735
4767
|
|
|
4736
|
-
def __init__(self, event_code, parameters=None, **kwargs):
|
|
4768
|
+
def __init__(self, event_code=-1, parameters=None, **kwargs):
|
|
4769
|
+
# Since the legacy implementation relies on an __init__ injector, typing always
|
|
4770
|
+
# complains that positional argument event_code is not passed, so here sets a
|
|
4771
|
+
# default value to allow building derived HCI_Event without event_code.
|
|
4772
|
+
assert event_code != -1
|
|
4737
4773
|
super().__init__(HCI_Event.event_name(event_code))
|
|
4738
4774
|
if (fields := getattr(self, 'fields', None)) and kwargs:
|
|
4739
4775
|
HCI_Object.init_from_fields(self, fields, kwargs)
|
bumble/l2cap.py
CHANGED
|
@@ -149,10 +149,11 @@ L2CAP_INVALID_CID_IN_REQUEST_REASON = 0x0002
|
|
|
149
149
|
|
|
150
150
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS = 65535
|
|
151
151
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU = 23
|
|
152
|
+
L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MTU = 65535
|
|
152
153
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS = 23
|
|
153
154
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS = 65533
|
|
154
155
|
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU = 2048
|
|
155
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS =
|
|
156
|
+
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS = 2048
|
|
156
157
|
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS = 256
|
|
157
158
|
|
|
158
159
|
L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE = 0x01
|
|
@@ -188,8 +189,11 @@ class LeCreditBasedChannelSpec:
|
|
|
188
189
|
or self.max_credits > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS
|
|
189
190
|
):
|
|
190
191
|
raise ValueError('max credits out of range')
|
|
191
|
-
if
|
|
192
|
-
|
|
192
|
+
if (
|
|
193
|
+
self.mtu < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU
|
|
194
|
+
or self.mtu > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MTU
|
|
195
|
+
):
|
|
196
|
+
raise ValueError('MTU out of range')
|
|
193
197
|
if (
|
|
194
198
|
self.mps < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS
|
|
195
199
|
or self.mps > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS
|
|
@@ -1644,12 +1648,13 @@ class ChannelManager:
|
|
|
1644
1648
|
|
|
1645
1649
|
def send_pdu(self, connection, cid: int, pdu: Union[SupportsBytes, bytes]) -> None:
|
|
1646
1650
|
pdu_str = pdu.hex() if isinstance(pdu, bytes) else str(pdu)
|
|
1651
|
+
pdu_bytes = bytes(pdu)
|
|
1647
1652
|
logger.debug(
|
|
1648
1653
|
f'{color(">>> Sending L2CAP PDU", "blue")} '
|
|
1649
1654
|
f'on connection [0x{connection.handle:04X}] (CID={cid}) '
|
|
1650
|
-
f'{connection.peer_address}: {pdu_str}'
|
|
1655
|
+
f'{connection.peer_address}: {len(pdu_bytes)} bytes, {pdu_str}'
|
|
1651
1656
|
)
|
|
1652
|
-
self.host.send_l2cap_pdu(connection.handle, cid,
|
|
1657
|
+
self.host.send_l2cap_pdu(connection.handle, cid, pdu_bytes)
|
|
1653
1658
|
|
|
1654
1659
|
def on_pdu(self, connection: Connection, cid: int, pdu: bytes) -> None:
|
|
1655
1660
|
if cid in (L2CAP_SIGNALING_CID, L2CAP_LE_SIGNALING_CID):
|
bumble/link.py
CHANGED
|
@@ -26,9 +26,13 @@ from bumble.hci import (
|
|
|
26
26
|
HCI_SUCCESS,
|
|
27
27
|
HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR,
|
|
28
28
|
HCI_CONNECTION_TIMEOUT_ERROR,
|
|
29
|
+
HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
|
|
29
30
|
HCI_PAGE_TIMEOUT_ERROR,
|
|
30
31
|
HCI_Connection_Complete_Event,
|
|
31
32
|
)
|
|
33
|
+
from bumble import controller
|
|
34
|
+
|
|
35
|
+
from typing import Optional, Set
|
|
32
36
|
|
|
33
37
|
# -----------------------------------------------------------------------------
|
|
34
38
|
# Logging
|
|
@@ -57,6 +61,8 @@ class LocalLink:
|
|
|
57
61
|
Link bus for controllers to communicate with each other
|
|
58
62
|
'''
|
|
59
63
|
|
|
64
|
+
controllers: Set[controller.Controller]
|
|
65
|
+
|
|
60
66
|
def __init__(self):
|
|
61
67
|
self.controllers = set()
|
|
62
68
|
self.pending_connection = None
|
|
@@ -79,7 +85,9 @@ class LocalLink:
|
|
|
79
85
|
return controller
|
|
80
86
|
return None
|
|
81
87
|
|
|
82
|
-
def find_classic_controller(
|
|
88
|
+
def find_classic_controller(
|
|
89
|
+
self, address: Address
|
|
90
|
+
) -> Optional[controller.Controller]:
|
|
83
91
|
for controller in self.controllers:
|
|
84
92
|
if controller.public_address == address:
|
|
85
93
|
return controller
|
|
@@ -271,6 +279,52 @@ class LocalLink:
|
|
|
271
279
|
initiator_controller.public_address, int(not (initiator_new_role))
|
|
272
280
|
)
|
|
273
281
|
|
|
282
|
+
def classic_sco_connect(
|
|
283
|
+
self,
|
|
284
|
+
initiator_controller: controller.Controller,
|
|
285
|
+
responder_address: Address,
|
|
286
|
+
link_type: int,
|
|
287
|
+
):
|
|
288
|
+
logger.debug(
|
|
289
|
+
f'[Classic] {initiator_controller.public_address} connects SCO to {responder_address}'
|
|
290
|
+
)
|
|
291
|
+
responder_controller = self.find_classic_controller(responder_address)
|
|
292
|
+
# Initiator controller should handle it.
|
|
293
|
+
assert responder_controller
|
|
294
|
+
|
|
295
|
+
responder_controller.on_classic_connection_request(
|
|
296
|
+
initiator_controller.public_address,
|
|
297
|
+
link_type,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
def classic_accept_sco_connection(
|
|
301
|
+
self,
|
|
302
|
+
responder_controller: controller.Controller,
|
|
303
|
+
initiator_address: Address,
|
|
304
|
+
link_type: int,
|
|
305
|
+
):
|
|
306
|
+
logger.debug(
|
|
307
|
+
f'[Classic] {responder_controller.public_address} accepts to connect SCO {initiator_address}'
|
|
308
|
+
)
|
|
309
|
+
initiator_controller = self.find_classic_controller(initiator_address)
|
|
310
|
+
if initiator_controller is None:
|
|
311
|
+
responder_controller.on_classic_sco_connection_complete(
|
|
312
|
+
responder_controller.public_address,
|
|
313
|
+
HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
|
|
314
|
+
link_type,
|
|
315
|
+
)
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
async def task():
|
|
319
|
+
initiator_controller.on_classic_sco_connection_complete(
|
|
320
|
+
responder_controller.public_address, HCI_SUCCESS, link_type
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
asyncio.create_task(task())
|
|
324
|
+
responder_controller.on_classic_sco_connection_complete(
|
|
325
|
+
initiator_controller.public_address, HCI_SUCCESS, link_type
|
|
326
|
+
)
|
|
327
|
+
|
|
274
328
|
|
|
275
329
|
# -----------------------------------------------------------------------------
|
|
276
330
|
class RemoteLink:
|
bumble/profiles/csip.py
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
import enum
|
|
21
21
|
import struct
|
|
22
|
-
from typing import Optional
|
|
22
|
+
from typing import Optional, Tuple
|
|
23
23
|
|
|
24
24
|
from bumble import core
|
|
25
25
|
from bumble import crypto
|
|
@@ -31,6 +31,9 @@ from bumble import gatt_client
|
|
|
31
31
|
# -----------------------------------------------------------------------------
|
|
32
32
|
# Constants
|
|
33
33
|
# -----------------------------------------------------------------------------
|
|
34
|
+
SET_IDENTITY_RESOLVING_KEY_LENGTH = 16
|
|
35
|
+
|
|
36
|
+
|
|
34
37
|
class SirkType(enum.IntEnum):
|
|
35
38
|
'''Coordinated Set Identification Service - 5.1 Set Identity Resolving Key.'''
|
|
36
39
|
|
|
@@ -66,6 +69,10 @@ def k1(n: bytes, salt: bytes, p: bytes) -> bytes:
|
|
|
66
69
|
def sef(k: bytes, r: bytes) -> bytes:
|
|
67
70
|
'''
|
|
68
71
|
Coordinated Set Identification Service - 4.5 SIRK encryption function sef.
|
|
72
|
+
|
|
73
|
+
SIRK decryption function sdf shares the same algorithm. The only difference is that argument r is:
|
|
74
|
+
* Plaintext in encryption
|
|
75
|
+
* Cipher in decryption
|
|
69
76
|
'''
|
|
70
77
|
return crypto.xor(k1(k, s1(b'SIRKenc'[::-1]), b'csis'[::-1]), r)
|
|
71
78
|
|
|
@@ -105,6 +112,11 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
|
|
|
105
112
|
set_member_lock: Optional[MemberLock] = None,
|
|
106
113
|
set_member_rank: Optional[int] = None,
|
|
107
114
|
) -> None:
|
|
115
|
+
if len(set_identity_resolving_key) != SET_IDENTITY_RESOLVING_KEY_LENGTH:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f'Invalid SIRK length {len(set_identity_resolving_key)}, expected {SET_IDENTITY_RESOLVING_KEY_LENGTH}'
|
|
118
|
+
)
|
|
119
|
+
|
|
108
120
|
characteristics = []
|
|
109
121
|
|
|
110
122
|
self.set_identity_resolving_key = set_identity_resolving_key
|
|
@@ -113,7 +125,7 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
|
|
|
113
125
|
uuid=gatt.GATT_SET_IDENTITY_RESOLVING_KEY_CHARACTERISTIC,
|
|
114
126
|
properties=gatt.Characteristic.Properties.READ
|
|
115
127
|
| gatt.Characteristic.Properties.NOTIFY,
|
|
116
|
-
permissions=gatt.Characteristic.Permissions.
|
|
128
|
+
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
117
129
|
value=gatt.CharacteristicValue(read=self.on_sirk_read),
|
|
118
130
|
)
|
|
119
131
|
characteristics.append(self.set_identity_resolving_key_characteristic)
|
|
@@ -123,7 +135,7 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
|
|
|
123
135
|
uuid=gatt.GATT_COORDINATED_SET_SIZE_CHARACTERISTIC,
|
|
124
136
|
properties=gatt.Characteristic.Properties.READ
|
|
125
137
|
| gatt.Characteristic.Properties.NOTIFY,
|
|
126
|
-
permissions=gatt.Characteristic.Permissions.
|
|
138
|
+
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
127
139
|
value=struct.pack('B', coordinated_set_size),
|
|
128
140
|
)
|
|
129
141
|
characteristics.append(self.coordinated_set_size_characteristic)
|
|
@@ -134,7 +146,7 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
|
|
|
134
146
|
properties=gatt.Characteristic.Properties.READ
|
|
135
147
|
| gatt.Characteristic.Properties.NOTIFY
|
|
136
148
|
| gatt.Characteristic.Properties.WRITE,
|
|
137
|
-
permissions=gatt.Characteristic.Permissions.
|
|
149
|
+
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
|
|
138
150
|
| gatt.Characteristic.Permissions.WRITEABLE,
|
|
139
151
|
value=struct.pack('B', set_member_lock),
|
|
140
152
|
)
|
|
@@ -145,18 +157,32 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
|
|
|
145
157
|
uuid=gatt.GATT_SET_MEMBER_RANK_CHARACTERISTIC,
|
|
146
158
|
properties=gatt.Characteristic.Properties.READ
|
|
147
159
|
| gatt.Characteristic.Properties.NOTIFY,
|
|
148
|
-
permissions=gatt.Characteristic.Permissions.
|
|
160
|
+
permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
|
|
149
161
|
value=struct.pack('B', set_member_rank),
|
|
150
162
|
)
|
|
151
163
|
characteristics.append(self.set_member_rank_characteristic)
|
|
152
164
|
|
|
153
165
|
super().__init__(characteristics)
|
|
154
166
|
|
|
155
|
-
def on_sirk_read(self,
|
|
167
|
+
async def on_sirk_read(self, connection: Optional[device.Connection]) -> bytes:
|
|
156
168
|
if self.set_identity_resolving_key_type == SirkType.PLAINTEXT:
|
|
157
|
-
|
|
169
|
+
sirk_bytes = self.set_identity_resolving_key
|
|
158
170
|
else:
|
|
159
|
-
|
|
171
|
+
assert connection
|
|
172
|
+
|
|
173
|
+
if connection.transport == core.BT_LE_TRANSPORT:
|
|
174
|
+
key = await connection.device.get_long_term_key(
|
|
175
|
+
connection_handle=connection.handle, rand=b'', ediv=0
|
|
176
|
+
)
|
|
177
|
+
else:
|
|
178
|
+
key = await connection.device.get_link_key(connection.peer_address)
|
|
179
|
+
|
|
180
|
+
if not key:
|
|
181
|
+
raise RuntimeError('LTK or LinkKey is not present')
|
|
182
|
+
|
|
183
|
+
sirk_bytes = sef(key, self.set_identity_resolving_key)
|
|
184
|
+
|
|
185
|
+
return bytes([self.set_identity_resolving_key_type]) + sirk_bytes
|
|
160
186
|
|
|
161
187
|
def get_advertising_data(self) -> bytes:
|
|
162
188
|
return bytes(
|
|
@@ -203,3 +229,29 @@ class CoordinatedSetIdentificationProxy(gatt_client.ProfileServiceProxy):
|
|
|
203
229
|
gatt.GATT_SET_MEMBER_RANK_CHARACTERISTIC
|
|
204
230
|
):
|
|
205
231
|
self.set_member_rank = characteristics[0]
|
|
232
|
+
|
|
233
|
+
async def read_set_identity_resolving_key(self) -> Tuple[SirkType, bytes]:
|
|
234
|
+
'''Reads SIRK and decrypts if encrypted.'''
|
|
235
|
+
response = await self.set_identity_resolving_key.read_value()
|
|
236
|
+
if len(response) != SET_IDENTITY_RESOLVING_KEY_LENGTH + 1:
|
|
237
|
+
raise RuntimeError('Invalid SIRK value')
|
|
238
|
+
|
|
239
|
+
sirk_type = SirkType(response[0])
|
|
240
|
+
if sirk_type == SirkType.PLAINTEXT:
|
|
241
|
+
sirk = response[1:]
|
|
242
|
+
else:
|
|
243
|
+
connection = self.service_proxy.client.connection
|
|
244
|
+
device = connection.device
|
|
245
|
+
if connection.transport == core.BT_LE_TRANSPORT:
|
|
246
|
+
key = await device.get_long_term_key(
|
|
247
|
+
connection_handle=connection.handle, rand=b'', ediv=0
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
key = await device.get_link_key(connection.peer_address)
|
|
251
|
+
|
|
252
|
+
if not key:
|
|
253
|
+
raise RuntimeError('LTK or LinkKey is not present')
|
|
254
|
+
|
|
255
|
+
sirk = sef(key, response[1:])
|
|
256
|
+
|
|
257
|
+
return (sirk_type, sirk)
|
bumble/rfcomm.py
CHANGED
|
@@ -454,6 +454,8 @@ class DLC(EventEmitter):
|
|
|
454
454
|
self.c_r = 1 if self.role == Multiplexer.Role.INITIATOR else 0
|
|
455
455
|
self.sink = None
|
|
456
456
|
self.connection_result = None
|
|
457
|
+
self.drained = asyncio.Event()
|
|
458
|
+
self.drained.set()
|
|
457
459
|
|
|
458
460
|
# Compute the MTU
|
|
459
461
|
max_overhead = 4 + 1 # header with 2-byte length + fcs
|
|
@@ -633,6 +635,8 @@ class DLC(EventEmitter):
|
|
|
633
635
|
)
|
|
634
636
|
|
|
635
637
|
rx_credits_needed = 0
|
|
638
|
+
if not self.tx_buffer:
|
|
639
|
+
self.drained.set()
|
|
636
640
|
|
|
637
641
|
# Stream protocol
|
|
638
642
|
def write(self, data: Union[bytes, str]) -> None:
|
|
@@ -645,11 +649,11 @@ class DLC(EventEmitter):
|
|
|
645
649
|
raise ValueError('write only accept bytes or strings')
|
|
646
650
|
|
|
647
651
|
self.tx_buffer += data
|
|
652
|
+
self.drained.clear()
|
|
648
653
|
self.process_tx()
|
|
649
654
|
|
|
650
|
-
def drain(self) -> None:
|
|
651
|
-
|
|
652
|
-
pass
|
|
655
|
+
async def drain(self) -> None:
|
|
656
|
+
await self.drained.wait()
|
|
653
657
|
|
|
654
658
|
def __str__(self) -> str:
|
|
655
659
|
return f'DLC(dlci={self.dlci},state={self.state.name})'
|
bumble/transport/__init__.py
CHANGED
|
@@ -198,12 +198,13 @@ async def open_transport_or_link(name: str) -> Transport:
|
|
|
198
198
|
|
|
199
199
|
"""
|
|
200
200
|
if name.startswith('link-relay:'):
|
|
201
|
+
logger.warning('Link Relay has been deprecated.')
|
|
201
202
|
from ..controller import Controller
|
|
202
203
|
from ..link import RemoteLink # lazy import
|
|
203
204
|
|
|
204
205
|
link = RemoteLink(name[11:])
|
|
205
206
|
await link.wait_until_connected()
|
|
206
|
-
controller = Controller('remote', link=link)
|
|
207
|
+
controller = Controller('remote', link=link) # type:ignore[arg-type]
|
|
207
208
|
|
|
208
209
|
class LinkTransport(Transport):
|
|
209
210
|
async def close(self):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
|
|
2
|
-
bumble/_version.py,sha256=
|
|
2
|
+
bumble/_version.py,sha256=jNNHSrRJTup4dBd5BRhfsjHh4m5-E9mCCGmW1eJZQnw,415
|
|
3
3
|
bumble/a2dp.py,sha256=NqdmHIeEe3kThoHqdOgC3Wkywc6H7zqwGbjRYYFtNoQ,22321
|
|
4
4
|
bumble/at.py,sha256=kdrcsx2C8Rg61EWESD2QHwpZntkXkRBJLrPn9auv9K8,2961
|
|
5
5
|
bumble/att.py,sha256=_HrhlCl4CXZM9mmBLTCPq5v2OW9eZ0-H6cSu5_jG_5o,32365
|
|
@@ -8,41 +8,42 @@ bumble/bridge.py,sha256=T6es5oS1dy8QgkxQ8iOD-YcZ0SWOv8jaqC7TGxqodk4,3003
|
|
|
8
8
|
bumble/codecs.py,sha256=Vc7FOo6d-6VCgDI0ibnLmX8vCZ4-jtX_-0vEUM-yOrI,15343
|
|
9
9
|
bumble/colors.py,sha256=9H-qzGgMr-YoWdIFpcGPaiRvTvkCzz7FPIkpwqKyKug,3033
|
|
10
10
|
bumble/company_ids.py,sha256=B68e2QPsDeRYP9jjbGs4GGDwEkGxcXGTsON_CHA0uuI,118528
|
|
11
|
-
bumble/controller.py,sha256=
|
|
11
|
+
bumble/controller.py,sha256=1Uvn1IkEIVkPOEMF8ed26SkiEBzmnH-PlCrE-WDFahE,49945
|
|
12
12
|
bumble/core.py,sha256=HjRpUseyVsueNkZ-KhPPTf1vNkNnI02eNcaOe685V_0,52949
|
|
13
13
|
bumble/crypto.py,sha256=L6z3dn9-dgKYRtOM6O3F6n6Ju4PwTM3LAFJtCg_ie78,9382
|
|
14
14
|
bumble/decoder.py,sha256=N9nMvuVhuwpnfw7EDVuNe9uYY6B6c3RY2dh8RhRPC1U,9608
|
|
15
|
-
bumble/device.py,sha256=
|
|
15
|
+
bumble/device.py,sha256=OmcjQS-z7xmJ55zgq20-0bUngAQKhdYJs4WJ-XdlS_I,154100
|
|
16
16
|
bumble/gap.py,sha256=axlOZIv99357Ehq2vMokeioU85z81qdQvplGn0pF70Q,2137
|
|
17
17
|
bumble/gatt.py,sha256=L3RLHiyAKMIZHLfkbH09V9yTNNKymacDtpvwmSoRQRY,38405
|
|
18
18
|
bumble/gatt_client.py,sha256=JuyFUHEc5xd2astr2XCZrTiAla2FoDkKe4lH1oSQOPA,42551
|
|
19
19
|
bumble/gatt_server.py,sha256=Ebu-VBIPUPZiwHl207ncuC-tCF7oUkR4B8BlE0GruY8,37112
|
|
20
|
-
bumble/hci.py,sha256=
|
|
20
|
+
bumble/hci.py,sha256=0pDWOQIQQ1e8WSnfKsFCOg1xXmEQtHQHmrLPBk6nKyo,238361
|
|
21
21
|
bumble/helpers.py,sha256=pxSNAuLnMhU8eg3AdqUNoCyA8Q5-GZ1_SdgFS2nOp_M,10412
|
|
22
22
|
bumble/hfp.py,sha256=8b6zR4YhGl9Rc2vxL9AFAYZ2kyxe5SaLWUUXHDpeEoU,38487
|
|
23
23
|
bumble/hid.py,sha256=KiY6v5G8qAQoP_IzjiY68b7vF9izpwJyGovUHRH3AmA,20584
|
|
24
24
|
bumble/host.py,sha256=dUoy421swHti2d0rNPR61_bsSkPqanHRYS7wHwsPswU,39154
|
|
25
25
|
bumble/keys.py,sha256=_ibaJ4CxM5zyxnTp7wnihIQSsEAEkCiPxx9qHsGA04Q,12601
|
|
26
|
-
bumble/l2cap.py,sha256=
|
|
27
|
-
bumble/link.py,sha256=
|
|
26
|
+
bumble/l2cap.py,sha256=Uh9qSTzR_xQA7zb_oz85AmNWmhqV3xXQYumNeuoPahQ,80965
|
|
27
|
+
bumble/link.py,sha256=PaQwHhKgZ80IMtkcZFXzH1bVotvYk3QXMBf4d7UU90k,22075
|
|
28
28
|
bumble/pairing.py,sha256=tgPUba6xNxMi-2plm3xfRlzHq-uPRNZEIGWaN0qNGCs,9853
|
|
29
29
|
bumble/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
bumble/rfcomm.py,sha256=
|
|
30
|
+
bumble/rfcomm.py,sha256=9uTvEDz8fY-xqUHV7p4WzZoRhvz9W0rE9n966jgnjj0,33984
|
|
31
31
|
bumble/sdp.py,sha256=D5pG7cuAoFBygdmxExyuRhU4aVsvkWB9WQVnONCU-L0,44854
|
|
32
32
|
bumble/smp.py,sha256=NmDWTxXJIzVnwFqG3M2OduduP_ryGh5y886RCigqPvs,76235
|
|
33
33
|
bumble/snoop.py,sha256=_QfF36eylBW6Snd-_KYOwKaGiM8i_Ed-B5XoFIPt3Dg,5631
|
|
34
34
|
bumble/utils.py,sha256=RXx5-6YkUwrCGQRChy80bJ2ifZ2frdXZCcMXHu_pnqI,14083
|
|
35
35
|
bumble/apps/README.md,sha256=XTwjRAY-EJWDXpl1V8K3Mw8B7kIqzUIUizRjVBVhoIE,1769
|
|
36
36
|
bumble/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
-
bumble/apps/bench.py,sha256=
|
|
37
|
+
bumble/apps/bench.py,sha256=HCMY9IQSvyfEQsNUDWWw_51iMgwZ7wE_pMdTAeHno2E,52444
|
|
38
38
|
bumble/apps/ble_rpa_tool.py,sha256=ZQtsbfnLPd5qUAkEBPpNgJLRynBBc7q_9cDHKUW2SQ0,1701
|
|
39
39
|
bumble/apps/console.py,sha256=rVR2jmP6Yd76B4zzGPYnpJFtgeYgq19CL6DMSe2-A1M,46093
|
|
40
|
-
bumble/apps/controller_info.py,sha256=
|
|
40
|
+
bumble/apps/controller_info.py,sha256=bgbyrHZFvrTBKzKfQkGcJvoSPsSaVv7xOzXmb42sHv8,9302
|
|
41
|
+
bumble/apps/controller_loopback.py,sha256=eBcutzSDT8bp8348b2cxa4LCjZPRqWm0bYTq6yPwg5I,7073
|
|
41
42
|
bumble/apps/controllers.py,sha256=R6XJ1XpyuXlyqSCmI7PromVIcoYTcYfpmO-TqTYXnUI,2326
|
|
42
43
|
bumble/apps/gatt_dump.py,sha256=-dCvCgjuHAp0h1zxm-gmqB4lVlSdas1Kp4cpzzx4gGw,4245
|
|
43
44
|
bumble/apps/gg_bridge.py,sha256=JdW5QT6xN9c2XDDJoHDRo5W3N_RdVkCtTmlcOsJhlx8,14693
|
|
44
45
|
bumble/apps/hci_bridge.py,sha256=KISv352tKnsQsoxjkDiCQbMFmhnPWdnug5wSFAAXxEs,4033
|
|
45
|
-
bumble/apps/l2cap_bridge.py,sha256=
|
|
46
|
+
bumble/apps/l2cap_bridge.py,sha256=524VgEmgCP4g7T0UdgmsePmNVhDFRJECeaZ_uzKsbco,13062
|
|
46
47
|
bumble/apps/pair.py,sha256=COU2D7YAIn4lo5iuM0ClObA1zZqQCdrXOcnsiCm0YlQ,17529
|
|
47
48
|
bumble/apps/pandora_server.py,sha256=5qaoLCpcZE2KsGO21-7t6Vg4dBjBWbnyOQXwrLhxkuE,1397
|
|
48
49
|
bumble/apps/scan.py,sha256=_fMG_1j1HZQ_8SrJ0ZOxJaWB1OR4mKBaZuQMgm8viYI,7439
|
|
@@ -73,7 +74,7 @@ bumble/profiles/asha_service.py,sha256=J4i5jkJciZWMtTWJ1zGJkEx65DlAEIADqjCRYf_CW
|
|
|
73
74
|
bumble/profiles/bap.py,sha256=GJOn1t7ZGcm-woFnI4JFx4I4Z_rnv5GQIB4dnrwULlY,44900
|
|
74
75
|
bumble/profiles/battery_service.py,sha256=w-uF4jLoDozJOoykimb2RkrKjVyCke6ts2-h-F1PYyc,2292
|
|
75
76
|
bumble/profiles/cap.py,sha256=6gH7oOnUKjOggMPuB7rtbwj0AneoNmnWzQ_iR3io8e0,1945
|
|
76
|
-
bumble/profiles/csip.py,sha256=
|
|
77
|
+
bumble/profiles/csip.py,sha256=wzSpNRCOMWtKw2Yd9OTAzPoFDoQWG-KYwWdA6sUkwiI,10102
|
|
77
78
|
bumble/profiles/device_information_service.py,sha256=H1Db4BAOnsC-rRtfpoAIsDETYT4F9yM_WgByn_3LfRQ,5658
|
|
78
79
|
bumble/profiles/heart_rate_service.py,sha256=7V2LGcWLp6RurjWxsVgMWr3wPDt5aS9qjNxTbHcOK6o,8575
|
|
79
80
|
bumble/profiles/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -81,7 +82,7 @@ bumble/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
81
82
|
bumble/tools/generate_company_id_list.py,sha256=ysbPb3zmxKFBiSQ_MBG2r15-sqR5P_GWT6i0YUTTXOM,1736
|
|
82
83
|
bumble/tools/rtk_fw_download.py,sha256=kTxR9UaNiD8VtzWVAVxsCx5c_tkfz9OKe-vxdlfKQRY,5454
|
|
83
84
|
bumble/tools/rtk_util.py,sha256=TwZhupHQrQYsYHLdRGyzXKd24pwCk8kkzqK1Rj2guco,5087
|
|
84
|
-
bumble/transport/__init__.py,sha256=
|
|
85
|
+
bumble/transport/__init__.py,sha256=DQe4KOqGOAu7UEDCuysGJXIhWka53Bo13vLt0W-dR_I,6852
|
|
85
86
|
bumble/transport/android_emulator.py,sha256=eH8H1aB7MvQ8lpdwVG6SGKg5uwCRb_aPJk8pPwoTXSY,4321
|
|
86
87
|
bumble/transport/android_netsim.py,sha256=SVh-IUZ2bhcIESZFGzOsofybsi4H0qoBRwBieeqUINE,16215
|
|
87
88
|
bumble/transport/common.py,sha256=ZrW-CuEqf7xNcCK29gvC9azOLgPA7IqKgoyfKQxdsLo,15654
|
|
@@ -131,9 +132,9 @@ bumble/vendor/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
131
132
|
bumble/vendor/android/hci.py,sha256=GZrkhaWmcMt1JpnRhv0NoySGkf2H4lNUV2f_omRZW0I,10741
|
|
132
133
|
bumble/vendor/zephyr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
133
134
|
bumble/vendor/zephyr/hci.py,sha256=d83bC0TvT947eN4roFjLkQefWtHOoNsr4xib2ctSkvA,3195
|
|
134
|
-
bumble-0.0.
|
|
135
|
-
bumble-0.0.
|
|
136
|
-
bumble-0.0.
|
|
137
|
-
bumble-0.0.
|
|
138
|
-
bumble-0.0.
|
|
139
|
-
bumble-0.0.
|
|
135
|
+
bumble-0.0.182.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
|
|
136
|
+
bumble-0.0.182.dist-info/METADATA,sha256=qwhQQGzsmCfpLay30Xzd7dUREps21OAGPhcgkfk-K3U,5681
|
|
137
|
+
bumble-0.0.182.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
138
|
+
bumble-0.0.182.dist-info/entry_points.txt,sha256=96i3Nmc2zjWeSWTYH2x3lPV7nH1KvOK2jrF0spSjpYc,817
|
|
139
|
+
bumble-0.0.182.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
|
|
140
|
+
bumble-0.0.182.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|