bumble 0.0.215__py3-none-any.whl → 0.0.217__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 CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.215'
32
- __version_tuple__ = version_tuple = (0, 0, 215)
31
+ __version__ = version = '0.0.217'
32
+ __version_tuple__ = version_tuple = (0, 0, 217)
33
33
 
34
34
  __commit_id__ = commit_id = None
bumble/device.py CHANGED
@@ -2169,10 +2169,12 @@ def with_connection_from_handle(function):
2169
2169
  # Decorator that converts the first argument from a bluetooth address to a connection
2170
2170
  def with_connection_from_address(function):
2171
2171
  @functools.wraps(function)
2172
- def wrapper(self, address: hci.Address, *args, **kwargs):
2173
- for connection in self.connections.values():
2172
+ def wrapper(device: Device, address: hci.Address, *args, **kwargs):
2173
+ if connection := device.pending_connections.get(address):
2174
+ return function(device, connection, *args, **kwargs)
2175
+ for connection in device.connections.values():
2174
2176
  if connection.peer_address == address:
2175
- return function(self, connection, *args, **kwargs)
2177
+ return function(device, connection, *args, **kwargs)
2176
2178
  raise ObjectLookupError('no connection for address')
2177
2179
 
2178
2180
  return wrapper
@@ -2182,11 +2184,13 @@ def with_connection_from_address(function):
2182
2184
  # connection
2183
2185
  def try_with_connection_from_address(function):
2184
2186
  @functools.wraps(function)
2185
- def wrapper(self, address, *args, **kwargs):
2186
- for connection in self.connections.values():
2187
+ def wrapper(device: Device, address: hci.Address, *args, **kwargs):
2188
+ if connection := device.pending_connections.get(address):
2189
+ return function(device, connection, address, *args, **kwargs)
2190
+ for connection in device.connections.values():
2187
2191
  if connection.peer_address == address:
2188
- return function(self, connection, address, *args, **kwargs)
2189
- return function(self, None, address, *args, **kwargs)
2192
+ return function(device, connection, address, *args, **kwargs)
2193
+ return function(device, None, address, *args, **kwargs)
2190
2194
 
2191
2195
  return wrapper
2192
2196
 
@@ -2234,7 +2238,7 @@ class Device(utils.CompositeEventEmitter):
2234
2238
  scan_response_data: bytes
2235
2239
  cs_capabilities: ChannelSoundingCapabilities | None = None
2236
2240
  connections: dict[int, Connection]
2237
- connection_roles: dict[hci.Address, hci.Role]
2241
+ pending_connections: dict[hci.Address, Connection]
2238
2242
  classic_pending_accepts: dict[
2239
2243
  hci.Address,
2240
2244
  list[asyncio.Future[Union[Connection, tuple[hci.Address, int, int]]]],
@@ -2356,9 +2360,9 @@ class Device(utils.CompositeEventEmitter):
2356
2360
  self.le_connecting = False
2357
2361
  self.disconnecting = False
2358
2362
  self.connections = {} # Connections, by connection handle
2359
- self.connection_roles = (
2363
+ self.pending_connections = (
2360
2364
  {}
2361
- ) # Local connection roles, by BD address (BR/EDR only)
2365
+ ) # Pending connections, by BD address (BR/EDR only)
2362
2366
  self.sco_links = {} # ScoLinks, by connection handle (BR/EDR only)
2363
2367
  self.cis_links = {} # CisLinks, by connection handle (LE only)
2364
2368
  self._pending_cis = {} # (CIS_ID, CIG_ID), by CIS_handle
@@ -3827,7 +3831,17 @@ class Device(utils.CompositeEventEmitter):
3827
3831
  )
3828
3832
  else:
3829
3833
  # Save pending connection
3830
- self.connection_roles[peer_address] = hci.Role.CENTRAL
3834
+ self.pending_connections[peer_address] = Connection(
3835
+ device=self,
3836
+ handle=0,
3837
+ transport=core.PhysicalTransport.BR_EDR,
3838
+ self_address=self.public_address,
3839
+ self_resolvable_address=None,
3840
+ peer_address=peer_address,
3841
+ peer_resolvable_address=None,
3842
+ role=hci.Role.CENTRAL,
3843
+ parameters=Connection.Parameters(0, 0, 0),
3844
+ )
3831
3845
 
