Coverage for src / bluetooth_sig / gatt / characteristics / supported_power_range.py: 82%
38 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"""Supported Power Range characteristic implementation."""
3from __future__ import annotations
5import msgspec
7from ...types.gatt_enums import ValueType
8from ..constants import SINT16_MAX, SINT16_MIN
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class SupportedPowerRangeData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
15 """Data class for supported power range."""
17 minimum: int # Minimum power in Watts
18 maximum: int # Maximum power in Watts
20 def __post_init__(self) -> None:
21 """Validate power range data."""
22 if self.minimum > self.maximum:
23 raise ValueError(f"Minimum power {self.minimum} W cannot be greater than maximum {self.maximum} W")
25 # Validate range for sint16 (SINT16_MIN to SINT16_MAX)
26 if not SINT16_MIN <= self.minimum <= SINT16_MAX:
27 raise ValueError(f"Minimum power {self.minimum} W is outside valid range (SINT16_MIN to SINT16_MAX W)")
28 if not SINT16_MIN <= self.maximum <= SINT16_MAX:
29 raise ValueError(f"Maximum power {self.maximum} W is outside valid range (SINT16_MIN to SINT16_MAX W)")
32class SupportedPowerRangeCharacteristic(BaseCharacteristic[SupportedPowerRangeData]):
33 """Supported Power Range characteristic (0x2AD8).
35 org.bluetooth.characteristic.supported_power_range
37 Supported Power Range characteristic.
39 Specifies minimum and maximum power values for power capability
40 specification.
41 """
43 min_length = 4
44 _characteristic_name: str = "Supported Power Range"
45 # Override since decode_value returns structured SupportedPowerRangeData
46 _manual_value_type: ValueType | str | None = ValueType.DICT
48 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> SupportedPowerRangeData:
49 """Parse supported power range data (2x sint16 in watts).
51 Args:
52 data: Raw bytes from the characteristic read.
53 ctx: Optional CharacteristicContext providing surrounding context (may be None).
55 Returns:
56 SupportedPowerRangeData with minimum and maximum power values in Watts.
58 Raises:
59 ValueError: If data is insufficient.
61 """
62 if len(data) < 4:
63 raise ValueError("Supported power range data must be at least 4 bytes")
65 # Convert 2x sint16 (little endian) to power range in Watts
66 min_power_raw = DataParser.parse_int16(data, 0, signed=True)
67 max_power_raw = DataParser.parse_int16(data, 2, signed=True)
69 return SupportedPowerRangeData(minimum=min_power_raw, maximum=max_power_raw)
71 def _encode_value(self, data: SupportedPowerRangeData) -> bytearray:
72 """Encode supported power range value back to bytes.
74 Args:
75 data: SupportedPowerRangeData instance with 'minimum' and 'maximum' power values in Watts
77 Returns:
78 Encoded bytes representing the power range (2x sint16)
80 """
81 if not isinstance(data, SupportedPowerRangeData):
82 raise TypeError(f"Supported power range data must be a SupportedPowerRangeData, got {type(data).__name__}")
84 # Validate range for sint16 (SINT16_MIN to SINT16_MAX)
85 if not SINT16_MIN <= data.minimum <= SINT16_MAX:
86 raise ValueError(f"Minimum power {data.minimum} exceeds sint16 range")
87 if not SINT16_MIN <= data.maximum <= SINT16_MAX:
88 raise ValueError(f"Maximum power {data.maximum} exceeds sint16 range")
89 # Encode as 2 sint16 values (little endian)
90 result = bytearray()
91 result.extend(DataParser.encode_int16(data.minimum, signed=True))
92 result.extend(DataParser.encode_int16(data.maximum, signed=True))
94 return result