Coverage for src / bluetooth_sig / gatt / characteristics / ase_control_point.py: 100%
36 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""ASE Control Point characteristic (0x2BC6)."""
3from __future__ import annotations
5from enum import IntEnum
7import msgspec
9from ...types.gatt_enums import CharacteristicRole
10from ..context import CharacteristicContext
11from .base import BaseCharacteristic
12from .utils import DataParser
15class ASEControlPointOpCode(IntEnum):
16 """ASE Control Point operation codes."""
18 CONFIG_CODEC = 0x01
19 CONFIG_QOS = 0x02
20 ENABLE = 0x03
21 RECEIVER_START_READY = 0x04
22 DISABLE = 0x05
23 RECEIVER_STOP_READY = 0x06
24 UPDATE_METADATA = 0x07
25 RELEASE = 0x08
28class ASEControlPointData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
29 """Parsed data from ASE Control Point characteristic.
31 Contains the opcode, number of ASEs, and any additional
32 ASE-specific parameters as raw bytes.
33 """
35 op_code: ASEControlPointOpCode
36 number_of_ases: int
37 parameter_data: bytes = b""
40_HEADER_SIZE = 2 # opcode (uint8) + number_of_ases (uint8)
43class ASEControlPointCharacteristic(BaseCharacteristic[ASEControlPointData]):
44 """ASE Control Point characteristic (0x2BC6).
46 org.bluetooth.characteristic.ase_control_point
48 Used for controlling Audio Stream Endpoints in the Audio
49 Stream Control Service.
50 """
52 _manual_role = CharacteristicRole.CONTROL
53 min_length = 2
54 allow_variable_length = True
56 def _decode_value(
57 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
58 ) -> ASEControlPointData:
59 """Parse ASE Control Point data.
61 Format: opcode (uint8) + number_of_ases (uint8) + ASE-specific params.
62 """
63 op_code = ASEControlPointOpCode(DataParser.parse_int8(data, 0, signed=False))
64 number_of_ases = DataParser.parse_int8(data, 1, signed=False)
65 parameter_data = bytes(data[_HEADER_SIZE:]) if len(data) > _HEADER_SIZE else b""
67 return ASEControlPointData(
68 op_code=op_code,
69 number_of_ases=number_of_ases,
70 parameter_data=parameter_data,
71 )
73 def _encode_value(self, data: ASEControlPointData) -> bytearray:
74 """Encode ASE Control Point data to bytes."""
75 result = bytearray()
76 result += DataParser.encode_int8(int(data.op_code))
77 result += DataParser.encode_int8(data.number_of_ases)
78 result += bytearray(data.parameter_data)
79 return result