bumble 0.0.216__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.216'
32
- __version_tuple__ = version_tuple = (0, 0, 216)
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
@@ -2171,7 +2171,7 @@ def with_connection_from_address(function):
2171
2171
  @functools.wraps(function)
2172
2172
  def wrapper(device: Device, address: hci.Address, *args, **kwargs):
2173
2173
  if connection := device.pending_connections.get(address):
2174
- return function(device, connection, address, *args, **kwargs)
2174
+ return function(device, connection, *args, **kwargs)
2175
2175
  for connection in device.connections.values():
2176
2176
  if connection.peer_address == address:
2177
2177
  return function(device, connection, *args, **kwargs)
@@ -6443,18 +6443,14 @@ class Device(utils.CompositeEventEmitter):
6443
6443
 
6444
6444
  # [Classic only]
6445
6445
  @host_event_handler
6446
- @try_with_connection_from_address
6446
+ @with_connection_from_address
6447
6447
  def on_role_change(
6448
6448
  self,
6449
- connection: Optional[Connection],
6450
- peer_address: hci.Address,
6449
+ connection: Connection,
6451
6450
  new_role: hci.Role,
6452
6451
  ):
6453
- if connection:
6454
- connection.role = new_role
6455
- connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
6456
- else:
6457
- logger.warning("Role change to unknown connection %s", peer_address)
6452
+ connection.role = new_role
6453
+ connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
6458
6454
 
6459
6455
  # [Classic only]
6460
6456
  @host_event_handler
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.216
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=veynzO9___YfPnIypSQv3tYyxNmoE4TBaM2ten8_gK4,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=VDvdXeMhoGTdG3AHuN0-glHDpJKDhSdKVGrauq2kni0,251675
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.216.dist-info/licenses/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
179
- bumble-0.0.216.dist-info/METADATA,sha256=MY056_VlCMC-BwKlWwCW6b29sSrlNeY1_LN4xrfp0pc,6155
180
- bumble-0.0.216.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
181
- bumble-0.0.216.dist-info/entry_points.txt,sha256=mX5dzixNMRo4CTAEQqiYTr-JIaWF62TYrRvJOstjjL8,1081
182
- bumble-0.0.216.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
183
- bumble-0.0.216.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,,