bumble 0.0.207__py3-none-any.whl → 0.0.208__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.
@@ -16,13 +16,14 @@
16
16
  # -----------------------------------------------------------------------------
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
+ from __future__ import annotations
19
20
  from enum import IntEnum
20
21
  import struct
22
+ from typing import Optional
21
23
 
22
24
  from bumble import core
23
- from ..gatt_client import ProfileServiceProxy
24
- from ..att import ATT_Error
25
- from ..gatt import (
25
+ from bumble.att import ATT_Error
26
+ from bumble.gatt import (
26
27
  GATT_HEART_RATE_SERVICE,
27
28
  GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
28
29
  GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
@@ -30,10 +31,13 @@ from ..gatt import (
30
31
  TemplateService,
31
32
  Characteristic,
32
33
  CharacteristicValue,
33
- SerializableCharacteristicAdapter,
34
+ )
35
+ from bumble.gatt_adapters import (
34
36
  DelegatedCharacteristicAdapter,
35
37
  PackedCharacteristicAdapter,
38
+ SerializableCharacteristicAdapter,
36
39
  )
40
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy
37
41
 
38
42
 
39
43
  # -----------------------------------------------------------------------------
@@ -43,6 +47,10 @@ class HeartRateService(TemplateService):
43
47
  CONTROL_POINT_NOT_SUPPORTED = 0x80
44
48
  RESET_ENERGY_EXPENDED = 0x01
45
49
 
50
+ heart_rate_measurement_characteristic: Characteristic[HeartRateMeasurement]
51
+ body_sensor_location_characteristic: Characteristic[BodySensorLocation]
52
+ heart_rate_control_point_characteristic: Characteristic[int]
53
+
46
54
  class BodySensorLocation(IntEnum):
47
55
  OTHER = 0
48
56
  CHEST = 1
@@ -198,6 +206,14 @@ class HeartRateService(TemplateService):
198
206
  class HeartRateServiceProxy(ProfileServiceProxy):
199
207
  SERVICE_CLASS = HeartRateService
200
208
 
209
+ heart_rate_measurement: Optional[
210
+ CharacteristicProxy[HeartRateService.HeartRateMeasurement]
211
+ ]
212
+ body_sensor_location: Optional[
213
+ CharacteristicProxy[HeartRateService.BodySensorLocation]
214
+ ]
215
+ heart_rate_control_point: Optional[CharacteristicProxy[int]]
216
+
201
217
  def __init__(self, service_proxy):
202
218
  self.service_proxy = service_proxy
203
219
 
bumble/profiles/mcp.py CHANGED
@@ -208,7 +208,7 @@ class MediaControlService(gatt.TemplateService):
208
208
  properties=gatt.Characteristic.Properties.READ
209
209
  | gatt.Characteristic.Properties.NOTIFY,
210
210
  permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
211
- value=media_player_name or 'Bumble Player',
211
+ value=(media_player_name or 'Bumble Player').encode(),
212
212
  )
213
213
  self.track_changed_characteristic = gatt.Characteristic(
214
214
  uuid=gatt.GATT_TRACK_CHANGED_CHARACTERISTIC,
@@ -247,14 +247,16 @@ class MediaControlService(gatt.TemplateService):
247
247
  permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
248
248
  value=b'',
249
249
  )
250
- self.media_control_point_characteristic = gatt.Characteristic(
251
- uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
252
- properties=gatt.Characteristic.Properties.WRITE
253
- | gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
254
- | gatt.Characteristic.Properties.NOTIFY,
255
- permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
256
- | gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
257
- value=gatt.CharacteristicValue(write=self.on_media_control_point),
250
+ self.media_control_point_characteristic: gatt.Characteristic[bytes] = (
251
+ gatt.Characteristic(
252
+ uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
253
+ properties=gatt.Characteristic.Properties.WRITE
254
+ | gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
255
+ | gatt.Characteristic.Properties.NOTIFY,
256
+ permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
257
+ | gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
258
+ value=gatt.CharacteristicValue(write=self.on_media_control_point),
259
+ )
258
260
  )
259
261
  self.media_control_point_opcodes_supported_characteristic = gatt.Characteristic(
260
262
  uuid=gatt.GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC,
bumble/profiles/pacs.py CHANGED
@@ -25,6 +25,7 @@ from typing import Optional, Sequence, Union
25
25
  from bumble.profiles.bap import AudioLocation, CodecSpecificCapabilities, ContextType
26
26
  from bumble.profiles import le_audio
27
27
  from bumble import gatt
28
+ from bumble import gatt_adapters
28
29
  from bumble import gatt_client
29
30
  from bumble import hci
30
31
 
@@ -185,34 +186,42 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
185
186
  class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
186
187
  SERVICE_CLASS = PublishedAudioCapabilitiesService
187
188
 
188
- sink_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
189
- sink_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
190
- source_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
191
- source_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
192
- available_audio_contexts: gatt.DelegatedCharacteristicAdapter
193
- supported_audio_contexts: gatt.DelegatedCharacteristicAdapter
189
+ sink_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
190
+ sink_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
191
+ None
192
+ )
193
+ source_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
194
+ source_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
195
+ None
196
+ )
197
+ available_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
198
+ supported_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
194
199
 
195
200
  def __init__(self, service_proxy: gatt_client.ServiceProxy):
196
201
  self.service_proxy = service_proxy
197
202
 
198
- self.available_audio_contexts = gatt.DelegatedCharacteristicAdapter(
199
- service_proxy.get_required_characteristic_by_uuid(
200
- gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
201
- ),
202
- decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
203
+ self.available_audio_contexts = (
204
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
205
+ service_proxy.get_required_characteristic_by_uuid(
206
+ gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
207
+ ),
208
+ decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
209
+ )
203
210
  )
204
211
 
205
- self.supported_audio_contexts = gatt.DelegatedCharacteristicAdapter(
206
- service_proxy.get_required_characteristic_by_uuid(
207
- gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
208
- ),
209
- decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
212
+ self.supported_audio_contexts = (
213
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
214
+ service_proxy.get_required_characteristic_by_uuid(
215
+ gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
216
+ ),
217
+ decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
218
+ )
210
219
  )
211
220
 
212
221
  if characteristics := service_proxy.get_characteristics_by_uuid(
213
222
  gatt.GATT_SINK_PAC_CHARACTERISTIC
214
223
  ):
215
- self.sink_pac = gatt.DelegatedCharacteristicAdapter(
224
+ self.sink_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
216
225
  characteristics[0],
217
226
  decode=PacRecord.list_from_bytes,
218
227
  )
@@ -220,7 +229,7 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
220
229
  if characteristics := service_proxy.get_characteristics_by_uuid(
221
230
  gatt.GATT_SOURCE_PAC_CHARACTERISTIC
222
231
  ):
223
- self.source_pac = gatt.DelegatedCharacteristicAdapter(
232
+ self.source_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
224
233
  characteristics[0],
225
234
  decode=PacRecord.list_from_bytes,
226
235
  )
@@ -228,15 +237,19 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
228
237
  if characteristics := service_proxy.get_characteristics_by_uuid(
229
238
  gatt.GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC
230
239
  ):
231
- self.sink_audio_locations = gatt.DelegatedCharacteristicAdapter(
232
- characteristics[0],
233
- decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
240
+ self.sink_audio_locations = (
241
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
242
+ characteristics[0],
243
+ decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
244
+ )
234
245
  )
235
246
 
236
247
  if characteristics := service_proxy.get_characteristics_by_uuid(
237
248
  gatt.GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC
238
249
  ):
239
- self.source_audio_locations = gatt.DelegatedCharacteristicAdapter(
240
- characteristics[0],
241
- decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
250
+ self.source_audio_locations = (
251
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
252
+ characteristics[0],
253
+ decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
254
+ )
242
255
  )
bumble/profiles/tmap.py CHANGED
@@ -24,11 +24,11 @@ import struct
24
24
  from bumble.gatt import (
25
25
  TemplateService,
26
26
  Characteristic,
27
- DelegatedCharacteristicAdapter,
28
27
  GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE,
29
28
  GATT_TMAP_ROLE_CHARACTERISTIC,
30
29
  )
31
- from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
30
+ from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
31
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
32
32
 
33
33
 
34
34
  # -----------------------------------------------------------------------------
@@ -53,6 +53,8 @@ class Role(enum.IntFlag):
53
53
  class TelephonyAndMediaAudioService(TemplateService):
54
54
  UUID = GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE
55
55
 
56
+ role_characteristic: Characteristic[bytes]
57
+
56
58
  def __init__(self, role: Role):
57
59
  self.role_characteristic = Characteristic(
58
60
  GATT_TMAP_ROLE_CHARACTERISTIC,
@@ -68,12 +70,12 @@ class TelephonyAndMediaAudioService(TemplateService):
68
70
  class TelephonyAndMediaAudioServiceProxy(ProfileServiceProxy):
69
71
  SERVICE_CLASS = TelephonyAndMediaAudioService
70
72
 
71
- role: DelegatedCharacteristicAdapter
73
+ role: CharacteristicProxy[Role]
72
74
 
73
75
  def __init__(self, service_proxy: ServiceProxy):
74
76
  self.service_proxy = service_proxy
75
77
 
76
- self.role = DelegatedCharacteristicAdapter(
78
+ self.role = DelegatedCharacteristicProxyAdapter(
77
79
  service_proxy.get_required_characteristic_by_uuid(
78
80
  GATT_TMAP_ROLE_CHARACTERISTIC
79
81
  ),
bumble/profiles/vcs.py CHANGED
@@ -25,6 +25,7 @@ from typing import Optional, Sequence
25
25
  from bumble import att
26
26
  from bumble import device
27
27
  from bumble import gatt
28
+ from bumble import gatt_adapters
28
29
  from bumble import gatt_client
29
30
 
30
31
 
@@ -209,14 +210,14 @@ class VolumeControlService(gatt.TemplateService):
209
210
  class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
210
211
  SERVICE_CLASS = VolumeControlService
211
212
 
212
- volume_control_point: gatt_client.CharacteristicProxy
213
- volume_state: gatt.SerializableCharacteristicAdapter
214
- volume_flags: gatt.DelegatedCharacteristicAdapter
213
+ volume_control_point: gatt_client.CharacteristicProxy[bytes]
214
+ volume_state: gatt_client.CharacteristicProxy[VolumeState]
215
+ volume_flags: gatt_client.CharacteristicProxy[VolumeFlags]
215
216
 
216
217
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
217
218
  self.service_proxy = service_proxy
218
219
 
219
- self.volume_state = gatt.SerializableCharacteristicAdapter(
220
+ self.volume_state = gatt_adapters.SerializableCharacteristicProxyAdapter(
220
221
  service_proxy.get_required_characteristic_by_uuid(
221
222
  gatt.GATT_VOLUME_STATE_CHARACTERISTIC
222
223
  ),
@@ -227,7 +228,7 @@ class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
227
228
  gatt.GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC
228
229
  )
229
230
 
230
- self.volume_flags = gatt.DelegatedCharacteristicAdapter(
231
+ self.volume_flags = gatt_adapters.DelegatedCharacteristicProxyAdapter(
231
232
  service_proxy.get_required_characteristic_by_uuid(
232
233
  gatt.GATT_VOLUME_FLAGS_CHARACTERISTIC
233
234
  ),
bumble/profiles/vocs.py CHANGED
@@ -24,17 +24,19 @@ from bumble.device import Connection
24
24
  from bumble.att import ATT_Error
25
25
  from bumble.gatt import (
26
26
  Characteristic,
27
- DelegatedCharacteristicAdapter,
28
27
  TemplateService,
29
28
  CharacteristicValue,
30
- SerializableCharacteristicAdapter,
31
- UTF8CharacteristicAdapter,
32
29
  GATT_VOLUME_OFFSET_CONTROL_SERVICE,
33
30
  GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC,
34
31
  GATT_AUDIO_LOCATION_CHARACTERISTIC,
35
32
  GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC,
36
33
  GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
37
34
  )
35
+ from bumble.gatt_adapters import (
36
+ DelegatedCharacteristicProxyAdapter,
37
+ SerializableCharacteristicProxyAdapter,
38
+ UTF8CharacteristicProxyAdapter,
39
+ )
38
40
  from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
39
41
  from bumble.utils import OpenIntEnum
40
42
  from bumble.profiles.bap import AudioLocation
@@ -67,7 +69,7 @@ class ErrorCode(OpenIntEnum):
67
69
  class VolumeOffsetState:
68
70
  volume_offset: int = 0
69
71
  change_counter: int = 0
70
- attribute_value: Optional[CharacteristicValue] = None
72
+ attribute: Optional[Characteristic] = None
71
73
 
72
74
  def __bytes__(self) -> bytes:
73
75
  return struct.pack('<hB', self.volume_offset, self.change_counter)
@@ -81,8 +83,8 @@ class VolumeOffsetState:
81
83
  self.change_counter = (self.change_counter + 1) % (CHANGE_COUNTER_MAX_VALUE + 1)
82
84
 
83
85
  async def notify_subscribers_via_connection(self, connection: Connection) -> None:
84
- assert self.attribute_value is not None
85
- await connection.device.notify_subscribers(attribute=self.attribute_value)
86
+ assert self.attribute is not None
87
+ await connection.device.notify_subscribers(attribute=self.attribute)
86
88
 
87
89
  def on_read(self, _connection: Optional[Connection]) -> bytes:
88
90
  return bytes(self)
@@ -91,7 +93,7 @@ class VolumeOffsetState:
91
93
  @dataclass
92
94
  class VocsAudioLocation:
93
95
  audio_location: AudioLocation = AudioLocation.NOT_ALLOWED
94
- attribute_value: Optional[CharacteristicValue] = None
96
+ attribute: Optional[Characteristic] = None
95
97
 
96
98
  def __bytes__(self) -> bytes:
97
99
  return struct.pack('<I', self.audio_location)
@@ -106,10 +108,10 @@ class VocsAudioLocation:
106
108
 
107
109
  async def on_write(self, connection: Optional[Connection], value: bytes) -> None:
108
110
  assert connection
109
- assert self.attribute_value
111
+ assert self.attribute
110
112
 
111
113
  self.audio_location = AudioLocation(int.from_bytes(value, 'little'))
112
- await connection.device.notify_subscribers(attribute=self.attribute_value)
114
+ await connection.device.notify_subscribers(attribute=self.attribute)
113
115
 
114
116
 
115
117
  @dataclass
@@ -148,7 +150,7 @@ class VolumeOffsetControlPoint:
148
150
  @dataclass
149
151
  class AudioOutputDescription:
150
152
  audio_output_description: str = ''
151
- attribute_value: Optional[CharacteristicValue] = None
153
+ attribute: Optional[Characteristic] = None
152
154
 
153
155
  @classmethod
154
156
  def from_bytes(cls, data: bytes):
@@ -162,10 +164,10 @@ class AudioOutputDescription:
162
164
 
163
165
  async def on_write(self, connection: Optional[Connection], value: bytes) -> None:
164
166
  assert connection
165
- assert self.attribute_value
167
+ assert self.attribute
166
168
 
167
169
  self.audio_output_description = value.decode('utf-8')
168
- await connection.device.notify_subscribers(attribute=self.attribute_value)
170
+ await connection.device.notify_subscribers(attribute=self.attribute)
169
171
 
170
172
 
171
173
  # -----------------------------------------------------------------------------
@@ -197,7 +199,7 @@ class VolumeOffsetControlService(TemplateService):
197
199
  VolumeOffsetControlPoint(self.volume_offset_state)
198
200
  )
199
201
 
200
- self.volume_offset_state_characteristic = Characteristic(
202
+ self.volume_offset_state_characteristic: Characteristic[bytes] = Characteristic(
201
203
  uuid=GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC,
202
204
  properties=(
203
205
  Characteristic.Properties.READ | Characteristic.Properties.NOTIFY
@@ -206,7 +208,7 @@ class VolumeOffsetControlService(TemplateService):
206
208
  value=CharacteristicValue(read=self.volume_offset_state.on_read),
207
209
  )
208
210
 
209
- self.audio_location_characteristic = Characteristic(
211
+ self.audio_location_characteristic: Characteristic[bytes] = Characteristic(
210
212
  uuid=GATT_AUDIO_LOCATION_CHARACTERISTIC,
211
213
  properties=(
212
214
  Characteristic.Properties.READ
@@ -222,33 +224,39 @@ class VolumeOffsetControlService(TemplateService):
222
224
  write=self.audio_location.on_write,
223
225
  ),
224
226
  )
225
- self.audio_location.attribute_value = self.audio_location_characteristic.value
226
-
227
- self.volume_offset_control_point_characteristic = Characteristic(
228
- uuid=GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC,
229
- properties=Characteristic.Properties.WRITE,
230
- permissions=Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
231
- value=CharacteristicValue(write=self.volume_offset_control_point.on_write),
227
+ self.audio_location.attribute = self.audio_location_characteristic
228
+
229
+ self.volume_offset_control_point_characteristic: Characteristic[bytes] = (
230
+ Characteristic(
231
+ uuid=GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC,
232
+ properties=Characteristic.Properties.WRITE,
233
+ permissions=Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
234
+ value=CharacteristicValue(
235
+ write=self.volume_offset_control_point.on_write
236
+ ),
237
+ )
232
238
  )
233
239
 
234
- self.audio_output_description_characteristic = Characteristic(
235
- uuid=GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
236
- properties=(
237
- Characteristic.Properties.READ
238
- | Characteristic.Properties.NOTIFY
239
- | Characteristic.Properties.WRITE_WITHOUT_RESPONSE
240
- ),
241
- permissions=(
242
- Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
243
- | Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION
244
- ),
245
- value=CharacteristicValue(
246
- read=self.audio_output_description.on_read,
247
- write=self.audio_output_description.on_write,
248
- ),
240
+ self.audio_output_description_characteristic: Characteristic[bytes] = (
241
+ Characteristic(
242
+ uuid=GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC,
243
+ properties=(
244
+ Characteristic.Properties.READ
245
+ | Characteristic.Properties.NOTIFY
246
+ | Characteristic.Properties.WRITE_WITHOUT_RESPONSE
247
+ ),
248
+ permissions=(
249
+ Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
250
+ | Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION
251
+ ),
252
+ value=CharacteristicValue(
253
+ read=self.audio_output_description.on_read,
254
+ write=self.audio_output_description.on_write,
255
+ ),
256
+ )
249
257
  )
250
- self.audio_output_description.attribute_value = (
251
- self.audio_output_description_characteristic.value
258
+ self.audio_output_description.attribute = (
259
+ self.audio_output_description_characteristic
252
260
  )
253
261
 
254
262
  super().__init__(
@@ -271,14 +279,14 @@ class VolumeOffsetControlServiceProxy(ProfileServiceProxy):
271
279
  def __init__(self, service_proxy: ServiceProxy) -> None:
272
280
  self.service_proxy = service_proxy
273
281
 
274
- self.volume_offset_state = SerializableCharacteristicAdapter(
282
+ self.volume_offset_state = SerializableCharacteristicProxyAdapter(
275
283
  service_proxy.get_required_characteristic_by_uuid(
276
284
  GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC
277
285
  ),
278
286
  VolumeOffsetState,
279
287
  )
280
288
 
281
- self.audio_location = DelegatedCharacteristicAdapter(
289
+ self.audio_location = DelegatedCharacteristicProxyAdapter(
282
290
  service_proxy.get_required_characteristic_by_uuid(
283
291
  GATT_AUDIO_LOCATION_CHARACTERISTIC
284
292
  ),
@@ -292,7 +300,7 @@ class VolumeOffsetControlServiceProxy(ProfileServiceProxy):
292
300
  )
293
301
  )
294
302
 
295
- self.audio_output_description = UTF8CharacteristicAdapter(
303
+ self.audio_output_description = UTF8CharacteristicProxyAdapter(
296
304
  service_proxy.get_required_characteristic_by_uuid(
297
305
  GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC
298
306
  )
bumble/utils.py CHANGED
@@ -502,3 +502,13 @@ class ByteSerializable(Protocol):
502
502
  def from_bytes(cls, data: bytes) -> Self: ...
503
503
 
504
504
  def __bytes__(self) -> bytes: ...
505
+
506
+
507
+ # -----------------------------------------------------------------------------
508
+ class IntConvertible(Protocol):
509
+ """
510
+ Type protocol for classes that can be instantiated from int and converted to int.
511
+ """
512
+
513
+ def __init__(self, value: int) -> None: ...
514
+ def __int__(self) -> int: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: bumble
3
- Version: 0.0.207
3
+ Version: 0.0.208
4
4
  Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
5
5
  Author-email: Google <bumble-dev@google.com>
6
6
  Project-URL: Homepage, https://github.com/google/bumble
@@ -47,7 +47,7 @@ Requires-Dist: types-invoke>=1.7.3; extra == "development"
47
47
  Requires-Dist: types-protobuf>=4.21.0; extra == "development"
48
48
  Provides-Extra: avatar
49
49
  Requires-Dist: pandora-avatar==0.0.10; extra == "avatar"
50
- Requires-Dist: rootcanal==1.10.0; python_version >= "3.10" and extra == "avatar"
50
+ Requires-Dist: rootcanal==1.11.1; python_version >= "3.10" and extra == "avatar"
51
51
  Provides-Extra: pandora
52
52
  Requires-Dist: bt-test-interfaces>=0.0.6; extra == "pandora"
53
53
  Provides-Extra: documentation
@@ -55,7 +55,7 @@ Requires-Dist: mkdocs>=1.6.0; extra == "documentation"
55
55
  Requires-Dist: mkdocs-material>=9.6; extra == "documentation"
56
56
  Requires-Dist: mkdocstrings[python]>=0.27.0; extra == "documentation"
57
57
  Provides-Extra: auracast
58
- Requires-Dist: lc3py; (python_version >= "3.10" and platform_system == "Linux" and platform_machine == "x86_64") and extra == "auracast"
58
+ Requires-Dist: lc3py>=1.1.3; (python_version >= "3.10" and ((platform_system == "Linux" and platform_machine == "x86_64") or (platform_system == "Darwin" and platform_machine == "arm64"))) and extra == "auracast"
59
59
  Requires-Dist: sounddevice>=0.5.1; extra == "auracast"
60
60
 
61
61
 
@@ -1,8 +1,8 @@
1
1
  bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
2
- bumble/_version.py,sha256=jmWBoK3noH8SOitnHxsR8mEYSEdPJ5p78NFlFLHEizc,415
2
+ bumble/_version.py,sha256=FgVZFp1Qjbq4umTLzRb77FwaMmS9AT9AXdMFlIzQpaQ,515
3
3
  bumble/a2dp.py,sha256=_dCq-qyG5OglDVlaOFwAgFe_ugvHuEdEYL-kWFf6sWQ,31775
4
4
  bumble/at.py,sha256=Giu2VUSJKH-jIh10lOfumiqy-FyO99Ra6nJ7UiWQ0H8,3114
5
- bumble/att.py,sha256=XyVtZ7AXbuperIm_uqMSN2haXYU_nPcXVz7HXGlex-Q,32567
5
+ bumble/att.py,sha256=Efic3D0nix9UG7L7BN8YXcbmxRBx5a2Iu8Sx-15Nx_k,33358
6
6
  bumble/avc.py,sha256=cO1-8x7BvuBCQVJg-9nTibkLFC4y0_2SNERZ8_7Kn_c,16407
7
7
  bumble/avctp.py,sha256=yHAjJRjLGtR0Q-iWcLS7cJRz5Jr2YiRmZd6LZV4Xjt4,9935
8
8
  bumble/avdtp.py,sha256=2ki_BE4SHiu3Sx9oHCknfjF-bBcgPB9TsyF5upciUYI,76773
@@ -12,42 +12,43 @@ bumble/codecs.py,sha256=75TGfq-XWWtr-mCRRG7QzJYNRebG50Ypt_QGQkoWlxU,20915
12
12
  bumble/colors.py,sha256=CC5tBDnN86bvlbYf1KIVdyj7QBLaqEDT_hQVB0p7FeU,3118
13
13
  bumble/company_ids.py,sha256=B68e2QPsDeRYP9jjbGs4GGDwEkGxcXGTsON_CHA0uuI,118528
14
14
  bumble/controller.py,sha256=w7KAbh1NH1_TRiiiXZX2EBgM9nGXteHH8fpns8Dshg4,62116
15
- bumble/core.py,sha256=TWXoBItq0UFwLmjbm4QXQoEK4eWFLnFhJzuTSpKb-LM,72544
15
+ bumble/core.py,sha256=2VkqVKUoOW6LLXHMMaec6K1hmH_SN2ztiWESvhjPm_w,78350
16
16
  bumble/crypto.py,sha256=L6z3dn9-dgKYRtOM6O3F6n6Ju4PwTM3LAFJtCg_ie78,9382
17
17
  bumble/decoder.py,sha256=0-VNWZT-u7lvK3qBpAuYT0M6Rz_bMgMi4CjfUXX_6RM,9728
18
- bumble/device.py,sha256=al6h9s0WAIWeWWEY0Z_O8BONhUqF4uifJFHzJKX5MQc,230241
18
+ bumble/device.py,sha256=J9orIOKlpnqfMTqTx9pvjrvp9bdiQu55dCL_AFvy2Ms,231348
19
19
  bumble/gap.py,sha256=dRU2_TWvqTDx80hxeSbXlWIeWvptWH4_XbItG5y948Q,2138
20
- bumble/gatt.py,sha256=cwU6c0UjhNICIVhA4hnVhvysHXDC7S-vB4byBY9SK-0,40972
21
- bumble/gatt_client.py,sha256=oZupZy_vHpyLoh8HvN0gtDBgK-zUnEvdweggdt1vQOE,43788
22
- bumble/gatt_server.py,sha256=cRIvPUrfTa8Kwn6KWsTzC33-AVoAPNzEMbXWbk_I6N4,37516
20
+ bumble/gatt.py,sha256=X28zoffSbAdrbts2XaTfXvUSzRUy-1eG3hhmVa25QXI,33512
21
+ bumble/gatt_adapters.py,sha256=00kmVHW63hBesBnZhfmPTEofaXEtsvx3RmTrSCZ7ruc,13254
22
+ bumble/gatt_client.py,sha256=TWI_iiI6AHiWwTZWMXnIDYR9JAsZTNg3dhidL6cy3wk,44187
23
+ bumble/gatt_server.py,sha256=Um-7gI3trC7qSpXyjOIk4072VMDCAivSDoL8suXtWHw,37503
23
24
  bumble/hci.py,sha256=xYKsSvwjZj0pAPgtGDyRaVxKOi4garuzSaiTd2ICWok,313243
24
25
  bumble/helpers.py,sha256=m0w4UgFFNDEnXwHrDyfRlcBObdVed2fqXGL0lvR3c8s,12733
25
26
  bumble/hfp.py,sha256=UDqB-z5nEzLgRojJeC6TbFRaPXV3Ht0jvQV03ksyiLU,75749
26
27
  bumble/hid.py,sha256=hJKm6qhNa0kQTGmp_VxNh3-ywgBDdJpPPFcvtFiRL0A,20335
27
- bumble/host.py,sha256=MNjN_NFQxPB1NKWw5jf35z0VbOzE3nrNub6opqecwtA,61315
28
+ bumble/host.py,sha256=8GN4TNjgDoBQuHIKmTnpZYAOHaqxt7Bv2E9L-KJDt2k,61439
28
29
  bumble/keys.py,sha256=WbIQ7Ob81mW75qmEPQ2rBLfnqBMA-ts2yowWXP9UaCY,12654
29
30
  bumble/l2cap.py,sha256=_bDO9VXhqDc1eNjqQBO82NYAsQipVxlrvcNSQIaWfL0,80809
30
31
  bumble/link.py,sha256=SU7Ls2Lyg1XuY8x6yP9tAC83SYmMTU2a-vQ_CWCfq90,24107
31
- bumble/pairing.py,sha256=OJ3mzv46iTtv8P0mZb3PEDUzTemlcIADPMah21jeSyA,10008
32
+ bumble/pairing.py,sha256=CEY7gC42DkNL8CitGyuBDvHcTiE82AxaznsGjdsnGkQ,10048
32
33
  bumble/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
34
  bumble/rfcomm.py,sha256=dh5t5vlDEfw3yHgQfzegMYPnShP8Zo-3ScABUvmXNLI,40751
34
35
  bumble/rtp.py,sha256=388X3aCv-QrWJ37r_VPqXYJtvNGWPsHnJasqs41g_-s,3487
35
36
  bumble/sdp.py,sha256=x7TQ1LlggWQDIaEZZaVNSd2PhfVtnjQ9xV1LTJ_y0Xs,49583
36
37
  bumble/smp.py,sha256=ic_9ozECbsZPsho0kU6eMcB2zIl6JODUx-tqdm8IdxU,77878
37
38
  bumble/snoop.py,sha256=1mzwmp9LToUXbPnFsLrt8S4UHs0kqzbu7LDydwbmkZI,5715
38
- bumble/utils.py,sha256=pddRUOgO8GJbymfLGDq-paWfk1N6lV0vieZ1DR1YCAY,15510
39
+ bumble/utils.py,sha256=ODWbIpQBkT0L5K1n6wn4GWMqkQg85NhDCC9REAsQLVA,15809
39
40
  bumble/apps/README.md,sha256=XTwjRAY-EJWDXpl1V8K3Mw8B7kIqzUIUizRjVBVhoIE,1769
40
41
  bumble/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- bumble/apps/auracast.py,sha256=ATs15CV082WbMm6Vl7tpz7DRzorY6x_of6DVJMywKeA,44040
42
- bumble/apps/bench.py,sha256=2Lf3fZD79rMRyaIZy5DqBSdMMT9YlcE3-aqE4c7eD38,61236
42
+ bumble/apps/auracast.py,sha256=zVuPJ-XSoNe7DyB6qFXImW-BXfln3qVDiPHRR8bSTbA,43535
43
+ bumble/apps/bench.py,sha256=m9J64h4_4t4ViNWpYQ_Dr438bqAmWOW2UGcRyRpSApE,61323
43
44
  bumble/apps/ble_rpa_tool.py,sha256=ZQtsbfnLPd5qUAkEBPpNgJLRynBBc7q_9cDHKUW2SQ0,1701
44
- bumble/apps/console.py,sha256=rwD9y3g8Mm_mAEvrcXjbtcv5d8mwF3yTbmE6Vet2BEk,45300
45
+ bumble/apps/console.py,sha256=VbwQElCKEWlWgsb5_k9UCeVIhWOKDn3S71J7fjN6OJ0,45475
45
46
  bumble/apps/controller_info.py,sha256=SRjTY66I8752oPa_G3y2D_H9NQj3HWxQ_fWKllNu4yo,12484
46
47
  bumble/apps/controller_loopback.py,sha256=VJAFsUdFwm2KgOrRuLADymMpZl5qVO0RGkDSr-1XKtY,7214
47
48
  bumble/apps/controllers.py,sha256=R6XJ1XpyuXlyqSCmI7PromVIcoYTcYfpmO-TqTYXnUI,2326
48
49
  bumble/apps/device_info.py,sha256=K7LcVpOOaunRMAKSSOTiDvqCavOZRdsDiXj2ofoJeG0,9942
49
50
  bumble/apps/gatt_dump.py,sha256=wwA-NhRnbgUkbj-Ukym7NDG2j2n_36t_tn93dLDVdIg,4487
50
- bumble/apps/gg_bridge.py,sha256=JdW5QT6xN9c2XDDJoHDRo5W3N_RdVkCtTmlcOsJhlx8,14693
51
+ bumble/apps/gg_bridge.py,sha256=hLWKq5fpX_OoRYHxFFPpwHiR7yW__1rarwicJCqoaHE,14716
51
52
  bumble/apps/hci_bridge.py,sha256=0mO36AO3ea0yrrTaYuySc7Un5NTuvVn08ItXvJql3CQ,4029
52
53
  bumble/apps/l2cap_bridge.py,sha256=524VgEmgCP4g7T0UdgmsePmNVhDFRJECeaZ_uzKsbco,13062
53
54
  bumble/apps/pair.py,sha256=cZovlR4alLGCruS4Iy_dcJkJchIoV4vNwcNyycWLhxw,18591
@@ -78,34 +79,34 @@ bumble/drivers/rtk.py,sha256=nsfAqNzvxvAwbh6UYgxOCSNwgw7XouIBFA03FrLTs4M,22088
78
79
  bumble/pandora/__init__.py,sha256=jaPtYCLfLeLUGj8-TmS3Gkv0l1_DBadYj8ZMAPLPAao,3463
79
80
  bumble/pandora/config.py,sha256=KD85n3oRbuvD65sRah2H0gpxEW4YbD7HbYbsxdcpDDA,2388
80
81
  bumble/pandora/device.py,sha256=LFqCWrgYkQWrFUSKArsAABXkge8sB2DhvaQoEsC4Jn0,5344
81
- bumble/pandora/host.py,sha256=PihXDb09a1IkV56MPNe4-5QRr54_Dp679pA4ldnLHlM,39235
82
+ bumble/pandora/host.py,sha256=IFEvtcj0M0xwmmILHZ8bjrMKzVhtgfdxYnCPMujNrrk,38997
82
83
  bumble/pandora/l2cap.py,sha256=OT-pPprVAQr7xwjYnNwDQujayG2zWUS5FPXVBGNCW3o,11725
83
84
  bumble/pandora/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
85
  bumble/pandora/security.py,sha256=YErueKNLsnmcRe6dKo724Ht-buOqmZl2Gauswcc8FW0,21946
85
86
  bumble/pandora/utils.py,sha256=Fq4glL0T5cJ2FODoDotmDNdYFOkTOR7DyyL8vkcxp20,3949
86
87
  bumble/profiles/__init__.py,sha256=yBGC8Ti5LvZuoh1F42XtfrBilb39T77_yuxESZeX2yI,581
87
- bumble/profiles/aics.py,sha256=Kb50MTBPxWHZbc0JUBogv3YEV2wZIGrqs8E7qhEm7aI,17230
88
- bumble/profiles/ascs.py,sha256=jRFZDPgym7ZRs_bqmLjLwi5nAHg_lwfRcq_P6Ynvg4s,24839
89
- bumble/profiles/asha.py,sha256=XLXinlmC1Zn1MiK_eg62F89gG-4HnBHco_KzoEXtCUE,10273
88
+ bumble/profiles/aics.py,sha256=ApqW_e7Z_8GOIeR-lFJKn9qjQeo_g4UVsNICVzuluW0,17793
89
+ bumble/profiles/ascs.py,sha256=417wewqM-zCcSyg8xWA-0-4ExzCzNzBvJ7T9BE9Lbhw,24880
90
+ bumble/profiles/asha.py,sha256=WRC9MItDXzjp6JDe4YYhSnRvUGmqrJM39hW1twp1EXI,10377
90
91
  bumble/profiles/bap.py,sha256=3fI8IB34l1CSzH0CwAD84qCxz_nV-SOP5q9VDzb3Aqk,21979
91
- bumble/profiles/bass.py,sha256=5t91bOZDolmfiv9c1pQ1FmewKhXUtqPH7c1Qr8GR9IA,14557
92
- bumble/profiles/battery_service.py,sha256=w-uF4jLoDozJOoykimb2RkrKjVyCke6ts2-h-F1PYyc,2292
92
+ bumble/profiles/bass.py,sha256=A0CpMMnoB7ODM7YwMXQ7bhlJ9bTwA_Hqcyufb-9rqHk,14641
93
+ bumble/profiles/battery_service.py,sha256=-PM8gwXV2Aym5Z2Z4PAbl31Wefa7E260qy5P5jYVC4A,2572
93
94
  bumble/profiles/cap.py,sha256=6gH7oOnUKjOggMPuB7rtbwj0AneoNmnWzQ_iR3io8e0,1945
94
95
  bumble/profiles/csip.py,sha256=qpI9W0_FWIpsuJrHhxfbKaa-TD21epxEa3EuCm8gh6c,10156
95
- bumble/profiles/device_information_service.py,sha256=3c_p3s7jMxPKWHObXOesZ4ZZKr1SgaTe8RvyuangZuM,6258
96
- bumble/profiles/gap.py,sha256=jUjfy6MPL7k6wgNn3ny3PVgSX6-SLmjUepFYHjGc3IU,3870
97
- bumble/profiles/gatt_service.py,sha256=n85zh22nNNcxeajLtX0ZzZLESPwXDGDxoZdttEtOZW4,6675
98
- bumble/profiles/gmap.py,sha256=jNhYoqD9oLkZubCQ9zllwKtXVyVoZVoIZJcI_DUuJGw,7035
99
- bumble/profiles/hap.py,sha256=IbE1KzjIMEq6FxtFqTgA2DN0pPnpu0996KJRjuN3oaQ,25614
100
- bumble/profiles/heart_rate_service.py,sha256=56LFL3lYoCMJZ-4ec3mQPDqcvr5lahCeZzDVRKsbakE,8609
96
+ bumble/profiles/device_information_service.py,sha256=CyJmLdMCBYpQMNp-35nljjlPVTR6ZIVmkORhT_FreaE,6351
97
+ bumble/profiles/gap.py,sha256=UvynHzS_MfN_Bjjfw81CdAY49Zm6ejq-dxFp988zvNE,4033
98
+ bumble/profiles/gatt_service.py,sha256=BgQLOO5iAlOfammLLXQIivnggiVMYGty94MxN9BBKv0,6729
99
+ bumble/profiles/gmap.py,sha256=t59LucWKYSW8vAZIf3DHU3sbt3tdbw5M8BDE2bxTN-k,7327
100
+ bumble/profiles/hap.py,sha256=sG8yG54nH6A8u5Jhw7hZoWAf_ki1Sged7KPvEwtq9Nk,25710
101
+ bumble/profiles/heart_rate_service.py,sha256=Hw46AHc1NEVl0HAvjt4kc_HNSFK3byvETcXraZtW0ow,9251
101
102
  bumble/profiles/le_audio.py,sha256=eDdMLwaisA3Wg7XhNVDoLOWchJ5CCfT0cqxOqCo8l04,5839
102
- bumble/profiles/mcp.py,sha256=vIN1r_if4LmOcGCgZuDYTYLMQzU6-1pKUFx1Z3iSAeY,17415
103
- bumble/profiles/pacs.py,sha256=3q_d2Dfc9kYIip9TChgu49SG_BE-vQhhLyDgJh-_0eM,10012
103
+ bumble/profiles/mcp.py,sha256=smfXmWDPNg1_ov-cisj1BRGaa59jvH5ByRL-DqN4SJU,17510
104
+ bumble/profiles/pacs.py,sha256=MyBD015F9nZz-D9T6EQ9oQabYin0QdvueU9BS3zUeFc,10427
104
105
  bumble/profiles/pbp.py,sha256=51aoQcZMXzTzeatHd0zNVN7kYcv4atzr2OWpzxlSVP8,1630
105
106
  bumble/profiles/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
- bumble/profiles/tmap.py,sha256=24hPPDc4xVKanRqTHRaOG_R95qmUVrO5_m_MhCcltiQ,2833
107
- bumble/profiles/vcs.py,sha256=cB9-DU9gFhGQxfjStRCAZmkGXj3SDtfZlBtBOR26c_E,8149
108
- bumble/profiles/vocs.py,sha256=V_UWVL83MPR2GNJMouiMzXUhsazD1xhbrQqZ2gP_mFw,10895
107
+ bumble/profiles/tmap.py,sha256=8VocfKspJpK8UiYS8sSNYXYaE6qUI-OPQnRPeMoFh6A,2935
108
+ bumble/profiles/vcs.py,sha256=3Y2HWNkhEqwY3xkfLy85ntTQm5lZyKO9X4FNSHvLuHs,8232
109
+ bumble/profiles/vocs.py,sha256=ZgufVLiSenmYgy1eteKhjOyLUbkJxC3iVo61azq39zc,11127
109
110
  bumble/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
111
  bumble/tools/generate_company_id_list.py,sha256=ysbPb3zmxKFBiSQ_MBG2r15-sqR5P_GWT6i0YUTTXOM,1736
111
112
  bumble/tools/intel_fw_download.py,sha256=8YJ3y3yww7bEvL9cINq9cFucoCWf-Gf3pyu-gl4-UJE,4453
@@ -171,9 +172,9 @@ bumble/vendor/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
171
172
  bumble/vendor/android/hci.py,sha256=-ZryisGrnxYEXEM9kcR2ta4joNhAgAxgRYAEYLq5tT0,11651
172
173
  bumble/vendor/zephyr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
174
  bumble/vendor/zephyr/hci.py,sha256=d83bC0TvT947eN4roFjLkQefWtHOoNsr4xib2ctSkvA,3195
174
- bumble-0.0.207.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
175
- bumble-0.0.207.dist-info/METADATA,sha256=-k4cZeBTfBD0smZbv1GOk9jsvJVd2toT4qS6bZRqXws,5966
176
- bumble-0.0.207.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
177
- bumble-0.0.207.dist-info/entry_points.txt,sha256=0mBShtMEyPU3NxVWkl-cixtC7W04yZxJmAh2WN-UzX8,1140
178
- bumble-0.0.207.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
179
- bumble-0.0.207.dist-info/RECORD,,
175
+ bumble-0.0.208.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
176
+ bumble-0.0.208.dist-info/METADATA,sha256=VFGkn5PZIbrQAgUhA8qIKjj3qqZxyd9jIckY0BRsx78,6042
177
+ bumble-0.0.208.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
178
+ bumble-0.0.208.dist-info/entry_points.txt,sha256=0mBShtMEyPU3NxVWkl-cixtC7W04yZxJmAh2WN-UzX8,1140
179
+ bumble-0.0.208.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
180
+ bumble-0.0.208.dist-info/RECORD,,