bumble 0.0.201__py3-none-any.whl → 0.0.202__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 +138 -93
- bumble/apps/rfcomm_bridge.py +10 -1
- bumble/avc.py +2 -0
- bumble/gatt_client.py +6 -0
- bumble/l2cap.py +1 -0
- bumble/link.py +2 -0
- bumble/profiles/bap.py +3 -0
- bumble/sdp.py +2 -0
- bumble/smp.py +1 -1
- bumble/transport/common.py +4 -2
- {bumble-0.0.201.dist-info → bumble-0.0.202.dist-info}/METADATA +4 -3
- {bumble-0.0.201.dist-info → bumble-0.0.202.dist-info}/RECORD +17 -17
- {bumble-0.0.201.dist-info → bumble-0.0.202.dist-info}/WHEEL +1 -1
- {bumble-0.0.201.dist-info → bumble-0.0.202.dist-info}/LICENSE +0 -0
- {bumble-0.0.201.dist-info → bumble-0.0.202.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.201.dist-info → bumble-0.0.202.dist-info}/top_level.txt +0 -0
bumble/_version.py
CHANGED
bumble/apps/bench.py
CHANGED
|
@@ -19,6 +19,7 @@ import asyncio
|
|
|
19
19
|
import enum
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
|
+
import statistics
|
|
22
23
|
import struct
|
|
23
24
|
import time
|
|
24
25
|
|
|
@@ -194,17 +195,19 @@ def make_sdp_records(channel):
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
|
|
197
|
-
def log_stats(title, stats):
|
|
198
|
+
def log_stats(title, stats, precision=2):
|
|
198
199
|
stats_min = min(stats)
|
|
199
200
|
stats_max = max(stats)
|
|
200
|
-
stats_avg =
|
|
201
|
+
stats_avg = statistics.mean(stats)
|
|
202
|
+
stats_stdev = statistics.stdev(stats)
|
|
201
203
|
logging.info(
|
|
202
204
|
color(
|
|
203
205
|
(
|
|
204
206
|
f'### {title} stats: '
|
|
205
|
-
f'min={stats_min:.
|
|
206
|
-
f'max={stats_max:.
|
|
207
|
-
f'average={stats_avg:.
|
|
207
|
+
f'min={stats_min:.{precision}f}, '
|
|
208
|
+
f'max={stats_max:.{precision}f}, '
|
|
209
|
+
f'average={stats_avg:.{precision}f}, '
|
|
210
|
+
f'stdev={stats_stdev:.{precision}f}'
|
|
208
211
|
),
|
|
209
212
|
'cyan',
|
|
210
213
|
)
|
|
@@ -448,9 +451,9 @@ class Ping:
|
|
|
448
451
|
self.repeat_delay = repeat_delay
|
|
449
452
|
self.pace = pace
|
|
450
453
|
self.done = asyncio.Event()
|
|
451
|
-
self.
|
|
452
|
-
self.
|
|
453
|
-
self.
|
|
454
|
+
self.ping_times = []
|
|
455
|
+
self.rtts = []
|
|
456
|
+
self.next_expected_packet_index = 0
|
|
454
457
|
self.min_stats = []
|
|
455
458
|
self.max_stats = []
|
|
456
459
|
self.avg_stats = []
|
|
@@ -477,60 +480,57 @@ class Ping:
|
|
|
477
480
|
logging.info(color('=== Sending RESET', 'magenta'))
|
|
478
481
|
await self.packet_io.send_packet(bytes([PacketType.RESET]))
|
|
479
482
|
|
|
480
|
-
self.
|
|
481
|
-
|
|
482
|
-
|
|
483
|
+
packet_interval = self.pace / 1000
|
|
484
|
+
start_time = time.time()
|
|
485
|
+
self.next_expected_packet_index = 0
|
|
486
|
+
for i in range(self.tx_packet_count):
|
|
487
|
+
target_time = start_time + (i * packet_interval)
|
|
488
|
+
now = time.time()
|
|
489
|
+
if now < target_time:
|
|
490
|
+
await asyncio.sleep(target_time - now)
|
|
491
|
+
|
|
492
|
+
packet = struct.pack(
|
|
493
|
+
'>bbI',
|
|
494
|
+
PacketType.SEQUENCE,
|
|
495
|
+
(PACKET_FLAG_LAST if i == self.tx_packet_count - 1 else 0),
|
|
496
|
+
i,
|
|
497
|
+
) + bytes(self.tx_packet_size - 6)
|
|
498
|
+
logging.info(color(f'Sending packet {i}', 'yellow'))
|
|
499
|
+
self.ping_times.append(time.time())
|
|
500
|
+
await self.packet_io.send_packet(packet)
|
|
483
501
|
|
|
484
502
|
await self.done.wait()
|
|
485
503
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
504
|
+
min_rtt = min(self.rtts)
|
|
505
|
+
max_rtt = max(self.rtts)
|
|
506
|
+
avg_rtt = statistics.mean(self.rtts)
|
|
507
|
+
stdev_rtt = statistics.stdev(self.rtts)
|
|
489
508
|
logging.info(
|
|
490
509
|
color(
|
|
491
|
-
'@@@
|
|
492
|
-
f'min={
|
|
493
|
-
f'max={
|
|
494
|
-
f'average={
|
|
510
|
+
'@@@ RTTs: '
|
|
511
|
+
f'min={min_rtt:.2f}, '
|
|
512
|
+
f'max={max_rtt:.2f}, '
|
|
513
|
+
f'average={avg_rtt:.2f}, '
|
|
514
|
+
f'stdev={stdev_rtt:.2f}'
|
|
495
515
|
)
|
|
496
516
|
)
|
|
497
517
|
|
|
498
|
-
self.min_stats.append(
|
|
499
|
-
self.max_stats.append(
|
|
500
|
-
self.avg_stats.append(
|
|
518
|
+
self.min_stats.append(min_rtt)
|
|
519
|
+
self.max_stats.append(max_rtt)
|
|
520
|
+
self.avg_stats.append(avg_rtt)
|
|
501
521
|
|
|
502
522
|
run_counter = f'[{run + 1} of {self.repeat + 1}]' if self.repeat else ''
|
|
503
523
|
logging.info(color(f'=== {run_counter} Done!', 'magenta'))
|
|
504
524
|
|
|
505
525
|
if self.repeat:
|
|
506
|
-
log_stats('Min
|
|
507
|
-
log_stats('Max
|
|
508
|
-
log_stats('Average
|
|
526
|
+
log_stats('Min RTT', self.min_stats)
|
|
527
|
+
log_stats('Max RTT', self.max_stats)
|
|
528
|
+
log_stats('Average RTT', self.avg_stats)
|
|
509
529
|
|
|
510
530
|
if self.repeat:
|
|
511
531
|
logging.info(color('--- End of runs', 'blue'))
|
|
512
532
|
|
|
513
|
-
async def send_next_ping(self):
|
|
514
|
-
if self.pace:
|
|
515
|
-
await asyncio.sleep(self.pace / 1000)
|
|
516
|
-
|
|
517
|
-
packet = struct.pack(
|
|
518
|
-
'>bbI',
|
|
519
|
-
PacketType.SEQUENCE,
|
|
520
|
-
(
|
|
521
|
-
PACKET_FLAG_LAST
|
|
522
|
-
if self.current_packet_index == self.tx_packet_count - 1
|
|
523
|
-
else 0
|
|
524
|
-
),
|
|
525
|
-
self.current_packet_index,
|
|
526
|
-
) + bytes(self.tx_packet_size - 6)
|
|
527
|
-
logging.info(color(f'Sending packet {self.current_packet_index}', 'yellow'))
|
|
528
|
-
self.ping_sent_time = time.time()
|
|
529
|
-
await self.packet_io.send_packet(packet)
|
|
530
|
-
|
|
531
533
|
def on_packet_received(self, packet):
|
|
532
|
-
elapsed = time.time() - self.ping_sent_time
|
|
533
|
-
|
|
534
534
|
try:
|
|
535
535
|
packet_type, packet_data = parse_packet(packet)
|
|
536
536
|
except ValueError:
|
|
@@ -542,21 +542,23 @@ class Ping:
|
|
|
542
542
|
return
|
|
543
543
|
|
|
544
544
|
if packet_type == PacketType.ACK:
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
elapsed = time.time() - self.ping_times[packet_index]
|
|
546
|
+
rtt = elapsed * 1000
|
|
547
|
+
self.rtts.append(rtt)
|
|
547
548
|
logging.info(
|
|
548
549
|
color(
|
|
549
|
-
f'<<< Received ACK [{packet_index}],
|
|
550
|
+
f'<<< Received ACK [{packet_index}], RTT={rtt:.2f}ms',
|
|
550
551
|
'green',
|
|
551
552
|
)
|
|
552
553
|
)
|
|
553
554
|
|
|
554
|
-
if packet_index == self.
|
|
555
|
-
self.
|
|
555
|
+
if packet_index == self.next_expected_packet_index:
|
|
556
|
+
self.next_expected_packet_index += 1
|
|
556
557
|
else:
|
|
557
558
|
logging.info(
|
|
558
559
|
color(
|
|
559
|
-
f'!!! Unexpected packet,
|
|
560
|
+
f'!!! Unexpected packet, '
|
|
561
|
+
f'expected {self.next_expected_packet_index} '
|
|
560
562
|
f'but received {packet_index}'
|
|
561
563
|
)
|
|
562
564
|
)
|
|
@@ -565,8 +567,6 @@ class Ping:
|
|
|
565
567
|
self.done.set()
|
|
566
568
|
return
|
|
567
569
|
|
|
568
|
-
AsyncRunner.spawn(self.send_next_ping())
|
|
569
|
-
|
|
570
570
|
|
|
571
571
|
# -----------------------------------------------------------------------------
|
|
572
572
|
# Pong
|
|
@@ -583,8 +583,11 @@ class Pong:
|
|
|
583
583
|
|
|
584
584
|
def reset(self):
|
|
585
585
|
self.expected_packet_index = 0
|
|
586
|
+
self.receive_times = []
|
|
586
587
|
|
|
587
588
|
def on_packet_received(self, packet):
|
|
589
|
+
self.receive_times.append(time.time())
|
|
590
|
+
|
|
588
591
|
try:
|
|
589
592
|
packet_type, packet_data = parse_packet(packet)
|
|
590
593
|
except ValueError:
|
|
@@ -599,10 +602,16 @@ class Pong:
|
|
|
599
602
|
packet_flags, packet_index = parse_packet_sequence(packet_data)
|
|
600
603
|
except ValueError:
|
|
601
604
|
return
|
|
605
|
+
interval = (
|
|
606
|
+
self.receive_times[-1] - self.receive_times[-2]
|
|
607
|
+
if len(self.receive_times) >= 2
|
|
608
|
+
else 0
|
|
609
|
+
)
|
|
602
610
|
logging.info(
|
|
603
611
|
color(
|
|
604
612
|
f'<<< Received packet {packet_index}: '
|
|
605
|
-
f'flags=0x{packet_flags:02X}, {len(packet)} bytes'
|
|
613
|
+
f'flags=0x{packet_flags:02X}, {len(packet)} bytes, '
|
|
614
|
+
f'interval={interval:.4f}',
|
|
606
615
|
'green',
|
|
607
616
|
)
|
|
608
617
|
)
|
|
@@ -623,8 +632,35 @@ class Pong:
|
|
|
623
632
|
)
|
|
624
633
|
)
|
|
625
634
|
|
|
626
|
-
if packet_flags & PACKET_FLAG_LAST
|
|
627
|
-
self.
|
|
635
|
+
if packet_flags & PACKET_FLAG_LAST:
|
|
636
|
+
if len(self.receive_times) >= 3:
|
|
637
|
+
# Show basic stats
|
|
638
|
+
intervals = [
|
|
639
|
+
self.receive_times[i + 1] - self.receive_times[i]
|
|
640
|
+
for i in range(len(self.receive_times) - 1)
|
|
641
|
+
]
|
|
642
|
+
log_stats('Packet intervals', intervals, 3)
|
|
643
|
+
|
|
644
|
+
# Show a histogram
|
|
645
|
+
bin_count = 20
|
|
646
|
+
bins = [0] * bin_count
|
|
647
|
+
interval_min = min(intervals)
|
|
648
|
+
interval_max = max(intervals)
|
|
649
|
+
interval_range = interval_max - interval_min
|
|
650
|
+
bin_thresholds = [
|
|
651
|
+
interval_min + i * (interval_range / bin_count)
|
|
652
|
+
for i in range(bin_count)
|
|
653
|
+
]
|
|
654
|
+
for interval in intervals:
|
|
655
|
+
for i in reversed(range(bin_count)):
|
|
656
|
+
if interval >= bin_thresholds[i]:
|
|
657
|
+
bins[i] += 1
|
|
658
|
+
break
|
|
659
|
+
for i in range(bin_count):
|
|
660
|
+
logging.info(f'@@@ >= {bin_thresholds[i]:.4f}: {bins[i]}')
|
|
661
|
+
|
|
662
|
+
if not self.linger:
|
|
663
|
+
self.done.set()
|
|
628
664
|
|
|
629
665
|
async def run(self):
|
|
630
666
|
await self.done.wait()
|
|
@@ -942,9 +978,12 @@ class RfcommClient(StreamedPacketIO):
|
|
|
942
978
|
channel = await bumble.rfcomm.find_rfcomm_channel_with_uuid(
|
|
943
979
|
connection, self.uuid
|
|
944
980
|
)
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
981
|
+
if channel:
|
|
982
|
+
logging.info(color(f'@@@ Channel number = {channel}', 'cyan'))
|
|
983
|
+
else:
|
|
984
|
+
logging.warning(
|
|
985
|
+
color('!!! No RFComm service with this UUID found', 'red')
|
|
986
|
+
)
|
|
948
987
|
await connection.disconnect()
|
|
949
988
|
return
|
|
950
989
|
|
|
@@ -1054,6 +1093,8 @@ class RfcommServer(StreamedPacketIO):
|
|
|
1054
1093
|
if self.credits_threshold is not None:
|
|
1055
1094
|
dlc.rx_credits_threshold = self.credits_threshold
|
|
1056
1095
|
|
|
1096
|
+
self.ready.set()
|
|
1097
|
+
|
|
1057
1098
|
async def drain(self):
|
|
1058
1099
|
assert self.dlc
|
|
1059
1100
|
await self.dlc.drain()
|
|
@@ -1068,7 +1109,7 @@ class Central(Connection.Listener):
|
|
|
1068
1109
|
transport,
|
|
1069
1110
|
peripheral_address,
|
|
1070
1111
|
classic,
|
|
1071
|
-
|
|
1112
|
+
scenario_factory,
|
|
1072
1113
|
mode_factory,
|
|
1073
1114
|
connection_interval,
|
|
1074
1115
|
phy,
|
|
@@ -1081,7 +1122,7 @@ class Central(Connection.Listener):
|
|
|
1081
1122
|
self.transport = transport
|
|
1082
1123
|
self.peripheral_address = peripheral_address
|
|
1083
1124
|
self.classic = classic
|
|
1084
|
-
self.
|
|
1125
|
+
self.scenario_factory = scenario_factory
|
|
1085
1126
|
self.mode_factory = mode_factory
|
|
1086
1127
|
self.authenticate = authenticate
|
|
1087
1128
|
self.encrypt = encrypt or authenticate
|
|
@@ -1134,7 +1175,7 @@ class Central(Connection.Listener):
|
|
|
1134
1175
|
DEFAULT_CENTRAL_NAME, central_address, hci_source, hci_sink
|
|
1135
1176
|
)
|
|
1136
1177
|
mode = self.mode_factory(self.device)
|
|
1137
|
-
|
|
1178
|
+
scenario = self.scenario_factory(mode)
|
|
1138
1179
|
self.device.classic_enabled = self.classic
|
|
1139
1180
|
|
|
1140
1181
|
# Set up a pairing config factory with minimal requirements.
|
|
@@ -1215,7 +1256,7 @@ class Central(Connection.Listener):
|
|
|
1215
1256
|
|
|
1216
1257
|
await mode.on_connection(self.connection)
|
|
1217
1258
|
|
|
1218
|
-
await
|
|
1259
|
+
await scenario.run()
|
|
1219
1260
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
|
1220
1261
|
await self.connection.disconnect()
|
|
1221
1262
|
|
|
@@ -1246,7 +1287,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1246
1287
|
def __init__(
|
|
1247
1288
|
self,
|
|
1248
1289
|
transport,
|
|
1249
|
-
|
|
1290
|
+
scenario_factory,
|
|
1250
1291
|
mode_factory,
|
|
1251
1292
|
classic,
|
|
1252
1293
|
extended_data_length,
|
|
@@ -1254,11 +1295,11 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1254
1295
|
):
|
|
1255
1296
|
self.transport = transport
|
|
1256
1297
|
self.classic = classic
|
|
1257
|
-
self.
|
|
1298
|
+
self.scenario_factory = scenario_factory
|
|
1258
1299
|
self.mode_factory = mode_factory
|
|
1259
1300
|
self.extended_data_length = extended_data_length
|
|
1260
1301
|
self.role_switch = role_switch
|
|
1261
|
-
self.
|
|
1302
|
+
self.scenario = None
|
|
1262
1303
|
self.mode = None
|
|
1263
1304
|
self.device = None
|
|
1264
1305
|
self.connection = None
|
|
@@ -1278,7 +1319,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1278
1319
|
)
|
|
1279
1320
|
self.device.listener = self
|
|
1280
1321
|
self.mode = self.mode_factory(self.device)
|
|
1281
|
-
self.
|
|
1322
|
+
self.scenario = self.scenario_factory(self.mode)
|
|
1282
1323
|
self.device.classic_enabled = self.classic
|
|
1283
1324
|
|
|
1284
1325
|
# Set up a pairing config factory with minimal requirements.
|
|
@@ -1315,7 +1356,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1315
1356
|
print_connection(self.connection)
|
|
1316
1357
|
|
|
1317
1358
|
await self.mode.on_connection(self.connection)
|
|
1318
|
-
await self.
|
|
1359
|
+
await self.scenario.run()
|
|
1319
1360
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
|
1320
1361
|
|
|
1321
1362
|
def on_connection(self, connection):
|
|
@@ -1344,7 +1385,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1344
1385
|
def on_disconnection(self, reason):
|
|
1345
1386
|
logging.info(color(f'!!! Disconnection: reason={reason}', 'red'))
|
|
1346
1387
|
self.connection = None
|
|
1347
|
-
self.
|
|
1388
|
+
self.scenario.reset()
|
|
1348
1389
|
|
|
1349
1390
|
if self.classic:
|
|
1350
1391
|
AsyncRunner.spawn(self.device.set_discoverable(True))
|
|
@@ -1426,13 +1467,13 @@ def create_mode_factory(ctx, default_mode):
|
|
|
1426
1467
|
|
|
1427
1468
|
|
|
1428
1469
|
# -----------------------------------------------------------------------------
|
|
1429
|
-
def
|
|
1430
|
-
|
|
1431
|
-
if
|
|
1432
|
-
|
|
1470
|
+
def create_scenario_factory(ctx, default_scenario):
|
|
1471
|
+
scenario = ctx.obj['scenario']
|
|
1472
|
+
if scenario is None:
|
|
1473
|
+
scenarion = default_scenario
|
|
1433
1474
|
|
|
1434
|
-
def
|
|
1435
|
-
if
|
|
1475
|
+
def create_scenario(packet_io):
|
|
1476
|
+
if scenario == 'send':
|
|
1436
1477
|
return Sender(
|
|
1437
1478
|
packet_io,
|
|
1438
1479
|
start_delay=ctx.obj['start_delay'],
|
|
@@ -1443,10 +1484,10 @@ def create_role_factory(ctx, default_role):
|
|
|
1443
1484
|
packet_count=ctx.obj['packet_count'],
|
|
1444
1485
|
)
|
|
1445
1486
|
|
|
1446
|
-
if
|
|
1487
|
+
if scenario == 'receive':
|
|
1447
1488
|
return Receiver(packet_io, ctx.obj['linger'])
|
|
1448
1489
|
|
|
1449
|
-
if
|
|
1490
|
+
if scenario == 'ping':
|
|
1450
1491
|
return Ping(
|
|
1451
1492
|
packet_io,
|
|
1452
1493
|
start_delay=ctx.obj['start_delay'],
|
|
@@ -1457,12 +1498,12 @@ def create_role_factory(ctx, default_role):
|
|
|
1457
1498
|
packet_count=ctx.obj['packet_count'],
|
|
1458
1499
|
)
|
|
1459
1500
|
|
|
1460
|
-
if
|
|
1501
|
+
if scenario == 'pong':
|
|
1461
1502
|
return Pong(packet_io, ctx.obj['linger'])
|
|
1462
1503
|
|
|
1463
|
-
raise ValueError('invalid
|
|
1504
|
+
raise ValueError('invalid scenario')
|
|
1464
1505
|
|
|
1465
|
-
return
|
|
1506
|
+
return create_scenario
|
|
1466
1507
|
|
|
1467
1508
|
|
|
1468
1509
|
# -----------------------------------------------------------------------------
|
|
@@ -1470,7 +1511,7 @@ def create_role_factory(ctx, default_role):
|
|
|
1470
1511
|
# -----------------------------------------------------------------------------
|
|
1471
1512
|
@click.group()
|
|
1472
1513
|
@click.option('--device-config', metavar='FILENAME', help='Device configuration file')
|
|
1473
|
-
@click.option('--
|
|
1514
|
+
@click.option('--scenario', type=click.Choice(['send', 'receive', 'ping', 'pong']))
|
|
1474
1515
|
@click.option(
|
|
1475
1516
|
'--mode',
|
|
1476
1517
|
type=click.Choice(
|
|
@@ -1503,7 +1544,7 @@ def create_role_factory(ctx, default_role):
|
|
|
1503
1544
|
'--rfcomm-channel',
|
|
1504
1545
|
type=int,
|
|
1505
1546
|
default=DEFAULT_RFCOMM_CHANNEL,
|
|
1506
|
-
help='RFComm channel to use',
|
|
1547
|
+
help='RFComm channel to use (specify 0 for channel discovery via SDP)',
|
|
1507
1548
|
)
|
|
1508
1549
|
@click.option(
|
|
1509
1550
|
'--rfcomm-uuid',
|
|
@@ -1565,7 +1606,7 @@ def create_role_factory(ctx, default_role):
|
|
|
1565
1606
|
metavar='SIZE',
|
|
1566
1607
|
type=click.IntRange(8, 8192),
|
|
1567
1608
|
default=500,
|
|
1568
|
-
help='Packet size (
|
|
1609
|
+
help='Packet size (send or ping scenario)',
|
|
1569
1610
|
)
|
|
1570
1611
|
@click.option(
|
|
1571
1612
|
'--packet-count',
|
|
@@ -1573,7 +1614,7 @@ def create_role_factory(ctx, default_role):
|
|
|
1573
1614
|
metavar='COUNT',
|
|
1574
1615
|
type=int,
|
|
1575
1616
|
default=10,
|
|
1576
|
-
help='Packet count (
|
|
1617
|
+
help='Packet count (send or ping scenario)',
|
|
1577
1618
|
)
|
|
1578
1619
|
@click.option(
|
|
1579
1620
|
'--start-delay',
|
|
@@ -1581,7 +1622,7 @@ def create_role_factory(ctx, default_role):
|
|
|
1581
1622
|
metavar='SECONDS',
|
|
1582
1623
|
type=int,
|
|
1583
1624
|
default=1,
|
|
1584
|
-
help='Start delay (
|
|
1625
|
+
help='Start delay (send or ping scenario)',
|
|
1585
1626
|
)
|
|
1586
1627
|
@click.option(
|
|
1587
1628
|
'--repeat',
|
|
@@ -1589,7 +1630,7 @@ def create_role_factory(ctx, default_role):
|
|
|
1589
1630
|
type=int,
|
|
1590
1631
|
default=0,
|
|
1591
1632
|
help=(
|
|
1592
|
-
'Repeat the run N times (
|
|
1633
|
+
'Repeat the run N times (send and ping scenario)'
|
|
1593
1634
|
'(0, which is the fault, to run just once) '
|
|
1594
1635
|
),
|
|
1595
1636
|
)
|
|
@@ -1613,13 +1654,13 @@ def create_role_factory(ctx, default_role):
|
|
|
1613
1654
|
@click.option(
|
|
1614
1655
|
'--linger',
|
|
1615
1656
|
is_flag=True,
|
|
1616
|
-
help="Don't exit at the end of a run (
|
|
1657
|
+
help="Don't exit at the end of a run (receive and pong scenarios)",
|
|
1617
1658
|
)
|
|
1618
1659
|
@click.pass_context
|
|
1619
1660
|
def bench(
|
|
1620
1661
|
ctx,
|
|
1621
1662
|
device_config,
|
|
1622
|
-
|
|
1663
|
+
scenario,
|
|
1623
1664
|
mode,
|
|
1624
1665
|
att_mtu,
|
|
1625
1666
|
extended_data_length,
|
|
@@ -1645,7 +1686,7 @@ def bench(
|
|
|
1645
1686
|
):
|
|
1646
1687
|
ctx.ensure_object(dict)
|
|
1647
1688
|
ctx.obj['device_config'] = device_config
|
|
1648
|
-
ctx.obj['
|
|
1689
|
+
ctx.obj['scenario'] = scenario
|
|
1649
1690
|
ctx.obj['mode'] = mode
|
|
1650
1691
|
ctx.obj['att_mtu'] = att_mtu
|
|
1651
1692
|
ctx.obj['rfcomm_channel'] = rfcomm_channel
|
|
@@ -1699,7 +1740,7 @@ def central(
|
|
|
1699
1740
|
ctx, transport, peripheral_address, connection_interval, phy, authenticate, encrypt
|
|
1700
1741
|
):
|
|
1701
1742
|
"""Run as a central (initiates the connection)"""
|
|
1702
|
-
|
|
1743
|
+
scenario_factory = create_scenario_factory(ctx, 'send')
|
|
1703
1744
|
mode_factory = create_mode_factory(ctx, 'gatt-client')
|
|
1704
1745
|
classic = ctx.obj['classic']
|
|
1705
1746
|
|
|
@@ -1708,7 +1749,7 @@ def central(
|
|
|
1708
1749
|
transport,
|
|
1709
1750
|
peripheral_address,
|
|
1710
1751
|
classic,
|
|
1711
|
-
|
|
1752
|
+
scenario_factory,
|
|
1712
1753
|
mode_factory,
|
|
1713
1754
|
connection_interval,
|
|
1714
1755
|
phy,
|
|
@@ -1726,13 +1767,13 @@ def central(
|
|
|
1726
1767
|
@click.pass_context
|
|
1727
1768
|
def peripheral(ctx, transport):
|
|
1728
1769
|
"""Run as a peripheral (waits for a connection)"""
|
|
1729
|
-
|
|
1770
|
+
scenario_factory = create_scenario_factory(ctx, 'receive')
|
|
1730
1771
|
mode_factory = create_mode_factory(ctx, 'gatt-server')
|
|
1731
1772
|
|
|
1732
1773
|
async def run_peripheral():
|
|
1733
1774
|
await Peripheral(
|
|
1734
1775
|
transport,
|
|
1735
|
-
|
|
1776
|
+
scenario_factory,
|
|
1736
1777
|
mode_factory,
|
|
1737
1778
|
ctx.obj['classic'],
|
|
1738
1779
|
ctx.obj['extended_data_length'],
|
|
@@ -1743,7 +1784,11 @@ def peripheral(ctx, transport):
|
|
|
1743
1784
|
|
|
1744
1785
|
|
|
1745
1786
|
def main():
|
|
1746
|
-
logging.basicConfig(
|
|
1787
|
+
logging.basicConfig(
|
|
1788
|
+
level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
|
|
1789
|
+
format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
|
|
1790
|
+
datefmt="%H:%M:%S",
|
|
1791
|
+
)
|
|
1747
1792
|
bench()
|
|
1748
1793
|
|
|
1749
1794
|
|
bumble/apps/rfcomm_bridge.py
CHANGED
|
@@ -237,6 +237,7 @@ class ClientBridge:
|
|
|
237
237
|
address: str,
|
|
238
238
|
tcp_host: str,
|
|
239
239
|
tcp_port: int,
|
|
240
|
+
authenticate: bool,
|
|
240
241
|
encrypt: bool,
|
|
241
242
|
):
|
|
242
243
|
self.channel = channel
|
|
@@ -245,6 +246,7 @@ class ClientBridge:
|
|
|
245
246
|
self.address = address
|
|
246
247
|
self.tcp_host = tcp_host
|
|
247
248
|
self.tcp_port = tcp_port
|
|
249
|
+
self.authenticate = authenticate
|
|
248
250
|
self.encrypt = encrypt
|
|
249
251
|
self.device: Optional[Device] = None
|
|
250
252
|
self.connection: Optional[Connection] = None
|
|
@@ -274,6 +276,11 @@ class ClientBridge:
|
|
|
274
276
|
print(color(f"@@@ Bluetooth connection: {self.connection}", "blue"))
|
|
275
277
|
self.connection.on("disconnection", self.on_disconnection)
|
|
276
278
|
|
|
279
|
+
if self.authenticate:
|
|
280
|
+
print(color("@@@ Authenticating Bluetooth connection", "blue"))
|
|
281
|
+
await self.connection.authenticate()
|
|
282
|
+
print(color("@@@ Bluetooth connection authenticated", "blue"))
|
|
283
|
+
|
|
277
284
|
if self.encrypt:
|
|
278
285
|
print(color("@@@ Encrypting Bluetooth connection", "blue"))
|
|
279
286
|
await self.connection.encrypt()
|
|
@@ -491,8 +498,9 @@ def server(context, tcp_host, tcp_port):
|
|
|
491
498
|
@click.argument("bluetooth-address")
|
|
492
499
|
@click.option("--tcp-host", help="TCP host", default="_")
|
|
493
500
|
@click.option("--tcp-port", help="TCP port", default=DEFAULT_CLIENT_TCP_PORT)
|
|
501
|
+
@click.option("--authenticate", is_flag=True, help="Authenticate the connection")
|
|
494
502
|
@click.option("--encrypt", is_flag=True, help="Encrypt the connection")
|
|
495
|
-
def client(context, bluetooth_address, tcp_host, tcp_port, encrypt):
|
|
503
|
+
def client(context, bluetooth_address, tcp_host, tcp_port, authenticate, encrypt):
|
|
496
504
|
bridge = ClientBridge(
|
|
497
505
|
context.obj["channel"],
|
|
498
506
|
context.obj["uuid"],
|
|
@@ -500,6 +508,7 @@ def client(context, bluetooth_address, tcp_host, tcp_port, encrypt):
|
|
|
500
508
|
bluetooth_address,
|
|
501
509
|
tcp_host,
|
|
502
510
|
tcp_port,
|
|
511
|
+
authenticate,
|
|
503
512
|
encrypt,
|
|
504
513
|
)
|
|
505
514
|
asyncio.run(run(context.obj["device_config"], context.obj["hci_transport"], bridge))
|
bumble/avc.py
CHANGED
|
@@ -134,6 +134,8 @@ class Frame:
|
|
|
134
134
|
opcode_offset = 3
|
|
135
135
|
elif subunit_id == 6:
|
|
136
136
|
raise core.InvalidPacketError("reserved subunit ID")
|
|
137
|
+
else:
|
|
138
|
+
raise core.InvalidPacketError("invalid subunit ID")
|
|
137
139
|
|
|
138
140
|
opcode = Frame.OperationCode(data[opcode_offset])
|
|
139
141
|
operands = data[opcode_offset + 1 :]
|
bumble/gatt_client.py
CHANGED
|
@@ -898,6 +898,12 @@ class Client:
|
|
|
898
898
|
) and subscriber in subscribers:
|
|
899
899
|
subscribers.remove(subscriber)
|
|
900
900
|
|
|
901
|
+
# The characteristic itself is added as subscriber. If it is the
|
|
902
|
+
# last remaining subscriber, we remove it, such that the clean up
|
|
903
|
+
# works correctly. Otherwise the CCCD never is set back to 0.
|
|
904
|
+
if len(subscribers) == 1 and characteristic in subscribers:
|
|
905
|
+
subscribers.remove(characteristic)
|
|
906
|
+
|
|
901
907
|
# Cleanup if we removed the last one
|
|
902
908
|
if not subscribers:
|
|
903
909
|
del subscriber_set[characteristic.handle]
|
bumble/l2cap.py
CHANGED
bumble/link.py
CHANGED
|
@@ -122,6 +122,8 @@ class LocalLink:
|
|
|
122
122
|
elif transport == BT_BR_EDR_TRANSPORT:
|
|
123
123
|
destination_controller = self.find_classic_controller(destination_address)
|
|
124
124
|
source_address = sender_controller.public_address
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError("unsupported transport type")
|
|
125
127
|
|
|
126
128
|
if destination_controller is not None:
|
|
127
129
|
destination_controller.on_link_acl_data(source_address, transport, data)
|
bumble/profiles/bap.py
CHANGED
|
@@ -102,6 +102,7 @@ class ContextType(enum.IntFlag):
|
|
|
102
102
|
|
|
103
103
|
# fmt: off
|
|
104
104
|
PROHIBITED = 0x0000
|
|
105
|
+
UNSPECIFIED = 0x0001
|
|
105
106
|
CONVERSATIONAL = 0x0002
|
|
106
107
|
MEDIA = 0x0004
|
|
107
108
|
GAME = 0x0008
|
|
@@ -350,6 +351,7 @@ class CodecSpecificCapabilities:
|
|
|
350
351
|
supported_max_codec_frames_per_sdu = value
|
|
351
352
|
|
|
352
353
|
# It is expected here that if some fields are missing, an error should be raised.
|
|
354
|
+
# pylint: disable=possibly-used-before-assignment,used-before-assignment
|
|
353
355
|
return CodecSpecificCapabilities(
|
|
354
356
|
supported_sampling_frequencies=supported_sampling_frequencies,
|
|
355
357
|
supported_frame_durations=supported_frame_durations,
|
|
@@ -426,6 +428,7 @@ class CodecSpecificConfiguration:
|
|
|
426
428
|
codec_frames_per_sdu = value
|
|
427
429
|
|
|
428
430
|
# It is expected here that if some fields are missing, an error should be raised.
|
|
431
|
+
# pylint: disable=possibly-used-before-assignment,used-before-assignment
|
|
429
432
|
return CodecSpecificConfiguration(
|
|
430
433
|
sampling_frequency=sampling_frequency,
|
|
431
434
|
frame_duration=frame_duration,
|
bumble/sdp.py
CHANGED
|
@@ -434,6 +434,8 @@ class DataElement:
|
|
|
434
434
|
if size != 1:
|
|
435
435
|
raise InvalidArgumentError('boolean must be 1 byte')
|
|
436
436
|
size_index = 0
|
|
437
|
+
else:
|
|
438
|
+
raise RuntimeError("internal error - self.type not supported")
|
|
437
439
|
|
|
438
440
|
self.bytes = bytes([self.type << 3 | size_index]) + size_bytes + data
|
|
439
441
|
return self.bytes
|
bumble/smp.py
CHANGED
|
@@ -1839,7 +1839,7 @@ class Session:
|
|
|
1839
1839
|
if self.is_initiator:
|
|
1840
1840
|
if self.pairing_method == PairingMethod.OOB:
|
|
1841
1841
|
self.send_pairing_random_command()
|
|
1842
|
-
|
|
1842
|
+
elif self.pairing_method == PairingMethod.PASSKEY:
|
|
1843
1843
|
self.send_pairing_confirm_command()
|
|
1844
1844
|
else:
|
|
1845
1845
|
if self.pairing_method == PairingMethod.PASSKEY:
|
bumble/transport/common.py
CHANGED
|
@@ -370,11 +370,13 @@ class PumpedPacketSource(ParserSource):
|
|
|
370
370
|
self.parser.feed_data(packet)
|
|
371
371
|
except asyncio.CancelledError:
|
|
372
372
|
logger.debug('source pump task done')
|
|
373
|
-
self.terminated.
|
|
373
|
+
if not self.terminated.done():
|
|
374
|
+
self.terminated.set_result(None)
|
|
374
375
|
break
|
|
375
376
|
except Exception as error:
|
|
376
377
|
logger.warning(f'exception while waiting for packet: {error}')
|
|
377
|
-
self.terminated.
|
|
378
|
+
if not self.terminated.done():
|
|
379
|
+
self.terminated.set_exception(error)
|
|
378
380
|
break
|
|
379
381
|
|
|
380
382
|
self.pump_task = asyncio.create_task(pump_packets())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bumble
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.202
|
|
4
4
|
Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
|
|
5
5
|
Home-page: https://github.com/google/bumble
|
|
6
6
|
Author: Google
|
|
@@ -35,9 +35,10 @@ Provides-Extra: development
|
|
|
35
35
|
Requires-Dist: black==24.3; extra == "development"
|
|
36
36
|
Requires-Dist: grpcio-tools>=1.62.1; extra == "development"
|
|
37
37
|
Requires-Dist: invoke>=1.7.3; extra == "development"
|
|
38
|
-
Requires-Dist:
|
|
38
|
+
Requires-Dist: mobly>=1.12.2; extra == "development"
|
|
39
|
+
Requires-Dist: mypy==1.12.0; extra == "development"
|
|
39
40
|
Requires-Dist: nox>=2022; extra == "development"
|
|
40
|
-
Requires-Dist: pylint==3.1
|
|
41
|
+
Requires-Dist: pylint==3.3.1; extra == "development"
|
|
41
42
|
Requires-Dist: pyyaml>=6.0; extra == "development"
|
|
42
43
|
Requires-Dist: types-appdirs>=1.4.3; extra == "development"
|
|
43
44
|
Requires-Dist: types-invoke>=1.7.3; extra == "development"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
|
|
2
|
-
bumble/_version.py,sha256=
|
|
2
|
+
bumble/_version.py,sha256=0o3Qq5M65Ds-43dG5u5R1z10hjSzTudi88nO90cBlv4,415
|
|
3
3
|
bumble/a2dp.py,sha256=_dCq-qyG5OglDVlaOFwAgFe_ugvHuEdEYL-kWFf6sWQ,31775
|
|
4
4
|
bumble/at.py,sha256=Giu2VUSJKH-jIh10lOfumiqy-FyO99Ra6nJ7UiWQ0H8,3114
|
|
5
5
|
bumble/att.py,sha256=jFUcIDP3JYYdazWAVynGy0pcgNOzsldRzoBhhOkLCRI,32767
|
|
6
|
-
bumble/avc.py,sha256=
|
|
6
|
+
bumble/avc.py,sha256=cO1-8x7BvuBCQVJg-9nTibkLFC4y0_2SNERZ8_7Kn_c,16407
|
|
7
7
|
bumble/avctp.py,sha256=yHAjJRjLGtR0Q-iWcLS7cJRz5Jr2YiRmZd6LZV4Xjt4,9935
|
|
8
8
|
bumble/avdtp.py,sha256=2ki_BE4SHiu3Sx9oHCknfjF-bBcgPB9TsyF5upciUYI,76773
|
|
9
9
|
bumble/avrcp.py,sha256=P_pLVpP3kRtoD2y0Ca0NTEEe1mA4SXO_ldtc4OP6Tcc,69976
|
|
@@ -18,7 +18,7 @@ bumble/decoder.py,sha256=0-VNWZT-u7lvK3qBpAuYT0M6Rz_bMgMi4CjfUXX_6RM,9728
|
|
|
18
18
|
bumble/device.py,sha256=vroAnNWlBW6_jw1KgmoG4ZWKyauLm8abS1XFrTX084Y,195024
|
|
19
19
|
bumble/gap.py,sha256=dRU2_TWvqTDx80hxeSbXlWIeWvptWH4_XbItG5y948Q,2138
|
|
20
20
|
bumble/gatt.py,sha256=a07mQ3O0LFt5zgUzSUwa4Jak_FXOXSFX-njxnQNS_zY,39014
|
|
21
|
-
bumble/gatt_client.py,sha256=
|
|
21
|
+
bumble/gatt_client.py,sha256=2Y9rp5kn-aPC2aC0maiWmvrixcWvCWSktgEdjj_xr5w,43202
|
|
22
22
|
bumble/gatt_server.py,sha256=pafGMeAuGAAELnr_1pB_l3CcR5u4H4Y1-MRHjN53gdE,37449
|
|
23
23
|
bumble/hci.py,sha256=e2JqP-D1h7b7vHxFbmdsLJtOacdiuMizzGgBWmCAhxQ,286058
|
|
24
24
|
bumble/helpers.py,sha256=m0w4UgFFNDEnXwHrDyfRlcBObdVed2fqXGL0lvR3c8s,12733
|
|
@@ -26,20 +26,20 @@ bumble/hfp.py,sha256=h5IcgxKnlFWapeJHcNDTvxup9oAE5CcZju92sOusTGc,75619
|
|
|
26
26
|
bumble/hid.py,sha256=hJKm6qhNa0kQTGmp_VxNh3-ywgBDdJpPPFcvtFiRL0A,20335
|
|
27
27
|
bumble/host.py,sha256=pPH7HTQ6ogOToj4Y5Jl985MKjbVlG_jeOcJrV0NTSqU,49204
|
|
28
28
|
bumble/keys.py,sha256=WbIQ7Ob81mW75qmEPQ2rBLfnqBMA-ts2yowWXP9UaCY,12654
|
|
29
|
-
bumble/l2cap.py,sha256=
|
|
30
|
-
bumble/link.py,sha256=
|
|
29
|
+
bumble/l2cap.py,sha256=PJxoXV6h1hhHlkKKY3KGuF8l3HTpV-C-mAE6jfTkLY4,81421
|
|
30
|
+
bumble/link.py,sha256=SU7Ls2Lyg1XuY8x6yP9tAC83SYmMTU2a-vQ_CWCfq90,24107
|
|
31
31
|
bumble/pairing.py,sha256=tgPUba6xNxMi-2plm3xfRlzHq-uPRNZEIGWaN0qNGCs,9853
|
|
32
32
|
bumble/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
bumble/rfcomm.py,sha256=dh5t5vlDEfw3yHgQfzegMYPnShP8Zo-3ScABUvmXNLI,40751
|
|
34
34
|
bumble/rtp.py,sha256=388X3aCv-QrWJ37r_VPqXYJtvNGWPsHnJasqs41g_-s,3487
|
|
35
|
-
bumble/sdp.py,sha256=
|
|
36
|
-
bumble/smp.py,sha256=
|
|
35
|
+
bumble/sdp.py,sha256=I45-qVmFcdRVd65iBuYTwrlKZX_AXAjnXrj4wjLR2co,45554
|
|
36
|
+
bumble/smp.py,sha256=Mc2q_tG3_ATJDrx5ZkPpNI7g6h6g1vRvH8OOQDbI39c,77685
|
|
37
37
|
bumble/snoop.py,sha256=1mzwmp9LToUXbPnFsLrt8S4UHs0kqzbu7LDydwbmkZI,5715
|
|
38
38
|
bumble/utils.py,sha256=e0i-4d28-9zP3gYcd1rdNd669rkPnRs5oJCERUEDfxo,15099
|
|
39
39
|
bumble/apps/README.md,sha256=XTwjRAY-EJWDXpl1V8K3Mw8B7kIqzUIUizRjVBVhoIE,1769
|
|
40
40
|
bumble/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
bumble/apps/auracast.py,sha256=hrG6dW79UkMXqcMLgYjziWEjcVibFUITvjPb4LX1lxM,24686
|
|
42
|
-
bumble/apps/bench.py,sha256=
|
|
42
|
+
bumble/apps/bench.py,sha256=RitVnx9mzmW-1cB4O7wan-lQeFDlazdjTW-BQcuTDb0,58410
|
|
43
43
|
bumble/apps/ble_rpa_tool.py,sha256=ZQtsbfnLPd5qUAkEBPpNgJLRynBBc7q_9cDHKUW2SQ0,1701
|
|
44
44
|
bumble/apps/console.py,sha256=rwD9y3g8Mm_mAEvrcXjbtcv5d8mwF3yTbmE6Vet2BEk,45300
|
|
45
45
|
bumble/apps/controller_info.py,sha256=WScuK5Ytp5aFEosAcgRTCEeey6SmxDFmyB7KBhgVx6s,11759
|
|
@@ -52,7 +52,7 @@ bumble/apps/hci_bridge.py,sha256=KISv352tKnsQsoxjkDiCQbMFmhnPWdnug5wSFAAXxEs,403
|
|
|
52
52
|
bumble/apps/l2cap_bridge.py,sha256=524VgEmgCP4g7T0UdgmsePmNVhDFRJECeaZ_uzKsbco,13062
|
|
53
53
|
bumble/apps/pair.py,sha256=NtDxLfdnlOY_ZEtVNGVXWjv6_x_hndok_ydhV6zkFtI,18503
|
|
54
54
|
bumble/apps/pandora_server.py,sha256=5qaoLCpcZE2KsGO21-7t6Vg4dBjBWbnyOQXwrLhxkuE,1397
|
|
55
|
-
bumble/apps/rfcomm_bridge.py,sha256=
|
|
55
|
+
bumble/apps/rfcomm_bridge.py,sha256=bAdDz84YpYkEfZ6vanQ_VUEpEF4MS4Y9fmbXB4bhoi4,17633
|
|
56
56
|
bumble/apps/scan.py,sha256=b6hIppiJqDfR7VFW2wl3-lkPdFvHLqYZKY8VjjNnhls,8366
|
|
57
57
|
bumble/apps/show.py,sha256=8w0-8jLtN6IM6_58pOHbEmE1Rmxm71O48ACrXixC2jk,6218
|
|
58
58
|
bumble/apps/unbond.py,sha256=LDPWpmgKLMGYDdIFGTdGciFDcUliZ0OmseEbGfJ-MAM,3176
|
|
@@ -86,7 +86,7 @@ bumble/profiles/__init__.py,sha256=yBGC8Ti5LvZuoh1F42XtfrBilb39T77_yuxESZeX2yI,5
|
|
|
86
86
|
bumble/profiles/aics.py,sha256=U0K03XqOKtqahXtq7U47WxftsAD7tkiDeUqxfaF4pwk,18540
|
|
87
87
|
bumble/profiles/ascs.py,sha256=Y2_fd_5LOb9hon60LcQl3R-FoAIsOd_J37RZpveh-7c,25471
|
|
88
88
|
bumble/profiles/asha.py,sha256=QXUp7ImuMD48L0mpuNxPcIkv8VvoENynCuF2PEVqzFM,10373
|
|
89
|
-
bumble/profiles/bap.py,sha256=
|
|
89
|
+
bumble/profiles/bap.py,sha256=E_D6dGMWgTM2HsAk7y-iK-IWVJxfPtHOB5KZZkYV6yw,19613
|
|
90
90
|
bumble/profiles/bass.py,sha256=Wiqum0Wsr5PpVzTAPDcyKLTfJoKXJUYOzqB320aSiUs,14950
|
|
91
91
|
bumble/profiles/battery_service.py,sha256=w-uF4jLoDozJOoykimb2RkrKjVyCke6ts2-h-F1PYyc,2292
|
|
92
92
|
bumble/profiles/cap.py,sha256=6gH7oOnUKjOggMPuB7rtbwj0AneoNmnWzQ_iR3io8e0,1945
|
|
@@ -109,7 +109,7 @@ bumble/tools/rtk_util.py,sha256=TwZhupHQrQYsYHLdRGyzXKd24pwCk8kkzqK1Rj2guco,5087
|
|
|
109
109
|
bumble/transport/__init__.py,sha256=Z01fvuKpqAbhJd0wYcGhW09W2tycM71ck80XoZ8a87Q,7012
|
|
110
110
|
bumble/transport/android_emulator.py,sha256=6HR2cEqdU0XbOldwxCtQuXtvwOUYhRfHkPz0TRt3mbo,4382
|
|
111
111
|
bumble/transport/android_netsim.py,sha256=P-keFdM9-iU_HQQYirYX-yEJtEM_gItzi9srNzWRQiI,17196
|
|
112
|
-
bumble/transport/common.py,sha256=
|
|
112
|
+
bumble/transport/common.py,sha256=caAHY0JqYmE91rrDFkTPX_FtUWtz4quqgQpMRSI9Jsc,16769
|
|
113
113
|
bumble/transport/file.py,sha256=eVM2V6Nk2nDAFdE7Rt01ZI3JdTovsH9OEU1gKYPJjpE,2010
|
|
114
114
|
bumble/transport/hci_socket.py,sha256=EdgWi3-O5yvYcH4R4BkPtG79pnUo7GQtXWawuUHDoDQ,6331
|
|
115
115
|
bumble/transport/pty.py,sha256=grTl-yvjMWHflNwuME4ccVqDbk6NIEgQMgH6Y9lf1fU,2732
|
|
@@ -165,9 +165,9 @@ bumble/vendor/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
165
165
|
bumble/vendor/android/hci.py,sha256=GZrkhaWmcMt1JpnRhv0NoySGkf2H4lNUV2f_omRZW0I,10741
|
|
166
166
|
bumble/vendor/zephyr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
167
167
|
bumble/vendor/zephyr/hci.py,sha256=d83bC0TvT947eN4roFjLkQefWtHOoNsr4xib2ctSkvA,3195
|
|
168
|
-
bumble-0.0.
|
|
169
|
-
bumble-0.0.
|
|
170
|
-
bumble-0.0.
|
|
171
|
-
bumble-0.0.
|
|
172
|
-
bumble-0.0.
|
|
173
|
-
bumble-0.0.
|
|
168
|
+
bumble-0.0.202.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
|
|
169
|
+
bumble-0.0.202.dist-info/METADATA,sha256=rao_wk6XWfEzXwGDY0rdDLSByA_rBfAiS9NSf9v111I,5726
|
|
170
|
+
bumble-0.0.202.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
171
|
+
bumble-0.0.202.dist-info/entry_points.txt,sha256=2TAnDAHiYVEo9Gnugk29QIsHpCgRgnPqBszLSgIX2T0,984
|
|
172
|
+
bumble-0.0.202.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
|
|
173
|
+
bumble-0.0.202.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|