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

39 statements  

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

1"""Hearing Aid Features characteristic (0x2BDA).""" 

2 

3from __future__ import annotations 

4 

5from enum import IntEnum, IntFlag 

6 

7import msgspec 

8 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11from .utils import DataParser 

12 

13 

14class HearingAidType(IntEnum): 

15 """Hearing Aid Type as per HAS 1.0, Section 3.1 (bits 0-1).""" 

16 

17 BINAURAL = 0x00 

18 MONAURAL = 0x01 

19 BANDED = 0x02 

20 

21 

22class HearingAidFeatureFlags(IntFlag): 

23 """Hearing Aid feature flags as per HAS 1.0, Section 3.1 (bits 2-5).""" 

24 

25 PRESET_SYNCHRONIZATION_SUPPORT = 0x04 

26 INDEPENDENT_PRESETS = 0x08 

27 DYNAMIC_PRESETS = 0x10 

28 WRITABLE_PRESETS = 0x20 

29 

30 

31class HearingAidFeaturesData(msgspec.Struct, frozen=True, kw_only=True): 

32 """Parsed data from Hearing Aid Features characteristic.""" 

33 

34 hearing_aid_type: HearingAidType 

35 preset_synchronization_support: bool 

36 independent_presets: bool 

37 dynamic_presets: bool 

38 writable_presets: bool 

39 

40 

41class HearingAidFeaturesCharacteristic(BaseCharacteristic[HearingAidFeaturesData]): 

42 """Hearing Aid Features characteristic (0x2BDA). 

43 

44 org.bluetooth.characteristic.hearing_aid_features 

45 

46 1-octet bitfield: bits 0-1 = Hearing Aid Type enum, 

47 bits 2-5 = feature flags. 

48 

49 References: 

50 Hearing Access Service 1.0, Section 3.1 

51 """ 

52 

53 expected_length = 1 

54 

55 _HEARING_AID_TYPE_MASK = 0x03 

56 

57 def _decode_value( 

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

59 ) -> HearingAidFeaturesData: 

60 raw = DataParser.parse_int8(data, 0, signed=False) 

61 hearing_aid_type = HearingAidType(raw & self._HEARING_AID_TYPE_MASK) 

62 

63 return HearingAidFeaturesData( 

64 hearing_aid_type=hearing_aid_type, 

65 preset_synchronization_support=bool(raw & HearingAidFeatureFlags.PRESET_SYNCHRONIZATION_SUPPORT), 

66 independent_presets=bool(raw & HearingAidFeatureFlags.INDEPENDENT_PRESETS), 

67 dynamic_presets=bool(raw & HearingAidFeatureFlags.DYNAMIC_PRESETS), 

68 writable_presets=bool(raw & HearingAidFeatureFlags.WRITABLE_PRESETS), 

69 ) 

70 

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

72 raw = int(data.hearing_aid_type) 

73 if data.preset_synchronization_support: 

74 raw |= HearingAidFeatureFlags.PRESET_SYNCHRONIZATION_SUPPORT 

75 if data.independent_presets: 

76 raw |= HearingAidFeatureFlags.INDEPENDENT_PRESETS 

77 if data.dynamic_presets: 

78 raw |= HearingAidFeatureFlags.DYNAMIC_PRESETS 

79 if data.writable_presets: 

80 raw |= HearingAidFeatureFlags.WRITABLE_PRESETS 

81 

82 return DataParser.encode_int8(raw, signed=False)