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
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""Hearing Aid Features characteristic (0x2BDA)."""
3from __future__ import annotations
5from enum import IntEnum, IntFlag
7import msgspec
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class HearingAidType(IntEnum):
15 """Hearing Aid Type as per HAS 1.0, Section 3.1 (bits 0-1)."""
17 BINAURAL = 0x00
18 MONAURAL = 0x01
19 BANDED = 0x02
22class HearingAidFeatureFlags(IntFlag):
23 """Hearing Aid feature flags as per HAS 1.0, Section 3.1 (bits 2-5)."""
25 PRESET_SYNCHRONIZATION_SUPPORT = 0x04
26 INDEPENDENT_PRESETS = 0x08
27 DYNAMIC_PRESETS = 0x10
28 WRITABLE_PRESETS = 0x20
31class HearingAidFeaturesData(msgspec.Struct, frozen=True, kw_only=True):
32 """Parsed data from Hearing Aid Features characteristic."""
34 hearing_aid_type: HearingAidType
35 preset_synchronization_support: bool
36 independent_presets: bool
37 dynamic_presets: bool
38 writable_presets: bool
41class HearingAidFeaturesCharacteristic(BaseCharacteristic[HearingAidFeaturesData]):
42 """Hearing Aid Features characteristic (0x2BDA).
44 org.bluetooth.characteristic.hearing_aid_features
46 1-octet bitfield: bits 0-1 = Hearing Aid Type enum,
47 bits 2-5 = feature flags.
49 References:
50 Hearing Access Service 1.0, Section 3.1
51 """
53 expected_length = 1
55 _HEARING_AID_TYPE_MASK = 0x03
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)
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 )
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
82 return DataParser.encode_int8(raw, signed=False)