Coverage for src / bluetooth_sig / gatt / characteristics / sink_ase.py: 100%

33 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-03 16:41 +0000

1"""Sink ASE characteristic (0x2BC4).""" 

2 

3from __future__ import annotations 

4 

5from enum import IntEnum 

6 

7import msgspec 

8 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11from .utils import DataParser 

12 

13 

14class ASEState(IntEnum): 

15 """Audio Stream Endpoint state values.""" 

16 

17 IDLE = 0x00 

18 CODEC_CONFIGURED = 0x01 

19 QOS_CONFIGURED = 0x02 

20 ENABLING = 0x03 

21 STREAMING = 0x04 

22 DISABLING = 0x05 

23 RELEASING = 0x06 

24 

25 

26_ADDITIONAL_DATA_START_INDEX = 2 

27 

28 

29class SinkASEData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods 

30 """Parsed data from Sink ASE characteristic. 

31 

32 Contains the ASE ID, current state, and any additional 

33 state-specific data as raw bytes. 

34 """ 

35 

36 ase_id: int 

37 ase_state: ASEState 

38 additional_data: bytes = b"" 

39 

40 

41class SinkASECharacteristic(BaseCharacteristic[SinkASEData]): 

42 """Sink ASE characteristic (0x2BC4). 

43 

44 org.bluetooth.characteristic.sink_ase 

45 

46 Audio Stream Endpoint for sink (audio receiver) role. 

47 """ 

48 

49 min_length = 2 

50 allow_variable_length = True 

51 

52 def _decode_value( 

53 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True 

54 ) -> SinkASEData: 

55 """Parse Sink ASE data. 

56 

57 Format: ASE_ID (uint8) + ASE_State (uint8) + variable state-specific data. 

58 """ 

59 ase_id = DataParser.parse_int8(data, 0, signed=False) 

60 ase_state = ASEState(DataParser.parse_int8(data, 1, signed=False)) 

61 additional_data = ( 

62 bytes(data[_ADDITIONAL_DATA_START_INDEX:]) if len(data) > _ADDITIONAL_DATA_START_INDEX else b"" 

63 ) 

64 

65 return SinkASEData( 

66 ase_id=ase_id, 

67 ase_state=ase_state, 

68 additional_data=additional_data, 

69 ) 

70 

71 def _encode_value(self, data: SinkASEData) -> bytearray: 

72 """Encode Sink ASE data to bytes.""" 

73 result = bytearray() 

74 result += DataParser.encode_int8(data.ase_id) 

75 result += DataParser.encode_int8(int(data.ase_state)) 

76 result += bytearray(data.additional_data) 

77 return result