Coverage for src / bluetooth_sig / gatt / characteristics / luminous_flux_range.py: 94%
32 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"""Luminous Flux Range characteristic implementation."""
3from __future__ import annotations
5import msgspec
7from ..constants import UINT16_MAX
8from ..context import CharacteristicContext
9from .base import BaseCharacteristic
10from .utils import DataParser
13class LuminousFluxRangeData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
14 """Data class for luminous flux range.
16 Each value is a luminous flux in lumens (resolution 1 lm).
17 """
19 minimum: int # Minimum luminous flux in lumens
20 maximum: int # Maximum luminous flux in lumens
22 def __post_init__(self) -> None:
23 """Validate luminous flux range data."""
24 if self.minimum > self.maximum:
25 raise ValueError(
26 f"Minimum luminous flux {self.minimum} lm cannot be greater than maximum {self.maximum} lm"
27 )
28 for name, val in [("minimum", self.minimum), ("maximum", self.maximum)]:
29 if not 0 <= val <= UINT16_MAX:
30 raise ValueError(
31 f"{name.capitalize()} luminous flux {val} lm is outside valid range (0 to {UINT16_MAX})"
32 )
35class LuminousFluxRangeCharacteristic(BaseCharacteristic[LuminousFluxRangeData]):
36 """Luminous Flux Range characteristic (0x2B00).
38 org.bluetooth.characteristic.luminous_flux_range
40 Represents a luminous flux range as a pair of Luminous Flux values.
41 Each field is a uint16 with resolution 1 lumen.
42 """
44 # Validation attributes
45 expected_length: int = 4 # 2 x uint16
46 min_length: int = 4
48 def _decode_value(
49 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
50 ) -> LuminousFluxRangeData:
51 """Parse luminous flux range data (2 x uint16, resolution 1 lm).
53 Args:
54 data: Raw bytes from the characteristic read.
55 ctx: Optional CharacteristicContext (may be None).
56 validate: Whether to validate ranges (default True).
58 Returns:
59 LuminousFluxRangeData with minimum and maximum luminous flux in lumens.
61 """
62 min_raw = DataParser.parse_int16(data, 0, signed=False)
63 max_raw = DataParser.parse_int16(data, 2, signed=False)
65 return LuminousFluxRangeData(minimum=min_raw, maximum=max_raw)
67 def _encode_value(self, data: LuminousFluxRangeData) -> bytearray:
68 """Encode luminous flux range to bytes.
70 Args:
71 data: LuminousFluxRangeData instance.
73 Returns:
74 Encoded bytes (2 x uint16, little-endian).
76 """
77 if not isinstance(data, LuminousFluxRangeData):
78 raise TypeError(f"Expected LuminousFluxRangeData, got {type(data).__name__}")
80 for name, value in [("minimum", data.minimum), ("maximum", data.maximum)]:
81 if not 0 <= value <= UINT16_MAX:
82 raise ValueError(f"Luminous flux {name} value {value} exceeds uint16 range")
84 result = bytearray()
85 result.extend(DataParser.encode_int16(data.minimum, signed=False))
86 result.extend(DataParser.encode_int16(data.maximum, signed=False))
87 return result