Coverage for src / bluetooth_sig / gatt / characteristics / esl_control_point.py: 100%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""ESL Control Point characteristic implementation."""
3from __future__ import annotations
5from enum import IntEnum
7import msgspec
9from ...types.gatt_enums import CharacteristicRole
10from ..context import CharacteristicContext
11from .base import BaseCharacteristic
12from .utils import DataParser
15class ESLCPOpcode(IntEnum):
16 """ESL Control Point opcodes per ESL specification."""
18 PING = 0x00
19 UNASSOCIATE = 0x01
20 SERVICE_RESET = 0x02
21 FACTORY_RESET = 0x03
22 UPDATE_COMPLETE = 0x04
23 READ_SENSOR_DATA = 0x10
24 REFRESH_DISPLAY = 0x11
25 DISPLAY_IMAGE = 0x20
26 DISPLAY_TIMED_IMAGE = 0x21
27 LED_CONTROL = 0x30
28 LED_TIMED_CONTROL = 0x31
29 VENDOR_SPECIFIC = 0xFF
32class ESLCPData(msgspec.Struct, frozen=True, kw_only=True):
33 """Parsed data from ESL Control Point characteristic.
35 Attributes:
36 opcode: The ESL CP opcode.
37 parameters: Raw parameter bytes (opcode-dependent).
39 """
41 opcode: ESLCPOpcode
42 parameters: bytes = b""
45class ESLControlPointCharacteristic(BaseCharacteristic[ESLCPData]):
46 """ESL Control Point characteristic (0x2BFE).
48 org.bluetooth.characteristic.esl_control_point
50 Control point for Electronic Shelf Label operations.
51 Complex multi-opcode characteristic supporting various ESL commands.
52 """
54 _manual_role = CharacteristicRole.CONTROL
55 min_length: int = 1 # At minimum: opcode (1 byte)
56 allow_variable_length: bool = True
58 def _decode_value(
59 self,
60 data: bytearray,
61 ctx: CharacteristicContext | None = None,
62 *,
63 validate: bool = True,
64 ) -> ESLCPData:
65 """Parse ESL CP data.
67 Args:
68 data: Raw bytes (1+ bytes).
69 ctx: Optional CharacteristicContext.
70 validate: Whether to validate ranges (default True).
72 Returns:
73 ESLCPData with opcode and parameters.
75 """
76 opcode = ESLCPOpcode(DataParser.parse_int8(data, 0, signed=False))
77 parameters = bytes(data[1:])
78 return ESLCPData(opcode=opcode, parameters=parameters)
80 def _encode_value(self, data: ESLCPData) -> bytearray:
81 """Encode ESL CP data to bytes.
83 Args:
84 data: ESLCPData to encode.
86 Returns:
87 Encoded bytes.
89 """
90 result = DataParser.encode_int8(int(data.opcode), signed=False)
91 result.extend(data.parameters)
92 return result