Coverage for src / bluetooth_sig / types / units.py: 92%
62 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
1"""Domain enums for measurement units in decoded characteristic data.
3These enums provide type-safe representations of measurement choices
4(e.g., Celsius vs Fahrenheit, metric vs imperial) used in decoded
5characteristic return values. They enable static type checking and
6IDE autocompletion for unit-aware data.
8Note: These are distinct from UnitInfo in registry/uuids/units.py which
9provides Bluetooth SIG metadata (UUID, name, symbol) loaded from YAML.
10The symbol values here intentionally match the registry symbols for
11consistency, but serve different purposes:
12- Domain enums: Type hints for decoded data (e.g., `unit: PressureUnit`)
13- Registry UnitInfo: UUID resolution and metadata lookup
15Reference: https://www.bipm.org/en/measurement-units
16"""
18from __future__ import annotations
20from enum import Enum
23class MeasurementSystem(Enum):
24 """Measurement system for body composition and weight data."""
26 METRIC = "metric"
27 IMPERIAL = "imperial"
30class WeightUnit(Enum):
31 """Units for weight/mass measurements."""
33 KG = "kg"
34 LB = "lb"
37class HeightUnit(Enum):
38 """Units for height measurements."""
40 METERS = "meters"
41 INCHES = "inches"
44class TemperatureUnit(Enum):
45 """Units for temperature measurements."""
47 CELSIUS = "°C"
48 FAHRENHEIT = "°F"
51class GlucoseConcentrationUnit(Enum):
52 """Units for glucose concentration measurements."""
54 MG_DL = "mg/dL"
55 MMOL_L = "mmol/L"
58class PressureUnit(Enum):
59 """Units for pressure measurements."""
61 KPA = "kPa"
62 MMHG = "mmHg"
65class ElectricalUnit(Enum):
66 """Units for electrical measurements."""
68 VOLTS = "V"
69 AMPS = "A"
70 HERTZ = "Hz"
71 DBM = "dBm"
74class ConcentrationUnit(Enum):
75 """Units for concentration measurements."""
77 MICROGRAMS_PER_CUBIC_METER = "µg/m³"
78 PARTS_PER_MILLION = "ppm"
79 PARTS_PER_BILLION = "ppb"
80 KILOGRAMS_PER_CUBIC_METER = "kg/m³"
81 GRAINS_PER_CUBIC_METER = "grains/m³"
84class PercentageUnit(Enum):
85 """Units for percentage measurements."""
87 PERCENT = "%"
90class AngleUnit(Enum):
91 """Units for angle measurements."""
93 DEGREES = "°"
96class SoundUnit(Enum):
97 """Units for sound measurements."""
99 DECIBELS_SPL = "dB SPL"
102class LengthUnit(Enum):
103 """Units for length measurements."""
105 MILLIMETERS = "mm"
106 METERS = "m"
107 INCHES = "'"
110class PhysicalUnit(Enum):
111 """Units for physical measurements."""
113 TESLA = "T"
116class SpecialValueType(Enum):
117 """Standard Bluetooth SIG special value categories.
119 These represent sentinel values in characteristic data that indicate
120 the measurement is not a normal reading. GSS YAML files define these
121 using patterns like "value is not known" or "value is not valid".
122 """
124 UNKNOWN = "unknown" # Value not known/not available (most common)
125 INVALID = "invalid" # Value not valid for current state
126 OVERFLOW = "overflow" # Value exceeds maximum representable
127 UNDERFLOW = "underflow" # Value below minimum representable
128 OUT_OF_RANGE = "out_of_range" # Sensor out of measurement range
131def classify_special_value(meaning: str) -> SpecialValueType:
132 """Classify a GSS meaning string into a standard category.
134 Parses the human-readable meaning from GSS YAML special value definitions
135 and maps it to a SpecialValueType enum.
137 Args:
138 meaning: Human-readable meaning from GSS (e.g., "value is not known")
140 Returns:
141 The appropriate SpecialValueType category.
142 """
143 meaning_lower = meaning.lower()
145 if "not known" in meaning_lower or "unknown" in meaning_lower:
146 return SpecialValueType.UNKNOWN
147 if "not valid" in meaning_lower or "invalid" in meaning_lower:
148 return SpecialValueType.INVALID
149 if "or greater" in meaning_lower or "overflow" in meaning_lower:
150 return SpecialValueType.OVERFLOW
151 if "less than" in meaning_lower or "underflow" in meaning_lower:
152 return SpecialValueType.UNDERFLOW
153 if "out of range" in meaning_lower:
154 return SpecialValueType.OUT_OF_RANGE
156 # Default fallback - most special values indicate unknown
157 return SpecialValueType.UNKNOWN