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

1"""Pushbutton Status 8 characteristic (0x2C21). 

2 

3Represents the status of up to 4 pushbuttons packed into a single byte. 

4Each button occupies 2 bits, yielding four independent status values. 

5""" 

6 

7from __future__ import annotations 

8 

9from enum import IntEnum 

10 

11import msgspec 

12 

13from ..context import CharacteristicContext 

14from .base import BaseCharacteristic 

15from .utils.data_parser import DataParser 

16 

17_BUTTON_MASK = 0x03 

18 

19 

20class ButtonStatus(IntEnum): 

21 """Status of an individual pushbutton. 

22 

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 """ 

29 

30 NOT_ACTUATED = 0 

31 PRESSED = 1 

32 RELEASED = 2 

33 RESERVED = 3 

34 

35 

36class PushbuttonStatus8Data(msgspec.Struct, frozen=True, kw_only=True): 

37 """Decoded pushbutton status for four buttons.""" 

38 

39 button_0: ButtonStatus 

40 button_1: ButtonStatus 

41 button_2: ButtonStatus 

42 button_3: ButtonStatus 

43 

44 

45class PushbuttonStatus8Characteristic(BaseCharacteristic[PushbuttonStatus8Data]): 

46 """Pushbutton Status 8 characteristic (0x2C21). 

47 

48 org.bluetooth.characteristic.pushbutton_status_8 

49 

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 """ 

53 

54 expected_length = 1 

55 

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) 

65 

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 ) 

72 

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)