bumble 0.0.180__py3-none-any.whl → 0.0.181__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/profiles/cap.py ADDED
@@ -0,0 +1,52 @@
1
+ # Copyright 2021-2023 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ # -----------------------------------------------------------------------------
17
+ # Imports
18
+ # -----------------------------------------------------------------------------
19
+ from __future__ import annotations
20
+
21
+ from bumble import gatt
22
+ from bumble import gatt_client
23
+ from bumble.profiles import csip
24
+
25
+
26
+ # -----------------------------------------------------------------------------
27
+ # Server
28
+ # -----------------------------------------------------------------------------
29
+ class CommonAudioServiceService(gatt.TemplateService):
30
+ UUID = gatt.GATT_COMMON_AUDIO_SERVICE
31
+
32
+ def __init__(
33
+ self,
34
+ coordinated_set_identification_service: csip.CoordinatedSetIdentificationService,
35
+ ) -> None:
36
+ self.coordinated_set_identification_service = (
37
+ coordinated_set_identification_service
38
+ )
39
+ super().__init__(
40
+ characteristics=[],
41
+ included_services=[coordinated_set_identification_service],
42
+ )
43
+
44
+
45
+ # -----------------------------------------------------------------------------
46
+ # Client
47
+ # -----------------------------------------------------------------------------
48
+ class CommonAudioServiceServiceProxy(gatt_client.ProfileServiceProxy):
49
+ SERVICE_CLASS = CommonAudioServiceService
50
+
51
+ def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
52
+ self.service_proxy = service_proxy
bumble/profiles/csip.py CHANGED
@@ -21,6 +21,9 @@ import enum
21
21
  import struct
22
22
  from typing import Optional
23
23
 
24
+ from bumble import core
25
+ from bumble import crypto
26
+ from bumble import device
24
27
  from bumble import gatt
25
28
  from bumble import gatt_client
26
29
 
@@ -43,9 +46,43 @@ class MemberLock(enum.IntEnum):
43
46
 
44
47
 
45
48
  # -----------------------------------------------------------------------------
46
- # Utils
49
+ # Crypto Toolbox
47
50
  # -----------------------------------------------------------------------------
48
- # TODO: Implement RSI Generator
51
+ def s1(m: bytes) -> bytes:
52
+ '''
53
+ Coordinated Set Identification Service - 4.3 s1 SALT generation function.
54
+ '''
55
+ return crypto.aes_cmac(m[::-1], bytes(16))[::-1]
56
+
57
+
58
+ def k1(n: bytes, salt: bytes, p: bytes) -> bytes:
59
+ '''
60
+ Coordinated Set Identification Service - 4.4 k1 derivation function.
61
+ '''
62
+ t = crypto.aes_cmac(n[::-1], salt[::-1])
63
+ return crypto.aes_cmac(p[::-1], t)[::-1]
64
+
65
+
66
+ def sef(k: bytes, r: bytes) -> bytes:
67
+ '''
68
+ Coordinated Set Identification Service - 4.5 SIRK encryption function sef.
69
+ '''
70
+ return crypto.xor(k1(k, s1(b'SIRKenc'[::-1]), b'csis'[::-1]), r)
71
+
72
+
73
+ def sih(k: bytes, r: bytes) -> bytes:
74
+ '''
75
+ Coordinated Set Identification Service - 4.7 Resolvable Set Identifier hash function sih.
76
+ '''
77
+ return crypto.e(k, r + bytes(13))[:3]
78
+
79
+
80
+ def generate_rsi(sirk: bytes) -> bytes:
81
+ '''
82
+ Coordinated Set Identification Service - 4.8 Resolvable Set Identifier generation operation.
83
+ '''
84
+ prand = crypto.generate_prand()
85
+ return sih(sirk, prand) + prand
49
86
 
50
87
 
51
88
  # -----------------------------------------------------------------------------
@@ -54,6 +91,7 @@ class MemberLock(enum.IntEnum):
54
91
  class CoordinatedSetIdentificationService(gatt.TemplateService):
55
92
  UUID = gatt.GATT_COORDINATED_SET_IDENTIFICATION_SERVICE
56
93
 
94
+ set_identity_resolving_key: bytes
57
95
  set_identity_resolving_key_characteristic: gatt.Characteristic
58
96
  coordinated_set_size_characteristic: Optional[gatt.Characteristic] = None
59
97
  set_member_lock_characteristic: Optional[gatt.Characteristic] = None
@@ -62,19 +100,21 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
62
100
  def __init__(
63
101
  self,
64
102
  set_identity_resolving_key: bytes,
103
+ set_identity_resolving_key_type: SirkType,
65
104
  coordinated_set_size: Optional[int] = None,
66
105
  set_member_lock: Optional[MemberLock] = None,
67
106
  set_member_rank: Optional[int] = None,
68
107
  ) -> None:
69
108
  characteristics = []
70
109
 
