bumble 0.0.214__py3-none-any.whl → 0.0.216__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.
Files changed (122) hide show
  1. bumble/_version.py +16 -3
  2. bumble/a2dp.py +15 -16
  3. bumble/apps/auracast.py +13 -38
  4. bumble/apps/bench.py +9 -10
  5. bumble/apps/ble_rpa_tool.py +1 -0
  6. bumble/apps/console.py +22 -25
  7. bumble/apps/controller_info.py +19 -19
  8. bumble/apps/controller_loopback.py +2 -2
  9. bumble/apps/controllers.py +1 -1
  10. bumble/apps/device_info.py +3 -3
  11. bumble/apps/gatt_dump.py +1 -1
  12. bumble/apps/gg_bridge.py +5 -6
  13. bumble/apps/hci_bridge.py +3 -3
  14. bumble/apps/l2cap_bridge.py +3 -3
  15. bumble/apps/lea_unicast/app.py +15 -25
  16. bumble/apps/pair.py +30 -43
  17. bumble/apps/pandora_server.py +5 -4
  18. bumble/apps/player/player.py +19 -22
  19. bumble/apps/rfcomm_bridge.py +3 -8
  20. bumble/apps/scan.py +16 -6
  21. bumble/apps/show.py +3 -4
  22. bumble/apps/speaker/speaker.py +22 -22
  23. bumble/apps/unbond.py +2 -1
  24. bumble/apps/usb_probe.py +1 -2
  25. bumble/att.py +241 -246
  26. bumble/audio/io.py +5 -9
  27. bumble/avc.py +2 -2
  28. bumble/avctp.py +6 -7
  29. bumble/avdtp.py +19 -22
  30. bumble/avrcp.py +1096 -588
  31. bumble/codecs.py +2 -0
  32. bumble/controller.py +52 -13
  33. bumble/core.py +567 -248
  34. bumble/crypto/__init__.py +2 -2
  35. bumble/crypto/builtin.py +1 -1
  36. bumble/crypto/cryptography.py +2 -4
  37. bumble/data_types.py +1025 -0
  38. bumble/device.py +318 -279
  39. bumble/drivers/__init__.py +3 -2
  40. bumble/drivers/intel.py +3 -4
  41. bumble/drivers/rtk.py +26 -9
  42. bumble/gap.py +4 -4
  43. bumble/gatt.py +3 -2
  44. bumble/gatt_adapters.py +3 -11
  45. bumble/gatt_client.py +69 -81
  46. bumble/gatt_server.py +124 -124
  47. bumble/hci.py +67 -18
  48. bumble/helpers.py +19 -26
  49. bumble/hfp.py +10 -21
  50. bumble/hid.py +22 -16
  51. bumble/host.py +181 -103
  52. bumble/keys.py +5 -3
  53. bumble/l2cap.py +121 -74
  54. bumble/link.py +8 -9
  55. bumble/pairing.py +7 -6
  56. bumble/pandora/__init__.py +8 -7
  57. bumble/pandora/config.py +3 -1
  58. bumble/pandora/device.py +3 -2
  59. bumble/pandora/host.py +38 -36
  60. bumble/pandora/l2cap.py +22 -21
  61. bumble/pandora/security.py +15 -15
  62. bumble/pandora/utils.py +5 -3
  63. bumble/profiles/aics.py +11 -11
  64. bumble/profiles/ams.py +7 -8
  65. bumble/profiles/ancs.py +6 -7
  66. bumble/profiles/ascs.py +4 -9
  67. bumble/profiles/asha.py +8 -12
  68. bumble/profiles/bap.py +11 -23
  69. bumble/profiles/bass.py +2 -7
  70. bumble/profiles/battery_service.py +3 -4
  71. bumble/profiles/cap.py +1 -2
  72. bumble/profiles/csip.py +2 -6
  73. bumble/profiles/device_information_service.py +2 -2
  74. bumble/profiles/gap.py +4 -4
  75. bumble/profiles/gatt_service.py +1 -4
  76. bumble/profiles/gmap.py +5 -5
  77. bumble/profiles/hap.py +62 -59
  78. bumble/profiles/heart_rate_service.py +5 -4
  79. bumble/profiles/le_audio.py +3 -1
  80. bumble/profiles/mcp.py +3 -7
  81. bumble/profiles/pacs.py +3 -6
  82. bumble/profiles/pbp.py +2 -0
  83. bumble/profiles/tmap.py +2 -3
  84. bumble/profiles/vcs.py +2 -8
  85. bumble/profiles/vocs.py +8 -8
  86. bumble/rfcomm.py +11 -14
  87. bumble/rtp.py +1 -0
  88. bumble/sdp.py +10 -8
  89. bumble/smp.py +142 -153
  90. bumble/snoop.py +5 -5
  91. bumble/tools/generate_company_id_list.py +1 -0
  92. bumble/tools/intel_fw_download.py +3 -3
  93. bumble/tools/intel_util.py +4 -4
  94. bumble/tools/rtk_fw_download.py +6 -3
  95. bumble/tools/rtk_util.py +24 -7
  96. bumble/transport/__init__.py +19 -15
  97. bumble/transport/android_emulator.py +8 -13
  98. bumble/transport/android_netsim.py +19 -18
  99. bumble/transport/common.py +12 -15
  100. bumble/transport/file.py +1 -1
  101. bumble/transport/hci_socket.py +4 -6
  102. bumble/transport/pty.py +5 -6
  103. bumble/transport/pyusb.py +7 -10
  104. bumble/transport/serial.py +2 -1
  105. bumble/transport/tcp_client.py +2 -2
  106. bumble/transport/tcp_server.py +11 -14
  107. bumble/transport/udp.py +3 -3
  108. bumble/transport/unix.py +67 -1
  109. bumble/transport/usb.py +6 -6
  110. bumble/transport/vhci.py +0 -1
  111. bumble/transport/ws_client.py +2 -1
  112. bumble/transport/ws_server.py +3 -2
  113. bumble/utils.py +20 -5
  114. bumble/vendor/android/hci.py +1 -2
  115. bumble/vendor/zephyr/hci.py +0 -1
  116. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/METADATA +2 -1
  117. bumble-0.0.216.dist-info/RECORD +183 -0
  118. bumble-0.0.214.dist-info/RECORD +0 -182
  119. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/WHEEL +0 -0
  120. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/entry_points.txt +0 -0
  121. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/licenses/LICENSE +0 -0
  122. {bumble-0.0.214.dist-info → bumble-0.0.216.dist-info}/top_level.txt +0 -0
