bumble 0.0.213__py3-none-any.whl → 0.0.214__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
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.213'
21
- __version_tuple__ = version_tuple = (0, 0, 213)
20
+ __version__ = version = '0.0.214'
21
+ __version_tuple__ = version_tuple = (0, 0, 214)
bumble/apps/auracast.py CHANGED
@@ -23,7 +23,6 @@ import contextlib
23
23
  import dataclasses
24
24
  import functools
25
25
  import logging
26
- import os
27
26
  import struct
28
27
  from typing import (
29
28
  Any,
@@ -54,6 +53,8 @@ from bumble.profiles import bass
54
53
  import bumble.device
55
54
  import bumble.transport
56
55
  import bumble.utils
56
+ import bumble.logging
57
+
57
58
 
58
59
  # -----------------------------------------------------------------------------
59
60
  # Logging
@@ -1235,7 +1236,7 @@ def transmit(
1235
1236
 
1236
1237
 
1237
1238
  def main():
1238
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
1239
+ bumble.logging.setup_basic_logging()
1239
1240
  auracast()
1240
1241
 
1241
1242
 
bumble/apps/bench.py CHANGED
@@ -19,7 +19,6 @@ import asyncio
19
19
  import dataclasses
20
20
  import enum
21
21
  import logging
22
- import os
23
22
  import statistics
24
23
  import struct
25
24
  import time
@@ -70,6 +69,7 @@ import bumble.rfcomm
70
69
  import bumble.core
71
70
  from bumble.utils import AsyncRunner
72
71
  from bumble.pairing import PairingConfig
72
+ import bumble.logging
73
73
 
74
74
 
75
75
  # -----------------------------------------------------------------------------
@@ -2321,11 +2321,7 @@ def peripheral(ctx, transport):
2321
2321
 
2322
2322
 
2323
2323
  def main():
2324
- logging.basicConfig(
2325
- level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
2326
- format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
2327
- datefmt="%H:%M:%S",
2328
- )
2324
+ bumble.logging.setup_basic_logging('INFO')
2329
2325
  bench()
2330
2326
 
2331
2327
 
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  import time
22
20
 
23
21
  import click
@@ -59,6 +57,7 @@ from bumble.hci import (
59
57
  )
60
58
  from bumble.host import Host
61
59
  from bumble.transport import open_transport
60
+ import bumble.logging
62
61
 
63
62
 
64
63
  # -----------------------------------------------------------------------------
@@ -342,11 +341,7 @@ async def async_main(
342
341
  )
343
342
  @click.argument('transport')
344
343
  def main(latency_probes, latency_probe_interval, latency_probe_command, transport):
345
- logging.basicConfig(
346
- level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
347
- format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
348
- datefmt="%H:%M:%S",
349
- )
344
+ bumble.logging.setup_basic_logging()
350
345
  asyncio.run(
351
346
  async_main(
352
347
  latency_probes, latency_probe_interval, latency_probe_command, transport
@@ -16,10 +16,11 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import logging
20
- import os
21
19
  import time
22
20
  from typing import Optional
21
+
22
+ import click
23
+
23
24
  from bumble.colors import color
24
25
  from bumble.hci import (
25
26
  HCI_READ_LOOPBACK_MODE_COMMAND,
@@ -30,7 +31,7 @@ from bumble.hci import (
30
31
  )
31
32
  from bumble.host import Host
32
33
  from bumble.transport import open_transport
33
- import click
34
+ import bumble.logging
34
35
 
35
36
 
36
37
  class Loopback:
@@ -194,12 +195,7 @@ class Loopback:
194
195
  )
195
196
  @click.argument('transport')
196
197
  def main(packet_size, packet_count, transport):
197
- logging.basicConfig(
198
- level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
199
- format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
200
- datefmt="%H:%M:%S",
201
- )
202
-
198
+ bumble.logging.setup_basic_logging()
203
199
  loopback = Loopback(packet_size, packet_count, transport)
204
200
  asyncio.run(loopback.run())
205
201
 
@@ -15,14 +15,13 @@
15
15
  # -----------------------------------------------------------------------------
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
- import logging
19
18
  import asyncio
20
19
  import sys
21
- import os
22
20
 
23
21
  from bumble.controller import Controller
24
22
  from bumble.link import LocalLink
25
23
  from bumble.transport import open_transport
24
+ import bumble.logging
26
25
 
27
26
 
28
27
  # -----------------------------------------------------------------------------
@@ -62,7 +61,7 @@ async def async_main():
62
61
 
63
62
  # -----------------------------------------------------------------------------
64
63
  def main():
65
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
64
+ bumble.logging.setup_basic_logging()
66
65
  asyncio.run(async_main())
67
66
 
68
67
 
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  from typing import Callable, Iterable, Optional
22
20
 
23
21
  import click
@@ -33,6 +31,7 @@ from bumble.profiles.pacs import PublishedAudioCapabilitiesServiceProxy
33
31
  from bumble.profiles.tmap import TelephonyAndMediaAudioServiceProxy
34
32
  from bumble.profiles.vcs import VolumeControlServiceProxy
35
33
  from bumble.transport import open_transport
34
+ import bumble.logging
36
35
 
37
36
 
38
37
  # -----------------------------------------------------------------------------
@@ -267,7 +266,7 @@ def main(device_config, encrypt, transport, address_or_name):
267
266
  Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
268
267
  wait for an incoming connection.
269
268
  """
270
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
269
+ bumble.logging.setup_basic_logging()
271
270
  asyncio.run(async_main(device_config, encrypt, transport, address_or_name))
272
271
 
273
272
 
bumble/apps/gatt_dump.py CHANGED
@@ -16,8 +16,7 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
19
+
21
20
  import click
22
21
 
23
22
  import bumble.core
@@ -25,6 +24,7 @@ from bumble.colors import color
25
24
  from bumble.device import Device, Peer
26
25
  from bumble.gatt import show_services
27
26
  from bumble.transport import open_transport
27
+ import bumble.logging
28
28
 
29
29
 
30
30
  # -----------------------------------------------------------------------------
@@ -112,7 +112,7 @@ def main(device_config, encrypt, transport, address_or_name):
112
112
  Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
113
113
  wait for an incoming connection.
114
114
  """
115
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
115
+ bumble.logging.setup_basic_logging()
116
116
  asyncio.run(async_main(device_config, encrypt, transport, address_or_name))
117
117
 
118
118
 
bumble/apps/gg_bridge.py CHANGED
@@ -16,9 +16,8 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
19
  import struct
21
- import logging
20
+
22
21
  import click
23
22
 
24
23
  from bumble import l2cap
@@ -29,6 +28,7 @@ from bumble.gatt import Service, Characteristic, CharacteristicValue
29
28
  from bumble.utils import AsyncRunner
30
29
  from bumble.transport import open_transport
31
30
  from bumble.hci import HCI_Constant
31
+ import bumble.logging
32
32
 
33
33
 
34
34
  # -----------------------------------------------------------------------------
@@ -383,6 +383,7 @@ def main(
383
383
  receive_host,
384
384
  receive_port,
385
385
  ):
386
+ bumble.logging.setup_basic_logging('WARNING')
386
387
  asyncio.run(
387
388
  run(
388
389
  hci_transport,
@@ -397,6 +398,5 @@ def main(
397
398
 
398
399
 
399
400
  # -----------------------------------------------------------------------------
400
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
401
401
  if __name__ == '__main__':
402
402
  main()
bumble/apps/hci_bridge.py CHANGED
@@ -17,11 +17,12 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  import logging
19
19
  import asyncio
20
- import os
21
20
  import sys
22
21
 
23
22
  from bumble import hci, transport
24
23
  from bumble.bridge import HCI_Bridge
24
+ import bumble.logging
25
+
25
26
 
26
27
  # -----------------------------------------------------------------------------
27
28
  # Logging
@@ -100,7 +101,7 @@ async def async_main():
100
101
 
101
102
  # -----------------------------------------------------------------------------
102
103
  def main():
103
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
104
+ bumble.logging.setup_basic_logging()
104
105
  asyncio.run(async_main())
105
106
 
106
107
 
@@ -16,8 +16,7 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import logging
20
- import os
19
+
21
20
  import click
22
21
 
23
22
  from bumble import l2cap
@@ -26,6 +25,7 @@ from bumble.transport import open_transport
26
25
  from bumble.device import Device
27
26
  from bumble.utils import FlowControlAsyncPipe
28
27
  from bumble.hci import HCI_Constant
28
+ import bumble.logging
29
29
 
30
30
 
31
31
  # -----------------------------------------------------------------------------
@@ -356,6 +356,6 @@ def client(context, bluetooth_address, tcp_host, tcp_port):
356
356
 
357
357
 
358
358
  # -----------------------------------------------------------------------------
359
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
360
359
  if __name__ == '__main__':
360
+ bumble.logging.setup_basic_logging('WARNING')
361
361
  cli(obj={}) # pylint: disable=no-value-for-parameter
@@ -22,7 +22,6 @@ import datetime
22
22
  import functools
23
23
  from importlib import resources
24
24
  import json
25
- import os
26
25
  import logging
27
26
  import pathlib
28
27
  import weakref
@@ -44,6 +43,7 @@ from bumble.device import Device, DeviceConfiguration, AdvertisingParameters, Ci
44
43
  from bumble.transport import open_transport
45
44
  from bumble.profiles import ascs, bap, pacs
46
45
  from bumble.hci import Address, CodecID, CodingFormat, HCI_IsoDataPacket
46
+ import bumble.logging
47
47
 
48
48
 
49
49
  # -----------------------------------------------------------------------------
@@ -454,7 +454,7 @@ def speaker(ui_port: int, device_config: str, transport: str, lc3_file: str) ->
454
454
 
455
455
  # -----------------------------------------------------------------------------
456
456
  def main():
457
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
457
+ bumble.logging.setup_basic_logging()
458
458
  speaker()
459
459
 
460
460
 
@@ -17,8 +17,6 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
  import asyncio
20
- import asyncio.subprocess
21
- import os
22
20
  import logging
23
21
  from typing import Optional, Union
24
22
 
@@ -63,6 +61,7 @@ from bumble.hci import Address, HCI_CONNECTION_ALREADY_EXISTS_ERROR, HCI_Constan
63
61
  from bumble.pairing import PairingConfig
64
62
  from bumble.transport import open_transport
65
63
  from bumble.utils import AsyncRunner
64
+ import bumble.logging
66
65
 
67
66
 
68
67
  # -----------------------------------------------------------------------------
@@ -599,7 +598,7 @@ def play(context, address, audio_format, audio_file):
599
598
 
600
599
  # -----------------------------------------------------------------------------
601
600
  def main():
602
- logging.basicConfig(level=os.environ.get("BUMBLE_LOGLEVEL", "WARNING").upper())
601
+ bumble.logging.setup_basic_logging("WARNING")
603
602
  player_cli()
604
603
 
605
604
 
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import logging
20
- import os
21
19
  import time
22
20
  from typing import Optional
23
21
 
@@ -30,6 +28,7 @@ from bumble import hci
30
28
  from bumble import rfcomm
31
29
  from bumble import transport
32
30
  from bumble import utils
31
+ import bumble.logging
33
32
 
34
33
 
35
34
  # -----------------------------------------------------------------------------
@@ -515,6 +514,6 @@ def client(context, bluetooth_address, tcp_host, tcp_port, authenticate, encrypt
515
514
 
516
515
 
517
516
  # -----------------------------------------------------------------------------
518
- logging.basicConfig(level=os.environ.get("BUMBLE_LOGLEVEL", "WARNING").upper())
519
517
  if __name__ == "__main__":
518
+ bumble.logging.setup_basic_logging("WARNING")
520
519
  cli(obj={}) # pylint: disable=no-value-for-parameter
bumble/apps/scan.py CHANGED
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  import click
22
20
 
23
21
  from bumble.colors import color
@@ -27,6 +25,7 @@ from bumble.keys import JsonKeyStore
27
25
  from bumble.smp import AddressResolver
28
26
  from bumble.device import Advertisement
29
27
  from bumble.hci import Address, HCI_Constant, HCI_LE_1M_PHY, HCI_LE_CODED_PHY
28
+ import bumble.logging
30
29
 
31
30
 
32
31
  # -----------------------------------------------------------------------------
@@ -237,7 +236,7 @@ def main(
237
236
  device_config,
238
237
  transport,
239
238
  ):
240
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
239
+ bumble.logging.setup_basic_logging('WARNING')
241
240
  asyncio.run(
242
241
  scan(
243
242
  min_rssi,
bumble/apps/show.py CHANGED
@@ -18,7 +18,6 @@
18
18
  import datetime
19
19
  import importlib
20
20
  import logging
21
- import os
22
21
  import struct
23
22
 
24
23
  import click
@@ -27,6 +26,7 @@ from bumble.colors import color
27
26
  from bumble import hci
28
27
  from bumble.transport.common import PacketReader
29
28
  from bumble.helpers import PacketTracer
29
+ import bumble.logging
30
30
 
31
31
 
32
32
  # -----------------------------------------------------------------------------
@@ -188,5 +188,5 @@ def main(format, vendor, filename):
188
188
 
189
189
  # -----------------------------------------------------------------------------
190
190
  if __name__ == '__main__':
191
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
191
+ bumble.logging.setup_basic_logging('WARNING')
192
192
  main() # pylint: disable=no-value-for-parameter
@@ -21,7 +21,6 @@ import asyncio.subprocess
21
21
  from importlib import resources
22
22
  import enum
23
23
  import json
24
- import os
25
24
  import logging
26
25
  import pathlib
27
26
  import subprocess
@@ -58,6 +57,7 @@ from bumble.a2dp import (
58
57
  from bumble.utils import AsyncRunner
59
58
  from bumble.codecs import AacAudioRtpPacket
60
59
  from bumble.rtp import MediaPacket
60
+ import bumble.logging
61
61
 
62
62
 
63
63
  # -----------------------------------------------------------------------------
@@ -833,11 +833,7 @@ def speaker(
833
833
 
834
834
  # -----------------------------------------------------------------------------
835
835
  def main():
836
- logging.basicConfig(
837
- level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper(),
838
- format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
839
- datefmt="%H:%M:%S",
840
- )
836
+ bumble.logging.setup_basic_logging('WARNING')
841
837
  speaker()
842
838
 
843
839
 
bumble/apps/unbond.py CHANGED
@@ -16,13 +16,12 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  import click
22
20
 
23
21
  from bumble.device import Device
24
22
  from bumble.keys import JsonKeyStore
25
23
  from bumble.transport import open_transport
24
+ import bumble.logging
26
25
 
27
26
 
28
27
  # -----------------------------------------------------------------------------
@@ -68,7 +67,7 @@ def main(keystore_file, hci_transport, device_config, address):
68
67
  instantiated.
69
68
  If no address is passed, the existing pairing keys for all addresses are printed.
70
69
  """
71
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
70
+ bumble.logging.setup_basic_logging()
72
71
 
73
72
  if not keystore_file and not hci_transport:
74
73
  print('either --keystore-file or --hci-transport must be specified.')
bumble/apps/usb_probe.py CHANGED
@@ -26,13 +26,12 @@
26
26
  # -----------------------------------------------------------------------------
27
27
  # Imports
28
28
  # -----------------------------------------------------------------------------
29
- import os
30
- import logging
31
29
  import click
32
30
  import usb1
33
31
 
34
32
  from bumble.colors import color
35
33
  from bumble.transport.usb import load_libusb
34
+ import bumble.logging
36
35
 
37
36
 
38
37
  # -----------------------------------------------------------------------------
@@ -169,7 +168,7 @@ def is_bluetooth_hci(device):
169
168
  @click.command()
170
169
  @click.option('--verbose', is_flag=True, default=False, help='Print more details')
171
170
  def main(verbose):
172
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
171
+ bumble.logging.setup_basic_logging('WARNING')
173
172
 
174
173
  load_libusb()
175
174
  with usb1.USBContext() as context:
bumble/avrcp.py CHANGED
@@ -1117,7 +1117,7 @@ class Protocol(utils.EventEmitter):
1117
1117
 
1118
1118
  @staticmethod
1119
1119
  def _check_vendor_dependent_frame(
1120
- frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame]
1120
+ frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame],
1121
1121
  ) -> bool:
1122
1122
  if frame.company_id != AVRCP_BLUETOOTH_SIG_COMPANY_ID:
1123
1123
  logger.debug("unsupported company id, ignoring")
bumble/controller.py CHANGED
@@ -370,6 +370,12 @@ class Controller:
370
370
  return connection
371
371
  return None
372
372
 
373
+ def find_peripheral_connection_by_handle(self, handle):
374
+ for connection in self.peripheral_connections.values():
375
+ if connection.handle == handle:
376
+ return connection
377
+ return None
378
+
373
379
  def find_classic_connection_by_handle(self, handle):
374
380
  for connection in self.classic_connections.values():
375
381
  if connection.handle == handle:
@@ -414,7 +420,7 @@ class Controller:
414
420
  )
415
421
  )
416
422
 
417
- def on_link_central_disconnected(self, peer_address, reason):
423
+ def on_link_disconnected(self, peer_address, reason):
418
424
  '''
419
425
  Called when an active disconnection occurs from a peer
420
426
  '''
@@ -431,6 +437,17 @@ class Controller:
431
437
 
432
438
  # Remove the connection
433
439
  del self.peripheral_connections[peer_address]
440
+ elif connection := self.central_connections.get(peer_address):
441
+ self.send_hci_packet(
442
+ HCI_Disconnection_Complete_Event(
443
+ status=HCI_SUCCESS,
444
+ connection_handle=connection.handle,
445
+ reason=reason,
446
+ )
447
+ )
448
+
449
+ # Remove the connection
450
+ del self.central_connections[peer_address]
434
451
  else:
435
452
  logger.warning(f'!!! No peripheral connection found for {peer_address}')
436
453
 
@@ -479,7 +496,7 @@ class Controller:
479
496
  )
480
497
  )
481
498
 
482
- def on_link_peripheral_disconnection_complete(self, disconnection_command, status):
499
+ def on_link_disconnection_complete(self, disconnection_command, status):
483
500
  '''
484
501
  Called when a disconnection has been completed
485
502
  '''
@@ -499,26 +516,11 @@ class Controller:
499
516
  ):
500
517
  logger.debug(f'CENTRAL Connection removed: {connection}')
501
518
  del self.central_connections[connection.peer_address]
502
-
503
- def on_link_peripheral_disconnected(self, peer_address):
504
- '''
505
- Called when a connection to a peripheral is broken
506
- '''
507
-
508
- # Send a disconnection complete event
509
- if connection := self.central_connections.get(peer_address):
510
- self.send_hci_packet(
511
- HCI_Disconnection_Complete_Event(
512
- status=HCI_SUCCESS,
513
- connection_handle=connection.handle,
514
- reason=HCI_CONNECTION_TIMEOUT_ERROR,
515
- )
516
- )
517
-
518
- # Remove the connection
519
- del self.central_connections[peer_address]
520
- else:
521
- logger.warning(f'!!! No central connection found for {peer_address}')
519
+ elif connection := self.find_peripheral_connection_by_handle(
520
+ disconnection_command.connection_handle
521
+ ):
522
+ logger.debug(f'PERIPHERAL Connection removed: {connection}')
523
+ del self.peripheral_connections[connection.peer_address]
522
524
 
523
525
  def on_link_encrypted(self, peer_address, _rand, _ediv, _ltk):
524
526
  # For now, just setup the encryption without asking the host
@@ -877,6 +879,14 @@ class Controller:
877
879
  else:
878
880
  # Remove the connection
879
881
  del self.central_connections[connection.peer_address]
882
+ elif connection := self.find_peripheral_connection_by_handle(handle):
883
+ if self.link:
884
+ self.link.disconnect(
885
+ self.random_address, connection.peer_address, command
886
+ )
887
+ else:
888
+ # Remove the connection
889
+ del self.peripheral_connections[connection.peer_address]
880
890
  elif connection := self.find_classic_connection_by_handle(handle):
881
891
  if self.link:
882
892
  self.link.classic_disconnect(
@@ -1259,6 +1269,56 @@ class Controller:
1259
1269
  )
1260
1270
  return bytes([HCI_SUCCESS]) + bd_addr
1261
1271
 
1272
+ def on_hci_le_set_default_subrate_command(
1273
+ self, command: hci.HCI_LE_Set_Default_Subrate_Command
1274
+ ):
1275
+ '''
1276
+ See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
1277
+ '''
1278
+
1279
+ if (
1280
+ command.subrate_max * (command.max_latency) > 500
1281
+ or command.subrate_max < command.subrate_min
1282
+ or command.continuation_number >= command.subrate_max
1283
+ ):
1284
+ return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1285
+
1286
+ return bytes([HCI_SUCCESS])
1287
+
1288
+ def on_hci_le_subrate_request_command(
1289
+ self, command: hci.HCI_LE_Subrate_Request_Command
1290
+ ):
1291
+ '''
1292
+ See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
1293
+ '''
1294
+ if (
1295
+ command.subrate_max * (command.max_latency) > 500
1296
+ or command.continuation_number < command.continuation_number
1297
+ or command.subrate_max < command.subrate_min
1298
+ or command.continuation_number >= command.subrate_max
1299
+ ):
1300
+ return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1301
+
1302
+ self.send_hci_packet(
1303
+ hci.HCI_Command_Status_Event(
1304
+ status=hci.HCI_SUCCESS,
1305
+ num_hci_command_packets=1,
1306
+ command_opcode=command.op_code,
1307
+ )
1308
+ )
1309
+
1310
+ self.send_hci_packet(
1311
+ hci.HCI_LE_Subrate_Change_Event(
1312
+ status=hci.HCI_SUCCESS,
1313
+ connection_handle=command.connection_handle,
1314
+ subrate_factor=2,
1315
+ peripheral_latency=2,
1316
+ continuation_number=command.continuation_number,
1317
+ supervision_timeout=command.supervision_timeout,
1318
+ )
1319
+ )
1320
+ return None
1321
+
1262
1322
  def on_hci_le_set_event_mask_command(self, command):
1263
1323
  '''
1264
1324
  See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
@@ -1805,3 +1865,11 @@ class Controller:
1805
1865
  See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
1806
1866
  '''
1807
1867
  return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
1868
+
1869
+ def on_hci_le_set_host_feature_command(
1870
+ self, _command: hci.HCI_LE_Set_Host_Feature_Command
1871
+ ):
1872
+ '''
1873
+ See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
1874
+ '''
1875
+ return bytes([HCI_SUCCESS])