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

59 statements  

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

1"""LN Feature characteristic implementation.""" 

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11from .utils import DataParser 

12 

13 

14class LNFeatures(IntFlag): 

15 """LN Feature flags as per Bluetooth SIG specification.""" 

16 

17 INSTANTANEOUS_SPEED_SUPPORTED = 0x0001 

18 TOTAL_DISTANCE_SUPPORTED = 0x0002 

19 LOCATION_SUPPORTED = 0x0004 

20 ELEVATION_SUPPORTED = 0x0008 

21 HEADING_SUPPORTED = 0x0010 

22 ROLLING_TIME_SUPPORTED = 0x0020 

23 UTC_TIME_SUPPORTED = 0x0040 

24 REMAINING_DISTANCE_SUPPORTED = 0x0080 

25 REMAINING_VERTICAL_DISTANCE_SUPPORTED = 0x0100 

26 ESTIMATED_TIME_OF_ARRIVAL_SUPPORTED = 0x0200 

27 NUMBER_OF_BEACONS_IN_SOLUTION_SUPPORTED = 0x0400 

28 NUMBER_OF_BEACONS_IN_VIEW_SUPPORTED = 0x0800 

29 TIME_TO_FIRST_FIX_SUPPORTED = 0x1000 

30 ESTIMATED_HORIZONTAL_POSITION_ERROR_SUPPORTED = 0x2000 

31 ESTIMATED_VERTICAL_POSITION_ERROR_SUPPORTED = 0x4000 

32 HORIZONTAL_DILUTION_OF_PRECISION_SUPPORTED = 0x8000 

33 VERTICAL_DILUTION_OF_PRECISION_SUPPORTED = 0x00010000 

34 LOCATION_AND_SPEED_CHARACTERISTIC_CONTENT_MASKING_SUPPORTED = 0x00020000 

35 FIX_RATE_SETTING_SUPPORTED = 0x00040000 

36 ELEVATION_SETTING_SUPPORTED = 0x00080000 

37 POSITION_STATUS_SUPPORTED = 0x00100000 

38 

39 

40class LNFeatureData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods 

41 """Parsed data from LN Feature characteristic.""" 

42 

43 features_bitmap: int 

44 instantaneous_speed_supported: bool 

45 total_distance_supported: bool 

46 location_supported: bool 

47 elevation_supported: bool 

48 heading_supported: bool 

49 rolling_time_supported: bool 

50 utc_time_supported: bool 

51 remaining_distance_supported: bool 

52 remaining_vertical_distance_supported: bool 

53 estimated_time_of_arrival_supported: bool 

54 number_of_beacons_in_solution_supported: bool 

55 number_of_beacons_in_view_supported: bool 

56 time_to_first_fix_supported: bool 

57 estimated_horizontal_position_error_supported: bool 

58 estimated_vertical_position_error_supported: bool 

59 horizontal_dilution_of_precision_supported: bool 

60 vertical_dilution_of_precision_supported: bool 

61 location_and_speed_characteristic_content_masking_supported: bool 

62 fix_rate_setting_supported: bool 

63 elevation_setting_supported: bool 

64 position_status_supported: bool 

65 

66 

67class LNFeatureCharacteristic(BaseCharacteristic[LNFeatureData]): 

68 """LN Feature characteristic. 

69 

70 Used to represent the supported features of a location and navigation sensor. 

71 """ 

72 

73 min_length = 4 

74 

75 def _decode_value( 

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

77 ) -> LNFeatureData: 

78 """Parse LN feature data according to Bluetooth specification. 

79 

80 Format: Features(4) - 32-bit bitmap indicating supported features. 

81 

82 Args: 

83 data: Raw bytearray from BLE characteristic. 

84 ctx: Optional CharacteristicContext providing surrounding context (may be None). 

85 validate: Whether to validate ranges (default True) 

86 

87 Returns: 

88 LNFeatureData containing parsed feature bitmap and capabilities. 

89 

90 """ 

