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
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""Audio Input Control Point characteristic (0x2B7B)."""
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 AudioInputControlPointOpCode(IntEnum):
16 """Audio Input Control Point operation codes."""
18 SET_GAIN_SETTING = 0x01
19 UNMUTE = 0x02
20 MUTE = 0x03
21 SET_MANUAL_GAIN_MODE = 0x04
22 SET_AUTOMATIC_GAIN_MODE = 0x05
25class AudioInputControlPointData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
26 """Parsed data from Audio Input Control Point characteristic.
28 The gain_setting field is present only for SET_GAIN_SETTING opcode.
29 """
31 op_code: AudioInputControlPointOpCode
32 change_counter: int
33 gain_setting: int | None = None
36_MIN_LENGTH_WITH_GAIN = 3 # opcode (uint8) + change_counter (uint8) + gain_setting (int8)
39class AudioInputControlPointCharacteristic(BaseCharacteristic[AudioInputControlPointData]):
40 """Audio Input Control Point characteristic (0x2B7B).
42 org.bluetooth.characteristic.audio_input_control_point
44 Used for controlling audio input settings in the Audio Input Control Service.
45 """
47 _manual_role = CharacteristicRole.CONTROL
48 min_length = 2
49 allow_variable_length = True
51 def _decode_value(
52 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
53 ) -> AudioInputControlPointData:
54 """Parse Audio Input Control Point data.
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)
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)
65 return AudioInputControlPointData(
66 op_code=op_code,
67 change_counter=change_counter,
68 gain_setting=gain_setting,
69 )
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