bumble 0.0.179__py3-none-any.whl → 0.0.181__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,12 +19,17 @@ like loading firmware after a cold start.
19
19
  # -----------------------------------------------------------------------------
20
20
  # Imports
21
21
  # -----------------------------------------------------------------------------
22
- import abc
22
+ from __future__ import annotations
23
23
  import logging
24
24
  import pathlib
25
25
  import platform
26
+ from typing import Dict, Iterable, Optional, Type, TYPE_CHECKING
27
+
26
28
  from . import rtk
29
+ from .common import Driver
27
30
 
31
+ if TYPE_CHECKING:
32
+ from bumble.host import Host
28
33
 
29
34
  # -----------------------------------------------------------------------------
30
35
  # Logging
@@ -32,40 +37,31 @@ from . import rtk
32
37
  logger = logging.getLogger(__name__)
33
38
 
34
39
 
35
- # -----------------------------------------------------------------------------
36
- # Classes
37
- # -----------------------------------------------------------------------------
38
- class Driver(abc.ABC):
39
- """Base class for drivers."""
40
-
41
- @staticmethod
42
- async def for_host(_host):
43
- """Return a driver instance for a host.
44
-
45
- Args:
46
- host: Host object for which a driver should be created.
47
-
48
- Returns:
49
- A Driver instance if a driver should be instantiated for this host, or
50
- None if no driver instance of this class is needed.
51
- """
52
- return None
53
-
54
- @abc.abstractmethod
55
- async def init_controller(self):
56
- """Initialize the controller."""
57
-
58
-
59
40
  # -----------------------------------------------------------------------------
60
41
  # Functions
61
42
  # -----------------------------------------------------------------------------
