Coverage for src/bluetooth_sig/gatt/characteristics/ln_feature.py: 100%
62 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
1"""LN Feature characteristic implementation."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ...types.gatt_enums import ValueType
10from ..context import CharacteristicContext
11from .base import BaseCharacteristic
12from .utils import DataParser
15class LNFeatures(IntFlag):
16 """LN Feature flags as per Bluetooth SIG specification."""
18 INSTANTANEOUS_SPEED_SUPPORTED = 0x0001
19 TOTAL_DISTANCE_SUPPORTED = 0x0002
20 LOCATION_SUPPORTED = 0x0004
21 ELEVATION_SUPPORTED = 0x0008
22 HEADING_SUPPORTED = 0x0010
23 ROLLING_TIME_SUPPORTED = 0x0020
24 UTC_TIME_SUPPORTED = 0x0040
25 REMAINING_DISTANCE_SUPPORTED = 0x0080
26 REMAINING_VERTICAL_DISTANCE_SUPPORTED = 0x0100
27 ESTIMATED_TIME_OF_ARRIVAL_SUPPORTED = 0x0200
28 NUMBER_OF_BEACONS_IN_SOLUTION_SUPPORTED = 0x0400
29 NUMBER_OF_BEACONS_IN_VIEW_SUPPORTED = 0x0800
30 TIME_TO_FIRST_FIX_SUPPORTED = 0x1000
31 ESTIMATED_HORIZONTAL_POSITION_ERROR_SUPPORTED = 0x2000
32 ESTIMATED_VERTICAL_POSITION_ERROR_SUPPORTED = 0x4000
33 HORIZONTAL_DILUTION_OF_PRECISION_SUPPORTED = 0x8000
34 VERTICAL_DILUTION_OF_PRECISION_SUPPORTED = 0x00010000
35 LOCATION_AND_SPEED_CHARACTERISTIC_CONTENT_MASKING_SUPPORTED = 0x00020000
36 FIX_RATE_SETTING_SUPPORTED = 0x00040000
37 ELEVATION_SETTING_SUPPORTED = 0x00080000
38 POSITION_STATUS_SUPPORTED = 0x00100000
41class LNFeatureData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
42 """Parsed data from LN Feature characteristic."""
44 features_bitmap: int
45 instantaneous_speed_supported: bool
46 total_distance_supported: bool
47 location_supported: bool
48 elevation_supported: bool
49 heading_supported: bool
50 rolling_time_supported: bool
51 utc_time_supported: bool
52 remaining_distance_supported: bool
53 remaining_vertical_distance_supported: bool
54 estimated_time_of_arrival_supported: bool
55 number_of_beacons_in_solution_supported: bool
56 number_of_beacons_in_view_supported: bool
57 time_to_first_fix_supported: bool
58 estimated_horizontal_position_error_supported: bool
59 estimated_vertical_position_error_supported: bool
60 horizontal_dilution_of_precision_supported: bool
61 vertical_dilution_of_precision_supported: bool
62 location_and_speed_characteristic_content_masking_supported: bool
63 fix_rate_setting_supported: bool
64 elevation_setting_supported: bool
65 position_status_supported: bool
68class LNFeatureCharacteristic(BaseCharacteristic):
69 """LN Feature characteristic.
71 Used to represent the supported features of a location and navigation sensor.
72 """
74 _manual_value_type: ValueType | str | None = ValueType.DICT # Override since decode_value returns dataclass
76 def decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> LNFeatureData:
77 """Parse LN feature data according to Bluetooth specification.
79 Format: Features(4) - 32-bit bitmap indicating supported features.
81 Args:
82 data: Raw bytearray from BLE characteristic.
83 ctx: Optional CharacteristicContext providing surrounding context (may be None).
85 Returns:
86 LNFeatureData containing parsed feature bitmap and capabilities.
88 """
89 if len(data) < 4:
90 raise ValueError("LN Feature data must be at least 4 bytes")
92 features_bitmap = DataParser.parse_int32(data, 0, signed=False)
94 # Extract individual feature flags
95 features = {
96 "instantaneous_speed_supported": bool(features_bitmap & LNFeatures.INSTANTANEOUS_SPEED_SUPPORTED),
97 "total_distance_supported": bool(features_bitmap & LNFeatures.TOTAL_DISTANCE_SUPPORTED),
98 "location_supported": bool(features_bitmap & LNFeatures.LOCATION_SUPPORTED),
99 "elevation_supported": bool(features_bitmap & LNFeatures.ELEVATION_SUPPORTED),
100 "heading_supported": bool(features_bitmap & LNFeatures.HEADING_SUPPORTED),
101 "rolling_time_supported": bool(features_bitmap & LNFeatures.ROLLING_TIME_SUPPORTED),
102 "utc_time_supported": bool(features_bitmap & LNFeatures.UTC_TIME_SUPPORTED),
103 "remaining_distance_supported": bool(features_bitmap & LNFeatures.REMAINING_DISTANCE_SUPPORTED),
104 "remaining_vertical_distance_supported": bool(
105 features_bitmap & LNFeatures.REMAINING_VERTICAL_DISTANCE_SUPPORTED
106 ),
107 "estimated_time_of_arrival_supported": bool(
108 features_bitmap & LNFeatures.ESTIMATED_TIME_OF_ARRIVAL_SUPPORTED
109 ),
110 "number_of_beacons_in_solution_supported": bool(
111 features_bitmap & LNFeatures.NUMBER_OF_BEACONS_IN_SOLUTION_SUPPORTED
112 ),
113 "number_of_beacons_in_view_supported": bool(
114 features_bitmap & LNFeatures.NUMBER_OF_BEACONS_IN_VIEW_SUPPORTED
115 ),
116 "time_to_first_fix_supported": bool(features_bitmap & LNFeatures.TIME_TO_FIRST_FIX_SUPPORTED),
117 "estimated_horizontal_position_error_supported": bool(
118 features_bitmap & LNFeatures.ESTIMATED_HORIZONTAL_POSITION_ERROR_SUPPORTED
119 ),
120 "estimated_vertical_position_error_supported": bool(
121 features_bitmap & LNFeatures.ESTIMATED_VERTICAL_POSITION_ERROR_SUPPORTED
122 ),
123 "horizontal_dilution_of_precision_supported": bool(
124 features_bitmap & LNFeatures.HORIZONTAL_DILUTION_OF_PRECISION_SUPPORTED
125 ),
126 "vertical_dilution_of_precision_supported": bool(
127 features_bitmap & LNFeatures.VERTICAL_DILUTION_OF_PRECISION_SUPPORTED
128 ),
129 "location_and_speed_characteristic_content_masking_supported": bool(
130 features_bitmap & LNFeatures.LOCATION_AND_SPEED_CHARACTERISTIC_CONTENT_MASKING_SUPPORTED
131 ),
132 "fix_rate_setting_supported": bool(features_bitmap & LNFeatures.FIX_RATE_SETTING_SUPPORTED),
133 "elevation_setting_supported": bool(features_bitmap & LNFeatures.ELEVATION_SETTING_SUPPORTED),
134 "position_status_supported": bool(features_bitmap & LNFeatures.POSITION_STATUS_SUPPORTED),
135 }
137 return LNFeatureData(features_bitmap=features_bitmap, **features)
139 def encode_value(self, data: LNFeatureData) -> bytearray:
140 """Encode LNFeatureData back to bytes.
142 Args:
143 data: LNFeatureData instance to encode
145 Returns:
146 Encoded bytes representing the LN features
148 """
149 return DataParser.encode_int32(data.features_bitmap, signed=False)