110
+ self.set_identity_resolving_key = set_identity_resolving_key
111
+ self.set_identity_resolving_key_type = set_identity_resolving_key_type
71
112
  self.set_identity_resolving_key_characteristic = gatt.Characteristic(
72
113
  uuid=gatt.GATT_SET_IDENTITY_RESOLVING_KEY_CHARACTERISTIC,
73
114
  properties=gatt.Characteristic.Properties.READ
74
115
  | gatt.Characteristic.Properties.NOTIFY,
75
116
  permissions=gatt.Characteristic.Permissions.READABLE,
76
- # TODO: Implement encrypted SIRK reader.
77
- value=struct.pack('B', SirkType.PLAINTEXT) + set_identity_resolving_key,
117
+ value=gatt.CharacteristicValue(read=self.on_sirk_read),
78
118
  )
79
119
  characteristics.append(self.set_identity_resolving_key_characteristic)
80
120
 
@@ -112,6 +152,24 @@ class CoordinatedSetIdentificationService(gatt.TemplateService):
112
152
 
113
153
  super().__init__(characteristics)
114
154
 
155
+ def on_sirk_read(self, _connection: Optional[device.Connection]) -> bytes:
156
+ if self.set_identity_resolving_key_type == SirkType.PLAINTEXT:
157
+ return bytes([SirkType.PLAINTEXT]) + self.set_identity_resolving_key
158
+ else:
159
+ raise NotImplementedError('TODO: Pending async Characteristic read.')
160
+
161
+ def get_advertising_data(self) -> bytes:
162
+ return bytes(
163
+ core.AdvertisingData(
164
+ [
165
+ (
166
+ core.AdvertisingData.RESOLVABLE_SET_IDENTIFIER,
167
+ generate_rsi(self.set_identity_resolving_key),
168
+ ),
169
+ ]
170
+ )
171
+ )
172
+
115
173
 
116
174
  # -----------------------------------------------------------------------------
117
175
  # Client
bumble/rfcomm.py CHANGED
@@ -118,8 +118,8 @@ CRC_TABLE = bytes([
118
118
  0XBA, 0X2B, 0X59, 0XC8, 0XBD, 0X2C, 0X5E, 0XCF
119
119
  ])
120
120
 
121
- RFCOMM_DEFAULT_INITIAL_RX_CREDITS = 7
122
- RFCOMM_DEFAULT_PREFERRED_MTU = 1280
121
+ RFCOMM_DEFAULT_WINDOW_SIZE = 16
122
+ RFCOMM_DEFAULT_MAX_FRAME_SIZE = 2000
123
123
 
124
124
  RFCOMM_DYNAMIC_CHANNEL_NUMBER_START = 1
125
125
  RFCOMM_DYNAMIC_CHANNEL_NUMBER_END = 30
@@ -438,14 +438,16 @@ class DLC(EventEmitter):
438
438
  multiplexer: Multiplexer,
439
439
  dlci: int,
440
440
  max_frame_size: int,
441
- initial_tx_credits: int,
441
+ window_size: int,
442
442
  ) -> None:
443
443
  super().__init__()
444
444
  self.multiplexer = multiplexer
445
445
  self.dlci = dlci
446
- self.rx_credits = RFCOMM_DEFAULT_INITIAL_RX_CREDITS
447
- self.rx_threshold = self.rx_credits // 2
448
- self.tx_credits = initial_tx_credits
446
+ self.max_frame_size = max_frame_size
447
+ self.window_size = window_size
448
+ self.rx_credits = window_size
449
+ self.rx_threshold = window_size // 2
450
+ self.tx_credits = window_size
449
451
  self.tx_buffer = b''
450
452
  self.state = DLC.State.INIT
451
453
  self.role = multiplexer.role
@@ -537,11 +539,11 @@ class DLC(EventEmitter):
537
539
  if len(data) and self.sink:
538
540
  self.sink(data) # pylint: disable=not-callable
539
541
 
540
- # Update the credits
541
- if self.rx_credits > 0:
542
- self.rx_credits -= 1
543
- else:
544
- logger.warning(color('!!! received frame with no rx credits', 'red'))
542
+ # Update the credits
543
+ if self.rx_credits > 0:
544
+ self.rx_credits -= 1
545
+ else:
546
+ logger.warning(color('!!! received frame with no rx credits', 'red'))
545
547
 
546
548
  # Check if there's anything to send (including credits)
547
549
  self.process_tx()
@@ -580,9 +582,9 @@ class DLC(EventEmitter):
580
582
  cl=0xE0,
581
583
  priority=7,
582
584
  ack_timer=0,
583
- max_frame_size=RFCOMM_DEFAULT_PREFERRED_MTU,
585
+ max_frame_size=self.max_frame_size,
584
586
  max_retransmissions=0,
585
- window_size=RFCOMM_DEFAULT_INITIAL_RX_CREDITS,
587
+ window_size=self.window_size,
586
588
  )
587
589
  mcc = RFCOMM_Frame.make_mcc(mcc_type=RFCOMM_MCC_PN_TYPE, c_r=0, data=bytes(pn))
588
590
  logger.debug(f'>>> PN Response: {pn}')
@@ -591,7 +593,7 @@ class DLC(EventEmitter):
591
593
 
592
594
  def rx_credits_needed(self) -> int:
593
595
  if self.rx_credits <= self.rx_threshold:
594
- return RFCOMM_DEFAULT_INITIAL_RX_CREDITS - self.rx_credits
596
+ return self.window_size - self.rx_credits
595
597
 