3832
3846
  # TODO: allow passing other settings
3833
3847
  result = await self.send_command(
@@ -3880,7 +3894,7 @@ class Device(utils.CompositeEventEmitter):
3880
3894
  self.le_connecting = False
3881
3895
  self.connect_own_address_type = None
3882
3896
  else:
3883
- self.connection_roles.pop(peer_address, None)
3897
+ self.pending_connections.pop(peer_address, None)
3884
3898
 
3885
3899
  async def accept(
3886
3900
  self,
@@ -3978,7 +3992,17 @@ class Device(utils.CompositeEventEmitter):
3978
3992
  # Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
3979
3993
  # command, this connection is still considered Peripheral until an eventual
3980
3994
  # role change event.
3981
- self.connection_roles[peer_address] = hci.Role.PERIPHERAL
3995
+ self.pending_connections[peer_address] = Connection(
3996
+ device=self,
3997
+ handle=0,
3998
+ transport=core.PhysicalTransport.BR_EDR,
3999
+ self_address=self.public_address,
4000
+ self_resolvable_address=None,
4001
+ peer_address=peer_address,
4002
+ peer_resolvable_address=None,
4003
+ role=hci.Role.PERIPHERAL,
4004
+ parameters=Connection.Parameters(0, 0, 0),
4005
+ )
3982
4006
 
3983
4007
  try:
3984
4008
  # Accept connection request
@@ -3996,7 +4020,7 @@ class Device(utils.CompositeEventEmitter):
3996
4020
  finally:
3997
4021
  self.remove_listener(self.EVENT_CONNECTION, on_connection)
3998
4022
  self.remove_listener(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
3999
- self.connection_roles.pop(peer_address, None)
4023
+ self.pending_connections.pop(peer_address, None)
4000
4024
 
4001
4025
  @asynccontextmanager
4002
4026
  async def connect_as_gatt(self, peer_address: Union[hci.Address, str]):
@@ -5441,29 +5465,27 @@ class Device(utils.CompositeEventEmitter):
5441
5465
  connection_handle: int,
5442
5466
  peer_address: hci.Address,
5443
5467
  ) -> None:
5444
- connection_role = self.connection_roles.pop(peer_address, hci.Role.PERIPHERAL)
5468
+ if connection := self.pending_connections.pop(peer_address, None):
5469
+ connection.handle = connection_handle
5470
+ else:
5471
+ # Create a new connection
5472
+ connection = Connection(
5473
+ device=self,
5474
+ handle=connection_handle,
5475
+ transport=PhysicalTransport.BR_EDR,
5476
+ self_address=self.public_address,
5477
+ self_resolvable_address=None,
5478
+ peer_address=peer_address,
5479
+ peer_resolvable_address=None,
5480
+ role=hci.Role.PERIPHERAL,
5481
+ parameters=Connection.Parameters(0.0, 0, 0.0),
5482
+ )
5445
5483
 
5446
- logger.debug(
5447
- f'*** Connection: [0x{connection_handle:04X}] '
5448
- f'{peer_address} {hci.HCI_Constant.role_name(connection_role)}'
5449
- )
5484
+ logger.debug('*** %s', connection)
5450
5485
  if connection_handle in self.connections:
5451
5486
  logger.warning(
5452
5487
  'new connection reuses the same handle as a previous connection'
5453
5488
  )
5454
-
5455
- # Create a new connection
5456
- connection = Connection(
5457
- device=self,
5458
- handle=connection_handle,
5459
- transport=PhysicalTransport.BR_EDR,
5460
- self_address=self.public_address,
5461
- self_resolvable_address=None,
5462
- peer_address=peer_address,
5463
- peer_resolvable_address=None,
5464
- role=connection_role,
5465
- parameters=Connection.Parameters(0.0, 0, 0.0),
5466
- )
5467
5489
  self.connections[connection_handle] = connection
5468
5490
 
5469
5491
  self.emit(self.EVENT_CONNECTION, connection)
@@ -5618,7 +5640,9 @@ class Device(utils.CompositeEventEmitter):
5618
5640
 
5619
5641
  # FIXME: Explore a delegate-model for BR/EDR wait connection #56.
5620
5642
  @host_event_handler
5621
- def on_connection_request(self, bd_addr, class_of_device, link_type):
5643
+ def on_connection_request(
5644
+ self, bd_addr: hci.Address, class_of_device: int, link_type: int
5645
+ ):
5622
5646
  logger.debug(f'*** Connection request: {bd_addr}')
5623
5647
 
5624
5648
  # Handle SCO request.
@@ -5647,7 +5671,17 @@ class Device(utils.CompositeEventEmitter):
5647
5671
  # device configuration is set to accept any incoming connection
5648
5672
  elif self.classic_accept_any:
5649
5673
  # Save pending connection
5650
- self.connection_roles[bd_addr] = hci.Role.PERIPHERAL
5674
+ self.pending_connections[bd_addr] = Connection(
5675
+ device=self,
5676
+ handle=0,
5677
+ transport=core.PhysicalTransport.BR_EDR,
5678
+ self_address=self.public_address,
5679
+ self_resolvable_address=None,
5680
+ peer_address=bd_addr,
5681
+ peer_resolvable_address=None,
5682
+ role=hci.Role.PERIPHERAL,
5683
+ parameters=Connection.Parameters(0, 0, 0),
5684
+ )
5651
5685
 
5652
5686
  self.host.send_command_sync(
5653
5687
  hci.HCI_Accept_Connection_Request_Command(
@@ -5958,7 +5992,7 @@ class Device(utils.CompositeEventEmitter):
5958
5992
  @host_event_handler
5959
5993
  @try_with_connection_from_address
5960
5994
  def on_remote_name(
5961
- self, connection: Connection, address: hci.Address, remote_name: bytes
5995
+ self, connection: Optional[Connection], address: hci.Address, remote_name: bytes
5962
5996
  ):
5963
5997
  # Try to decode the name
5964
5998
  try:
@@ -5977,7 +6011,7 @@ class Device(utils.CompositeEventEmitter):
5977
6011
  @host_event_handler
5978
6012
  @try_with_connection_from_address
5979
6013
  def on_remote_name_failure(
5980
- self, connection: Connection, address: hci.Address, error: int
6014
+ self, connection: Optional[Connection], address: hci.Address, error: int
5981
6015
  ):
5982
6016
  if connection:
5983
6017
  connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
@@ -6409,21 +6443,20 @@ class Device(utils.CompositeEventEmitter):
6409
6443
 
6410
6444
  # [Classic only]
6411
6445
  @host_event_handler
6412
- @try_with_connection_from_address
6446
+ @with_connection_from_address
6413
6447
  def on_role_change(
6414
- self, connection: Connection, peer_address: hci.Address, new_role: hci.Role
6448
+ self,
6449
+ connection: Connection,
6450
+ new_role: hci.Role,
6415
6451
  ):
6416
- if connection:
6417
- connection.role = new_role
6418
- connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
6419
- else:
6420
- self.connection_roles[peer_address] = new_role
6452
+ connection.role = new_role
6453
+ connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
6421
6454
 
6422
6455
  # [Classic only]
6423
6456
  @host_event_handler
6424
6457
  @try_with_connection_from_address
6425
6458
  def on_role_change_failure(
6426
- self, connection: Connection, address: hci.Address, error: int
6459
+ self, connection: Optional[Connection], address: hci.Address, error: int
6427
6460
  ):
6428
6461
  if connection:
6429
6462
  connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
bumble/host.py CHANGED
@@ -550,7 +550,7 @@ class Host(utils.EventEmitter):
550
550
  logger.debug(
551
551
  'HCI LE flow control: '
552
552
  f'le_acl_data_packet_length={le_acl_data_packet_length},'
553
- f'total_num_le_acl_data_packets={total_num_le_acl_data_packets}'
553
+ f'total_num_le_acl_data_packets={total_num_le_acl_data_packets},'
554
554
  f'iso_data_packet_length={iso_data_packet_length},'
555
555
  f'total_num_iso_data_packets={total_num_iso_data_packets}'
556
556
  )
bumble/profiles/hap.py CHANGED
@@ -273,12 +273,19 @@ class HearingAccessService(gatt.TemplateService):
273
273
  def on_disconnection(_reason) -> None:
274
274
  self.currently_connected_clients.discard(connection)
275
275
 
276
+ @connection.on(connection.EVENT_CONNECTION_ATT_MTU_UPDATE)
277
+ def on_mtu_update(*_: Any) -> None:
278
+ self.on_incoming_connection(connection)
279
+
280
+ @connection.on(connection.EVENT_CONNECTION_ENCRYPTION_CHANGE)
281
+ def on_encryption_change(*_: Any) -> None:
282
+ self.on_incoming_connection(connection)
283
+
276
284
  @connection.on(connection.EVENT_PAIRING)
277
285
  def on_pairing(*_: Any) -> None:
278
- self.on_incoming_paired_connection(connection)
286
+ self.on_incoming_connection(connection)
279
287
 
280
- if connection.peer_resolvable_address:
281
- self.on_incoming_paired_connection(connection)
288
+ self.on_incoming_connection(connection)
282
289
 
283
290
  self.hearing_aid_features_characteristic = gatt.Characteristic(
284
291
  uuid=gatt.GATT_HEARING_AID_FEATURES_CHARACTERISTIC,
@@ -315,9 +322,30 @@ class HearingAccessService(gatt.TemplateService):
315
322
  ]
316
323
  )
317
324
 
318
- def on_incoming_paired_connection(self, connection: Connection):
325
+ def on_incoming_connection(self, connection: Connection):
319
326
  '''Setup initial operations to handle a remote bonded HAP device'''
320
327
  # TODO Should we filter on HAP device only ?
328
+
329
+ if not connection.is_encrypted:
330
+ logging.debug(f'HAS: {connection.peer_address} is not encrypted')
331
+ return
332
+
333
+ if not connection.peer_resolvable_address:
334
+ logging.debug(f'HAS: {connection.peer_address} is not paired')
335
+ return
336
+
337
+ if connection.att_mtu < 49:
338
+ logging.debug(
339
+ f'HAS: {connection.peer_address} invalid MTU={connection.att_mtu}'
340
+ )
341
+ return
342
+
343
+ if connection.peer_address in self.currently_connected_clients:
344
+ logging.debug(
345
+ f'HAS: Already connected to {connection.peer_address} nothing to do'
346
+ )
347
+ return
348
+
321
349
  self.currently_connected_clients.add(connection)
322
350
  if (
323
351
  connection.peer_address
@@ -457,6 +485,7 @@ class HearingAccessService(gatt.TemplateService):
457
485
  connection,
458
486
  self.hearing_aid_preset_control_point,
459
487
  value=op_list[0].to_bytes(len(op_list) == 1),
488
+ force=True, # TODO GATT notification subscription should be persistent
460
489
  )
461
490
  # Remove item once sent, and keep the non sent item in the list
462
491
  op_list.pop(0)
@@ -131,7 +131,11 @@ def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:
131
131
 
132
132
  def cleanup():
133
133
  logger.debug("removing .ini file")
134
- ini_file.unlink()
134
+ try:
135
+ ini_file.unlink()
136
+ except OSError as error:
137
+ # Don't log at exception level, since this may happen normally.
138
+ logger.debug(f'failed to remove .ini file ({error})')
135
139
 
136
140
  atexit.register(cleanup)
137
141
  return True
@@ -17,6 +17,7 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
19
  import logging
20
+ from typing import Optional
20
21
 
21
22
  import serial_asyncio
22
23
 
@@ -28,25 +29,56 @@ from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transp
28
29
  logger = logging.getLogger(__name__)
29
30
 
30
31
 
32
+ # -----------------------------------------------------------------------------
33
+ # Constants
34
+ # -----------------------------------------------------------------------------
35
+ DEFAULT_POST_OPEN_DELAY = 0.5 # in seconds
36
+
37
+ # -----------------------------------------------------------------------------
38
+ # Classes and Functions
39
+ # -----------------------------------------------------------------------------
40
+
41
+
42
+ # -----------------------------------------------------------------------------
43
+ class SerialPacketSource(StreamPacketSource):
44
+ def __init__(self) -> None:
45
+ super().__init__()
46
+ self._ready = asyncio.Event()
47
+
48
+ async def wait_until_ready(self) -> None:
49
+ await self._ready.wait()
50
+
51
+ def connection_made(self, transport: asyncio.BaseTransport) -> None:
52
+ logger.debug('connection made')
53
+ self._ready.set()
54
+
55
+ def connection_lost(self, exc: Optional[Exception]) -> None:
56
+ logger.debug('connection lost')
57
+ self.on_transport_lost()
58
+
59
+
31
60
  # -----------------------------------------------------------------------------
32
61
  async def open_serial_transport(spec: str) -> Transport:
33
62
  '''
34
63
  Open a serial port transport.
35
64
  The parameter string has this syntax:
36
- <device-path>[,<speed>][,rtscts][,dsrdtr]
65
+ <device-path>[,<speed>][,rtscts][,dsrdtr][,delay]
37
66
  When <speed> is omitted, the default value of 1000000 is used
38
67
  When "rtscts" is specified, RTS/CTS hardware flow control is enabled
39
68
  When "dsrdtr" is specified, DSR/DTR hardware flow control is enabled
69
+ When "delay" is specified, a short delay is added after opening the port
40
70
 
41
71
  Examples:
42
72
  /dev/tty.usbmodem0006839912172
43
73
  /dev/tty.usbmodem0006839912172,1000000
44
74
  /dev/tty.usbmodem0006839912172,rtscts
75
+ /dev/tty.usbmodem0006839912172,rtscts,delay
45
76
  '''
46
77
 
47
78
  speed = 1000000
48
79
  rtscts = False
49
80
  dsrdtr = False
81
+ delay = 0.0
50
82
  if ',' in spec:
51
83
  parts = spec.split(',')
52
84
  device = parts[0]
@@ -55,13 +87,16 @@ async def open_serial_transport(spec: str) -> Transport:
55
87
  rtscts = True
56
88
  elif part == 'dsrdtr':
57
89
  dsrdtr = True
90
+ elif part == 'delay':
91
+ delay = DEFAULT_POST_OPEN_DELAY
58
92
  elif part.isnumeric():
59
93
  speed = int(part)
60
94
  else:
61
95
  device = spec
96
+
62
97
  serial_transport, packet_source = await serial_asyncio.create_serial_connection(
63
98
  asyncio.get_running_loop(),
64
- StreamPacketSource,
99
+ SerialPacketSource,
65
100
  device,
66
101
  baudrate=speed,
67
102
  rtscts=rtscts,
@@ -69,4 +104,23 @@ async def open_serial_transport(spec: str) -> Transport:
69
104
  )
70
105
  packet_sink = StreamPacketSink(serial_transport)
71
106
 
107
+ logger.debug('waiting for the port to be ready')
108
+ await packet_source.wait_until_ready()
109
+ logger.debug('port is ready')
110
+
111
+ # Try to assert DTR
112
+ assert serial_transport.serial is not None
113
+ try:
114
+ serial_transport.serial.dtr = True
115
+ logger.debug(
116
+ f"DSR={serial_transport.serial.dsr}, DTR={serial_transport.serial.dtr}"
117
+ )
118
+ except Exception as e:
119
+ logger.warning(f'could not assert DTR: {e}')
120
+
121
+ # Wait a bit after opening the port, if requested
122
+ if delay > 0.0:
123
+ logger.debug(f'waiting {delay} seconds after opening the port')
124
+ await asyncio.sleep(delay)
125
+
72
126
  return Transport(packet_source, packet_sink)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bumble
3
- Version: 0.0.215
3
+ Version: 0.0.217
4
4
  Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
5
5
  Author-email: Google <bumble-dev@google.com>
6
6
  License-Expression: Apache-2.0
@@ -1,5 +1,5 @@
1
1
  bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
2
- bumble/_version.py,sha256=SguWdOraXlfu-EcPyXxkjHQJ1IdMH5Jtv9-nD3eaHQw,708
2
+ bumble/_version.py,sha256=PoO2jYBkVti2LSUuEHv8sargBSqX0YyvJ9djrfZmGJs,708
3
3
  bumble/a2dp.py,sha256=sVo3w6qSsGm7Ttsqu4zeTO_Gfo63a7STTlJnaq8iJ-M,32031
4
4
  bumble/at.py,sha256=u93DzB5Ghrxks_ufuUqoYV2RUSowLNLMDhq_j9GrCBc,3108
5
5
  bumble/att.py,sha256=vS1dUwiuUgNU6PztARg_qPB_olLOWsAxFz6DZGX8zII,33423
@@ -15,7 +15,7 @@ bumble/controller.py,sha256=oIo6_3Y2_lhMPn0MUfEEN8997ry1blzT2rTUQabQwv8,68282
15
15
  bumble/core.py,sha256=QojIUs4nP9qo_r-FWPVxHrCdVSg6CaIzHf6GVPTuu9s,94583
16
16
  bumble/data_types.py,sha256=jLELcRiUcvRc4JcEo6vMO9kZmumZ9WuibFN-AK7ZYbc,32831
17
17
  bumble/decoder.py,sha256=0-VNWZT-u7lvK3qBpAuYT0M6Rz_bMgMi4CjfUXX_6RM,9728
18
- bumble/device.py,sha256=AAOvl06muirnc0RWN884Xfo2mRL60MWYQO-W9gmCRS4,250023
18
+ bumble/device.py,sha256=pV8krw94d42c0hml_XG8v0hSjb7DXLwgs5U2sgIbXNc,251491
19
19
  bumble/gap.py,sha256=j5mevt__i_fSdIc_B3x7YvCJLeCc7bVq8omRphxwXNw,2144
20
20
  bumble/gatt.py,sha256=dBd-opQVUumlLPGw7EYZM5DF-ssnym3LsxhskAJMhig,34552
21
21
  bumble/gatt_adapters.py,sha256=9uN2uXGIdlvNeSvv3nfwxCEa15uPfm5DVLR5bog_knI,13190
@@ -25,7 +25,7 @@ bumble/hci.py,sha256=2yEJnR6JBtxK6ghituuXCbSalifevnTqvPvnCjyrtAo,327200
25
25
  bumble/helpers.py,sha256=WFyikseIRdXUqqeOplNlmd0giOegahlXv5e324ax9ck,12611
26
26
  bumble/hfp.py,sha256=0ktRzzRSSwGVcmw_jZlayYjvPUGhzlWZY5fVuSH6FcE,76680
27
27
  bumble/hid.py,sha256=UCPGHw-jm4i4Akk9pXVEL8_i6EdOwaOuqv2Xd_8EhXo,21054
28
- bumble/host.py,sha256=kjXdD9vOqwdf9A5faGm9vGNYjW9pl9irtK6KReH051U,68018
28
+ bumble/host.py,sha256=46fwcskNlfHyW8Ejo5wp8rHtMBZSMkR4URSdekKLPzg,68019
29
29
  bumble/keys.py,sha256=VCOrtuw-xF_4Oa5Qt-ECRKaTqabL_GbwdQUKoZX20II,13416
30
30
  bumble/l2cap.py,sha256=PZQnZQFB_GJZCCFuPpAsTuYz4E3WTgimDUAk83DUUtE,80758
31
31
  bumble/link.py,sha256=O-T9fCJokvpLpTvIMAboEQbT3Sjg8L2ivSWSBH43fP0,14470
@@ -100,7 +100,7 @@ bumble/profiles/device_information_service.py,sha256=oQ4bWPq-LxlJth4H39H8xc06ulj
100
100
  bumble/profiles/gap.py,sha256=Fd-J2_d6TdWDyBPDmvEykQPYzkE_T9UGNoiP5uQ6FJg,4026
101
101
  bumble/profiles/gatt_service.py,sha256=WVAuW9q-01jmpjpUVG7Gs4vZE_CSLAo51FSK5HDE4tA,6697
102
102
  bumble/profiles/gmap.py,sha256=jNsPobH_lSDZQ4-jwlDRhNfpPWPsXcWFJbCCXGTWZQk,7327
103
- bumble/profiles/hap.py,sha256=8BJhTXw6rFLPWgGZhTMys18pOM7zw5CIFezoPJorcfk,25475
103
+ bumble/profiles/hap.py,sha256=yU-G12ykmj6NVD4f5M1IHV9FAiG83Cy7Dx-3nkI_rzY,26536
104
104
  bumble/profiles/heart_rate_service.py,sha256=VB2CKSG2p5GDb5C_q1elpwzKEIIR7slrJfgf4kdTu30,9252
105
105
  bumble/profiles/le_audio.py,sha256=w8Ui2AKvt_tDpRXY8AZjlYR-P90caRahBWM3KJ97nRw,5829
106
106
  bumble/profiles/mcp.py,sha256=gWB8FqVi7wA8zBsPrh__p_57u4EHwVhA10_YdJXR40c,17763
@@ -118,14 +118,14 @@ bumble/tools/rtk_fw_download.py,sha256=kuJ2q-75Troc9hCrow0q5zKSMsVhocMeWHKvfdg6H
118
118
  bumble/tools/rtk_util.py,sha256=4kdaHON_pg2HElzBRaStolqvEJVWPd0eZPUyAQWAqxc,5726
119
119
  bumble/transport/__init__.py,sha256=Dk84_3nAC24jY5zcCWVhjC2RxjJahKTz4zChygAqlvs,7027
120
120
  bumble/transport/android_emulator.py,sha256=axEdFBj17c0dOD5lMqJjiULInG_SQVx5W7ptjyLtRJs,4281
121
- bumble/transport/android_netsim.py,sha256=mxkZszD-8glZ1WbTVWR2k6fCQUvG_uFrRbWJlswID_c,17355
121
+ bumble/transport/android_netsim.py,sha256=8C2kUITCl7L1ggl5FPwWcFq4It8e9O3esEpMjRaYkkw,17563
122
122
  bumble/transport/common.py,sha256=C0wurvCvcEX4r6F0cWbD2x1GhamJwJoyYmYJ21pU04Q,16770
123
123
  bumble/transport/file.py,sha256=aqJD5US5TVl01dOb42rV_0OzuvFsBDDlqzipyrYA4q4,2026
124
124
  bumble/transport/hci_socket.py,sha256=2PKQ43oDjayKDklx9sF98hSeyNmvTSUOWx_ooPWTjd0,6345
125
125
  bumble/transport/pty.py,sha256=yzItc5ZO7WkHZKnAeEK-1XEyY4BWot4ZAMFzQvRiSQ4,2747
126
126
  bumble/transport/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
127
  bumble/transport/pyusb.py,sha256=x1uXCVSs-h0dxhOuJ60RkmuuYrcTrxDngFkW8cWEPc4,16187
128
- bumble/transport/serial.py,sha256=jGBK1gbZAqPZ8N1LV3FTuAFfYG1xGsxLLEjj_XeQyJg,2463
128
+ bumble/transport/serial.py,sha256=lQsdpv3VHKJwrAgHWnft2pNEzevEHHVutA2SvQNSCpY,4343
129
129
  bumble/transport/tcp_client.py,sha256=YM7Y9CN0fBSGSb9dPiOAXAUC9R1fRRwYG4HiP2rvDyc,1883
130
130
  bumble/transport/tcp_server.py,sha256=7ZPk097pKbMJBvyTRSrJXla4RDU6SBJ-DeQmSK54mHM,3755
131
131
  bumble/transport/udp.py,sha256=_3mePVr9ALzZbOfnlMwcaaMnnLkd5kxhw5-BxkeCSMU,2281
@@ -175,9 +175,9 @@ bumble/vendor/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
175
175
  bumble/vendor/android/hci.py,sha256=LcV4_OBpzoLtpSiP2su7F4r3bDrHWxcQPR4OGnMVXDk,10485
176
176
  bumble/vendor/zephyr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
177
  bumble/vendor/zephyr/hci.py,sha256=ysct40uIVohaKKLrWjcWo-7YqozU17xGia2dWeHoMoI,3435
178
- bumble-0.0.215.dist-info/licenses/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
179
- bumble-0.0.215.dist-info/METADATA,sha256=KRRdkhAUhXZLRfsCh4wjIzTpGrkXelFnS0ovbEYnzy8,6155
180
- bumble-0.0.215.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
181
- bumble-0.0.215.dist-info/entry_points.txt,sha256=mX5dzixNMRo4CTAEQqiYTr-JIaWF62TYrRvJOstjjL8,1081
182
- bumble-0.0.215.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
183
- bumble-0.0.215.dist-info/RECORD,,
178
+ bumble-0.0.217.dist-info/licenses/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
179
+ bumble-0.0.217.dist-info/METADATA,sha256=Mdy5v7BSROxcolAFXgbeSX1fX0lOAQW319HWJNcwicg,6155
180
+ bumble-0.0.217.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
181
+ bumble-0.0.217.dist-info/entry_points.txt,sha256=mX5dzixNMRo4CTAEQqiYTr-JIaWF62TYrRvJOstjjL8,1081
182
+ bumble-0.0.217.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
183
+ bumble-0.0.217.dist-info/RECORD,,