Coverage for src/bluetooth_sig/gatt/characteristics/electric_current_specification.py: 91%
32 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"""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
13class ElectricCurrentSpecificationData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
14 """Data class for electric current specification."""
16 minimum: float # Minimum current in Amperes
17 maximum: float # Maximum current in Amperes
19 def __post_init__(self) -> None:
20 """Validate current specification data."""
21 if self.minimum > self.maximum:
22 raise ValueError(f"Minimum current {self.minimum} A cannot be greater than maximum {self.maximum} A")
24 # Validate range for uint16 with 0.01 A resolution (0 to 655.35 A)
25 max_current_value = UINT16_MAX * 0.01
26 if not 0.0 <= self.minimum <= max_current_value:
27 raise ValueError(f"Minimum current {self.minimum} A is outside valid range (0.0 to {max_current_value} A)")
28 if not 0.0 <= self.maximum <= max_current_value:
29 raise ValueError(f"Maximum current {self.maximum} A is outside valid range (0.0 to {max_current_value} A)")
32class ElectricCurrentSpecificationCharacteristic(BaseCharacteristic):
33 """Electric Current Specification characteristic (0x2AF0).
35 org.bluetooth.characteristic.electric_current_specification
37 Electric Current Specification characteristic.
39 Specifies minimum and maximum current values for electrical
40 specifications.
41 """
43 # Override since decode_value returns structured ElectricCurrentSpecificationData
44 _manual_value_type: ValueType | str | None = ValueType.DICT
46 def decode_value(
47 self, data: bytearray, _ctx: CharacteristicContext | None = None
48 ) -> ElectricCurrentSpecificationData:
49 """Parse current specification data (2x uint16 in units of 0.01 A).
51 Args:
52 data: Raw bytes from the characteristic read
54 Returns:
55 ElectricCurrentSpecificationData with 'minimum' and 'maximum' current specification values in Amperes
57 Raises:
58 ValueError: If data is insufficient
60 """
61 if len(data) < 4:
62 raise ValueError("Electric current specification data must be at least 4 bytes")
64 # Convert 2x uint16 (little endian) to current specification in Amperes
65 min_current_raw = int.from_bytes(data[:2], byteorder="little", signed=False)
66 max_current_raw = int.from_bytes(data[2:4], byteorder="little", signed=False)
68 return ElectricCurrentSpecificationData(minimum=min_current_raw * 0.01, maximum=max_current_raw * 0.01)
70 def encode_value(self, data: ElectricCurrentSpecificationData) -> bytearray:
71 """Encode electric current specification value back to bytes.
73 Args:
74 data: ElectricCurrentSpecificationData instance
76 Returns:
77 Encoded bytes representing the current specification (2x uint16, 0.01 A resolution)
79 """
80 # Convert Amperes to raw values (multiply by 100 for 0.01 A resolution)
81 min_current_raw = round(data.minimum * 100)
82 max_current_raw = round(data.maximum * 100)
84 # Encode as 2 uint16 values (little endian)
85 result = bytearray()
86 result.extend(min_current_raw.to_bytes(2, byteorder="little", signed=False))
87 result.extend(max_current_raw.to_bytes(2, byteorder="little", signed=False))
89 return result