596
598
  return 0
597
599
 
@@ -843,7 +845,12 @@ class Multiplexer(EventEmitter):
843
845
  )
844
846
  await self.disconnection_result
845
847
 
846
- async def open_dlc(self, channel: int) -> DLC:
848
+ async def open_dlc(
849
+ self,
850
+ channel: int,
851
+ max_frame_size: int = RFCOMM_DEFAULT_MAX_FRAME_SIZE,
852
+ window_size: int = RFCOMM_DEFAULT_WINDOW_SIZE,
853
+ ) -> DLC:
847
854
  if self.state != Multiplexer.State.CONNECTED:
848
855
  if self.state == Multiplexer.State.OPENING:
849
856
  raise InvalidStateError('open already in progress')
@@ -855,9 +862,9 @@ class Multiplexer(EventEmitter):
855
862
  cl=0xF0,
856
863
  priority=7,
857
864
  ack_timer=0,
858
- max_frame_size=RFCOMM_DEFAULT_PREFERRED_MTU,
865
+ max_frame_size=max_frame_size,
859
866
  max_retransmissions=0,
860
- window_size=RFCOMM_DEFAULT_INITIAL_RX_CREDITS,
867
+ window_size=window_size,
861
868
  )
862
869
  mcc = RFCOMM_Frame.make_mcc(mcc_type=RFCOMM_MCC_PN_TYPE, c_r=1, data=bytes(pn))
863
870
  logger.debug(f'>>> Sending MCC: {pn}')
bumble/smp.py CHANGED
@@ -1090,7 +1090,7 @@ class Session:
1090
1090
  # We can now encrypt the connection with the short term key, so that we can
1091
1091
  # distribute the long term and/or other keys over an encrypted connection
