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

52 statements  

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

1"""RC Feature characteristic (0x2B1D). 

2 

3Describes the supported features of the Reconnection Configuration server. 

4 

5Structure: E2E-CRC (uint16) + RC Feature field (3+n octets). 

6The RC Feature field is a variable-length bit field with an extension 

7mechanism via bit 23. 

8 

9References: 

10 Bluetooth SIG Reconnection Configuration Service v1.0.1, Section 3.1 

11""" 

12 

13from __future__ import annotations 

14 

15from enum import IntFlag 

16 

17import msgspec 

18 

19from ..context import CharacteristicContext 

20from .base import BaseCharacteristic 

21from .utils import DataParser 

22 

23 

24class RCFeatureFlags(IntFlag): 

25 """RC Feature bit definitions as per RCS v1.0.1 Table 3.3.""" 

26 

27 E2E_CRC_SUPPORTED = 0x000001 

28 ENABLE_DISCONNECT_SUPPORTED = 0x000002 

29 READY_FOR_DISCONNECT_SUPPORTED = 0x000004 

30 PROPOSE_RECONNECTION_TIMEOUT_SUPPORTED = 0x000008 

31 PROPOSE_CONNECTION_INTERVAL_SUPPORTED = 0x000010 

32 PROPOSE_PERIPHERAL_LATENCY_SUPPORTED = 0x000020 

33 PROPOSE_SUPERVISION_TIMEOUT_SUPPORTED = 0x000040 

34 PROPOSE_ADVERTISEMENT_INTERVAL_SUPPORTED = 0x000080 

35 PROPOSE_ADVERTISEMENT_COUNT_SUPPORTED = 0x000100 

36 PROPOSE_ADVERTISEMENT_REPETITION_TIME_SUPPORTED = 0x000200 

37 ADVERTISEMENT_CONFIGURATION_1_SUPPORTED = 0x000400 

38 ADVERTISEMENT_CONFIGURATION_2_SUPPORTED = 0x000800 

39 ADVERTISEMENT_CONFIGURATION_3_SUPPORTED = 0x001000 

40 ADVERTISEMENT_CONFIGURATION_4_SUPPORTED = 0x002000 

41 UPGRADE_TO_LESC_ONLY_SUPPORTED = 0x004000 

42 NEXT_PAIRING_OOB_SUPPORTED = 0x008000 

43 USE_OF_FILTER_ACCEPT_LIST_SUPPORTED = 0x010000 

44 LIMITED_ACCESS_SUPPORTED = 0x020000 

45 

46 

47class RCFeatureData(msgspec.Struct, frozen=True, kw_only=True): 

48 """Parsed RC Feature characteristic data. 

49 

50 Attributes: 

51 e2e_crc: CRC-CCITT value (0xFFFF if E2E-safety not supported). 

52 features: Supported feature flags from the RC Feature field. 

53 

54 """ 

55 

56 e2e_crc: int 

57 features: RCFeatureFlags 

58 

59 

60class RCFeatureCharacteristic(BaseCharacteristic[RCFeatureData]): 

61 """RC Feature characteristic (0x2B1D). 

62 

63 org.bluetooth.characteristic.rc_feature 

64 

65 Composite characteristic: E2E-CRC (uint16) followed by 

66 a variable-length RC Feature bit field (3+ octets). 

67 """ 

68 

69 _E2E_CRC_SIZE = 2 

70 _MIN_FEATURE_OCTETS = 3 

71 _DEFINED_BITS_MASK = 0x03FFFF 

72 _BYTE_MASK = 0xFF 

73 _BITS_PER_BYTE = 8 

74 

75 min_length = _E2E_CRC_SIZE + _MIN_FEATURE_OCTETS 

76 allow_variable_length = True 

77 

78 def _decode_value( 

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

80 ) -> RCFeatureData: 

81 """Parse RC Feature data per RCS v1.0.1 Section 3.1.""" 

82 e2e_crc = DataParser.parse_int16(data, 0, signed=False) 

83 

84 # RC Feature field starts after E2E-CRC, variable length (3+n octets). 

85 # Read all remaining bytes as little-endian integer. 

86 feature_bytes = data[self._E2E_CRC_SIZE :] 

87 value = 0 

88 for i, byte in enumerate(feature_bytes): 

89 value |= byte << (self._BITS_PER_BYTE * i) 

90 features = RCFeatureFlags(value & self._DEFINED_BITS_MASK) 

91 

92 return RCFeatureData(e2e_crc=e2e_crc, features=features) 

93 

94 def _encode_value(self, data: RCFeatureData) -> bytearray: 

95 """Encode RC Feature data.""" 

96 result = bytearray() 

97 result.extend(DataParser.encode_int16(data.e2e_crc, signed=False)) 

98 

99 value = int(data.features) 

100 num_octets = max(self._MIN_FEATURE_OCTETS, (value.bit_length() + 7) // self._BITS_PER_BYTE) 

101 for i in range(num_octets): 

102 result.append((value >> (self._BITS_PER_BYTE * i)) & self._BYTE_MASK) 

103 return result