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

34 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 20:14 +0000

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

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11from .utils import DataParser 

12 

13 

14class MouseButtons(IntFlag): 

15 """Mouse button states bitmap.""" 

16 

17 LEFT = 0x01 

18 RIGHT = 0x02 

19 MIDDLE = 0x04 

20 

21 

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

23 """Boot mouse input report data. 

24 

25 Attributes: 

26 buttons: Mouse button states bitmap 

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

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

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

30 """ 

31 

32 buttons: MouseButtons 

33 x_displacement: int 

34 y_displacement: int 

35 wheel: int | None = None 

36 

37 

38class BootMouseInputReportCharacteristic(BaseCharacteristic[BootMouseInputReportData]): 

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

40 

41 org.bluetooth.characteristic.boot_mouse_input_report 

42 

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

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

45 

46 Spec Reference: 

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

48 """ 

49 

50 _manual_value_type = "BootMouseInputReportData" 

51 

52 min_length = 3 

53 max_length = 4 

54 allow_variable_length = True 

55 

56 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> BootMouseInputReportData: 

57 """Parse HID mouse report. 

58 

59 Args: 

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

61 ctx: Optional CharacteristicContext. 

62 

63 Returns: 

64 BootMouseInputReportData with parsed mouse state. 

65 """ 

66 buttons = MouseButtons(data[0]) 

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

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

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

70 

71 return BootMouseInputReportData( 

72 buttons=buttons, 

73 x_displacement=x_displacement, 

74 y_displacement=y_displacement, 

75 wheel=wheel, 

76 ) 

77 

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

79 """Encode mouse report to bytes. 

80 

81 Args: 

82 data: BootMouseInputReportData to encode 

83 

84 Returns: 

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

86 """ 

87 result = bytearray() 

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

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

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

91 

92 if data.wheel is not None: 

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

94 

95 return result