91 features_bitmap = DataParser.parse_int32(data, 0, signed=False) 

92 

93 # Extract individual feature flags 

94 features = { 

95 "instantaneous_speed_supported": bool(features_bitmap & LNFeatures.INSTANTANEOUS_SPEED_SUPPORTED), 

96 "total_distance_supported": bool(features_bitmap & LNFeatures.TOTAL_DISTANCE_SUPPORTED), 

97 "location_supported": bool(features_bitmap & LNFeatures.LOCATION_SUPPORTED), 

98 "elevation_supported": bool(features_bitmap & LNFeatures.ELEVATION_SUPPORTED), 

99 "heading_supported": bool(features_bitmap & LNFeatures.HEADING_SUPPORTED), 

100 "rolling_time_supported": bool(features_bitmap & LNFeatures.ROLLING_TIME_SUPPORTED), 

101 "utc_time_supported": bool(features_bitmap & LNFeatures.UTC_TIME_SUPPORTED), 

102 "remaining_distance_supported": bool(features_bitmap & LNFeatures.REMAINING_DISTANCE_SUPPORTED), 

103 "remaining_vertical_distance_supported": bool( 

104 features_bitmap & LNFeatures.REMAINING_VERTICAL_DISTANCE_SUPPORTED 

105 ), 

106 "estimated_time_of_arrival_supported": bool( 

107 features_bitmap & LNFeatures.ESTIMATED_TIME_OF_ARRIVAL_SUPPORTED 

108 ), 

109 "number_of_beacons_in_solution_supported": bool( 

110 features_bitmap & LNFeatures.NUMBER_OF_BEACONS_IN_SOLUTION_SUPPORTED 

111 ), 

112 "number_of_beacons_in_view_supported": bool( 

113 features_bitmap & LNFeatures.NUMBER_OF_BEACONS_IN_VIEW_SUPPORTED 

114 ), 

115 "time_to_first_fix_supported": bool(features_bitmap & LNFeatures.TIME_TO_FIRST_FIX_SUPPORTED), 

116 "estimated_horizontal_position_error_supported": bool( 

117 features_bitmap & LNFeatures.ESTIMATED_HORIZONTAL_POSITION_ERROR_SUPPORTED 

118 ), 

119 "estimated_vertical_position_error_supported": bool( 

120 features_bitmap & LNFeatures.ESTIMATED_VERTICAL_POSITION_ERROR_SUPPORTED 

121 ), 

122 "horizontal_dilution_of_precision_supported": bool( 

123 features_bitmap & LNFeatures.HORIZONTAL_DILUTION_OF_PRECISION_SUPPORTED 

124 ), 

125 "vertical_dilution_of_precision_supported": bool( 

126 features_bitmap & LNFeatures.VERTICAL_DILUTION_OF_PRECISION_SUPPORTED 

127 ), 

128 "location_and_speed_characteristic_content_masking_supported": bool( 

129 features_bitmap & LNFeatures.LOCATION_AND_SPEED_CHARACTERISTIC_CONTENT_MASKING_SUPPORTED 

130 ), 

131 "fix_rate_setting_supported": bool(features_bitmap & LNFeatures.FIX_RATE_SETTING_SUPPORTED), 

132 "elevation_setting_supported": bool(features_bitmap & LNFeatures.ELEVATION_SETTING_SUPPORTED), 

133 "position_status_supported": bool(features_bitmap & LNFeatures.POSITION_STATUS_SUPPORTED), 

134 } 

135 

136 return LNFeatureData(features_bitmap=features_bitmap, **features) 

137 

138 def _encode_value(self, data: LNFeatureData) -> bytearray: 

139 """Encode LNFeatureData back to bytes. 

140 

141 Args: 

142 data: LNFeatureData instance to encode 

143 

144 Returns: 

145 Encoded bytes representing the LN features 

146 

147 """ 

148 return DataParser.encode_int32(data.features_bitmap, signed=False)