Coverage for src / bluetooth_sig / types / advertising / features.py: 75%

106 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-18 11:17 +0000

1"""LE Supported Features (Core Spec Vol 6, Part B, Section 4.6).""" 

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9 

10class LEFeatureBits(IntFlag): 

11 """LE Supported Features bit definitions (Core Spec Vol 6, Part B, Section 4.6). 

12 

13 Byte 0 features (bits 0-7). 

14 """ 

15 

16 # Byte 0 features 

17 LE_ENCRYPTION = 0x0001 

18 CONNECTION_PARAMETERS_REQUEST = 0x0002 

19 EXTENDED_REJECT_INDICATION = 0x0004 

20 PERIPHERAL_INITIATED_FEATURES_EXCHANGE = 0x0008 

21 LE_PING = 0x0010 

22 LE_DATA_PACKET_LENGTH_EXTENSION = 0x0020 

23 LL_PRIVACY = 0x0040 

24 EXTENDED_SCANNER_FILTER_POLICIES = 0x0080 

25 

26 # Byte 1 features (bits 8-15) 

27 LE_2M_PHY = 0x0100 

28 STABLE_MODULATION_INDEX_TX = 0x0200 

29 STABLE_MODULATION_INDEX_RX = 0x0400 

30 LE_CODED_PHY = 0x0800 

31 LE_EXTENDED_ADVERTISING = 0x1000 

32 LE_PERIODIC_ADVERTISING = 0x2000 

33 CHANNEL_SELECTION_ALGORITHM_2 = 0x4000 

34 LE_POWER_CLASS_1 = 0x8000 

35 

36 # Byte 2 features (bits 16-23) 

37 MIN_NUMBER_OF_USED_CHANNELS = 0x010000 

38 CONNECTION_CTE_REQUEST = 0x020000 

39 CONNECTION_CTE_RESPONSE = 0x040000 

40 CONNECTIONLESS_CTE_TX = 0x080000 

41 CONNECTIONLESS_CTE_RX = 0x100000 

42 ANTENNA_SWITCHING_TX = 0x200000 

43 ANTENNA_SWITCHING_RX = 0x400000 

44 RECEIVING_CTE = 0x800000 

45 

46 

47class LEFeatures(msgspec.Struct, kw_only=True): # pylint: disable=too-many-public-methods # One property per LE feature bit 

48 """LE Supported Features bit field (Core Spec Vol 6, Part B, Section 4.6). 

49 

50 Attributes: 

51 raw_value: Raw feature bit field bytes (up to 8 bytes) 

52 """ 

53 

54 raw_value: bytes 

55 

56 def _get_features_int(self) -> int: 

57 """Convert raw bytes to integer for bit checking.""" 

58 return int.from_bytes(self.raw_value, byteorder="little") if self.raw_value else 0 

59 

60 def _has_feature(self, feature: LEFeatureBits) -> bool: 

61 """Check if a specific feature bit is set.""" 

62 return bool(self._get_features_int() & feature) 

63 

64 @property 

65 def le_encryption(self) -> bool: 

66 """LE Encryption supported.""" 

67 return self._has_feature(LEFeatureBits.LE_ENCRYPTION) 

68 

69 @property 

70 def connection_parameters_request(self) -> bool: 

71 """Connection Parameters Request Procedure.""" 

72 return self._has_feature(LEFeatureBits.CONNECTION_PARAMETERS_REQUEST) 

73 

74 @property 

75 def extended_reject_indication(self) -> bool: 

76 """Extended Reject Indication.""" 

77 return self._has_feature(LEFeatureBits.EXTENDED_REJECT_INDICATION) 

78 

79 @property 

80 def peripheral_initiated_features_exchange(self) -> bool: 

81 """Peripheral-initiated Features Exchange.""" 

82 return self._has_feature(LEFeatureBits.PERIPHERAL_INITIATED_FEATURES_EXCHANGE) 

83 

84 @property 

85 def le_ping(self) -> bool: 

86 """LE Ping.""" 

87 return self._has_feature(LEFeatureBits.LE_PING) 

88 

89 @property 

90 def le_data_packet_length_extension(self) -> bool: 