bumble/profiles/ams.py CHANGED
@@ -20,25 +20,24 @@ Apple Media Service (AMS).
20
20
  # Imports
21
21
  # -----------------------------------------------------------------------------
22
22
  from __future__ import annotations
23
+
23
24
  import asyncio
24
25
  import dataclasses
25
26
  import enum
26
27
  import logging
27
- from typing import Optional, Iterable, Union
28
-
28
+ from typing import Iterable, Optional, Union
29
29
 
30
+ from bumble import utils
30
31
  from bumble.device import Peer
31
32
  from bumble.gatt import (
32
- Characteristic,
33
- GATT_AMS_SERVICE,
34
- GATT_AMS_REMOTE_COMMAND_CHARACTERISTIC,
35
- GATT_AMS_ENTITY_UPDATE_CHARACTERISTIC,
36
33
  GATT_AMS_ENTITY_ATTRIBUTE_CHARACTERISTIC,
34
+ GATT_AMS_ENTITY_UPDATE_CHARACTERISTIC,
35
+ GATT_AMS_REMOTE_COMMAND_CHARACTERISTIC,
36
+ GATT_AMS_SERVICE,
37
+ Characteristic,
37
38
  TemplateService,
38
39
  )
39
40
  from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
40
- from bumble import utils
41
-
42
41
 
43
42
  # -----------------------------------------------------------------------------
44
43
  # Logging
bumble/profiles/ancs.py CHANGED
@@ -20,6 +20,7 @@ Apple Notification Center Service (ANCS).
20
20
  # Imports
21
21
  # -----------------------------------------------------------------------------
22
22
  from __future__ import annotations
23
+
23
24
  import asyncio
24
25
  import dataclasses
25
26
  import datetime
@@ -28,21 +29,19 @@ import logging
28
29
  import struct
29
30
  from typing import Optional, Sequence, Union
