bumble 0.0.199__py3-none-any.whl → 0.0.201__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 (48) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +502 -202
  3. bumble/apps/controller_info.py +60 -0
  4. bumble/apps/player/player.py +608 -0
  5. bumble/apps/speaker/speaker.py +25 -27
  6. bumble/att.py +2 -2
  7. bumble/avc.py +1 -2
  8. bumble/avdtp.py +54 -97
  9. bumble/avrcp.py +48 -29
  10. bumble/codecs.py +214 -68
  11. bumble/device.py +19 -11
  12. bumble/hci.py +31 -5
  13. bumble/hfp.py +52 -48
  14. bumble/host.py +12 -0
  15. bumble/profiles/hap.py +27 -18
  16. bumble/rtp.py +110 -0
  17. bumble/transport/android_netsim.py +31 -11
  18. bumble/transport/grpc_protobuf/netsim/__init__.py +0 -0
  19. bumble/transport/grpc_protobuf/{common_pb2.py → netsim/common_pb2.py} +9 -8
  20. bumble/transport/grpc_protobuf/{common_pb2.pyi → netsim/common_pb2.pyi} +11 -5
  21. bumble/transport/grpc_protobuf/netsim/hci_packet_pb2.py +29 -0
  22. bumble/transport/grpc_protobuf/{hci_packet_pb2.pyi → netsim/hci_packet_pb2.pyi} +13 -7
  23. bumble/transport/grpc_protobuf/netsim/model_pb2.py +63 -0
  24. bumble/transport/grpc_protobuf/netsim/model_pb2.pyi +238 -0
  25. bumble/transport/grpc_protobuf/netsim/packet_streamer_pb2.py +32 -0
  26. bumble/transport/grpc_protobuf/{packet_streamer_pb2.pyi → netsim/packet_streamer_pb2.pyi} +6 -6
  27. bumble/transport/grpc_protobuf/{packet_streamer_pb2_grpc.py → netsim/packet_streamer_pb2_grpc.py} +7 -7
  28. bumble/transport/grpc_protobuf/netsim/startup_pb2.py +41 -0
  29. bumble/transport/grpc_protobuf/netsim/startup_pb2.pyi +76 -0
  30. bumble/transport/grpc_protobuf/netsim/startup_pb2_grpc.py +4 -0
  31. bumble/transport/grpc_protobuf/rootcanal/__init__.py +0 -0
  32. bumble/transport/grpc_protobuf/rootcanal/configuration_pb2.py +39 -0
  33. bumble/transport/grpc_protobuf/rootcanal/configuration_pb2.pyi +78 -0
  34. bumble/transport/grpc_protobuf/rootcanal/configuration_pb2_grpc.py +4 -0
  35. bumble/transport/pyusb.py +2 -1
  36. {bumble-0.0.199.dist-info → bumble-0.0.201.dist-info}/METADATA +2 -2
  37. {bumble-0.0.199.dist-info → bumble-0.0.201.dist-info}/RECORD +44 -34
  38. {bumble-0.0.199.dist-info → bumble-0.0.201.dist-info}/WHEEL +1 -1
  39. {bumble-0.0.199.dist-info → bumble-0.0.201.dist-info}/entry_points.txt +1 -0
  40. bumble/transport/grpc_protobuf/hci_packet_pb2.py +0 -28
  41. bumble/transport/grpc_protobuf/packet_streamer_pb2.py +0 -31
  42. bumble/transport/grpc_protobuf/startup_pb2.py +0 -32
  43. bumble/transport/grpc_protobuf/startup_pb2.pyi +0 -46
  44. /bumble/transport/grpc_protobuf/{common_pb2_grpc.py → netsim/common_pb2_grpc.py} +0 -0
  45. /bumble/transport/grpc_protobuf/{hci_packet_pb2_grpc.py → netsim/hci_packet_pb2_grpc.py} +0 -0
  46. /bumble/transport/grpc_protobuf/{startup_pb2_grpc.py → netsim/model_pb2_grpc.py} +0 -0
  47. {bumble-0.0.199.dist-info → bumble-0.0.201.dist-info}/LICENSE +0 -0
  48. {bumble-0.0.199.dist-info → bumble-0.0.201.dist-info}/top_level.txt +0 -0
@@ -44,25 +44,18 @@ from bumble.avdtp import (
44
44
  AVDTP_AUDIO_MEDIA_TYPE,
45
45
  Listener,
46
46
  MediaCodecCapabilities,
47
- MediaPacket,
48
47
  Protocol,
49
48
  )