62
- async def get_driver_for_host(host):
63
- """Probe all known diver classes until one returns a valid instance for a host,
64
- or none is found.
43
+ async def get_driver_for_host(host: Host) -> Optional[Driver]:
44
+ """Probe diver classes until one returns a valid instance for a host, or none is
45
+ found.
46
+ If a "driver" HCI metadata entry is present, only that driver class will be probed.
65
47
  """
66
- if driver := await rtk.Driver.for_host(host):
67
- logger.debug("Instantiated RTK driver")
68
- return driver
48
+ driver_classes: Dict[str, Type[Driver]] = {"rtk": rtk.Driver}
49
+ probe_list: Iterable[str]
50
+ if driver_name := host.hci_metadata.get("driver"):
51
+ # Only probe a single driver
52
+ probe_list = [driver_name]
53
+ else:
54
+ # Probe all drivers
55
+ probe_list = driver_classes.keys()
56
+
57
+ for driver_name in probe_list:
58
+ if driver_class := driver_classes.get(driver_name):
59
+ logger.debug(f"Probing driver class: {driver_name}")
60
+ if driver := await driver_class.for_host(host):
61
+ logger.debug(f"Instantiated {driver_name} driver")
62
+ return driver
63
+ else:
64
+ logger.debug(f"Skipping unknown driver class: {driver_name}")
69
65
 
70
66
  return None
71
67
 
@@ -0,0 +1,45 @@
1
+ # Copyright 2021-2023 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """
15
+ Common types for drivers.
16
+ """
17
+
18
+ # -----------------------------------------------------------------------------
19
+ # Imports
20
+ # -----------------------------------------------------------------------------
21
+ import abc
22
+
23
+
24
+ # -----------------------------------------------------------------------------
25
+ # Classes
26
+ # -----------------------------------------------------------------------------
27
+ class Driver(abc.ABC):
28
+ """Base class for drivers."""
29
+
30
+ @staticmethod
31
+ async def for_host(_host):
32
+ """Return a driver instance for a host.
33
+
34
+ Args:
35
+ host: Host object for which a driver should be created.
36
+
37
+ Returns:
38
+ A Driver instance if a driver should be instantiated for this host, or
39
+ None if no driver instance of this class is needed.
40
+ """
41
+ return None
42
+
43
+ @abc.abstractmethod
44
+ async def init_controller(self):
45
+ """Initialize the controller."""
bumble/drivers/rtk.py CHANGED
@@ -41,7 +41,7 @@ from bumble.hci import (
41
41
  HCI_Reset_Command,
42
42
  HCI_Read_Local_Version_Information_Command,
43
43
  )
44
-
44
+ from bumble.drivers import common
45
45
 
46
46
  # -----------------------------------------------------------------------------
47
47
  # Logging
@@ -285,7 +285,7 @@ class Firmware:
285
285
  )
286
286
 
287
287
 
288
- class Driver:
288
+ class Driver(common.Driver):
289
289
  @dataclass
290
290
  class DriverInfo:
291
291
  rom: int
@@ -470,8 +470,12 @@ class Driver:
470
470
  logger.debug("USB metadata not found")
471
471
  return False
472
472
 
473
- vendor_id = host.hci_metadata.get("vendor_id", None)
474
- product_id = host.hci_metadata.get("product_id", None)
473
+ if host.hci_metadata.get('driver') == 'rtk':
474
+ # Forced driver
475
+ return True
476
+
477
+ vendor_id = host.hci_metadata.get("vendor_id")
478
+ product_id = host.hci_metadata.get("product_id")
475
479
  if vendor_id is None or product_id is None:
476
480
  logger.debug("USB metadata not sufficient")
477
481
  return False
@@ -486,6 +490,9 @@ class Driver:
486
490
 
487
491
  @classmethod
488
492
  async def driver_info_for_host(cls, host):
493
+ await host.send_command(HCI_Reset_Command(), check_result=True)
494
+ host.ready = True # Needed to let the host know the controller is ready.
495
+
489
496
  response = await host.send_command(
490
497
  HCI_Read_Local_Version_Information_Command(), check_result=True
491
498
  )
bumble/gatt.py CHANGED
@@ -23,16 +23,28 @@
23
23
  # Imports
24
24
  # -----------------------------------------------------------------------------
25
25
  from __future__ import annotations
26
- import asyncio
27
26
  import enum
28
27
  import functools
29
28
  import logging
30
29
  import struct
31
- from typing import Optional, Sequence, Iterable, List, Union
32
-
33
- from .colors import color
34
- from .core import UUID, get_dict_key_by_value
35
- from .att import Attribute
30
+ from typing import (
31
+ Callable,
32
+ Dict,
33
+ Iterable,
34
+ List,
35
+ Optional,
36
+ Sequence,
37
+ Union,
38
+ TYPE_CHECKING,
39
+ )
40
+
41
+ from bumble.colors import color
42
+ from bumble.core import UUID
43
+ from bumble.att import Attribute, AttributeValue
44
+
45
+ if TYPE_CHECKING:
46
+ from bumble.gatt_client import AttributeProxy
47
+ from bumble.device import Connection
36
48
 
37
49
 
38
50
  # -----------------------------------------------------------------------------
@@ -93,20 +105,35 @@ GATT_RECONNECTION_CONFIGURATION_SERVICE = UUID.from_16_bits(0x1829, 'Reconne
93
105
  GATT_INSULIN_DELIVERY_SERVICE = UUID.from_16_bits(0x183A, 'Insulin Delivery')
94
106
  GATT_BINARY_SENSOR_SERVICE = UUID.from_16_bits(0x183B, 'Binary Sensor')
95
107
  GATT_EMERGENCY_CONFIGURATION_SERVICE = UUID.from_16_bits(0x183C, 'Emergency Configuration')
108
+ GATT_AUTHORIZATION_CONTROL_SERVICE = UUID.from_16_bits(0x183D, 'Authorization Control')
96
109
  GATT_PHYSICAL_ACTIVITY_MONITOR_SERVICE = UUID.from_16_bits(0x183E, 'Physical Activity Monitor')
110
+ GATT_ELAPSED_TIME_SERVICE = UUID.from_16_bits(0x183F, 'Elapsed Time')
111
+ GATT_GENERIC_HEALTH_SENSOR_SERVICE = UUID.from_16_bits(0x1840, 'Generic Health Sensor')
97
112
  GATT_AUDIO_INPUT_CONTROL_SERVICE = UUID.from_16_bits(0x1843, 'Audio Input Control')
98
113
  GATT_VOLUME_CONTROL_SERVICE = UUID.from_16_bits(0x1844, 'Volume Control')
99
114
  GATT_VOLUME_OFFSET_CONTROL_SERVICE = UUID.from_16_bits(0x1845, 'Volume Offset Control')
100
- GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification Service')
115
+ GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification')
101
116
  GATT_DEVICE_TIME_SERVICE = UUID.from_16_bits(0x1847, 'Device Time')
102
- GATT_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1848, 'Media Control Service')
103
- GATT_GENERIC_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1849, 'Generic Media Control Service')
117
+ GATT_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1848, 'Media Control')
118
+ GATT_GENERIC_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1849, 'Generic Media Control')
104
119
  GATT_CONSTANT_TONE_EXTENSION_SERVICE = UUID.from_16_bits(0x184A, 'Constant Tone Extension')
105
- GATT_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184B, 'Telephone Bearer Service')
106
- GATT_GENERIC_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer Service')
120
+ GATT_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184B, 'Telephone Bearer')
121
+ GATT_GENERIC_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer')
107
122
  GATT_MICROPHONE_CONTROL_SERVICE = UUID.from_16_bits(0x184D, 'Microphone Control')
108
-
109
- # Types
123
+ GATT_AUDIO_STREAM_CONTROL_SERVICE = UUID.from_16_bits(0x184E, 'Audio Stream Control')
124
+ GATT_BROADCAST_AUDIO_SCAN_SERVICE = UUID.from_16_bits(0x184F, 'Broadcast Audio Scan')
125
+ GATT_PUBLISHED_AUDIO_CAPABILITIES_SERVICE = UUID.from_16_bits(0x1850, 'Published Audio Capabilities')
126
+ GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE = UUID.from_16_bits(0x1851, 'Basic Audio Announcement')
127
+ GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE = UUID.from_16_bits(0x1852, 'Broadcast Audio Announcement')
128
+ GATT_COMMON_AUDIO_SERVICE = UUID.from_16_bits(0x1853, 'Common Audio')
129
+ GATT_HEARING_ACCESS_SERVICE = UUID.from_16_bits(0x1854, 'Hearing Access')
130
+ GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE = UUID.from_16_bits(0x1855, 'Telephony and Media Audio')
131
+ GATT_PUBLIC_BROADCAST_ANNOUNCEMENT_SERVICE = UUID.from_16_bits(0x1856, 'Public Broadcast Announcement')
132
+ GATT_ELECTRONIC_SHELF_LABEL_SERVICE = UUID.from_16_bits(0X1857, 'Electronic Shelf Label')
133
+ GATT_GAMING_AUDIO_SERVICE = UUID.from_16_bits(0x1858, 'Gaming Audio')
134
+ GATT_MESH_PROXY_SOLICITATION_SERVICE = UUID.from_16_bits(0x1859, 'Mesh Audio Solicitation')
135
+
136
+ # Attribute Types
110
137
  GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2800, 'Primary Service')
111
138
  GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2801, 'Secondary Service')
112
139
  GATT_INCLUDE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2802, 'Include')
@@ -129,6 +156,8 @@ GATT_ENVIRONMENTAL_SENSING_MEASUREMENT_DESCRIPTOR = UUID.from_16_bits(0x290C,
129
156
  GATT_ENVIRONMENTAL_SENSING_TRIGGER_DESCRIPTOR = UUID.from_16_bits(0x290D, 'Environmental Sensing Trigger Setting')
130
157
  GATT_TIME_TRIGGER_DESCRIPTOR = UUID.from_16_bits(0x290E, 'Time Trigger Setting')
131
158
  GATT_COMPLETE_BR_EDR_TRANSPORT_BLOCK_DATA_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Complete BR-EDR Transport Block Data')
159
+ GATT_OBSERVATION_SCHEDULE_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Observation Schedule')
160
+ GATT_VALID_RANGE_AND_ACCURACY_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Valid Range And Accuracy')
132
161
 
133
162
  # Device Information Service
134
163
  GATT_SYSTEM_ID_CHARACTERISTIC = UUID.from_16_bits(0x2A23, 'System ID')
@@ -156,6 +185,96 @@ GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A39, 'Heart
156
185
  # Battery Service
157
186
  GATT_BATTERY_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A19, 'Battery Level')
158
187
 
188
+ # Telephony And Media Audio Service (TMAS)
189
+ GATT_TMAP_ROLE_CHARACTERISTIC = UUID.from_16_bits(0x2B51, 'TMAP Role')
190
+
191
+ # Audio Input Control Service (AICS)
192
+ GATT_AUDIO_INPUT_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2B77, 'Audio Input State')
193
+ GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC = UUID.from_16_bits(0x2B78, 'Gain Settings Attribute')
194
+ GATT_AUDIO_INPUT_TYPE_CHARACTERISTIC = UUID.from_16_bits(0x2B79, 'Audio Input Type')
195
+ GATT_AUDIO_INPUT_STATUS_CHARACTERISTIC = UUID.from_16_bits(0x2B7A, 'Audio Input Status')
196
+ GATT_AUDIO_INPUT_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B7B, 'Audio Input Control Point')
197
+ GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC = UUID.from_16_bits(0x2B7C, 'Audio Input Description')
198
+
199
+ # Volume Control Service (VCS)
200
+ GATT_VOLUME_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2B7D, 'Volume State')
201
+ GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B7E, 'Volume Control Point')
202
+ GATT_VOLUME_FLAGS_CHARACTERISTIC = UUID.from_16_bits(0x2B7F, 'Volume Flags')
203
+
204
+ # Volume Offset Control Service (VOCS)
205
+ GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2B80, 'Volume Offset State')
206
+ GATT_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2B81, 'Audio Location')
207
+ GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B82, 'Volume Offset Control Point')
208
+ GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC = UUID.from_16_bits(0x2B83, 'Audio Output Description')
209
+
210
+ # Coordinated Set Identification Service (CSIS)
211
+ GATT_SET_IDENTITY_RESOLVING_KEY_CHARACTERISTIC = UUID.from_16_bits(0x2B84, 'Set Identity Resolving Key')
212
+ GATT_COORDINATED_SET_SIZE_CHARACTERISTIC = UUID.from_16_bits(0x2B85, 'Coordinated Set Size')
213
+ GATT_SET_MEMBER_LOCK_CHARACTERISTIC = UUID.from_16_bits(0x2B86, 'Set Member Lock')
214
+ GATT_SET_MEMBER_RANK_CHARACTERISTIC = UUID.from_16_bits(0x2B87, 'Set Member Rank')
215
+
216
+ # Media Control Service (MCS)
217
+ GATT_MEDIA_PLAYER_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2B93, 'Media Player Name')
218
+ GATT_MEDIA_PLAYER_ICON_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B94, 'Media Player Icon Object ID')
219
+ GATT_MEDIA_PLAYER_ICON_URL_CHARACTERISTIC = UUID.from_16_bits(0x2B95, 'Media Player Icon URL')
220
+ GATT_TRACK_CHANGED_CHARACTERISTIC = UUID.from_16_bits(0x2B96, 'Track Changed')
221
+ GATT_TRACK_TITLE_CHARACTERISTIC = UUID.from_16_bits(0x2B97, 'Track Title')
222
+ GATT_TRACK_DURATION_CHARACTERISTIC = UUID.from_16_bits(0x2B98, 'Track Duration')
223
+ GATT_TRACK_POSITION_CHARACTERISTIC = UUID.from_16_bits(0x2B99, 'Track Position')
224
+ GATT_PLAYBACK_SPEED_CHARACTERISTIC = UUID.from_16_bits(0x2B9A, 'Playback Speed')
225
+ GATT_SEEKING_SPEED_CHARACTERISTIC = UUID.from_16_bits(0x2B9B, 'Seeking Speed')
226
+ GATT_CURRENT_TRACK_SEGMENTS_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9C, 'Current Track Segments Object ID')
227
+ GATT_CURRENT_TRACK_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9D, 'Current Track Object ID')
228
+ GATT_NEXT_TRACK_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9E, 'Next Track Object ID')
229
+ GATT_PARENT_GROUP_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2B9F, 'Parent Group Object ID')
230
+ GATT_CURRENT_GROUP_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BA0, 'Current Group Object ID')
231
+ GATT_PLAYING_ORDER_CHARACTERISTIC = UUID.from_16_bits(0x2BA1, 'Playing Order')
232
+ GATT_PLAYING_ORDERS_SUPPORTED_CHARACTERISTIC = UUID.from_16_bits(0x2BA2, 'Playing Orders Supported')
233
+ GATT_MEDIA_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BA3, 'Media State')
234
+ GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BA4, 'Media Control Point')
235
+ GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC = UUID.from_16_bits(0x2BA5, 'Media Control Point Opcodes Supported')
236
+ GATT_SEARCH_RESULTS_OBJECT_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BA6, 'Search Results Object ID')
237
+ GATT_SEARCH_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BA7, 'Search Control Point')
238
+ GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Content Control Id')
239
+
240
+ # Telephone Bearer Service (TBS)
241
+ GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BB4, 'Bearer Provider Name')
242
+ GATT_BEARER_UCI_CHARACTERISTIC = UUID.from_16_bits(0x2BB5, 'Bearer UCI')
243
+ GATT_BEARER_TECHNOLOGY_CHARACTERISTIC = UUID.from_16_bits(0x2BB6, 'Bearer Technology')
244
+ GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2BB7, 'Bearer URI Schemes Supported List')
245
+ GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC = UUID.from_16_bits(0x2BB8, 'Bearer Signal Strength')
246
+ GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(0x2BB9, 'Bearer Signal Strength Reporting Interval')
247
+ GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC = UUID.from_16_bits(0x2BBA, 'Bearer List Current Calls')
248
+ GATT_CONTENT_CONTROL_ID_CHARACTERISTIC = UUID.from_16_bits(0x2BBB, 'Content Control ID')
249
+ GATT_STATUS_FLAGS_CHARACTERISTIC = UUID.from_16_bits(0x2BBC, 'Status Flags')
250
+ GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC = UUID.from_16_bits(0x2BBD, 'Incoming Call Target Bearer URI')
251
+ GATT_CALL_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BBE, 'Call State')
252
+ GATT_CALL_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BBF, 'Call Control Point')
253
+ GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC = UUID.from_16_bits(0x2BC0, 'Call Control Point Optional Opcodes')
254
+ GATT_TERMINATION_REASON_CHARACTERISTIC = UUID.from_16_bits(0x2BC1, 'Termination Reason')
255
+ GATT_INCOMING_CALL_CHARACTERISTIC = UUID.from_16_bits(0x2BC2, 'Incoming Call')
256
+ GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Call Friendly Name')
257
+
258
+ # Microphone Control Service (MICS)
259
+ GATT_MUTE_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Mute')
260
+
261
+ # Audio Stream Control Service (ASCS)
262
+ GATT_SINK_ASE_CHARACTERISTIC = UUID.from_16_bits(0x2BC4, 'Sink ASE')
263
+ GATT_SOURCE_ASE_CHARACTERISTIC = UUID.from_16_bits(0x2BC5, 'Source ASE')
264
+ GATT_ASE_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BC6, 'ASE Control Point')
265
+
266
+ # Broadcast Audio Scan Service (BASS)
267
+ GATT_BROADCAST_AUDIO_SCAN_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BC7, 'Broadcast Audio Scan Control Point')
268
+ GATT_BROADCAST_RECEIVE_STATE_CHARACTERISTIC = UUID.from_16_bits(0x2BC8, 'Broadcast Receive State')
269
+
270
+ # Published Audio Capabilities Service (PACS)
271
+ GATT_SINK_PAC_CHARACTERISTIC = UUID.from_16_bits(0x2BC9, 'Sink PAC')
272
+ GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCA, 'Sink Audio Location')
273
+ GATT_SOURCE_PAC_CHARACTERISTIC = UUID.from_16_bits(0x2BCB, 'Source PAC')
274
+ GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCC, 'Source Audio Location')
275
+ GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
276
+ GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
277
+
159
278
  # ASHA Service
160
279
  GATT_ASHA_SERVICE = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid')
161
280
  GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID('6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties')
@@ -177,6 +296,9 @@ GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC = UUID.from_16_bi
177
296
  GATT_CURRENT_TIME_CHARACTERISTIC = UUID.from_16_bits(0x2A2B, 'Current Time')
178
297
  GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report')
179
298
  GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bits(0x2AA6, 'Central Address Resolution')
299
+ GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B29, 'Client Supported Features')
300
+ GATT_DATABASE_HASH_CHARACTERISTIC = UUID.from_16_bits(0x2B2A, 'Database Hash')
301
+ GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B3A, 'Server Supported Features')
180
302
 
181
303
  # fmt: on
182
304
  # pylint: enable=line-too-long
@@ -258,9 +380,12 @@ class TemplateService(Service):
258
380
  UUID: UUID
259
381
 
260
382
  def __init__(
261
- self, characteristics: List[Characteristic], primary: bool = True
383
+ self,
384
+ characteristics: List[Characteristic],
385
+ primary: bool = True,
386
+ included_services: List[Service] = [],
262
387
  ) -> None:
263
- super().__init__(self.UUID, characteristics, primary)
388
+ super().__init__(self.UUID, characteristics, primary, included_services)
264
389
 
265
390
 
266
391
  # -----------------------------------------------------------------------------
@@ -409,56 +534,43 @@ class CharacteristicDeclaration(Attribute):
409
534
 
410
535
 
411
536
  # -----------------------------------------------------------------------------
412
- class CharacteristicValue:
413
- '''
414
- Characteristic value where reading and/or writing is delegated to functions
415
- passed as arguments to the constructor.
416
- '''
417
-
418
- def __init__(self, read=None, write=None):
419
- self._read = read
420
- self._write = write
421
-
422
- def read(self, connection):
423
- return self._read(connection) if self._read else b''
424
-
425
- def write(self, connection, value):
426
- if self._write:
427
- self._write(connection, value)
537
+ class CharacteristicValue(AttributeValue):
538
+ """Same as AttributeValue, for backward compatibility"""
428
539
 
429
540
 
430
541
  # -----------------------------------------------------------------------------
431
542
  class CharacteristicAdapter:
432
543
  '''
433
- An adapter that can adapt any object with `read_value` and `write_value`
434
- methods (like Characteristic and CharacteristicProxy objects) by wrapping
435
- those methods with ones that return/accept encoded/decoded values.
436
- Objects with async methods are considered proxies, so the adaptation is one
437
- where the return value of `read_value` is decoded and the value passed to
438
- `write_value` is encoded. Other objects are considered local characteristics
439
- so the adaptation is one where the return value of `read_value` is encoded
440
- and the value passed to `write_value` is decoded.
441
- If the characteristic has a `subscribe` method, it is wrapped with one where
442
- the values are decoded before being passed to the subscriber.
544
+ An adapter that can adapt Characteristic and AttributeProxy objects
545
+ by wrapping their `read_value()` and `write_value()` methods with ones that
546
+ return/accept encoded/decoded values.
547
+
548
+ For proxies (i.e used by a GATT client), the adaptation is one where the return
549
+ value of `read_value()` is decoded and the value passed to `write_value()` is
550
+ encoded. The `subscribe()` method, is wrapped with one where the values are decoded
551
+ before being passed to the subscriber.
552
+
553
+ For local values (i.e hosted by a GATT server) the adaptation is one where the
554
+ return value of `read_value()` is encoded and the value passed to `write_value()`
555
+ is decoded.
443
556
  '''
444
557
 
445
- def __init__(self, characteristic):
558
+ read_value: Callable
559
+ write_value: Callable
560
+
561
+ def __init__(self, characteristic: Union[Characteristic, AttributeProxy]):
446
562
  self.wrapped_characteristic = characteristic
447
- self.subscribers = {} # Map from subscriber to proxy subscriber
563
+ self.subscribers: Dict[
564
+ Callable, Callable
565
+ ] = {} # Map from subscriber to proxy subscriber
448
566
 
449
- if asyncio.iscoroutinefunction(
450
- characteristic.read_value
451
- ) and asyncio.iscoroutinefunction(characteristic.write_value):
452
- self.read_value = self.read_decoded_value
453
- self.write_value = self.write_decoded_value
454
- else:
567
+ if isinstance(characteristic, Characteristic):
455
568
  self.read_value = self.read_encoded_value
456
569
  self.write_value = self.write_encoded_value
457
-
458
- if hasattr(self.wrapped_characteristic, 'subscribe'):
570
+ else:
571
+ self.read_value = self.read_decoded_value
572
+ self.write_value = self.write_decoded_value
459
573
  self.subscribe = self.wrapped_subscribe
460
-
461
- if hasattr(self.wrapped_characteristic, 'unsubscribe'):
462
574
  self.unsubscribe = self.wrapped_unsubscribe
463
575
 
464
576
  def __getattr__(self, name):
@@ -477,11 +589,13 @@ class CharacteristicAdapter:
477
589
  else:
478
590
  setattr(self.wrapped_characteristic, name, value)
479
591
 
480
- def read_encoded_value(self, connection):
481
- return self.encode_value(self.wrapped_characteristic.read_value(connection))
592
+ async def read_encoded_value(self, connection):
593
+ return self.encode_value(
594
+ await self.wrapped_characteristic.read_value(connection)
595
+ )
482
596
 
483
- def write_encoded_value(self, connection, value):
484
- return self.wrapped_characteristic.write_value(
597
+ async def write_encoded_value(self, connection, value):
598
+ return await self.wrapped_characteristic.write_value(
485
599
  connection, self.decode_value(value)
486
600
  )
487
601
 
@@ -616,13 +730,24 @@ class Descriptor(Attribute):
616
730
  '''
617
731
 
618
732
  def __str__(self) -> str:
733
+ if isinstance(self.value, bytes):
734
+ value_str = self.value.hex()
735
+ elif isinstance(self.value, CharacteristicValue):
736
+ value = self.value.read(None)
737
+ if isinstance(value, bytes):
738
+ value_str = value.hex()
739
+ else:
740
+ value_str = '<async>'
741
+ else:
742
+ value_str = '<...>'
619
743
  return (
620
744
  f'Descriptor(handle=0x{self.handle:04X}, '
621
745
  f'type={self.type}, '
622
- f'value={self.read_value(None).hex()})'
746
+ f'value={value_str})'
623
747
  )
624
748
 
625
749
 
750
+ # -----------------------------------------------------------------------------
626
751
  class ClientCharacteristicConfigurationBits(enum.IntFlag):
627
752
  '''
628
753
  See Vol 3, Part G - 3.3.3.3 - Table 3.11 Client Characteristic Configuration bit