91 """LE Data Packet Length Extension.""" 

92 return self._has_feature(LEFeatureBits.LE_DATA_PACKET_LENGTH_EXTENSION) 

93 

94 @property 

95 def ll_privacy(self) -> bool: 

96 """LL Privacy.""" 

97 return self._has_feature(LEFeatureBits.LL_PRIVACY) 

98 

99 @property 

100 def extended_scanner_filter_policies(self) -> bool: 

101 """Extended Scanner Filter Policies.""" 

102 return self._has_feature(LEFeatureBits.EXTENDED_SCANNER_FILTER_POLICIES) 

103 

104 @property 

105 def le_2m_phy(self) -> bool: 

106 """LE 2M PHY.""" 

107 return self._has_feature(LEFeatureBits.LE_2M_PHY) 

108 

109 @property 

110 def stable_modulation_index_tx(self) -> bool: 

111 """Stable Modulation Index - Transmitter.""" 

112 return self._has_feature(LEFeatureBits.STABLE_MODULATION_INDEX_TX) 

113 

114 @property 

115 def stable_modulation_index_rx(self) -> bool: 

116 """Stable Modulation Index - Receiver.""" 

117 return self._has_feature(LEFeatureBits.STABLE_MODULATION_INDEX_RX) 

118 

119 @property 

120 def le_coded_phy(self) -> bool: 

121 """LE Coded PHY.""" 

122 return self._has_feature(LEFeatureBits.LE_CODED_PHY) 

123 

124 @property 

125 def le_extended_advertising(self) -> bool: 

126 """LE Extended Advertising.""" 

127 return self._has_feature(LEFeatureBits.LE_EXTENDED_ADVERTISING) 

128 

129 @property 

130 def le_periodic_advertising(self) -> bool: 

131 """LE Periodic Advertising.""" 

132 return self._has_feature(LEFeatureBits.LE_PERIODIC_ADVERTISING) 

133 

134 @property 

135 def channel_selection_algorithm_2(self) -> bool: 

136 """Channel Selection Algorithm #2.""" 

137 return self._has_feature(LEFeatureBits.CHANNEL_SELECTION_ALGORITHM_2) 

138 

139 @property 

140 def le_power_class_1(self) -> bool: 

141 """LE Power Class 1.""" 

142 return self._has_feature(LEFeatureBits.LE_POWER_CLASS_1) 

143 

144 @property 

145 def min_number_of_used_channels(self) -> bool: 

146 """Minimum Number of Used Channels Procedure.""" 

147 return self._has_feature(LEFeatureBits.MIN_NUMBER_OF_USED_CHANNELS) 

148 

149 @property 

150 def connection_cte_request(self) -> bool: 

151 """Connection CTE Request.""" 

152 return self._has_feature(LEFeatureBits.CONNECTION_CTE_REQUEST) 

153 

154 @property 

155 def connection_cte_response(self) -> bool: 

156 """Connection CTE Response.""" 

157 return self._has_feature(LEFeatureBits.CONNECTION_CTE_RESPONSE) 

158 

159 @property 

160 def connectionless_cte_tx(self) -> bool: 

161 """Connectionless CTE Transmitter.""" 

162 return self._has_feature(LEFeatureBits.CONNECTIONLESS_CTE_TX) 

163 

164 @property 

165 def connectionless_cte_rx(self) -> bool: 

166 """Connectionless CTE Receiver.""" 

167 return self._has_feature(LEFeatureBits.CONNECTIONLESS_CTE_RX) 

168 

169 @property 

170 def antenna_switching_tx(self) -> bool: 

171 """Antenna Switching During CTE Transmission.""" 

172 return self._has_feature(LEFeatureBits.ANTENNA_SWITCHING_TX) 

173 

174 @property 

175 def antenna_switching_rx(self) -> bool: 

176 """Antenna Switching During CTE Reception.""" 

177 return self._has_feature(LEFeatureBits.ANTENNA_SWITCHING_RX) 

178 

179 @property 

180 def receiving_cte(self) -> bool: 

181 """Receiving Constant Tone Extensions.""" 

182 return self._has_feature(LEFeatureBits.RECEIVING_CTE)