Coverage for src / bluetooth_sig / gatt / characteristics / pushbutton_status_8.py: 100%
25 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"""Pushbutton Status 8 characteristic (0x2C21).
3Represents the status of up to 4 pushbuttons packed into a single byte.
4Each button occupies 2 bits, yielding four independent status values.
5"""
7from __future__ import annotations
9from enum import IntEnum
11import msgspec
13from ..context import CharacteristicContext
14from .base import BaseCharacteristic
15from .utils.data_parser import DataParser
17_BUTTON_MASK = 0x03
20class ButtonStatus(IntEnum):
21 """Status of an individual pushbutton.
23 Values:
24 NOT_ACTUATED: Button not actuated or not in use (0)
25 PRESSED: Button pressed (1)
26 RELEASED: Button released (2)
27 RESERVED: Reserved for future use (3)
28 """
30 NOT_ACTUATED = 0
31 PRESSED = 1
32 RELEASED = 2
33 RESERVED = 3
36class PushbuttonStatus8Data(msgspec.Struct, frozen=True, kw_only=True):
37 """Decoded pushbutton status for four buttons."""
39 button_0: ButtonStatus
40 button_1: ButtonStatus
41 button_2: ButtonStatus
42 button_3: ButtonStatus
45class PushbuttonStatus8Characteristic(BaseCharacteristic[PushbuttonStatus8Data]):
46 """Pushbutton Status 8 characteristic (0x2C21).
48 org.bluetooth.characteristic.pushbutton_status_8
50 Four independent 2-bit button status fields packed into a single byte.
51 Bits [1:0] → Button 0, [3:2] → Button 1, [5:4] → Button 2, [7:6] → Button 3.
52 """
54 expected_length = 1
56 def _decode_value(
57 self,
58 data: bytearray,
59 ctx: CharacteristicContext | None = None,
60 *,
61 validate: bool = True,
62 ) -> PushbuttonStatus8Data:
63 """Decode four 2-bit button status fields from a single byte."""
64 raw = DataParser.parse_int8(data, 0, signed=False)
66 return PushbuttonStatus8Data(
67 button_0=ButtonStatus((raw >> 0) & _BUTTON_MASK),
68 button_1=ButtonStatus((raw >> 2) & _BUTTON_MASK),
69 button_2=ButtonStatus((raw >> 4) & _BUTTON_MASK),
70 button_3=ButtonStatus((raw >> 6) & _BUTTON_MASK),
71 )
73 def _encode_value(self, data: PushbuttonStatus8Data) -> bytearray:
74 """Encode four button statuses into a single byte."""
75 encoded = (
76 (data.button_0 & _BUTTON_MASK)
77 | ((data.button_1 & _BUTTON_MASK) << 2)
78 | ((data.button_2 & _BUTTON_MASK) << 4)
79 | ((data.button_3 & _BUTTON_MASK) << 6)
80 )
81 return DataParser.encode_int8(encoded, signed=False)