1092
1092
  self.manager.device.host.send_command_sync(
1093
- HCI_LE_Enable_Encryption_Command( # type: ignore[call-arg]
1093
+ HCI_LE_Enable_Encryption_Command(
1094
1094
  connection_handle=self.connection.handle,
1095
1095
  random_number=bytes(8),
1096
1096
  encrypted_diversifier=0,
@@ -18,6 +18,7 @@
18
18
  from contextlib import asynccontextmanager
19
19
  import logging
20
20
  import os
21
+ from typing import Optional
21
22
 
22
23
  from .common import Transport, AsyncPipeSink, SnoopingTransport
23
24
  from ..snoop import create_snooper
@@ -52,8 +53,16 @@ def _wrap_transport(transport: Transport) -> Transport:
52
53
  async def open_transport(name: str) -> Transport:
53
54
  """
54
55
  Open a transport by name.
55
- The name must be <type>:<parameters>
56
- Where <parameters> depend on the type (and may be empty for some types).
56
+ The name must be <type>:<metadata><parameters>
57
+ Where <parameters> depend on the type (and may be empty for some types), and
58
+ <metadata> is either omitted, or a ,-separated list of <key>=<value> pairs,
59
+ enclosed in [].
60
+ If there are not metadata or parameter, the : after the <type> may be omitted.
61
+ Examples:
62
+ * usb:0
63
+ * usb:[driver=rtk]0
64
+ * android-netsim
65
+
57
66
  The supported types are:
58
67
  * serial
59
68
  * udp
@@ -71,87 +80,106 @@ async def open_transport(name: str) -> Transport:
71
80
  * android-netsim
72
81
  """
73
82
 
74
- return _wrap_transport(await _open_transport(name))
83
+ scheme, *tail = name.split(':', 1)
84
+ spec = tail[0] if tail else None
85
+ if spec:
86
+ # Metadata may precede the spec
87
+ if spec.startswith('['):
88
+ metadata_str, *tail = spec[1:].split(']')
89
+ spec = tail[0] if tail else None
90
+ metadata = dict([entry.split('=') for entry in metadata_str.split(',')])
91
+ else:
92
+ metadata = None
93
+
94
+ transport = await _open_transport(scheme, spec)
95
+ if metadata:
96
+ transport.source.metadata = { # type: ignore[attr-defined]
97
+ **metadata,
98
+ **getattr(transport.source, 'metadata', {}),
99
+ }
100
+ # pylint: disable=line-too-long
101
+ logger.debug(f'HCI metadata: {transport.source.metadata}') # type: ignore[attr-defined]
102
+
103
+ return _wrap_transport(transport)
75
104
 
76
105
 
77
106
  # -----------------------------------------------------------------------------
78
- async def _open_transport(name: str) -> Transport:
107
+ async def _open_transport(scheme: str, spec: Optional[str]) -> Transport:
79
108
  # pylint: disable=import-outside-toplevel
80
109
  # pylint: disable=too-many-return-statements
81
110
 
82
- scheme, *spec = name.split(':', 1)
83
111
  if scheme == 'serial' and spec:
84
112
  from .serial import open_serial_transport
85
113
 
86
- return await open_serial_transport(spec[0])
114
+ return await open_serial_transport(spec)
87
115
 
88
116
  if scheme == 'udp' and spec:
89
117
  from .udp import open_udp_transport
90
118
 
91
- return await open_udp_transport(spec[0])
119
+ return await open_udp_transport(spec)
92
120
 
93
121
  if scheme == 'tcp-client' and spec:
94
122
  from .tcp_client import open_tcp_client_transport
95
123
 
96
- return await open_tcp_client_transport(spec[0])
124
+ return await open_tcp_client_transport(spec)
97
125
 
98
126
  if scheme == 'tcp-server' and spec:
99
127
  from .tcp_server import open_tcp_server_transport
100
128
 
101
- return await open_tcp_server_transport(spec[0])
129
+ return await open_tcp_server_transport(spec)
102
130
 
103
131
  if scheme == 'ws-client' and spec:
104
132
  from .ws_client import open_ws_client_transport
105
133
 
106
- return await open_ws_client_transport(spec[0])
134
+ return await open_ws_client_transport(spec)
107
135
 
108
136
  if scheme == 'ws-server' and spec:
109
137
  from .ws_server import open_ws_server_transport
110
138
 
111
- return await open_ws_server_transport(spec[0])
139
+ return await open_ws_server_transport(spec)
112
140
 
113
141
  if scheme == 'pty':
114
142
  from .pty import open_pty_transport
115
143
 
116
- return await open_pty_transport(spec[0] if spec else None)
144
+ return await open_pty_transport(spec)
117
145
 
118
146
  if scheme == 'file':
119
147
  from .file import open_file_transport
120
148
 
121
149
  assert spec is not None
122
- return await open_file_transport(spec[0])
150
+ return await open_file_transport(spec)
123
151
 
124
152
  if scheme == 'vhci':
125
153
  from .vhci import open_vhci_transport
126
154
 
127
- return await open_vhci_transport(spec[0] if spec else None)
155
+ return await open_vhci_transport(spec)
128
156
 
129
157
  if scheme == 'hci-socket':
130
158
  from .hci_socket import open_hci_socket_transport
131
159
 
132
- return await open_hci_socket_transport(spec[0] if spec else None)
160
+ return await open_hci_socket_transport(spec)
133
161
 
134
162
  if scheme == 'usb':
135
163
  from .usb import open_usb_transport
136
164
 
137
- assert spec is not None
138
- return await open_usb_transport(spec[0])
165
+ assert spec
166
+ return await open_usb_transport(spec)
139
167
 
140
168
  if scheme == 'pyusb':
141
169
  from .pyusb import open_pyusb_transport
142
170
 
143
- assert spec is not None
144
- return await open_pyusb_transport(spec[0])
171
+ assert spec
172
+ return await open_pyusb_transport(spec)
145
173
 
146
174
  if scheme == 'android-emulator':
147
175
  from .android_emulator import open_android_emulator_transport
148
176
 
149
- return await open_android_emulator_transport(spec[0] if spec else None)
177
+ return await open_android_emulator_transport(spec)
150
178
 
151
179
  if scheme == 'android-netsim':
152
180
  from .android_netsim import open_android_netsim_transport
153
181
 
154
- return await open_android_netsim_transport(spec[0] if spec else None)
182
+ return await open_android_netsim_transport(spec)
155
183
 
156
184
  raise ValueError('unknown transport scheme')
157
185
 
@@ -69,7 +69,7 @@ async def open_android_emulator_transport(spec: Optional[str]) -> Transport:
69
69
  mode = 'host'
70
70
  server_host = 'localhost'
71
71
  server_port = '8554'
72
- if spec is not None:
72
+ if spec:
73
73
  params = spec.split(',')
74
74
  for param in params:
75
75
  if param.startswith('mode='):
@@ -21,7 +21,7 @@ import struct
21
21
  import asyncio
22
22
  import logging
23
23
  import io
24
- from typing import ContextManager, Tuple, Optional, Protocol, Dict
24
+ from typing import Any, ContextManager, Tuple, Optional, Protocol, Dict
25
25
 
26
26
  from bumble import hci
27
27
  from bumble.colors import color
@@ -42,6 +42,7 @@ HCI_PACKET_INFO: Dict[int, Tuple[int, int, str]] = {
42
42
  hci.HCI_ACL_DATA_PACKET: (2, 2, 'H'),
43
43
  hci.HCI_SYNCHRONOUS_DATA_PACKET: (1, 2, 'B'),
44
44
  hci.HCI_EVENT_PACKET: (1, 1, 'B'),
45
+ hci.HCI_ISO_DATA_PACKET: (2, 2, 'H'),
45
46
  }
46
47
 
47
48
 
@@ -59,10 +59,7 @@ async def open_hci_socket_transport(spec: Optional[str]) -> Transport:
59
59
  ) from error
60
60
 
61
61
  # Compute the adapter index
62
- if spec is None:
63
- adapter_index = 0
64
- else:
65
- adapter_index = int(spec)
62
+ adapter_index = int(spec) if spec else 0
66
63
 
67
64
  # Bind the socket
68
65
  # NOTE: since Python doesn't support binding with the required address format (yet),
bumble/transport/usb.py CHANGED
@@ -108,7 +108,7 @@ async def open_usb_transport(spec: str) -> Transport:
108
108
  USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER,
109
109
  )
110
110
 
111
- READ_SIZE = 1024
111
+ READ_SIZE = 4096
112
112
 
113
113
  class UsbPacketSink:
114
114
  def __init__(self, device, acl_out):
bumble/utils.py CHANGED
@@ -280,17 +280,14 @@ class AsyncRunner:
280
280
  def wrapper(*args, **kwargs):
281
281
  coroutine = func(*args, **kwargs)
282
282
  if queue is None:
283
- # Create a task to run the coroutine
283
+ # Spawn the coroutine as a task
284
284
  async def run():
285
285
  try:
286
286
  await coroutine
287
287
  except Exception:
288
- logger.warning(
289
- f'{color("!!! Exception in wrapper:", "red")} '
290
- f'{traceback.format_exc()}'
291
- )
288
+ logger.exception(color("!!! Exception in wrapper:", "red"))
292
289
 
293
- asyncio.create_task(run())
290
+ AsyncRunner.spawn(run())
294
291
  else:
295
292
  # Queue the coroutine to be awaited by the work queue
296
293
  queue.enqueue(coroutine)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bumble
3
- Version: 0.0.180
3
+ Version: 0.0.181
4
4
  Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
5
5
  Home-page: https://github.com/google/bumble
6
6
  Author: Google
@@ -1,48 +1,49 @@
1
1
  bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
2
- bumble/_version.py,sha256=5oaj9BgfeJteuh4NknBm42CZG1RsQs3FBEs6JGSoHXE,415
2
+ bumble/_version.py,sha256=kEdx0bkEZGn_C1UTJUkEEzk1Q-ubIPoEpesXzdNR5jw,415
3
3
  bumble/a2dp.py,sha256=NqdmHIeEe3kThoHqdOgC3Wkywc6H7zqwGbjRYYFtNoQ,22321
4
4
  bumble/at.py,sha256=kdrcsx2C8Rg61EWESD2QHwpZntkXkRBJLrPn9auv9K8,2961
5
- bumble/att.py,sha256=emTsIoc_C067QE3osdyocic52Wr8N5TJdkpZeJJH6dk,31328
5
+ bumble/att.py,sha256=_HrhlCl4CXZM9mmBLTCPq5v2OW9eZ0-H6cSu5_jG_5o,32365
6
6
  bumble/avdtp.py,sha256=dzipF0kXu3OPWVdy347539US9M20n9BeOi37MDvCpaE,77097
7
7
  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=9lyDMD836oK5nqvwfjFEQgQ3bTl56qn4kT5ix-HJc8w,45204
11
+ bumble/controller.py,sha256=Wd6TpcJJCvXaIw9Kf9D7FKc7xQYSm1xJS9usnCYsYDE,46131
12
12
  bumble/core.py,sha256=HjRpUseyVsueNkZ-KhPPTf1vNkNnI02eNcaOe685V_0,52949
13
- bumble/crypto.py,sha256=o3lzVPeiaPX_aNeeg2Ir-UHiGpNU7Z2VyimKzDwMsHw,9012
13
+ bumble/crypto.py,sha256=L6z3dn9-dgKYRtOM6O3F6n6Ju4PwTM3LAFJtCg_ie78,9382
14
14
  bumble/decoder.py,sha256=N9nMvuVhuwpnfw7EDVuNe9uYY6B6c3RY2dh8RhRPC1U,9608
15
- bumble/device.py,sha256=JMRrTiK6ua9tpNRYL_491NpwC0mjBsd3TDBUVzj7Ms4,136996
15
+ bumble/device.py,sha256=jKIJ_V-xMV5R-FTHY18r1PSemuWd9oZo2CKKDxM3swU,153640
16
16
  bumble/gap.py,sha256=axlOZIv99357Ehq2vMokeioU85z81qdQvplGn0pF70Q,2137
17
- bumble/gatt.py,sha256=ASbYauOKyOHgHrN67Ue12MFU1rayqePG5mEI0vaS0SY,38191
17
+ bumble/gatt.py,sha256=L3RLHiyAKMIZHLfkbH09V9yTNNKymacDtpvwmSoRQRY,38405
18
18
  bumble/gatt_client.py,sha256=JuyFUHEc5xd2astr2XCZrTiAla2FoDkKe4lH1oSQOPA,42551
19
- bumble/gatt_server.py,sha256=pLkydlyqQTyi9g89-WH7tZTIVtOd8gi6bpUn2b-OzAU,36728
20
- bumble/hci.py,sha256=Kbu2oESm6Xa3AkdyVzbU64OLzAzHXiQfLTO1tfGfoGo,231749
21
- bumble/helpers.py,sha256=M7yGMbyI6_ZxIUNZA2krWJXP1qkedVcCOYog1lNPm-M,10084
22
- bumble/hfp.py,sha256=SzoYpcXgY3LEHVEKUT64PFAJ7G5TZ29hhBbNnW9v8Cs,39067
23
- bumble/hid.py,sha256=O_FzevrBsCVzuCL5v28NY5VoXmTNQ_4ditW3MYuOaP8,11341
24
- bumble/host.py,sha256=woncM0_1siA9WKIAVkKjut3dUKiiDJbVzkTj3V3WKIw,36999
19
+ bumble/gatt_server.py,sha256=Ebu-VBIPUPZiwHl207ncuC-tCF7oUkR4B8BlE0GruY8,37112
20
+ bumble/hci.py,sha256=gotHTh9hpcVDKztCe-SE5wdV2LosHVN94mNH5AhK-EI,237190
21
+ bumble/helpers.py,sha256=pxSNAuLnMhU8eg3AdqUNoCyA8Q5-GZ1_SdgFS2nOp_M,10412
22
+ bumble/hfp.py,sha256=8b6zR4YhGl9Rc2vxL9AFAYZ2kyxe5SaLWUUXHDpeEoU,38487
23
+ bumble/hid.py,sha256=KiY6v5G8qAQoP_IzjiY68b7vF9izpwJyGovUHRH3AmA,20584
24
+ bumble/host.py,sha256=dUoy421swHti2d0rNPR61_bsSkPqanHRYS7wHwsPswU,39154
25
25
  bumble/keys.py,sha256=_ibaJ4CxM5zyxnTp7wnihIQSsEAEkCiPxx9qHsGA04Q,12601
26
- bumble/l2cap.py,sha256=1wtMmpVMAY5Djfo7Rs8pKKU6ulBqutCY7UGvv8Vnfck,80778
26
+ bumble/l2cap.py,sha256=yNfS76khPHbhabfCWP-iRh9Po3GGzv3tk_R0ESBclxg,80752
27
27
  bumble/link.py,sha256=dvb3fdmV8689A4rLhFVHHKMR3u4wQbUapFi-GeheK0M,20220
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=JufZgWWUvFRQuNQ0qqi5HrDYMU1BzL-z-1ElmkAX76s,33690
30
+ bumble/rfcomm.py,sha256=9q0c8ngZV-F0OzC5CWdocHfj4nsw1pk9qdey9jycSyY,33807
31
31
  bumble/sdp.py,sha256=D5pG7cuAoFBygdmxExyuRhU4aVsvkWB9WQVnONCU-L0,44854
32
- bumble/smp.py,sha256=gs6KkgWMbfWJ8-ZEqTAWe3wgqvqe3McSG989bXDvMgs,76261
32
+ bumble/smp.py,sha256=NmDWTxXJIzVnwFqG3M2OduduP_ryGh5y886RCigqPvs,76235
33
33
  bumble/snoop.py,sha256=_QfF36eylBW6Snd-_KYOwKaGiM8i_Ed-B5XoFIPt3Dg,5631
34
- bumble/utils.py,sha256=yE5w1h11_t7PYq0HUQ9swpXHOs7XC-MTQXFIeTsHVsU,14216
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=S8J_kRovna_3t3AJMHETjdGxEQH-kK1ipuWNHqVP--4,44220
38
- bumble/apps/console.py,sha256=Qgv0OYwfriQHyoZ0Wj7qwrpnKzG4juRHkgvp0VdVpec,46069
39
- bumble/apps/controller_info.py,sha256=FCfTalW1vHCp34mzRh78DuFl0os5tULPkcdNQCMaCDI,7157
37
+ bumble/apps/bench.py,sha256=YQT-Ord7o7_D8R2EcJTJwr50EzEuV1ezAvRGJijBapM,45735
38
+ bumble/apps/ble_rpa_tool.py,sha256=ZQtsbfnLPd5qUAkEBPpNgJLRynBBc7q_9cDHKUW2SQ0,1701
39
+ bumble/apps/console.py,sha256=rVR2jmP6Yd76B4zzGPYnpJFtgeYgq19CL6DMSe2-A1M,46093
40
+ bumble/apps/controller_info.py,sha256=K9Yci0cJvVviUQdNLOKFGAgSDurv4oQddHdYGcTI3vk,8410
40
41
  bumble/apps/controllers.py,sha256=R6XJ1XpyuXlyqSCmI7PromVIcoYTcYfpmO-TqTYXnUI,2326
41
42
  bumble/apps/gatt_dump.py,sha256=-dCvCgjuHAp0h1zxm-gmqB4lVlSdas1Kp4cpzzx4gGw,4245
42
43
  bumble/apps/gg_bridge.py,sha256=JdW5QT6xN9c2XDDJoHDRo5W3N_RdVkCtTmlcOsJhlx8,14693
43
44
  bumble/apps/hci_bridge.py,sha256=KISv352tKnsQsoxjkDiCQbMFmhnPWdnug5wSFAAXxEs,4033
44
45
  bumble/apps/l2cap_bridge.py,sha256=9yjpznRivCtwcO1ivLmEvOAe3fdqyJZLxN82uwZqLiQ,12844
45
- bumble/apps/pair.py,sha256=vngIbodDbeX5Zt1_mI-OJcgQZOz7xuQDgA4U2Y5a1c0,17588
46
+ bumble/apps/pair.py,sha256=COU2D7YAIn4lo5iuM0ClObA1zZqQCdrXOcnsiCm0YlQ,17529
46
47
  bumble/apps/pandora_server.py,sha256=5qaoLCpcZE2KsGO21-7t6Vg4dBjBWbnyOQXwrLhxkuE,1397
47
48
  bumble/apps/scan.py,sha256=_fMG_1j1HZQ_8SrJ0ZOxJaWB1OR4mKBaZuQMgm8viYI,7439
48
49
  bumble/apps/show.py,sha256=lvy-_u_fRlswBRAd_peXbR9QQFC_NEaygRYFicmAGSg,4738
@@ -57,8 +58,9 @@ bumble/apps/speaker/speaker.css,sha256=nyM5TjzDYkkLwFzsaIOuTSngzSvgDnkLe0Z-fAn1_
57
58
  bumble/apps/speaker/speaker.html,sha256=kfAZ5oZSFc9ygBFIUuZEn5LUNQnHBvrnuHU6VAptyiU,1188
58
59
  bumble/apps/speaker/speaker.js,sha256=DrT831yg3oBXKZ5usnfZjRU9X6Nw3zjIWSkz6sIgVtw,9373
59
60
  bumble/apps/speaker/speaker.py,sha256=lV4N-M8V95UwioTMfcfsVImvQ-jmMehU6jJhA_16A24,24196
60
- bumble/drivers/__init__.py,sha256=KYfHln8_kyF66qylE4AFPZ-KXzmnE1raXETwmnUe-TY,3014
61
- bumble/drivers/rtk.py,sha256=XhxXdpVEQGOrW3N1Us3IxmttsALL2gquerFYDtE4tQo,21072
61
+ bumble/drivers/__init__.py,sha256=0xJfjhq6LHBvnQfg0vOt4u6lGcut1sxSi4g4-85o5yY,3166
62
+ bumble/drivers/common.py,sha256=pS783hudolLZAzF8IUWp7g6TXyQsUCEzqCsd1VGeYfQ,1507
63
+ bumble/drivers/rtk.py,sha256=MMxmUo85VkRhFuhbc9SJEZVL9dnRjmsU9k8djmbUGcA,21369
62
64
  bumble/pandora/__init__.py,sha256=5NBVmndeTulANawift0jPT9ISp562wyIHTZ-4uP34Mg,3283
63
65
  bumble/pandora/config.py,sha256=KD85n3oRbuvD65sRah2H0gpxEW4YbD7HbYbsxdcpDDA,2388
64
66
  bumble/pandora/device.py,sha256=LFqCWrgYkQWrFUSKArsAABXkge8sB2DhvaQoEsC4Jn0,5344
@@ -67,9 +69,11 @@ bumble/pandora/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
69
  bumble/pandora/security.py,sha256=cZoG3hPBCs6F_HQsJljrag9dZw-fbe8c9a6RrP13lIA,22000
68
70
  bumble/pandora/utils.py,sha256=Fq4glL0T5cJ2FODoDotmDNdYFOkTOR7DyyL8vkcxp20,3949
69
71
  bumble/profiles/__init__.py,sha256=yBGC8Ti5LvZuoh1F42XtfrBilb39T77_yuxESZeX2yI,581
70
- bumble/profiles/asha_service.py,sha256=wKAYgc36a9mBWx9wfIofA2e1fpnErrjHM-U1GfCX9LQ,7200
72
+ bumble/profiles/asha_service.py,sha256=J4i5jkJciZWMtTWJ1zGJkEx65DlAEIADqjCRYf_CWNs,7220
73
+ bumble/profiles/bap.py,sha256=GJOn1t7ZGcm-woFnI4JFx4I4Z_rnv5GQIB4dnrwULlY,44900
71
74
  bumble/profiles/battery_service.py,sha256=w-uF4jLoDozJOoykimb2RkrKjVyCke6ts2-h-F1PYyc,2292
72
- bumble/profiles/csip.py,sha256=PQ8A_5pJa2R96g4_wKM4JGgEFuvK7SAjoXS1xDLXt4g,6144
75
+ bumble/profiles/cap.py,sha256=6gH7oOnUKjOggMPuB7rtbwj0AneoNmnWzQ_iR3io8e0,1945
76
+ bumble/profiles/csip.py,sha256=cnbZN4UuxheBFtWDe_42U7mzp9XWJ_WV8mVlTNbIGZk,8070
73
77
  bumble/profiles/device_information_service.py,sha256=H1Db4BAOnsC-rRtfpoAIsDETYT4F9yM_WgByn_3LfRQ,5658
74
78
  bumble/profiles/heart_rate_service.py,sha256=7V2LGcWLp6RurjWxsVgMWr3wPDt5aS9qjNxTbHcOK6o,8575
75
79
  bumble/profiles/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -77,12 +81,12 @@ bumble/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
81
  bumble/tools/generate_company_id_list.py,sha256=ysbPb3zmxKFBiSQ_MBG2r15-sqR5P_GWT6i0YUTTXOM,1736
78
82
  bumble/tools/rtk_fw_download.py,sha256=kTxR9UaNiD8VtzWVAVxsCx5c_tkfz9OKe-vxdlfKQRY,5454
79
83
  bumble/tools/rtk_util.py,sha256=TwZhupHQrQYsYHLdRGyzXKd24pwCk8kkzqK1Rj2guco,5087
80
- bumble/transport/__init__.py,sha256=Yz2aau52Hi0B6sh06WtfJzMwOYr1Y5VlqjkJCw4hcbA,5901
81
- bumble/transport/android_emulator.py,sha256=n6nPti0eb6JqPkAj5-fdtiMfSzA2Hgd2q4B1arudIhM,4333
84
+ bumble/transport/__init__.py,sha256=_gd1PnGJsRyojkfS2ZpnW3RnyKjuo9veN7tpsgwd2aw,6769
85
+ bumble/transport/android_emulator.py,sha256=eH8H1aB7MvQ8lpdwVG6SGKg5uwCRb_aPJk8pPwoTXSY,4321
82
86
  bumble/transport/android_netsim.py,sha256=SVh-IUZ2bhcIESZFGzOsofybsi4H0qoBRwBieeqUINE,16215
83
- bumble/transport/common.py,sha256=pOnr1GdIQtz8BM7-O3Q4OlEbMs5tfU3Fc9zFo2b62S0,15607
87
+ bumble/transport/common.py,sha256=ZrW-CuEqf7xNcCK29gvC9azOLgPA7IqKgoyfKQxdsLo,15654
84
88
  bumble/transport/file.py,sha256=eVM2V6Nk2nDAFdE7Rt01ZI3JdTovsH9OEU1gKYPJjpE,2010
85
- bumble/transport/hci_socket.py,sha256=y9hrIY7QIgP994lffJHaAi2jfpC9FCANhzHO5F6k3vk,6377
89
+ bumble/transport/hci_socket.py,sha256=EdgWi3-O5yvYcH4R4BkPtG79pnUo7GQtXWawuUHDoDQ,6331
86
90
  bumble/transport/pty.py,sha256=grTl-yvjMWHflNwuME4ccVqDbk6NIEgQMgH6Y9lf1fU,2732
87
91
  bumble/transport/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
92
  bumble/transport/pyusb.py,sha256=cE6VPDNE6a79AYR1D4H55WDLkFOM4sLPNDSYiX4worc,11496
@@ -90,7 +94,7 @@ bumble/transport/serial.py,sha256=loQxkeG7uE09enXWg2uGbxi6CeG70wn3kzPbEwULKw4,24
90
94
  bumble/transport/tcp_client.py,sha256=deyUJYpj04QE00Mw_PTU5PHPA6mr1Nui3f5-QCy2zOw,1854
91
95
  bumble/transport/tcp_server.py,sha256=hixsSzB-fmzR1yuiHDWd1WhqAia3UA4Cog1Wu6DCLeg,3211
92
96
  bumble/transport/udp.py,sha256=di8I6HHACgBx3un-dzAahz9lTIUrh4LdeuYpeoifQEM,2239
93
- bumble/transport/usb.py,sha256=wSBebCda0VGRKfekCjGVx8ZtBOwJWCkvtIHIA-CEfcw,21024
97
+ bumble/transport/usb.py,sha256=Q7C3iTnNkTHDFmaT96go9r8rLo0PfqSzlmSPCXEcpZs,21024
94
98
  bumble/transport/vhci.py,sha256=iI2WpighnvIP5zeyJUFSbjEdmCo24CWMdICamIcyJck,2250
95
99
  bumble/transport/ws_client.py,sha256=9gqm5jlVT_H6LfwsQwPpky07CINhgOK96ef53SMAxms,1757
96
100
  bumble/transport/ws_server.py,sha256=goe4xx7OnZiJy1a00Bg0CXM8uJhsGXbsijMYq2n62bI,3328
@@ -127,9 +131,9 @@ bumble/vendor/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
127
131
  bumble/vendor/android/hci.py,sha256=GZrkhaWmcMt1JpnRhv0NoySGkf2H4lNUV2f_omRZW0I,10741
128
132
  bumble/vendor/zephyr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
133
  bumble/vendor/zephyr/hci.py,sha256=d83bC0TvT947eN4roFjLkQefWtHOoNsr4xib2ctSkvA,3195
130
- bumble-0.0.180.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
131
- bumble-0.0.180.dist-info/METADATA,sha256=U4ohfLC-oVuJa-g-Dllxl0QaucDvft6qL2o7O43T3Sw,5681
132
- bumble-0.0.180.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
133
- bumble-0.0.180.dist-info/entry_points.txt,sha256=AjCwgm9SvZDOhV7T6jWwAhWdE728pd759LQCscMLjnM,765
134
- bumble-0.0.180.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
135
- bumble-0.0.180.dist-info/RECORD,,
134
+ bumble-0.0.181.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
135
+ bumble-0.0.181.dist-info/METADATA,sha256=VEwA-oV3z1wx7OJDfOAuV9XVmRsMxhOON11AdcwjKW8,5681
136
+ bumble-0.0.181.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
137
+ bumble-0.0.181.dist-info/entry_points.txt,sha256=96i3Nmc2zjWeSWTYH2x3lPV7nH1KvOK2jrF0spSjpYc,817
138
+ bumble-0.0.181.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
139
+ bumble-0.0.181.dist-info/RECORD,,
@@ -1,5 +1,6 @@
1
1
  [console_scripts]
2
2
  bumble-bench = bumble.apps.bench:main
3
+ bumble-ble-rpa-tool = bumble.apps.ble_rpa_tool:main
3
4
  bumble-console = bumble.apps.console:main
4
5
  bumble-controller-info = bumble.apps.controller_info:main
5
6
  bumble-gatt-dump = bumble.apps.gatt_dump:main