bumble 0.0.154__py3-none-any.whl → 0.0.156__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/a2dp.py +1 -0
- bumble/apps/pair.py +14 -14
- bumble/apps/scan.py +5 -6
- bumble/apps/speaker/__init__.py +0 -0
- bumble/apps/speaker/logo.svg +42 -0
- bumble/apps/speaker/speaker.css +76 -0
- bumble/apps/speaker/speaker.html +34 -0
- bumble/apps/speaker/speaker.js +315 -0
- bumble/apps/speaker/speaker.py +747 -0
- bumble/apps/unbond.py +40 -22
- bumble/avdtp.py +50 -31
- bumble/codecs.py +381 -0
- bumble/device.py +17 -4
- bumble/hci.py +13 -9
- bumble/hfp.py +14 -8
- bumble/host.py +7 -1
- bumble/keys.py +72 -46
- bumble/pandora/host.py +2 -1
- bumble/pandora/security.py +4 -7
- bumble/rfcomm.py +110 -67
- bumble/smp.py +1 -1
- {bumble-0.0.154.dist-info → bumble-0.0.156.dist-info}/METADATA +5 -4
- {bumble-0.0.154.dist-info → bumble-0.0.156.dist-info}/RECORD +28 -21
- {bumble-0.0.154.dist-info → bumble-0.0.156.dist-info}/entry_points.txt +1 -0
- {bumble-0.0.154.dist-info → bumble-0.0.156.dist-info}/LICENSE +0 -0
- {bumble-0.0.154.dist-info → bumble-0.0.156.dist-info}/WHEEL +0 -0
- {bumble-0.0.154.dist-info → bumble-0.0.156.dist-info}/top_level.txt +0 -0
bumble/rfcomm.py
CHANGED
|
@@ -19,8 +19,9 @@ import logging
|
|
|
19
19
|
import asyncio
|
|
20
20
|
|
|
21
21
|
from pyee import EventEmitter
|
|
22
|
+
from typing import Optional, Tuple, Callable, Dict, Union
|
|
22
23
|
|
|
23
|
-
from . import core
|
|
24
|
+
from . import core, l2cap
|
|
24
25
|
from .colors import color
|
|
25
26
|
from .core import BT_BR_EDR_TRANSPORT, InvalidStateError, ProtocolError
|
|
26
27
|
|
|
@@ -105,7 +106,7 @@ RFCOMM_DYNAMIC_CHANNEL_NUMBER_END = 30
|
|
|
105
106
|
|
|
106
107
|
|
|
107
108
|
# -----------------------------------------------------------------------------
|
|
108
|
-
def compute_fcs(buffer):
|
|
109
|
+
def compute_fcs(buffer: bytes) -> int:
|
|
109
110
|
result = 0xFF
|
|
110
111
|
for byte in buffer:
|
|
111
112
|
result = CRC_TABLE[result ^ byte]
|
|
@@ -114,7 +115,15 @@ def compute_fcs(buffer):
|
|
|
114
115
|
|
|
115
116
|
# -----------------------------------------------------------------------------
|
|
116
117
|
class RFCOMM_Frame:
|
|
117
|
-
def __init__(
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
frame_type: int,
|
|
121
|
+
c_r: int,
|
|
122
|
+
dlci: int,
|
|
123
|
+
p_f: int,
|
|
124
|
+
information: bytes = b'',
|
|
125
|
+
with_credits: bool = False,
|
|
126
|
+
) -> None:
|
|
118
127
|
self.type = frame_type
|
|
119
128
|
self.c_r = c_r
|
|
120
129
|
self.dlci = dlci
|
|
@@ -136,11 +145,11 @@ class RFCOMM_Frame:
|
|
|
136
145
|
else:
|
|
137
146
|
self.fcs = compute_fcs(bytes([self.address, self.control]) + self.length)
|
|
138
147
|
|
|
139
|
-
def type_name(self):
|
|
148
|
+
def type_name(self) -> str:
|
|
140
149
|
return RFCOMM_FRAME_TYPE_NAMES[self.type]
|
|
141
150
|
|
|
142
151
|
@staticmethod
|
|
143
|
-
def parse_mcc(data):
|
|
152
|
+
def parse_mcc(data) -> Tuple[int, int, bytes]:
|
|
144
153
|
mcc_type = data[0] >> 2
|
|
145
154
|
c_r = (data[0] >> 1) & 1
|
|
146
155
|
length = data[1]
|
|
@@ -154,36 +163,36 @@ class RFCOMM_Frame:
|
|
|
154
163
|
return (mcc_type, c_r, value)
|
|
155
164
|
|
|
156
165
|
@staticmethod
|
|
157
|
-
def make_mcc(mcc_type, c_r, data):
|
|
166
|
+
def make_mcc(mcc_type: int, c_r: int, data: bytes) -> bytes:
|
|
158
167
|
return (
|
|
159
168
|
bytes([(mcc_type << 2 | c_r << 1 | 1) & 0xFF, (len(data) & 0x7F) << 1 | 1])
|
|
160
169
|
+ data
|
|
161
170
|
)
|
|
162
171
|
|
|
163
172
|
@staticmethod
|
|
164
|
-
def sabm(c_r, dlci):
|
|
173
|
+
def sabm(c_r: int, dlci: int):
|
|
165
174
|
return RFCOMM_Frame(RFCOMM_SABM_FRAME, c_r, dlci, 1)
|
|
166
175
|
|
|
167
176
|
@staticmethod
|
|
168
|
-
def ua(c_r, dlci):
|
|
177
|
+
def ua(c_r: int, dlci: int):
|
|
169
178
|
return RFCOMM_Frame(RFCOMM_UA_FRAME, c_r, dlci, 1)
|
|
170
179
|
|
|
171
180
|
@staticmethod
|
|
172
|
-
def dm(c_r, dlci):
|
|
181
|
+
def dm(c_r: int, dlci: int):
|
|
173
182
|
return RFCOMM_Frame(RFCOMM_DM_FRAME, c_r, dlci, 1)
|
|
174
183
|
|
|
175
184
|
@staticmethod
|
|
176
|
-
def disc(c_r, dlci):
|
|
185
|
+
def disc(c_r: int, dlci: int):
|
|
177
186
|
return RFCOMM_Frame(RFCOMM_DISC_FRAME, c_r, dlci, 1)
|
|
178
187
|
|
|
179
188
|
@staticmethod
|
|
180
|
-
def uih(c_r, dlci, information, p_f=0):
|
|
189
|
+
def uih(c_r: int, dlci: int, information: bytes, p_f: int = 0):
|
|
181
190
|
return RFCOMM_Frame(
|
|
182
191
|
RFCOMM_UIH_FRAME, c_r, dlci, p_f, information, with_credits=(p_f == 1)
|
|
183
192
|
)
|
|
184
193
|
|
|
185
194
|
@staticmethod
|
|
186
|
-
def from_bytes(data):
|
|
195
|
+
def from_bytes(data: bytes):
|
|
187
196
|
# Extract fields
|
|
188
197
|
dlci = (data[0] >> 2) & 0x3F
|
|
189
198
|
c_r = (data[0] >> 1) & 0x01
|
|
@@ -227,15 +236,23 @@ class RFCOMM_Frame:
|
|
|
227
236
|
|
|
228
237
|
# -----------------------------------------------------------------------------
|
|
229
238
|
class RFCOMM_MCC_PN:
|
|
239
|
+
dlci: int
|
|
240
|
+
cl: int
|
|
241
|
+
priority: int
|
|
242
|
+
ack_timer: int
|
|
243
|
+
max_frame_size: int
|
|
244
|
+
max_retransmissions: int
|
|
245
|
+
window_size: int
|
|
246
|
+
|
|
230
247
|
def __init__(
|
|
231
248
|
self,
|
|
232
|
-
dlci,
|
|
233
|
-
cl,
|
|
234
|
-
priority,
|
|
235
|
-
ack_timer,
|
|
236
|
-
max_frame_size,
|
|
237
|
-
max_retransmissions,
|
|
238
|
-
window_size,
|
|
249
|
+
dlci: int,
|
|
250
|
+
cl: int,
|
|
251
|
+
priority: int,
|
|
252
|
+
ack_timer: int,
|
|
253
|
+
max_frame_size: int,
|
|
254
|
+
max_retransmissions: int,
|
|
255
|
+
window_size: int,
|
|
239
256
|
):
|
|
240
257
|
self.dlci = dlci
|
|
241
258
|
self.cl = cl
|
|
@@ -246,7 +263,7 @@ class RFCOMM_MCC_PN:
|
|
|
246
263
|
self.window_size = window_size
|
|
247
264
|
|
|
248
265
|
@staticmethod
|
|
249
|
-
def from_bytes(data):
|
|
266
|
+
def from_bytes(data: bytes):
|
|
250
267
|
return RFCOMM_MCC_PN(
|
|
251
268
|
dlci=data[0],
|
|
252
269
|
cl=data[1],
|
|
@@ -285,7 +302,14 @@ class RFCOMM_MCC_PN:
|
|
|
285
302
|
|
|
286
303
|
# -----------------------------------------------------------------------------
|
|
287
304
|
class RFCOMM_MCC_MSC:
|
|
288
|
-
|
|
305
|
+
dlci: int
|
|
306
|
+
fc: int
|
|
307
|
+
rtc: int
|
|
308
|
+
rtr: int
|
|
309
|
+
ic: int
|
|
310
|
+
dv: int
|
|
311
|
+
|
|
312
|
+
def __init__(self, dlci: int, fc: int, rtc: int, rtr: int, ic: int, dv: int):
|
|
289
313
|
self.dlci = dlci
|
|
290
314
|
self.fc = fc
|
|
291
315
|
self.rtc = rtc
|
|
@@ -294,7 +318,7 @@ class RFCOMM_MCC_MSC:
|
|
|
294
318
|
self.dv = dv
|
|
295
319
|
|
|
296
320
|
@staticmethod
|
|
297
|
-
def from_bytes(data):
|
|
321
|
+
def from_bytes(data: bytes):
|
|
298
322
|
return RFCOMM_MCC_MSC(
|
|
299
323
|
dlci=data[0] >> 2,
|
|
300
324
|
fc=data[1] >> 1 & 1,
|
|
@@ -347,7 +371,12 @@ class DLC(EventEmitter):
|
|
|
347
371
|
RESET: 'RESET',
|
|
348
372
|
}
|
|
349
373
|
|
|
350
|
-
|
|
374
|
+
connection_result: Optional[asyncio.Future]
|
|
375
|
+
sink: Optional[Callable[[bytes], None]]
|
|
376
|
+
|
|
377
|
+
def __init__(
|
|
378
|
+
self, multiplexer, dlci: int, max_frame_size: int, initial_tx_credits: int
|
|
379
|
+
):
|
|
351
380
|
super().__init__()
|
|
352
381
|
self.multiplexer = multiplexer
|
|
353
382
|
self.dlci = dlci
|
|
@@ -368,23 +397,23 @@ class DLC(EventEmitter):
|
|
|
368
397
|
)
|
|
369
398
|
|
|
370
399
|
@staticmethod
|
|
371
|
-
def state_name(state):
|
|
400
|
+
def state_name(state: int) -> str:
|
|
372
401
|
return DLC.STATE_NAMES[state]
|
|
373
402
|
|
|
374
|
-
def change_state(self, new_state):
|
|
403
|
+
def change_state(self, new_state: int) -> None:
|
|
375
404
|
logger.debug(
|
|
376
405
|
f'{self} state change -> {color(self.state_name(new_state), "magenta")}'
|
|
377
406
|
)
|
|
378
407
|
self.state = new_state
|
|
379
408
|
|
|
380
|
-
def send_frame(self, frame):
|
|
409
|
+
def send_frame(self, frame: RFCOMM_Frame) -> None:
|
|
381
410
|
self.multiplexer.send_frame(frame)
|
|
382
411
|
|
|
383
|
-
def on_frame(self, frame):
|
|
412
|
+
def on_frame(self, frame: RFCOMM_Frame) -> None:
|
|
384
413
|
handler = getattr(self, f'on_{frame.type_name()}_frame'.lower())
|
|
385
414
|
handler(frame)
|
|
386
415
|
|
|
387
|
-
def on_sabm_frame(self, _frame):
|
|
416
|
+
def on_sabm_frame(self, _frame) -> None:
|
|
388
417
|
if self.state != DLC.CONNECTING:
|
|
389
418
|
logger.warning(
|
|
390
419
|
color('!!! received SABM when not in CONNECTING state', 'red')
|
|
@@ -404,7 +433,7 @@ class DLC(EventEmitter):
|
|
|
404
433
|
self.change_state(DLC.CONNECTED)
|
|
405
434
|
self.emit('open')
|
|
406
435
|
|
|
407
|
-
def on_ua_frame(self, _frame):
|
|
436
|
+
def on_ua_frame(self, _frame) -> None:
|
|
408
437
|
if self.state != DLC.CONNECTING:
|
|
409
438
|
logger.warning(
|
|
410
439
|
color('!!! received SABM when not in CONNECTING state', 'red')
|
|
@@ -422,15 +451,15 @@ class DLC(EventEmitter):
|
|
|
422
451
|
self.change_state(DLC.CONNECTED)
|
|
423
452
|
self.multiplexer.on_dlc_open_complete(self)
|
|
424
453
|
|
|
425
|
-
def on_dm_frame(self, frame):
|
|
454
|
+
def on_dm_frame(self, frame) -> None:
|
|
426
455
|
# TODO: handle all states
|
|
427
456
|
pass
|
|
428
457
|
|
|
429
|
-
def on_disc_frame(self, _frame):
|
|
458
|
+
def on_disc_frame(self, _frame) -> None:
|
|
430
459
|
# TODO: handle all states
|
|
431
460
|
self.send_frame(RFCOMM_Frame.ua(c_r=1 - self.c_r, dlci=self.dlci))
|
|
432
461
|
|
|
433
|
-
def on_uih_frame(self, frame):
|
|
462
|
+
def on_uih_frame(self, frame: RFCOMM_Frame) -> None:
|
|
434
463
|
data = frame.information
|
|
435
464
|
if frame.p_f == 1:
|
|
436
465
|
# With credits
|
|
@@ -460,10 +489,10 @@ class DLC(EventEmitter):
|
|
|
460
489
|
# Check if there's anything to send (including credits)
|
|
461
490
|
self.process_tx()
|
|
462
491
|
|
|
463
|
-
def on_ui_frame(self, frame):
|
|
492
|
+
def on_ui_frame(self, frame) -> None:
|
|
464
493
|
pass
|
|
465
494
|
|
|
466
|
-
def on_mcc_msc(self, c_r, msc):
|
|
495
|
+
def on_mcc_msc(self, c_r, msc) -> None:
|
|
467
496
|
if c_r:
|
|
468
497
|
# Command
|
|
469
498
|
logger.debug(f'<<< MCC MSC Command: {msc}')
|
|
@@ -477,7 +506,7 @@ class DLC(EventEmitter):
|
|
|
477
506
|
# Response
|
|
478
507
|
logger.debug(f'<<< MCC MSC Response: {msc}')
|
|
479
508
|
|
|
480
|
-
def connect(self):
|
|
509
|
+
def connect(self) -> None:
|
|
481
510
|
if self.state != DLC.INIT:
|
|
482
511
|
raise InvalidStateError('invalid state')
|
|
483
512
|
|
|
@@ -485,7 +514,7 @@ class DLC(EventEmitter):
|
|
|
485
514
|
self.connection_result = asyncio.get_running_loop().create_future()
|
|
486
515
|
self.send_frame(RFCOMM_Frame.sabm(c_r=self.c_r, dlci=self.dlci))
|
|
487
516
|
|
|
488
|
-
def accept(self):
|
|
517
|
+
def accept(self) -> None:
|
|
489
518
|
if self.state != DLC.INIT:
|
|
490
519
|
raise InvalidStateError('invalid state')
|
|
491
520
|
|
|
@@ -503,13 +532,13 @@ class DLC(EventEmitter):
|
|
|
503
532
|
self.send_frame(RFCOMM_Frame.uih(c_r=self.c_r, dlci=0, information=mcc))
|
|
504
533
|
self.change_state(DLC.CONNECTING)
|
|
505
534
|
|
|
506
|
-
def rx_credits_needed(self):
|
|
535
|
+
def rx_credits_needed(self) -> int:
|
|
507
536
|
if self.rx_credits <= self.rx_threshold:
|
|
508
537
|
return RFCOMM_DEFAULT_INITIAL_RX_CREDITS - self.rx_credits
|
|
509
538
|
|
|
510
539
|
return 0
|
|
511
540
|
|
|
512
|
-
def process_tx(self):
|
|
541
|
+
def process_tx(self) -> None:
|
|
513
542
|
# Send anything we can (or an empty frame if we need to send rx credits)
|
|
514
543
|
rx_credits_needed = self.rx_credits_needed()
|
|
515
544
|
while (self.tx_buffer and self.tx_credits > 0) or rx_credits_needed > 0:
|
|
@@ -547,7 +576,7 @@ class DLC(EventEmitter):
|
|
|
547
576
|
rx_credits_needed = 0
|
|
548
577
|
|
|
549
578
|
# Stream protocol
|
|
550
|
-
def write(self, data):
|
|
579
|
+
def write(self, data: Union[bytes, str]) -> None:
|
|
551
580
|
# We can only send bytes
|
|
552
581
|
if not isinstance(data, bytes):
|
|
553
582
|
if isinstance(data, str):
|
|
@@ -559,7 +588,7 @@ class DLC(EventEmitter):
|
|
|
559
588
|
self.tx_buffer += data
|
|
560
589
|
self.process_tx()
|
|
561
590
|
|
|
562
|
-
def drain(self):
|
|
591
|
+
def drain(self) -> None:
|
|
563
592
|
# TODO
|
|
564
593
|
pass
|
|
565
594
|
|
|
@@ -592,7 +621,13 @@ class Multiplexer(EventEmitter):
|
|
|
592
621
|
RESET: 'RESET',
|
|
593
622
|
}
|
|
594
623
|
|
|
595
|
-
|
|
624
|
+
connection_result: Optional[asyncio.Future]
|
|
625
|
+
disconnection_result: Optional[asyncio.Future]
|
|
626
|
+
open_result: Optional[asyncio.Future]
|
|
627
|
+
acceptor: Optional[Callable[[int], bool]]
|
|
628
|
+
dlcs: Dict[int, DLC]
|
|
629
|
+
|
|
630
|
+
def __init__(self, l2cap_channel: l2cap.Channel, role: int) -> None:
|
|
596
631
|
super().__init__()
|
|
597
632
|
self.role = role
|
|
598
633
|
self.l2cap_channel = l2cap_channel
|
|
@@ -607,20 +642,20 @@ class Multiplexer(EventEmitter):
|
|
|
607
642
|
l2cap_channel.sink = self.on_pdu
|
|
608
643
|
|
|
609
644
|
@staticmethod
|
|
610
|
-
def state_name(state):
|
|
645
|
+
def state_name(state: int):
|
|
611
646
|
return Multiplexer.STATE_NAMES[state]
|
|
612
647
|
|
|
613
|
-
def change_state(self, new_state):
|
|
648
|
+
def change_state(self, new_state: int) -> None:
|
|
614
649
|
logger.debug(
|
|
615
650
|
f'{self} state change -> {color(self.state_name(new_state), "cyan")}'
|
|
616
651
|
)
|
|
617
652
|
self.state = new_state
|
|
618
653
|
|
|
619
|
-
def send_frame(self, frame):
|
|
654
|
+
def send_frame(self, frame: RFCOMM_Frame) -> None:
|
|
620
655
|
logger.debug(f'>>> Multiplexer sending {frame}')
|
|
621
656
|
self.l2cap_channel.send_pdu(frame)
|
|
622
657
|
|
|
623
|
-
def on_pdu(self, pdu):
|
|
658
|
+
def on_pdu(self, pdu: bytes) -> None:
|
|
624
659
|
frame = RFCOMM_Frame.from_bytes(pdu)
|
|
625
660
|
logger.debug(f'<<< Multiplexer received {frame}')
|
|
626
661
|
|
|
@@ -640,18 +675,18 @@ class Multiplexer(EventEmitter):
|
|
|
640
675
|
return
|
|
641
676
|
dlc.on_frame(frame)
|
|
642
677
|
|
|
643
|
-
def on_frame(self, frame):
|
|
678
|
+
def on_frame(self, frame: RFCOMM_Frame) -> None:
|
|
644
679
|
handler = getattr(self, f'on_{frame.type_name()}_frame'.lower())
|
|
645
680
|
handler(frame)
|
|
646
681
|
|
|
647
|
-
def on_sabm_frame(self, _frame):
|
|
682
|
+
def on_sabm_frame(self, _frame) -> None:
|
|
648
683
|
if self.state != Multiplexer.INIT:
|
|
649
684
|
logger.debug('not in INIT state, ignoring SABM')
|
|
650
685
|
return
|
|
651
686
|
self.change_state(Multiplexer.CONNECTED)
|
|
652
687
|
self.send_frame(RFCOMM_Frame.ua(c_r=1, dlci=0))
|
|
653
688
|
|
|
654
|
-
def on_ua_frame(self, _frame):
|
|
689
|
+
def on_ua_frame(self, _frame) -> None:
|
|
655
690
|
if self.state == Multiplexer.CONNECTING:
|
|
656
691
|
self.change_state(Multiplexer.CONNECTED)
|
|
657
692
|
if self.connection_result:
|
|
@@ -663,7 +698,7 @@ class Multiplexer(EventEmitter):
|
|
|
663
698
|
self.disconnection_result.set_result(None)
|
|
664
699
|
self.disconnection_result = None
|
|
665
700
|
|
|
666
|
-
def on_dm_frame(self, _frame):
|
|
701
|
+
def on_dm_frame(self, _frame) -> None:
|
|
667
702
|
if self.state == Multiplexer.OPENING:
|
|
668
703
|
self.change_state(Multiplexer.CONNECTED)
|
|
669
704
|
if self.open_result:
|
|
@@ -678,13 +713,13 @@ class Multiplexer(EventEmitter):
|
|
|
678
713
|
else:
|
|
679
714
|
logger.warning(f'unexpected state for DM: {self}')
|
|
680
715
|
|
|
681
|
-
def on_disc_frame(self, _frame):
|
|
716
|
+
def on_disc_frame(self, _frame) -> None:
|
|
682
717
|
self.change_state(Multiplexer.DISCONNECTED)
|
|
683
718
|
self.send_frame(
|
|
684
719
|
RFCOMM_Frame.ua(c_r=0 if self.role == Multiplexer.INITIATOR else 1, dlci=0)
|
|
685
720
|
)
|
|
686
721
|
|
|
687
|
-
def on_uih_frame(self, frame):
|
|
722
|
+
def on_uih_frame(self, frame: RFCOMM_Frame) -> None:
|
|
688
723
|
(mcc_type, c_r, value) = RFCOMM_Frame.parse_mcc(frame.information)
|
|
689
724
|
|
|
690
725
|
if mcc_type == RFCOMM_MCC_PN_TYPE:
|
|
@@ -694,10 +729,10 @@ class Multiplexer(EventEmitter):
|
|
|
694
729
|
mcs = RFCOMM_MCC_MSC.from_bytes(value)
|
|
695
730
|
self.on_mcc_msc(c_r, mcs)
|
|
696
731
|
|
|
697
|
-
def on_ui_frame(self, frame):
|
|
732
|
+
def on_ui_frame(self, frame) -> None:
|
|
698
733
|
pass
|
|
699
734
|
|
|
700
|
-
def on_mcc_pn(self, c_r, pn):
|
|
735
|
+
def on_mcc_pn(self, c_r, pn) -> None:
|
|
701
736
|
if c_r == 1:
|
|
702
737
|
# Command
|
|
703
738
|
logger.debug(f'<<< PN Command: {pn}')
|
|
@@ -736,14 +771,14 @@ class Multiplexer(EventEmitter):
|
|
|
736
771
|
else:
|
|
737
772
|
logger.warning('ignoring PN response')
|
|
738
773
|
|
|
739
|
-
def on_mcc_msc(self, c_r, msc):
|
|
774
|
+
def on_mcc_msc(self, c_r, msc) -> None:
|
|
740
775
|
dlc = self.dlcs.get(msc.dlci)
|
|
741
776
|
if dlc is None:
|
|
742
777
|
logger.warning(f'no dlc for DLCI {msc.dlci}')
|
|
743
778
|
return
|
|
744
779
|
dlc.on_mcc_msc(c_r, msc)
|
|
745
780
|
|
|
746
|
-
async def connect(self):
|
|
781
|
+
async def connect(self) -> None:
|
|
747
782
|
if self.state != Multiplexer.INIT:
|
|
748
783
|
raise InvalidStateError('invalid state')
|
|
749
784
|
|
|
@@ -752,7 +787,7 @@ class Multiplexer(EventEmitter):
|
|
|
752
787
|
self.send_frame(RFCOMM_Frame.sabm(c_r=1, dlci=0))
|
|
753
788
|
return await self.connection_result
|
|
754
789
|
|
|
755
|
-
async def disconnect(self):
|
|
790
|
+
async def disconnect(self) -> None:
|
|
756
791
|
if self.state != Multiplexer.CONNECTED:
|
|
757
792
|
return
|
|
758
793
|
|
|
@@ -765,7 +800,7 @@ class Multiplexer(EventEmitter):
|
|
|
765
800
|
)
|
|
766
801
|
await self.disconnection_result
|
|
767
802
|
|
|
768
|
-
async def open_dlc(self, channel):
|
|
803
|
+
async def open_dlc(self, channel: int) -> DLC:
|
|
769
804
|
if self.state != Multiplexer.CONNECTED:
|
|
770
805
|
if self.state == Multiplexer.OPENING:
|
|
771
806
|
raise InvalidStateError('open already in progress')
|
|
@@ -796,7 +831,7 @@ class Multiplexer(EventEmitter):
|
|
|
796
831
|
self.open_result = None
|
|
797
832
|
return result
|
|
798
833
|
|
|
799
|
-
def on_dlc_open_complete(self, dlc):
|
|
834
|
+
def on_dlc_open_complete(self, dlc: DLC):
|
|
800
835
|
logger.debug(f'DLC [{dlc.dlci}] open complete')
|
|
801
836
|
self.change_state(Multiplexer.CONNECTED)
|
|
802
837
|
if self.open_result:
|
|
@@ -808,13 +843,16 @@ class Multiplexer(EventEmitter):
|
|
|
808
843
|
|
|
809
844
|
# -----------------------------------------------------------------------------
|
|
810
845
|
class Client:
|
|
811
|
-
|
|
846
|
+
multiplexer: Optional[Multiplexer]
|
|
847
|
+
l2cap_channel: Optional[l2cap.Channel]
|
|
848
|
+
|
|
849
|
+
def __init__(self, device, connection) -> None:
|
|
812
850
|
self.device = device
|
|
813
851
|
self.connection = connection
|
|
814
852
|
self.l2cap_channel = None
|
|
815
853
|
self.multiplexer = None
|
|
816
854
|
|
|
817
|
-
async def start(self):
|
|
855
|
+
async def start(self) -> Multiplexer:
|
|
818
856
|
# Create a new L2CAP connection
|
|
819
857
|
try:
|
|
820
858
|
self.l2cap_channel = await self.device.l2cap_channel_manager.connect(
|
|
@@ -824,6 +862,7 @@ class Client:
|
|
|
824
862
|
logger.warning(f'L2CAP connection failed: {error}')
|
|
825
863
|
raise
|
|
826
864
|
|
|
865
|
+
assert self.l2cap_channel is not None
|
|
827
866
|
# Create a mutliplexer to manage DLCs with the server
|
|
828
867
|
self.multiplexer = Multiplexer(self.l2cap_channel, Multiplexer.INITIATOR)
|
|
829
868
|
|
|
@@ -832,7 +871,9 @@ class Client:
|
|
|
832
871
|
|
|
833
872
|
return self.multiplexer
|
|
834
873
|
|
|
835
|
-
async def shutdown(self):
|
|
874
|
+
async def shutdown(self) -> None:
|
|
875
|
+
if self.multiplexer is None:
|
|
876
|
+
return
|
|
836
877
|
# Disconnect the multiplexer
|
|
837
878
|
await self.multiplexer.disconnect()
|
|
838
879
|
self.multiplexer = None
|
|
@@ -843,7 +884,9 @@ class Client:
|
|
|
843
884
|
|
|
844
885
|
# -----------------------------------------------------------------------------
|
|
845
886
|
class Server(EventEmitter):
|
|
846
|
-
|
|
887
|
+
acceptors: Dict[int, Callable[[DLC], None]]
|
|
888
|
+
|
|
889
|
+
def __init__(self, device) -> None:
|
|
847
890
|
super().__init__()
|
|
848
891
|
self.device = device
|
|
849
892
|
self.multiplexer = None
|
|
@@ -852,7 +895,7 @@ class Server(EventEmitter):
|
|
|
852
895
|
# Register ourselves with the L2CAP channel manager
|
|
853
896
|
device.register_l2cap_server(RFCOMM_PSM, self.on_connection)
|
|
854
897
|
|
|
855
|
-
def listen(self, acceptor, channel=0):
|
|
898
|
+
def listen(self, acceptor: Callable[[DLC], None], channel: int = 0) -> int:
|
|
856
899
|
if channel:
|
|
857
900
|
if channel in self.acceptors:
|
|
858
901
|
# Busy
|
|
@@ -874,11 +917,11 @@ class Server(EventEmitter):
|
|
|
874
917
|
self.acceptors[channel] = acceptor
|
|
875
918
|
return channel
|
|
876
919
|
|
|
877
|
-
def on_connection(self, l2cap_channel):
|
|
920
|
+
def on_connection(self, l2cap_channel: l2cap.Channel) -> None:
|
|
878
921
|
logger.debug(f'+++ new L2CAP connection: {l2cap_channel}')
|
|
879
922
|
l2cap_channel.on('open', lambda: self.on_l2cap_channel_open(l2cap_channel))
|
|
880
923
|
|
|
881
|
-
def on_l2cap_channel_open(self, l2cap_channel):
|
|
924
|
+
def on_l2cap_channel_open(self, l2cap_channel: l2cap.Channel) -> None:
|
|
882
925
|
logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}')
|
|
883
926
|
|
|
884
927
|
# Create a new multiplexer for the channel
|
|
@@ -889,10 +932,10 @@ class Server(EventEmitter):
|
|
|
889
932
|
# Notify
|
|
890
933
|
self.emit('start', multiplexer)
|
|
891
934
|
|
|
892
|
-
def accept_dlc(self, channel_number):
|
|
935
|
+
def accept_dlc(self, channel_number: int) -> bool:
|
|
893
936
|
return channel_number in self.acceptors
|
|
894
937
|
|
|
895
|
-
def on_dlc(self, dlc):
|
|
938
|
+
def on_dlc(self, dlc: DLC) -> None:
|
|
896
939
|
logger.debug(f'@@@ new DLC connected: {dlc}')
|
|
897
940
|
|
|
898
941
|
# Let the acceptor know
|
bumble/smp.py
CHANGED
|
@@ -1805,7 +1805,7 @@ class Manager(EventEmitter):
|
|
|
1805
1805
|
self.device.abort_on('flush', store_keys())
|
|
1806
1806
|
|
|
1807
1807
|
# Notify the device
|
|
1808
|
-
self.device.on_pairing(session.connection, keys, session.sc)
|
|
1808
|
+
self.device.on_pairing(session.connection, identity_address, keys, session.sc)
|
|
1809
1809
|
|
|
1810
1810
|
def on_pairing_failure(self, session: Session, reason: int) -> None:
|
|
1811
1811
|
self.device.on_pairing_failure(session.connection, reason)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bumble
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.156
|
|
4
4
|
Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
|
|
5
5
|
Home-page: https://github.com/google/bumble
|
|
6
6
|
Author: Google
|
|
@@ -9,11 +9,12 @@ Requires-Python: >=3.8
|
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Dist: appdirs (>=1.4)
|
|
12
|
+
Requires-Dist: bt-test-interfaces (>=0.0.2)
|
|
13
|
+
Requires-Dist: humanize (>=4.6.0)
|
|
14
|
+
Requires-Dist: prettytable (>=3.6.0)
|
|
12
15
|
Requires-Dist: protobuf (>=3.12.4)
|
|
13
16
|
Requires-Dist: pyee (>=8.2.2)
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist: humanize (>=4.6.0)
|
|
16
|
-
Requires-Dist: bt-test-interfaces (>=0.0.2)
|
|
17
|
+
Requires-Dist: aiohttp (>=3.8.4) ; platform_system != "Emscripten"
|
|
17
18
|
Requires-Dist: click (>=7.1.2) ; platform_system != "Emscripten"
|
|
18
19
|
Requires-Dist: cryptography (==35) ; platform_system != "Emscripten"
|
|
19
20
|
Requires-Dist: grpcio (==1.51.1) ; platform_system != "Emscripten"
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
|
|
2
|
-
bumble/_version.py,sha256=
|
|
3
|
-
bumble/a2dp.py,sha256=
|
|
2
|
+
bumble/_version.py,sha256=uN9kco0Y-N-7Tp48dZrOIxHFhGYovN79kmVBnZPMLR0,164
|
|
3
|
+
bumble/a2dp.py,sha256=vwnKknvSKPR7IdTz8JHHNvHGObjepVy1kFRWm0kNWEI,21923
|
|
4
4
|
bumble/att.py,sha256=qXwC5gtC-4Bwjqul0sCm_bWA_BbK4NVf5xNlOCRE7LI,30231
|
|
5
|
-
bumble/avdtp.py,sha256=
|
|
5
|
+
bumble/avdtp.py,sha256=sT0mFeFvp9jTEP-dOp4_ibdtKX3mtw1Gyi-72tHfkiE,72268
|
|
6
6
|
bumble/bridge.py,sha256=T6es5oS1dy8QgkxQ8iOD-YcZ0SWOv8jaqC7TGxqodk4,3003
|
|
7
|
+
bumble/codecs.py,sha256=Vc7FOo6d-6VCgDI0ibnLmX8vCZ4-jtX_-0vEUM-yOrI,15343
|
|
7
8
|
bumble/colors.py,sha256=9H-qzGgMr-YoWdIFpcGPaiRvTvkCzz7FPIkpwqKyKug,3033
|
|
8
9
|
bumble/company_ids.py,sha256=mHmG9mk_0lkjiRm-IMggDnuXzY2QlZzH9jugy_ijhDc,95931
|
|
9
10
|
bumble/controller.py,sha256=YwOVfcsPD8BvzXqfswY-wUECym2BX7_8zEBjIjUnhWw,44266
|
|
10
11
|
bumble/core.py,sha256=_BnrlhR4OcS0RJE3510CshdD5iQ1zQeoMrdCD0oDebQ,48167
|
|
11
12
|
bumble/crypto.py,sha256=JX3wg6kd-aFMxkxJz2FYFvyXHOukD-z0QcGH9jpEYvk,8679
|
|
12
13
|
bumble/decoder.py,sha256=N9nMvuVhuwpnfw7EDVuNe9uYY6B6c3RY2dh8RhRPC1U,9608
|
|
13
|
-
bumble/device.py,sha256=
|
|
14
|
+
bumble/device.py,sha256=VmLZ9V_XQWjYQBO9KMURMye8UcYJLUgUkv0HJO-K5ag,122190
|
|
14
15
|
bumble/gap.py,sha256=axlOZIv99357Ehq2vMokeioU85z81qdQvplGn0pF70Q,2137
|
|
15
16
|
bumble/gatt.py,sha256=bq6-oR0qx2Vp05IxnAiDZEGv3WsWCiNtdjYHpi0A6Fg,28150
|
|
16
17
|
bumble/gatt_client.py,sha256=0NrwW7Y3oy1HUWisIyBLwnBpYNnuI9jtbcNkS6CD__U,40297
|
|
17
18
|
bumble/gatt_server.py,sha256=vi_-sofp5NjPWDYX5K_hldsAaoFuNlt8GFA-eVzFkb8,35277
|
|
18
|
-
bumble/hci.py,sha256=
|
|
19
|
+
bumble/hci.py,sha256=ky1_4ettxVk-ALKxBxEoUdcRroKLibznh4Fdb-HRLqk,209492
|
|
19
20
|
bumble/helpers.py,sha256=AgtOigAqQzzTEMMwHQJ2EO7dOTp77qhnyfHQ2LCDd-k,8896
|
|
20
|
-
bumble/hfp.py,sha256=
|
|
21
|
-
bumble/host.py,sha256=
|
|
22
|
-
bumble/keys.py,sha256=
|
|
21
|
+
bumble/hfp.py,sha256=BQEuh7A9WkP2V-YuuveuI5W-k1-O1Ovq2DvRao_2psk,3434
|
|
22
|
+
bumble/host.py,sha256=mNdGRnHNqk6eMpSdE49UT2uLk3Wi9TNhchsXfpDtA_o,33877
|
|
23
|
+
bumble/keys.py,sha256=_ibaJ4CxM5zyxnTp7wnihIQSsEAEkCiPxx9qHsGA04Q,12601
|
|
23
24
|
bumble/l2cap.py,sha256=Md_1af1jEP0dXlXpVkaXINnnmElq423o5m9OJFBaiYk,72106
|
|
24
25
|
bumble/link.py,sha256=dvb3fdmV8689A4rLhFVHHKMR3u4wQbUapFi-GeheK0M,20220
|
|
25
26
|
bumble/pairing.py,sha256=smMCrWdgtdLXKJ_Ix9e5x1SuvSs_49mKtwzJS94ygd4,7002
|
|
26
27
|
bumble/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
-
bumble/rfcomm.py,sha256=
|
|
28
|
+
bumble/rfcomm.py,sha256=LgcHvRelcToJy3PiPedqkrj4CRZjCg2zFVba-hTkZCY,31632
|
|
28
29
|
bumble/sdp.py,sha256=h0DyXOYpQ5OLzwVZsE4b5YZq_jxxu7m_QSbFkaPy9mQ,42464
|
|
29
|
-
bumble/smp.py,sha256
|
|
30
|
+
bumble/smp.py,sha256=NxbAlbijmTy3V1Yc8bqn0MlK3qnyNqFomgO0R_Wedsc,68767
|
|
30
31
|
bumble/snoop.py,sha256=_QfF36eylBW6Snd-_KYOwKaGiM8i_Ed-B5XoFIPt3Dg,5631
|
|
31
32
|
bumble/utils.py,sha256=x7AKW6QD35Dl1OkROLJ6TX4dNN6ajwFjW8w_BZGGcA4,10195
|
|
32
33
|
bumble/apps/README.md,sha256=XTwjRAY-EJWDXpl1V8K3Mw8B7kIqzUIUizRjVBVhoIE,1769
|
|
@@ -39,21 +40,27 @@ bumble/apps/gatt_dump.py,sha256=-dCvCgjuHAp0h1zxm-gmqB4lVlSdas1Kp4cpzzx4gGw,4245
|
|
|
39
40
|
bumble/apps/gg_bridge.py,sha256=G32XdCqYzt8Kqv9jZZDY29-ENjOxDMX_7CPolA8Jm1U,14555
|
|
40
41
|
bumble/apps/hci_bridge.py,sha256=KISv352tKnsQsoxjkDiCQbMFmhnPWdnug5wSFAAXxEs,4033
|
|
41
42
|
bumble/apps/l2cap_bridge.py,sha256=9914J7UomNtdWyE3VPgvzfJvZ304OM9AW9jzO_zRwNY,12604
|
|
42
|
-
bumble/apps/pair.py,sha256=
|
|
43
|
+
bumble/apps/pair.py,sha256=Tl-QeJwahK17_iQHHy7dZW9gk0f5ETQhJ21Cxoj4k40,14860
|
|
43
44
|
bumble/apps/pandora_server.py,sha256=VK6FDGluUkiQKiHPxCI5sd6jkCFJuSXDCdt8bAHykpo,900
|
|
44
|
-
bumble/apps/scan.py,sha256=
|
|
45
|
+
bumble/apps/scan.py,sha256=_fMG_1j1HZQ_8SrJ0ZOxJaWB1OR4mKBaZuQMgm8viYI,7439
|
|
45
46
|
bumble/apps/show.py,sha256=qvCY0RcACws8iU5EKPQ3GcURJkykvVvBi8EMAu5GOus,4385
|
|
46
|
-
bumble/apps/unbond.py,sha256
|
|
47
|
+
bumble/apps/unbond.py,sha256=P_zqdK_WsQHReqnhcaeeG3Kg6LhGBzCrRAyhcewQ7lU,3175
|
|
47
48
|
bumble/apps/usb_probe.py,sha256=zJqrqKSGVYcOntXzgONdluZDE6jfj3IwPNuLqmDPDsU,10351
|
|
48
49
|
bumble/apps/link_relay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
50
|
bumble/apps/link_relay/link_relay.py,sha256=GOESYPzkMJFrz5VOI_BSmnmgz4Y8EdSLHMWgdA63aDg,10066
|
|
50
51
|
bumble/apps/link_relay/logging.yml,sha256=t-P72RAHsTZOESw0M4I3CQPonymcjkd9SLE0eY_ZNiM,311
|
|
52
|
+
bumble/apps/speaker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
+
bumble/apps/speaker/logo.svg,sha256=SQ9XXIqhh07BnGaBZ653nJ7vOslm2Dogdqadhg2MdE4,4205
|
|
54
|
+
bumble/apps/speaker/speaker.css,sha256=KTyvN0r0Q0aXWG7SWSE904lODfPQJTxRsvbuRJg5OBg,1236
|
|
55
|
+
bumble/apps/speaker/speaker.html,sha256=CAYYRK7S-uAN5BYV4B5TbPkT5tisqNl3v_xCqtBwYgw,1211
|
|
56
|
+
bumble/apps/speaker/speaker.js,sha256=DrT831yg3oBXKZ5usnfZjRU9X6Nw3zjIWSkz6sIgVtw,9373
|
|
57
|
+
bumble/apps/speaker/speaker.py,sha256=FOVTbJePBSfd1lZaW7wDR7ExcJiUOjmrpYsCz4Lg-oM,24516
|
|
51
58
|
bumble/pandora/__init__.py,sha256=5NBVmndeTulANawift0jPT9ISp562wyIHTZ-4uP34Mg,3283
|
|
52
59
|
bumble/pandora/config.py,sha256=fxKSACOSpsu8kfiVh3b1M1u_JHEBAIlhO88V9-s4kOk,2010
|
|
53
60
|
bumble/pandora/device.py,sha256=xT_3f3IAeg8k_AWDyN0MvnRxZQ4Qf7AnDWD8LT0x_pI,5154
|
|
54
|
-
bumble/pandora/host.py,sha256=
|
|
61
|
+
bumble/pandora/host.py,sha256=6TGEGyDwd2Wlq9BSjiOdAQnOvXqiImlltIt2olpMaTA,33393
|
|
55
62
|
bumble/pandora/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
-
bumble/pandora/security.py,sha256=
|
|
63
|
+
bumble/pandora/security.py,sha256=dEyDC7YSgavQX-Oq1SOcEQNJd46DsOYt8pjyaMRXbgM,20545
|
|
57
64
|
bumble/pandora/utils.py,sha256=2s2oZXbsVz16sYVyfiWE8ehOMHCIU5gIUAChSTULrW8,3914
|
|
58
65
|
bumble/profiles/__init__.py,sha256=yBGC8Ti5LvZuoh1F42XtfrBilb39T77_yuxESZeX2yI,581
|
|
59
66
|
bumble/profiles/asha_service.py,sha256=C7-xwG1DgdPbt6IKLglIP1VYAg1X5s61YPAiOzHHgVw,7089
|
|
@@ -106,9 +113,9 @@ bumble/transport/grpc_protobuf/packet_streamer_pb2_grpc.py,sha256=k4RDFvE5Y4QgGL
|
|
|
106
113
|
bumble/transport/grpc_protobuf/startup_pb2.py,sha256=viPPlvFuRE1HoM3zg3Ayy5wfNnuSSAX80MNqtKVqv-o,1854
|
|
107
114
|
bumble/transport/grpc_protobuf/startup_pb2.pyi,sha256=8bDHVKJ_PIZuPvXRVK7tJCPOY8E8ZKW-5xzqbRLEJcw,2140
|
|
108
115
|
bumble/transport/grpc_protobuf/startup_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
109
|
-
bumble-0.0.
|
|
110
|
-
bumble-0.0.
|
|
111
|
-
bumble-0.0.
|
|
112
|
-
bumble-0.0.
|
|
113
|
-
bumble-0.0.
|
|
114
|
-
bumble-0.0.
|
|
116
|
+
bumble-0.0.156.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
|
|
117
|
+
bumble-0.0.156.dist-info/METADATA,sha256=DKqhKEprZCG3ypJRbuJbWkE3NGC7oPIf9bSzxmLCS-s,5223
|
|
118
|
+
bumble-0.0.156.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
119
|
+
bumble-0.0.156.dist-info/entry_points.txt,sha256=E4FgYbSefpIthFFC6S9l9PVJECQ-GvZBsEfcMTovM74,661
|
|
120
|
+
bumble-0.0.156.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
|
|
121
|
+
bumble-0.0.156.dist-info/RECORD,,
|
|
@@ -10,5 +10,6 @@ bumble-pair = bumble.apps.pair:main
|
|
|
10
10
|
bumble-pandora-server = bumble.apps.pandora_server:main
|
|
11
11
|
bumble-scan = bumble.apps.scan:main
|
|
12
12
|
bumble-show = bumble.apps.show:main
|
|
13
|
+
bumble-speaker = bumble.apps.speaker.speaker:main
|
|
13
14
|
bumble-unbond = bumble.apps.unbond:main
|
|
14
15
|
bumble-usb-probe = bumble.apps.usb_probe:main
|
|
File without changes
|
|
File without changes
|
|
File without changes
|