Coverage for src / bluetooth_sig / gatt / characteristics / acs_control_point.py: 100%
28 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"""ACS Control Point characteristic (0x2B33)."""
3from __future__ import annotations
5from ...types import MAX_ROLLING_SEGMENT_COUNTER, ACSControlPointData, ACSSegmentationHeader
6from ..context import CharacteristicContext
7from .base import BaseCharacteristic
8from .utils import DataParser
11class ACSControlPointCharacteristic(BaseCharacteristic[ACSControlPointData]):
12 """ACS Control Point characteristic (0x2B33).
14 org.bluetooth.characteristic.acs_control_point
16 Segmented control point for Authorization Control Service operations.
17 """
19 min_length = 2
20 allow_variable_length = True
22 def _decode_value(
23 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
24 ) -> ACSControlPointData:
25 """Parse ACS Control Point data.
27 Format: Segmentation Header (uint8) + OpCode (uint8) + Operand (variable).
28 """
29 header = DataParser.parse_int8(data, 0, signed=False)
30 opcode = DataParser.parse_int8(data, 1, signed=False)
31 operand = bytes(data[2:])
33 return ACSControlPointData(
34 header=ACSSegmentationHeader(
35 first_segment=bool(header & 0x01),
36 last_segment=bool(header & 0x02),
37 rolling_segment_counter=(header >> 2) & 0x3F,
38 ),
39 opcode=opcode,
40 operand=operand,
41 )
43 def _encode_value(self, data: ACSControlPointData) -> bytearray:
44 """Encode ACS Control Point data."""
45 counter = data.header.rolling_segment_counter
46 if not 0 <= counter <= MAX_ROLLING_SEGMENT_COUNTER:
47 raise ValueError(f"rolling_segment_counter must be in range 0-63, got {counter}")
49 header = 0
50 if data.header.first_segment:
51 header |= 0x01
52 if data.header.last_segment:
53 header |= 0x02
55 header |= counter << 2
57 result = bytearray()
58 result.extend(DataParser.encode_int8(header, signed=False))
59 result.extend(DataParser.encode_int8(int(data.opcode), signed=False))
60 result.extend(data.operand)
61 return result