30
31
 
31
-
32
+ from bumble import utils
32
33
  from bumble.att import ATT_Error
33
34
  from bumble.device import Peer
34
35
  from bumble.gatt import (
35
- Characteristic,
36
- GATT_ANCS_SERVICE,
37
- GATT_ANCS_NOTIFICATION_SOURCE_CHARACTERISTIC,
38
36
  GATT_ANCS_CONTROL_POINT_CHARACTERISTIC,
39
37
  GATT_ANCS_DATA_SOURCE_CHARACTERISTIC,
38
+ GATT_ANCS_NOTIFICATION_SOURCE_CHARACTERISTIC,
39
+ GATT_ANCS_SERVICE,
40
+ Characteristic,
40
41
  TemplateService,
41
42
  )
42
- from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
43
43
  from bumble.gatt_adapters import SerializableCharacteristicProxyAdapter
44
- from bumble import utils
45
-
44
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
46
45
 
47
46
  # -----------------------------------------------------------------------------
48
47
  # Constants
bumble/profiles/ascs.py CHANGED
@@ -18,22 +18,17 @@
18
18
  # -----------------------------------------------------------------------------
19
19
  from __future__ import annotations
20
20
 
21
- from dataclasses import dataclass, field
22
21
  import enum
23
22
  import functools
24
23
  import logging
25
24
  import struct
26
- from typing import Any, Optional, Union, TypeVar
27
25
  from collections.abc import Sequence
26
+ from dataclasses import dataclass, field
27
+ from typing import Any, Optional, TypeVar, Union
28
28
 
29
- from bumble import utils
30
- from bumble import colors
31
- from bumble.profiles.bap import CodecSpecificConfiguration
29
+ from bumble import colors, device, gatt, gatt_client, hci, utils
32
30
  from bumble.profiles import le_audio
33
- from bumble import device
34
- from bumble import gatt
35
- from bumble import gatt_client
36
- from bumble import hci
31
+ from bumble.profiles.bap import CodecSpecificConfiguration
37
32
 
38
33
  # -----------------------------------------------------------------------------
39
34
  # Logging
bumble/profiles/asha.py CHANGED
@@ -17,16 +17,13 @@
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
19
  import enum
20
- import struct
21
20
  import logging
22
- from typing import Optional, Callable, Union, Any
21
+ import struct
22
+ from typing import Any, Callable, Optional, Union
23
23
 
24
- from bumble import l2cap
25
- from bumble import utils
26
- from bumble import gatt
27
- from bumble import gatt_client
24
+ from bumble import data_types, gatt, gatt_client, l2cap, utils
28
25
  from bumble.core import AdvertisingData
29
- from bumble.device import Device, Connection
26
+ from bumble.device import Connection, Device
30
27
 
31
28
  # -----------------------------------------------------------------------------
32
29
  # Logging
@@ -188,12 +185,11 @@ class AshaService(gatt.TemplateService):
188
185
  return bytes(
189
186
  AdvertisingData(
190
187
  [
191
- (
192
- AdvertisingData.SERVICE_DATA_16_BIT_UUID,
193
- bytes(gatt.GATT_ASHA_SERVICE)
194
- + bytes([self.protocol_version, self.capability])
188
+ data_types.ServiceData16BitUUID(
189
+ gatt.GATT_ASHA_SERVICE,
190
+ bytes([self.protocol_version, self.capability])
195
191
  + self.hisyncid[:4],
196
- ),
192
+ )
197
193
  ]
198
194
  )
199
195
  )
bumble/profiles/bap.py CHANGED
@@ -18,21 +18,18 @@
18
18
  # -----------------------------------------------------------------------------
19
19
  from __future__ import annotations
20
20
 
21
- from collections.abc import Sequence
22
21
  import dataclasses
23
22
  import enum
24
- import struct
25
23
  import functools
26
24
  import logging
25
+ import struct
26
+ from collections.abc import Sequence
27
+
27
28
  from typing_extensions import Self
28
29
 
29
- from bumble import core
30
- from bumble import hci
31
- from bumble import gatt
32
- from bumble import utils
30
+ from bumble import core, data_types, gatt, hci, utils
33
31
  from bumble.profiles import le_audio
