Coverage for src / bluetooth_sig / gatt / characteristics / boot_mouse_input_report.py: 97%

36 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-18 11:17 +0000

1"""Boot Mouse Input Report characteristic implementation.""" 

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9from ...types.gatt_enums import CharacteristicRole 

10from ..constants import SIZE_UINT32 

11from ..context import CharacteristicContext 

12from .base import BaseCharacteristic 

13from .utils import DataParser 

14 

15 

16class MouseButtons(IntFlag): 

17 """Mouse button states bitmap.""" 

18 

19 LEFT = 0x01 

20 RIGHT = 0x02 

21 MIDDLE = 0x04 

22 

23 

24class BootMouseInputReportData(msgspec.Struct, frozen=True, kw_only=True): 

25 """Boot mouse input report data. 

26 

27 Attributes: 

28 buttons: Mouse button states bitmap 

29 x_displacement: Horizontal movement (signed, -127 to 127) 

30 y_displacement: Vertical movement (signed, -127 to 127) 

31 wheel: Optional scroll wheel movement (signed, -127 to 127) 

32 """ 

33 

34 buttons: MouseButtons 

35 x_displacement: int 

36 y_displacement: int 

37 wheel: int | None = None 

38 

39 

40class BootMouseInputReportCharacteristic(BaseCharacteristic[BootMouseInputReportData]): 

41 """Boot Mouse Input Report characteristic (0x2A33). 

42 

43 org.bluetooth.characteristic.boot_mouse_input_report 

44 

45 Contains mouse input report data in boot mode following USB HID boot protocol. 

46 Format: 3-4 bytes - buttons(1) + X(1) + Y(1) + [wheel(1)]. 

47 

48 Spec Reference: 

49 USB HID Specification v1.11, Appendix B - Boot Interface Descriptors 

50 """ 

51 

52 _manual_role = CharacteristicRole.INFO 

53 min_length = 3 

54 max_length = 4 

55 allow_variable_length = True 

56 

57 def _decode_value( 

58 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True 

59 ) -> BootMouseInputReportData: 

60 """Parse HID mouse report. 

61 

62 Args: 

63 data: Raw bytearray from BLE characteristic (3-4 bytes). 

64 ctx: Optional CharacteristicContext. 

65 validate: Whether to validate ranges (default True) 

66 

67 Returns: 

68 BootMouseInputReportData with parsed mouse state. 

69 """ 

70 buttons = MouseButtons(data[0]) 

71 x_displacement = DataParser.parse_int8(data, 1, signed=True) 

72 y_displacement = DataParser.parse_int8(data, 2, signed=True) 

73 wheel = DataParser.parse_int8(data, 3, signed=True) if len(data) == SIZE_UINT32 else None 

74 

75 return BootMouseInputReportData( 

76 buttons=buttons, 

77 x_displacement=x_displacement, 

78 y_displacement=y_displacement, 

79 wheel=wheel, 

80 ) 

81 

82 def _encode_value(self, data: BootMouseInputReportData) -> bytearray: 

83 """Encode mouse report to bytes. 

84 

85 Args: 

86 data: BootMouseInputReportData to encode 

87 

88 validate: Whether to validate ranges (default True) 

89 

90 Returns: 

91 Encoded bytes (3-4 bytes depending on wheel presence) 

92 """ 

93 result = bytearray() 

94 result.append(int(data.buttons)) 

95 result.extend(DataParser.encode_int8(data.x_displacement, signed=True)) 

96 result.extend(DataParser.encode_int8(data.y_displacement, signed=True)) 

97 

98 if data.wheel is not None: 

99 result.extend(DataParser.encode_int8(data.wheel, signed=True)) 

100 

101 return result