50
49
  from bumble.a2dp import (
51
- MPEG_2_AAC_LC_OBJECT_TYPE,
52
50
  make_audio_sink_service_sdp_records,
53
51
  A2DP_SBC_CODEC_TYPE,
54
52
  A2DP_MPEG_2_4_AAC_CODEC_TYPE,
55
- SBC_MONO_CHANNEL_MODE,
56
- SBC_DUAL_CHANNEL_MODE,
57
- SBC_SNR_ALLOCATION_METHOD,
58
- SBC_LOUDNESS_ALLOCATION_METHOD,
59
- SBC_STEREO_CHANNEL_MODE,
60
- SBC_JOINT_STEREO_CHANNEL_MODE,
61
53
  SbcMediaCodecInformation,
62
54
  AacMediaCodecInformation,
63
55
  )
64
56
  from bumble.utils import AsyncRunner
65
57
  from bumble.codecs import AacAudioRtpPacket
58
+ from bumble.rtp import MediaPacket
66
59
 
67
60
 
68
61
  # -----------------------------------------------------------------------------
@@ -93,7 +86,7 @@ class AudioExtractor:
93
86
  # -----------------------------------------------------------------------------
94
87
  class AacAudioExtractor:
95
88
  def extract_audio(self, packet: MediaPacket) -> bytes:
96
- return AacAudioRtpPacket(packet.payload).to_adts()
89
+ return AacAudioRtpPacket.from_bytes(packet.payload).to_adts()
97
90
 
98
91
 
99
92
  # -----------------------------------------------------------------------------