34
32
 
35
-
36
33
  # -----------------------------------------------------------------------------
37
34
  # Logging
38
35
  # -----------------------------------------------------------------------------
@@ -260,11 +257,10 @@ class UnicastServerAdvertisingData:
260
257
  return bytes(
261
258
  core.AdvertisingData(
262
259
  [
263
- (
264
- core.AdvertisingData.SERVICE_DATA_16_BIT_UUID,
260
+ data_types.ServiceData16BitUUID(
261
+ gatt.GATT_AUDIO_STREAM_CONTROL_SERVICE,
265
262
  struct.pack(
266
- '<2sBIB',
267
- bytes(gatt.GATT_AUDIO_STREAM_CONTROL_SERVICE),
263
+ '<BIB',
268
264
  self.announcement_type,
269
265
  self.available_audio_contexts,
270
266
  len(self.metadata),
@@ -493,12 +489,8 @@ class BroadcastAudioAnnouncement:
493
489
  return bytes(
494
490
  core.AdvertisingData(
495
491
  [
496
- (
497
- core.AdvertisingData.SERVICE_DATA_16_BIT_UUID,
498
- (
499
- bytes(gatt.GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE)
500
- + bytes(self)
501
- ),
492
+ data_types.ServiceData16BitUUID(
493
+ gatt.GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE, bytes(self)
502
494
  )
503
495
  ]
504
496
  )
@@ -610,12 +602,8 @@ class BasicAudioAnnouncement:
610
602
  return bytes(
611
603
  core.AdvertisingData(
612
604
  [
613
- (
614
- core.AdvertisingData.SERVICE_DATA_16_BIT_UUID,
615
- (
616
- bytes(gatt.GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE)
617
- + bytes(self)
618
- ),
605
+ data_types.ServiceData16BitUUID(
606
+ gatt.GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE, bytes(self)
619
607
  )
620
608
  ]
621
609
  )
bumble/profiles/bass.py CHANGED
@@ -17,18 +17,13 @@
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
19
  from __future__ import annotations
20
+
20
21
  import dataclasses
21
22
  import logging
22
23
  import struct
23
24
  from typing import ClassVar, Optional, Sequence
24
25
 
25
- from bumble import core
26
- from bumble import device
27
- from bumble import gatt
28
- from bumble import gatt_adapters
29
- from bumble import gatt_client
30
- from bumble import hci
31
- from bumble import utils
26
+ from bumble import core, device, gatt, gatt_adapters, gatt_client, hci, utils
32
27
 
33
28
  # -----------------------------------------------------------------------------
34
29
  # Logging
@@ -18,19 +18,18 @@
18
18
  # -----------------------------------------------------------------------------
19
19
  from typing import Optional
20
20
 
21
- from bumble.gatt_client import ProfileServiceProxy
22
21
  from bumble.gatt import (
23
- GATT_BATTERY_SERVICE,
24
22
  GATT_BATTERY_LEVEL_CHARACTERISTIC,
25
- TemplateService,
23
+ GATT_BATTERY_SERVICE,
26
24
  Characteristic,
27
25
  CharacteristicValue,
26
+ TemplateService,
28
27
  )
29
- from bumble.gatt_client import CharacteristicProxy
30
28
  from bumble.gatt_adapters import (
31
29
  PackedCharacteristicAdapter,
32
30
  PackedCharacteristicProxyAdapter,
33
31
  )
32
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy
34
33
 
35
34
 
36
35
  # -----------------------------------------------------------------------------
bumble/profiles/cap.py CHANGED
@@ -18,8 +18,7 @@
18
18
  # -----------------------------------------------------------------------------
19
19
  from __future__ import annotations
20
20
 
21
- from bumble import gatt
22
- from bumble import gatt_client
21
+ from bumble import gatt, gatt_client
23
22
  from bumble.profiles import csip
24
23
 
25
24
 
bumble/profiles/csip.py CHANGED
@@ -17,16 +17,12 @@
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
19
  from __future__ import annotations
20
+
20
21
  import enum
21
22
  import struct
22
23
  from typing import Optional
23
24
 
24
- from bumble import core
25
- from bumble import crypto
26
- from bumble import device
27
- from bumble import gatt
28
- from bumble import gatt_client
29
-
25
+ from bumble import core, crypto, device, gatt, gatt_client
30
26
 
31
27
  # -----------------------------------------------------------------------------
32
28
  # Constants
@@ -25,12 +25,12 @@ from bumble.gatt import (
25
25
  GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC,
26
26
  GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
27
27
  GATT_MODEL_NUMBER_STRING_CHARACTERISTIC,
28
+ GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
28
29
  GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC,
29
30
  GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC,
30
31
  GATT_SYSTEM_ID_CHARACTERISTIC,
31
- GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
32
- TemplateService,
33
32
  Characteristic,
33
+ TemplateService,
34
34
  )
35
35
  from bumble.gatt_adapters import (
36
36
  DelegatedCharacteristicProxyAdapter,
bumble/profiles/gap.py CHANGED
@@ -23,11 +23,11 @@ from typing import Optional, Union
23
23
 
24
24
  from bumble.core import Appearance
25
25
  from bumble.gatt import (
26
- TemplateService,
27
- Characteristic,
28
- GATT_GENERIC_ACCESS_SERVICE,
29
- GATT_DEVICE_NAME_CHARACTERISTIC,
30
26
  GATT_APPEARANCE_CHARACTERISTIC,
27
+ GATT_DEVICE_NAME_CHARACTERISTIC,
28
+ GATT_GENERIC_ACCESS_SERVICE,
29
+ Characteristic,
30
+ TemplateService,
31
31
  )
32
32
  from bumble.gatt_adapters import (
33
33
  DelegatedCharacteristicProxyAdapter,
@@ -17,10 +17,7 @@ from __future__ import annotations
17
17
  import struct
18
18
  from typing import TYPE_CHECKING
19
19
 
20
- from bumble import att
21
- from bumble import gatt
22
- from bumble import gatt_client
23
- from bumble import crypto
20
+ from bumble import att, crypto, gatt, gatt_client
24
21
 
25
22
  if TYPE_CHECKING:
26
23
  from bumble import device
bumble/profiles/gmap.py CHANGED
@@ -18,21 +18,21 @@
18
18
  # Imports
19
19
  # -----------------------------------------------------------------------------
20
20
  import struct
21
+ from enum import IntFlag
21
22
  from typing import Optional
22
23
 
23
24
  from bumble.gatt import (
24
- TemplateService,
25
- Characteristic,
25
+ GATT_BGR_FEATURES_CHARACTERISTIC,
26
+ GATT_BGS_FEATURES_CHARACTERISTIC,
26
27
  GATT_GAMING_AUDIO_SERVICE,
27
28
  GATT_GMAP_ROLE_CHARACTERISTIC,
28
29
  GATT_UGG_FEATURES_CHARACTERISTIC,
29
30
  GATT_UGT_FEATURES_CHARACTERISTIC,
30
- GATT_BGS_FEATURES_CHARACTERISTIC,
31
- GATT_BGR_FEATURES_CHARACTERISTIC,
31
+ Characteristic,
32
+ TemplateService,
32
33
  )
33
34
  from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
34
35
  from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
35
- from enum import IntFlag
36
36
 
37
37
 
38
38
  # -----------------------------------------------------------------------------
bumble/profiles/hap.py CHANGED
@@ -16,16 +16,15 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
+
19
20
  import asyncio
20
- import functools
21
- from dataclasses import dataclass, field
22
21
  import logging
22
+ from dataclasses import dataclass, field
23
23
  from typing import Any, Optional, Union
24
24
 
25
- from bumble import att, gatt, gatt_adapters, gatt_client
25
+ from bumble import att, gatt, gatt_adapters, gatt_client, utils
26
26
  from bumble.core import InvalidArgumentError, InvalidStateError
27
- from bumble.device import Device, Connection
28
- from bumble import utils
27
+ from bumble.device import Connection, Device
29
28
  from bumble.hci import Address
30
29
 
31
30
 
@@ -272,7 +271,7 @@ class HearingAccessService(gatt.TemplateService):
272
271
  def on_connection(connection: Connection) -> None:
273
272
  @connection.on(connection.EVENT_DISCONNECTION)
274
273
  def on_disconnection(_reason) -> None:
275
- self.currently_connected_clients.remove(connection)
274
+ self.currently_connected_clients.discard(connection)
276
275
 
277
276
  @connection.on(connection.EVENT_PAIRING)
278
277
  def on_pairing(*_: Any) -> None:
@@ -373,8 +372,7 @@ class HearingAccessService(gatt.TemplateService):
373
372
  self.preset_records[key]
374
373
  for key in sorted(self.preset_records.keys())
375
374
  if self.preset_records[key].index >= start_index
376
- ]
377
- del presets[num_presets:]
375
+ ][:num_presets]
378
376
  if len(presets) == 0:
379
377
  raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
380
378
 
@@ -383,7 +381,10 @@ class HearingAccessService(gatt.TemplateService):
383
381
  async def _read_preset_response(
384
382
  self, connection: Connection, presets: list[PresetRecord]
385
383
  ):
386
- # If the ATT bearer is terminated before all notifications or indications are sent, then the server shall consider the Read Presets Request operation aborted and shall not either continue or restart the operation when the client reconnects.
384
+ # If the ATT bearer is terminated before all notifications or indications are
385
+ # sent, then the server shall consider the Read Presets Request operation
386
+ # aborted and shall not either continue or restart the operation when the client
387
+ # reconnects.
387
388
  try:
388
389
  for i, preset in enumerate(presets):
389
390
  await connection.device.indicate_subscriber(
@@ -404,7 +405,7 @@ class HearingAccessService(gatt.TemplateService):
404
405
 
405
406
  async def generic_update(self, op: PresetChangedOperation) -> None:
406
407
  '''Server API to perform a generic update. It is the responsibility of the caller to modify the preset_records to match the PresetChangedOperation being sent'''
407
- await self._notifyPresetOperations(op)
408
+ await self._notify_preset_operations(op)
408
409
 
409
410
  async def delete_preset(self, index: int) -> None:
410
411
  '''Server API to delete a preset. It should not be the current active preset'''
@@ -413,14 +414,14 @@ class HearingAccessService(gatt.TemplateService):
413
414
  raise InvalidStateError('Cannot delete active preset')
414
415
 
415
416
  del self.preset_records[index]
416
- await self._notifyPresetOperations(PresetChangedOperationDeleted(index))
417
+ await self._notify_preset_operations(PresetChangedOperationDeleted(index))
417
418
 
418
419
  async def available_preset(self, index: int) -> None:
419
420
  '''Server API to make a preset available'''
420
421
 
421
422
  preset = self.preset_records[index]
422
423
  preset.properties.is_available = PresetRecord.Property.IsAvailable.IS_AVAILABLE
423
- await self._notifyPresetOperations(PresetChangedOperationAvailable(index))
424
+ await self._notify_preset_operations(PresetChangedOperationAvailable(index))
424
425
 
425
426
  async def unavailable_preset(self, index: int) -> None:
426
427
  '''Server API to make a preset unavailable. It should not be the current active preset'''
@@ -432,7 +433,7 @@ class HearingAccessService(gatt.TemplateService):
432
433
  preset.properties.is_available = (
433
434
  PresetRecord.Property.IsAvailable.IS_UNAVAILABLE
434
435
  )
435
- await self._notifyPresetOperations(PresetChangedOperationUnavailable(index))
436
+ await self._notify_preset_operations(PresetChangedOperationUnavailable(index))
436
437
 
437
438
  async def _preset_changed_operation(self, connection: Connection) -> None:
438
439
  '''Send all PresetChangedOperation saved for a given connection'''
@@ -447,8 +448,10 @@ class HearingAccessService(gatt.TemplateService):
447
448
  return op.additional_parameters
448
449
 
449
450
  op_list.sort(key=get_op_index)
450
- # If the ATT bearer is terminated before all notifications or indications are sent, then the server shall consider the Preset Changed operation aborted and shall continue the operation when the client reconnects.
451
- while len(op_list) > 0:
451
+ # If the ATT bearer is terminated before all notifications or indications are
452
+ # sent, then the server shall consider the Preset Changed operation aborted and
453
+ # shall continue the operation when the client reconnects.
454
+ while op_list:
452
455
  try:
453
456
  await connection.device.indicate_subscriber(
454
457
  connection,
@@ -460,14 +463,15 @@ class HearingAccessService(gatt.TemplateService):
460
463
  except TimeoutError:
461
464
  break
462
465
 
463
- async def _notifyPresetOperations(self, op: PresetChangedOperation) -> None:
464
- for historyList in self.preset_changed_operations_history_per_device.values():
465
- historyList.append(op)
466
+ async def _notify_preset_operations(self, op: PresetChangedOperation) -> None:
467
+ for history_list in self.preset_changed_operations_history_per_device.values():
468
+ history_list.append(op)
466
469
 
467
470
  for connection in self.currently_connected_clients:
468
471
  await self._preset_changed_operation(connection)
469
472
 
470
473
  async def _on_write_preset_name(self, connection: Connection, value: bytes):
474
+ del connection # Unused
471
475
 
472
476
  if self.read_presets_request_in_progress:
473
477
  raise att.ATT_Error(att.ErrorCode.PROCEDURE_ALREADY_IN_PROGRESS)
@@ -532,48 +536,51 @@ class HearingAccessService(gatt.TemplateService):
532
536
  self.active_preset_index = index
533
537
  await self.notify_active_preset()
534
538
 
535
- async def _on_set_active_preset(self, _: Connection, value: bytes):
539
+ async def _on_set_active_preset(self, connection: Connection, value: bytes):
540
+ del connection # Unused
536
541
  await self.set_active_preset(value)
537
542
 
538
- async def set_next_or_previous_preset(self, is_previous):
543
+ async def set_next_or_previous_preset(self, is_previous: bool) -> None:
539
544
  '''Set the next or the previous preset as active'''
540
545
 
541
546
  if self.active_preset_index == 0x00:
542
547
  raise att.ATT_Error(ErrorCode.PRESET_OPERATION_NOT_POSSIBLE)
543
548
 
544
- first_preset: Optional[PresetRecord] = None # To loop to first preset
545
- next_preset: Optional[PresetRecord] = None
546
- for index, record in sorted(self.preset_records.items(), reverse=is_previous):
547
- if not record.is_available():
548
- continue
549
- if first_preset == None:
550
- first_preset = record
551
- if is_previous:
552
- if index >= self.active_preset_index:
553
- continue
554
- elif index <= self.active_preset_index:
555
- continue
556
- next_preset = record
557
- break
558
-
559
- if not first_preset: # If no other preset are available
549
+ presets = sorted(
550
+ [
551
+ record
552
+ for record in self.preset_records.values()
553
+ if record.is_available()
554
+ ],
555
+ key=lambda record: record.index,
556
+ )
557
+ current_preset = self.preset_records[self.active_preset_index]
558
+ current_preset_pos = presets.index(current_preset)
559
+ if is_previous:
560
+ new_preset = presets[(current_preset_pos - 1) % len(presets)]
561
+ else:
562
+ new_preset = presets[(current_preset_pos + 1) % len(presets)]
563
+
564
+ if current_preset == new_preset: # If no other preset are available
560
565
  raise att.ATT_Error(ErrorCode.PRESET_OPERATION_NOT_POSSIBLE)
561
566
 
562
- if next_preset:
563
- self.active_preset_index = next_preset.index
564
- else:
565
- self.active_preset_index = first_preset.index
567
+ self.active_preset_index = new_preset.index
566
568
  await self.notify_active_preset()
567
569
 
568
- async def _on_set_next_preset(self, _: Connection, __value__: bytes) -> None:
570
+ async def _on_set_next_preset(self, connection: Connection, value: bytes) -> None:
571
+ del connection, value # Unused.
569
572
  await self.set_next_or_previous_preset(False)
570
573
 
571
- async def _on_set_previous_preset(self, _: Connection, __value__: bytes) -> None:
574
+ async def _on_set_previous_preset(
575
+ self, connection: Connection, value: bytes
576
+ ) -> None:
577
+ del connection, value # Unused.
572
578
  await self.set_next_or_previous_preset(True)
573
579
 
574
580
  async def _on_set_active_preset_synchronized_locally(
575
- self, _: Connection, value: bytes
581
+ self, connection: Connection, value: bytes
576
582
  ):
583
+ del connection # Unused.
577
584
  if (
578
585
  self.server_features.preset_synchronization_support
579
586
  == PresetSynchronizationSupport.PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED
@@ -584,8 +591,9 @@ class HearingAccessService(gatt.TemplateService):
584
591
  await self.other_server_in_binaural_set.set_active_preset(value)
585
592
 
586
593
  async def _on_set_next_preset_synchronized_locally(
587
- self, _: Connection, __value__: bytes
594
+ self, connection: Connection, value: bytes
588
595
  ):
596
+ del connection, value # Unused.
589
597
  if (
590
598
  self.server_features.preset_synchronization_support
591
599
  == PresetSynchronizationSupport.PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED
@@ -596,8 +604,9 @@ class HearingAccessService(gatt.TemplateService):
596
604
  await self.other_server_in_binaural_set.set_next_or_previous_preset(False)
597
605
 
598
606
  async def _on_set_previous_preset_synchronized_locally(
599
- self, _: Connection, __value__: bytes
607
+ self, connection: Connection, value: bytes
600
608
  ):
609
+ del connection, value # Unused.
601
610
  if (
602
611
  self.server_features.preset_synchronization_support
603
612
  == PresetSynchronizationSupport.PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED
@@ -615,11 +624,13 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
615
624
  SERVICE_CLASS = HearingAccessService
616
625
 
617
626
  hearing_aid_preset_control_point: gatt_client.CharacteristicProxy
618
- preset_control_point_indications: asyncio.Queue
619
- active_preset_index_notification: asyncio.Queue
627
+ preset_control_point_indications: asyncio.Queue[bytes]
628
+ active_preset_index_notification: asyncio.Queue[bytes]
620
629
 
621
630
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
622
631
  self.service_proxy = service_proxy
632
+ self.preset_control_point_indications = asyncio.Queue()
633
+ self.active_preset_index_notification = asyncio.Queue()
623
634
 
624
635
  self.server_features = gatt_adapters.PackedCharacteristicProxyAdapter(
625
636
  service_proxy.get_characteristics_by_uuid(
@@ -641,20 +652,12 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
641
652
  'B',
642
653
  )
643
654
 
644
- async def setup_subscription(self):
645
- self.preset_control_point_indications = asyncio.Queue()
646
- self.active_preset_index_notification = asyncio.Queue()
647
-
648
- def on_active_preset_index_notification(data: bytes):
649
- self.active_preset_index_notification.put_nowait(data)
650
-
651
- def on_preset_control_point_indication(data: bytes):
652
- self.preset_control_point_indications.put_nowait(data)
653
-
655
+ async def setup_subscription(self) -> None:
654
656
  await self.hearing_aid_preset_control_point.subscribe(
655
- functools.partial(on_preset_control_point_indication), prefer_notify=False
657
+ self.preset_control_point_indications.put_nowait,
658
+ prefer_notify=False,
656
659
  )
657
660
 
658
661
  await self.active_preset_index.subscribe(
659
- functools.partial(on_active_preset_index_notification)
662
+ self.active_preset_index_notification.put_nowait
660
663
  )
@@ -17,20 +17,21 @@
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
19
  from __future__ import annotations
20
- from enum import IntEnum
20
+
21
21
  import struct
22
+ from enum import IntEnum
22
23
  from typing import Optional
23
24
 
24
25
  from bumble import core
25
26
  from bumble.att import ATT_Error
26
27
  from bumble.gatt import (
27
- GATT_HEART_RATE_SERVICE,
28
- GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
29
28
  GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
30
29
  GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC,
31
- TemplateService,
30
+ GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
31
+ GATT_HEART_RATE_SERVICE,
32
32
  Characteristic,
33
33
  CharacteristicValue,
34
+ TemplateService,
34
35
  )
35
36
  from bumble.gatt_adapters import (
36
37
  DelegatedCharacteristicAdapter,
@@ -16,14 +16,16 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
+
19
20
  import dataclasses
20
21
  import enum
21
22
  import struct
22
23
  from typing import Any
24
+
23
25
  from typing_extensions import Self
24
26
 
25
- from bumble.profiles import bap
26
27
  from bumble import utils
28
+ from bumble.profiles import bap
27
29
 
28
30
 
29
31
  # -----------------------------------------------------------------------------