Coverage for src / bluetooth_sig / gatt / characteristics / electric_current_specification.py: 88%
34 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"""Electric Current Specification characteristic implementation."""
3from __future__ import annotations
5import msgspec
7from ...types.gatt_enums import ValueType
8from ..constants import UINT16_MAX
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class ElectricCurrentSpecificationData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
15 """Data class for electric current specification."""
17 minimum: float # Minimum current in Amperes
18 maximum: float # Maximum current in Amperes
20 def __post_init__(self) -> None:
21 """Validate current specification data."""
22 if self.minimum > self.maximum:
23 raise ValueError(f"Minimum current {self.minimum} A cannot be greater than maximum {self.maximum} A")
25 # Validate range for uint16 with 0.01 A resolution (0 to 655.35 A)
26 max_current_value = UINT16_MAX * 0.01
27 if not 0.0 <= self.minimum <= max_current_value:
28 raise ValueError(f"Minimum current {self.minimum} A is outside valid range (0.0 to {max_current_value} A)")
29 if not 0.0 <= self.maximum <= max_current_value:
30 raise ValueError(f"Maximum current {self.maximum} A is outside valid range (0.0 to {max_current_value} A)")
33class ElectricCurrentSpecificationCharacteristic(BaseCharacteristic[ElectricCurrentSpecificationData]):
34 """Electric Current Specification characteristic (0x2AF0).
36 org.bluetooth.characteristic.electric_current_specification
38 Electric Current Specification characteristic.
40 Specifies minimum and maximum current values for electrical
41 specifications.
42 """
44 # Validation attributes
45 expected_length: int = 4 # 2x uint16
47 # Override since decode_value returns structured ElectricCurrentSpecificationData
48 _manual_value_type: ValueType | str | None = ValueType.DICT
50 def _decode_value(
51 self, data: bytearray, _ctx: CharacteristicContext | None = None
52 ) -> ElectricCurrentSpecificationData:
53 """Parse current specification data (2x uint16 in units of 0.01 A).
55 Args:
56 data: Raw bytes from the characteristic read
58 Returns:
59 ElectricCurrentSpecificationData with 'minimum' and 'maximum' current specification values in Amperes
61 Raises:
62 ValueError: If data is insufficient
64 """
65 if len(data) < 4:
66 raise ValueError("Electric current specification data must be at least 4 bytes")
68 # Convert 2x uint16 (little endian) to current specification in Amperes
69 min_current_raw = DataParser.parse_int16(data, 0, signed=False)
70 max_current_raw = DataParser.parse_int16(data, 2, signed=False)
72 return ElectricCurrentSpecificationData(minimum=min_current_raw * 0.01, maximum=max_current_raw * 0.01)
74 def _encode_value(self, data: ElectricCurrentSpecificationData) -> bytearray:
75 """Encode electric current specification value back to bytes.
77 Args:
78 data: ElectricCurrentSpecificationData instance
80 Returns:
81 Encoded bytes representing the current specification (2x uint16, 0.01 A resolution)
83 """
84 # Convert Amperes to raw values (multiply by 100 for 0.01 A resolution)
85 min_current_raw = round(data.minimum * 100)
86 max_current_raw = round(data.maximum * 100)
88 # Encode as 2 uint16 values (little endian)
89 result = bytearray()
90 result.extend(DataParser.encode_int16(min_current_raw, signed=False))
91 result.extend(DataParser.encode_int16(max_current_raw, signed=False))
93 return result