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

36 statements  

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

1"""Audio Input Control Point characteristic (0x2B7B).""" 

2 

3from __future__ import annotations 

4 

5from enum import IntEnum 

6 

7import msgspec 

8 

9from ...types.gatt_enums import CharacteristicRole 

10from ..context import CharacteristicContext 

11from .base import BaseCharacteristic 

12from .utils import DataParser 

13 

14 

15class AudioInputControlPointOpCode(IntEnum): 

16 """Audio Input Control Point operation codes.""" 

17 

18 SET_GAIN_SETTING = 0x01 

19 UNMUTE = 0x02 

20 MUTE = 0x03 

21 SET_MANUAL_GAIN_MODE = 0x04 

22 SET_AUTOMATIC_GAIN_MODE = 0x05 

23 

24 

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

26 """Parsed data from Audio Input Control Point characteristic. 

27 

28 The gain_setting field is present only for SET_GAIN_SETTING opcode. 

29 """ 

30 

31 op_code: AudioInputControlPointOpCode 

32 change_counter: int 

33 gain_setting: int | None = None 

34 

35 

36_MIN_LENGTH_WITH_GAIN = 3 # opcode (uint8) + change_counter (uint8) + gain_setting (int8) 

37 

38 

39class AudioInputControlPointCharacteristic(BaseCharacteristic[AudioInputControlPointData]): 

40 """Audio Input Control Point characteristic (0x2B7B). 

41 

42 org.bluetooth.characteristic.audio_input_control_point 

43 

44 Used for controlling audio input settings in the Audio Input Control Service. 

45 """ 

46 

47 _manual_role = CharacteristicRole.CONTROL 

48 min_length = 2 

49 allow_variable_length = True 

50 

51 def _decode_value( 

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

53 ) -> AudioInputControlPointData: 

54 """Parse Audio Input Control Point data. 

55 

56 Format: opcode (uint8) + change_counter (uint8) + optional parameter. 

57 """ 

58 op_code = AudioInputControlPointOpCode(DataParser.parse_int8(data, 0, signed=False)) 

59 change_counter = DataParser.parse_int8(data, 1, signed=False) 

60 

61 gain_setting = None 

62 if op_code == AudioInputControlPointOpCode.SET_GAIN_SETTING and len(data) >= _MIN_LENGTH_WITH_GAIN: 

63 gain_setting = DataParser.parse_int8(data, 2, signed=True) 

64 

65 return AudioInputControlPointData( 

66 op_code=op_code, 

67 change_counter=change_counter, 

68 gain_setting=gain_setting, 

69 ) 

70 

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

72 """Encode Audio Input Control Point data to bytes.""" 

73 result = bytearray() 

74 result += DataParser.encode_int8(int(data.op_code)) 

75 result += DataParser.encode_int8(data.change_counter) 

76 if data.gain_setting is not None: 

77 result += DataParser.encode_int8(data.gain_setting, signed=True) 

78 return result