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
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-18 11:17 +0000
1"""LN Feature characteristic implementation."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class LNFeatures(IntFlag):
15 """LN Feature flags as per Bluetooth SIG specification."""
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
40class LNFeatureData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
41 """Parsed data from LN Feature characteristic."""
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
67class LNFeatureCharacteristic(BaseCharacteristic[LNFeatureData]):
68 """LN Feature characteristic.
70 Used to represent the supported features of a location and navigation sensor.
71 """
73 min_length = 4
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.
80 Format: Features(4) - 32-bit bitmap indicating supported features.
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)
87 Returns:
88 LNFeatureData containing parsed feature bitmap and capabilities.
90 """
91 features_bitmap = DataParser.parse_int32(data, 0, signed=False)
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 }
136 return LNFeatureData(features_bitmap=features_bitmap, **features)
138 def _encode_value(self, data: LNFeatureData) -> bytearray:
139 """Encode LNFeatureData back to bytes.
141 Args:
142 data: LNFeatureData instance to encode
144 Returns:
145 Encoded bytes representing the LN features
147 """
148 return DataParser.encode_int32(data.features_bitmap, signed=False)