@@ -451,10 +444,12 @@ class Speaker:
451
444
  return MediaCodecCapabilities(
452
445
  media_type=AVDTP_AUDIO_MEDIA_TYPE,
453
446
  media_codec_type=A2DP_MPEG_2_4_AAC_CODEC_TYPE,
454
- media_codec_information=AacMediaCodecInformation.from_lists(
455
- object_types=[MPEG_2_AAC_LC_OBJECT_TYPE],
456
- sampling_frequencies=[48000, 44100],
457
- channels=[1, 2],
447
+ media_codec_information=AacMediaCodecInformation(
448
+ object_type=AacMediaCodecInformation.ObjectType.MPEG_2_AAC_LC,
449
+ sampling_frequency=AacMediaCodecInformation.SamplingFrequency.SF_48000
450
+ | AacMediaCodecInformation.SamplingFrequency.SF_44100,
451
+ channels=AacMediaCodecInformation.Channels.MONO
452
+ | AacMediaCodecInformation.Channels.STEREO,
458
453
  vbr=1,
459
454
  bitrate=256000,
460
455
  ),
@@ -464,20 +459,23 @@ class Speaker:
464
459
  return MediaCodecCapabilities(
465
460
  media_type=AVDTP_AUDIO_MEDIA_TYPE,
466
461
  media_codec_type=A2DP_SBC_CODEC_TYPE,
467
- media_codec_information=SbcMediaCodecInformation.from_lists(
468
- sampling_frequencies=[48000, 44100, 32000, 16000],
469
- channel_modes=[
470
- SBC_MONO_CHANNEL_MODE,
471
- SBC_DUAL_CHANNEL_MODE,
472
- SBC_STEREO_CHANNEL_MODE,
473
- SBC_JOINT_STEREO_CHANNEL_MODE,
474
- ],
475
- block_lengths=[4, 8, 12, 16],
476
- subbands=[4, 8],
477
- allocation_methods=[
478
- SBC_LOUDNESS_ALLOCATION_METHOD,
479
- SBC_SNR_ALLOCATION_METHOD,
480
- ],
462
+ media_codec_information=SbcMediaCodecInformation(
463
+ sampling_frequency=SbcMediaCodecInformation.SamplingFrequency.SF_48000
464
+ | SbcMediaCodecInformation.SamplingFrequency.SF_44100
465
+ | SbcMediaCodecInformation.SamplingFrequency.SF_32000
466
+ | SbcMediaCodecInformation.SamplingFrequency.SF_16000,
467
+ channel_mode=SbcMediaCodecInformation.ChannelMode.MONO
468
+ | SbcMediaCodecInformation.ChannelMode.DUAL_CHANNEL
469
+ | SbcMediaCodecInformation.ChannelMode.STEREO
470
+ | SbcMediaCodecInformation.ChannelMode.JOINT_STEREO,
471
+ block_length=SbcMediaCodecInformation.BlockLength.BL_4
472
+ | SbcMediaCodecInformation.BlockLength.BL_8
473
+ | SbcMediaCodecInformation.BlockLength.BL_12
474
+ | SbcMediaCodecInformation.BlockLength.BL_16,
475
+ subbands=SbcMediaCodecInformation.Subbands.S_4
476
+ | SbcMediaCodecInformation.Subbands.S_8,
477
+ allocation_method=SbcMediaCodecInformation.AllocationMethod.LOUDNESS
478
+ | SbcMediaCodecInformation.AllocationMethod.SNR,
481
479
  minimum_bitpool_value=2,
482
480
  maximum_bitpool_value=53,
483
481
  ),
bumble/att.py CHANGED
@@ -710,7 +710,7 @@ class ATT_Prepare_Write_Response(ATT_PDU):
710
710
 
711
711
 
712
712
  # -----------------------------------------------------------------------------
713
- @ATT_PDU.subclass([])
713
+ @ATT_PDU.subclass([("flags", 1)])
714
714
  class ATT_Execute_Write_Request(ATT_PDU):
715
715
  '''
716
716
  See Bluetooth spec @ Vol 3, Part F - 3.4.6.3 Execute Write Request
@@ -811,7 +811,7 @@ class Attribute(EventEmitter):
811
811
  enum_list: List[str] = [p.name for p in cls if p.name is not None]
812
812
  enum_list_str = ",".join(enum_list)
813
813
  raise TypeError(
814
- f"Attribute::permissions error:\nExpected a string containing any of the keys, separated by commas: {enum_list_str }\nGot: {permissions_str}"
814
+ f"Attribute::permissions error:\nExpected a string containing any of the keys, separated by commas: {enum_list_str}\nGot: {permissions_str}"
815
815
  ) from exc
816
816
 
817
817
  # Permission flags(legacy-use only)
bumble/avc.py CHANGED
@@ -119,7 +119,7 @@ class Frame:
119
119
  # Not supported
120
120
  raise NotImplementedError("extended subunit types not supported")
121
121
 
122
- if subunit_id < 5:
122
+ if subunit_id < 5 or subunit_id == 7:
123
123
  opcode_offset = 2
124
124
  elif subunit_id == 5:
125
125
  # Extended to the next byte
@@ -132,7 +132,6 @@ class Frame:
132
132
  else:
133
133
  subunit_id = 5 + extension
134
134
  opcode_offset = 3
135
-
136
135
  elif subunit_id == 6:
137
136
  raise core.InvalidPacketError("reserved subunit ID")
138
137
 
bumble/avdtp.py CHANGED
@@ -17,12 +17,10 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
  import asyncio
20
- import struct
21
20
  import time
22
21
  import logging
23
22
  import enum
24
23
  import warnings
25
- from pyee import EventEmitter
26
24
  from typing import (
27
25
  Any,
28
26
  Awaitable,
@@ -39,6 +37,8 @@ from typing import (
39
37
  cast,
40
38
  )
41
39
 
40
+ from pyee import EventEmitter
41
+
42
42
  from .core import (
43
43
  BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
44
44
  InvalidStateError,
@@ -51,13 +51,16 @@ from .a2dp import (
51
51
  A2DP_MPEG_2_4_AAC_CODEC_TYPE,
52
52
  A2DP_NON_A2DP_CODEC_TYPE,
53
53
  A2DP_SBC_CODEC_TYPE,
54
+ A2DP_VENDOR_MEDIA_CODEC_INFORMATION_CLASSES,
54
55
  AacMediaCodecInformation,
55
56
  SbcMediaCodecInformation,
56
57
  VendorSpecificMediaCodecInformation,
57
58
  )
59
+ from .rtp import MediaPacket
58
60
  from . import sdp, device, l2cap
59
61
  from .colors import color
60
62
 
63
+
61
64
  # -----------------------------------------------------------------------------
62
65
  # Logging
63
66
  # -----------------------------------------------------------------------------
@@ -278,95 +281,6 @@ class RealtimeClock:
278
281
  await asyncio.sleep(duration)
279
282
 
280
283
 
281
- # -----------------------------------------------------------------------------
282
- class MediaPacket:
283
- @staticmethod
284
- def from_bytes(data: bytes) -> MediaPacket:
285
- version = (data[0] >> 6) & 0x03
286
- padding = (data[0] >> 5) & 0x01
287
- extension = (data[0] >> 4) & 0x01
288
- csrc_count = data[0] & 0x0F
289
- marker = (data[1] >> 7) & 0x01
290
- payload_type = data[1] & 0x7F
291
- sequence_number = struct.unpack_from('>H', data, 2)[0]
292
- timestamp = struct.unpack_from('>I', data, 4)[0]
293
- ssrc = struct.unpack_from('>I', data, 8)[0]
294
- csrc_list = [
295
- struct.unpack_from('>I', data, 12 + i)[0] for i in range(csrc_count)
296
- ]
297
- payload = data[12 + csrc_count * 4 :]
298
-
299
- return MediaPacket(
300
- version,
301
- padding,
302
- extension,
303
- marker,
304
- sequence_number,
305
- timestamp,
306
- ssrc,
307
- csrc_list,
308
- payload_type,
309
- payload,
310
- )
311
-
312
- def __init__(
313
- self,
314
- version: int,
315
- padding: int,
316
- extension: int,
317
- marker: int,
318
- sequence_number: int,
319
- timestamp: int,
320
- ssrc: int,
321
- csrc_list: List[int],
322
- payload_type: int,
323
- payload: bytes,
324
- ) -> None:
325
- self.version = version
326
- self.padding = padding
327
- self.extension = extension
328
- self.marker = marker
329
- self.sequence_number = sequence_number & 0xFFFF
330
- self.timestamp = timestamp & 0xFFFFFFFF
331
- self.ssrc = ssrc
332
- self.csrc_list = csrc_list
333
- self.payload_type = payload_type
334
- self.payload = payload
335
-
336
- def __bytes__(self) -> bytes:
337
- header = bytes(
338
- [
339
- self.version << 6
340
- | self.padding << 5
341
- | self.extension << 4
342
- | len(self.csrc_list),
343
- self.marker << 7 | self.payload_type,
344
- ]
345
- ) + struct.pack(
346
- '>HII',
347
- self.sequence_number,
348
- self.timestamp,
349
- self.ssrc,
350
- )
351
- for csrc in self.csrc_list:
352
- header += struct.pack('>I', csrc)
353
- return header + self.payload
354
-
355
- def __str__(self) -> str:
356
- return (
357
- f'RTP(v={self.version},'
358
- f'p={self.padding},'
359
- f'x={self.extension},'
360
- f'm={self.marker},'
361
- f'pt={self.payload_type},'
362
- f'sn={self.sequence_number},'
363
- f'ts={self.timestamp},'
364
- f'ssrc={self.ssrc},'
365
- f'csrcs={self.csrc_list},'
366
- f'payload_size={len(self.payload)})'
367
- )
368
-
369
-
370
284
  # -----------------------------------------------------------------------------
371
285
  class MediaPacketPump:
372
286
  pump_task: Optional[asyncio.Task]
@@ -377,6 +291,7 @@ class MediaPacketPump:
377
291
  self.packets = packets
378
292
  self.clock = clock
379
293
  self.pump_task = None
294
+ self.completed = asyncio.Event()
380
295
 
381
296
  async def start(self, rtp_channel: l2cap.ClassicChannel) -> None:
382
297
  async def pump_packets():
@@ -406,6 +321,8 @@ class MediaPacketPump:
406
321
  )
407
322
  except asyncio.exceptions.CancelledError:
408
323
  logger.debug('pump canceled')
324
+ finally:
325
+ self.completed.set()
409
326
 
410
327
  # Pump packets
411
328
  self.pump_task = asyncio.create_task(pump_packets())
@@ -417,6 +334,9 @@ class MediaPacketPump:
417
334
  await self.pump_task
418
335
  self.pump_task = None
419
336
 
337
+ async def wait_for_completion(self) -> None:
338
+ await self.completed.wait()
339
+
420
340
 
421
341
  # -----------------------------------------------------------------------------
422
342
  class MessageAssembler:
@@ -615,11 +535,25 @@ class MediaCodecCapabilities(ServiceCapabilities):
615
535
  self.media_codec_information
616
536
  )
617
537
  elif self.media_codec_type == A2DP_NON_A2DP_CODEC_TYPE:
618
- self.media_codec_information = (
538
+ vendor_media_codec_information = (
619
539
  VendorSpecificMediaCodecInformation.from_bytes(
620
540
  self.media_codec_information
621
541
  )
622
542
  )
543
+ if (
544
+ vendor_class_map := A2DP_VENDOR_MEDIA_CODEC_INFORMATION_CLASSES.get(
545
+ vendor_media_codec_information.vendor_id
546
+ )
547
+ ) and (
548
+ media_codec_information_class := vendor_class_map.get(
549
+ vendor_media_codec_information.codec_id
550
+ )
551
+ ):
552
+ self.media_codec_information = media_codec_information_class.from_bytes(
553
+ vendor_media_codec_information.value
554
+ )
555
+ else:
556
+ self.media_codec_information = vendor_media_codec_information
623
557
 
624
558
  def __init__(
625
559
  self,
@@ -1316,10 +1250,20 @@ class Protocol(EventEmitter):
1316
1250
  return None
1317
1251
 
1318
1252
  def add_source(
1319
- self, codec_capabilities: MediaCodecCapabilities, packet_pump: MediaPacketPump
1253
+ self,
1254
+ codec_capabilities: MediaCodecCapabilities,
1255
+ packet_pump: MediaPacketPump,
1256
+ delay_reporting: bool = False,
1320
1257
  ) -> LocalSource:
1321
1258
  seid = len(self.local_endpoints) + 1
1322
- source = LocalSource(self, seid, codec_capabilities, packet_pump)
1259
+ service_capabilities = (
1260
+ [ServiceCapabilities(AVDTP_DELAY_REPORTING_SERVICE_CATEGORY)]
1261
+ if delay_reporting
1262
+ else []
1263
+ )
1264
+ source = LocalSource(
1265
+ self, seid, codec_capabilities, service_capabilities, packet_pump
1266
+ )
1323
1267
  self.local_endpoints.append(source)
1324
1268
 
1325
1269
  return source
@@ -1372,7 +1316,7 @@ class Protocol(EventEmitter):
1372
1316
  return self.remote_endpoints.values()
1373
1317
 
1374
1318
  def find_remote_sink_by_codec(
1375
- self, media_type: int, codec_type: int
1319
+ self, media_type: int, codec_type: int, vendor_id: int = 0, codec_id: int = 0
1376
1320
  ) -> Optional[DiscoveredStreamEndPoint]:
1377
1321
  for endpoint in self.remote_endpoints.values():
1378
1322
  if (
@@ -1397,7 +1341,19 @@ class Protocol(EventEmitter):
1397
1341
  codec_capabilities.media_type == AVDTP_AUDIO_MEDIA_TYPE
1398
1342
  and codec_capabilities.media_codec_type == codec_type
1399
1343
  ):
1400
- has_codec = True
1344
+ if isinstance(
1345
+ codec_capabilities.media_codec_information,
1346
+ VendorSpecificMediaCodecInformation,
1347
+ ):
1348
+ if (
1349
+ codec_capabilities.media_codec_information.vendor_id
1350
+ == vendor_id
1351
+ and codec_capabilities.media_codec_information.codec_id
1352
+ == codec_id
1353
+ ):
1354
+ has_codec = True
1355
+ else:
1356
+ has_codec = True
1401
1357
  if has_media_transport and has_codec:
1402
1358
  return endpoint
1403
1359
 
@@ -2180,12 +2136,13 @@ class LocalSource(LocalStreamEndPoint):
2180
2136
  protocol: Protocol,
2181
2137
  seid: int,
2182
2138
  codec_capabilities: MediaCodecCapabilities,
2139
+ other_capabilitiles: Iterable[ServiceCapabilities],
2183
2140
  packet_pump: MediaPacketPump,
2184
2141
  ) -> None:
2185
2142
  capabilities = [
2186
2143
  ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
2187
2144
  codec_capabilities,
2188
- ]
2145
+ ] + list(other_capabilitiles)
2189
2146
  super().__init__(
2190
2147
  protocol,
2191
2148
  seid,
bumble/avrcp.py CHANGED
@@ -1491,10 +1491,14 @@ class Protocol(pyee.EventEmitter):
1491
1491
  f"<<< AVCTP Command, transaction_label={transaction_label}: " f"{command}"
1492
1492
  )
1493
1493
 
1494
- # Only the PANEL subunit type with subunit ID 0 is supported in this profile.
1495
- if (
1496
- command.subunit_type != avc.Frame.SubunitType.PANEL
1497
- or command.subunit_id != 0
1494
+ # Only addressing the unit, or the PANEL subunit with subunit ID 0 is supported
1495
+ # in this profile.
1496
+ if not (
1497
+ command.subunit_type == avc.Frame.SubunitType.UNIT
1498
+ and command.subunit_id == 7
1499
+ ) and not (
1500
+ command.subunit_type == avc.Frame.SubunitType.PANEL
1501
+ and command.subunit_id == 0
1498
1502
  ):
1499
1503
  logger.debug("subunit not supported")
1500
1504
  self.send_not_implemented_response(transaction_label, command)
@@ -1528,8 +1532,8 @@ class Protocol(pyee.EventEmitter):
1528
1532
  # TODO: delegate
1529
1533
  response = avc.PassThroughResponseFrame(
1530
1534
  avc.ResponseFrame.ResponseCode.ACCEPTED,
1531
- avc.Frame.SubunitType.PANEL,
1532
- 0,
1535
+ command.subunit_type,
1536
+ command.subunit_id,
1533
1537
  command.state_flag,
1534
1538
  command.operation_id,
1535
1539
  command.operation_data,
@@ -1846,6 +1850,15 @@ class Protocol(pyee.EventEmitter):
1846
1850
  RejectedResponse(pdu_id, status_code),
1847
1851
  )
1848
1852
 
1853
+ def send_not_implemented_avrcp_response(
1854
+ self, transaction_label: int, pdu_id: Protocol.PduId
1855
+ ) -> None:
1856
+ self.send_avrcp_response(
1857
+ transaction_label,
1858
+ avc.ResponseFrame.ResponseCode.NOT_IMPLEMENTED,
1859
+ NotImplementedResponse(pdu_id, b''),
1860
+ )
1861
+
1849
1862
  def _on_get_capabilities_command(
1850
1863
  self, transaction_label: int, command: GetCapabilitiesCommand
1851
1864
  ) -> None:
@@ -1891,29 +1904,35 @@ class Protocol(pyee.EventEmitter):
1891
1904
  async def register_notification():
1892
1905
  # Check if the event is supported.
1893
1906
  supported_events = await self.delegate.get_supported_events()
1894
- if command.event_id in supported_events:
1895
- if command.event_id == EventId.VOLUME_CHANGED:
1896
- volume = await self.delegate.get_absolute_volume()
1897
- response = RegisterNotificationResponse(VolumeChangedEvent(volume))
1898
- self.send_avrcp_response(
1899
- transaction_label,
1900
- avc.ResponseFrame.ResponseCode.INTERIM,
1901
- response,
1902
- )
1903
- self._register_notification_listener(transaction_label, command)
1904
- return
1907
+ if command.event_id not in supported_events:
1908
+ logger.debug("event not supported")
1909
+ self.send_not_implemented_avrcp_response(
1910
+ transaction_label, self.PduId.REGISTER_NOTIFICATION
1911
+ )
1912
+ return
1905
1913
 
1906
- if command.event_id == EventId.PLAYBACK_STATUS_CHANGED:
1907
- # TODO: testing only, use delegate
1908
- response = RegisterNotificationResponse(
1909
- PlaybackStatusChangedEvent(play_status=PlayStatus.PLAYING)
1910
- )
1911
- self.send_avrcp_response(
1912
- transaction_label,
1913
- avc.ResponseFrame.ResponseCode.INTERIM,
1914
- response,
1915
- )
1916
- self._register_notification_listener(transaction_label, command)
1917
- return
1914
+ if command.event_id == EventId.VOLUME_CHANGED:
1915
+ volume = await self.delegate.get_absolute_volume()
1916
+ response = RegisterNotificationResponse(VolumeChangedEvent(volume))
1917
+ self.send_avrcp_response(
1918
+ transaction_label,
1919
+ avc.ResponseFrame.ResponseCode.INTERIM,
1920
+ response,
1921
+ )
1922
+ self._register_notification_listener(transaction_label, command)
1923
+ return
1924
+
1925
+ if command.event_id == EventId.PLAYBACK_STATUS_CHANGED:
1926
+ # TODO: testing only, use delegate
1927
+ response = RegisterNotificationResponse(
1928
+ PlaybackStatusChangedEvent(play_status=PlayStatus.PLAYING)
1929
+ )
1930
+ self.send_avrcp_response(
1931
+ transaction_label,
1932
+ avc.ResponseFrame.ResponseCode.INTERIM,
1933
+ response,
1934
+ )
1935
+ self._register_notification_listener(transaction_label, command)
1936
+ return
1918
1937
 
1919
1938
  self._delegate_command(transaction_label, command, register_notification())