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

1"""ACS Control Point characteristic (0x2B33).""" 

2 

3from __future__ import annotations 

4 

5from ...types import MAX_ROLLING_SEGMENT_COUNTER, ACSControlPointData, ACSSegmentationHeader 

6from ..context import CharacteristicContext 

7from .base import BaseCharacteristic 

8from .utils import DataParser 

9 

10 

11class ACSControlPointCharacteristic(BaseCharacteristic[ACSControlPointData]): 

12 """ACS Control Point characteristic (0x2B33). 

13 

14 org.bluetooth.characteristic.acs_control_point 

15 

16 Segmented control point for Authorization Control Service operations. 

17 """ 

18 

19 min_length = 2 

20 allow_variable_length = True 

21 

22 def _decode_value( 

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

24 ) -> ACSControlPointData: 

25 """Parse ACS Control Point data. 

26 

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:]) 

32 

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 ) 

42 

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}") 

48 

49 header = 0 

50 if data.header.first_segment: 

51 header |= 0x01 

52 if data.header.last_segment: 

53 header |= 0x02 

54 

55 header |= counter << 2 

56 

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