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
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
1"""Boot Mouse Input Report characteristic implementation."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class MouseButtons(IntFlag):
15 """Mouse button states bitmap."""
17 LEFT = 0x01
18 RIGHT = 0x02
19 MIDDLE = 0x04
22class BootMouseInputReportData(msgspec.Struct, frozen=True, kw_only=True):
23 """Boot mouse input report data.
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 """
32 buttons: MouseButtons
33 x_displacement: int
34 y_displacement: int
35 wheel: int | None = None
38class BootMouseInputReportCharacteristic(BaseCharacteristic[BootMouseInputReportData]):
39 """Boot Mouse Input Report characteristic (0x2A33).
41 org.bluetooth.characteristic.boot_mouse_input_report
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)].
46 Spec Reference:
47 USB HID Specification v1.11, Appendix B - Boot Interface Descriptors
48 """
50 _manual_value_type = "BootMouseInputReportData"
52 min_length = 3
53 max_length = 4
54 allow_variable_length = True
56 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> BootMouseInputReportData:
57 """Parse HID mouse report.
59 Args:
60 data: Raw bytearray from BLE characteristic (3-4 bytes).
61 ctx: Optional CharacteristicContext.
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
71 return BootMouseInputReportData(
72 buttons=buttons,
73 x_displacement=x_displacement,
74 y_displacement=y_displacement,
75 wheel=wheel,
76 )
78 def _encode_value(self, data: BootMouseInputReportData) -> bytearray:
79 """Encode mouse report to bytes.
81 Args:
82 data: BootMouseInputReportData to encode
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))
92 if data.wheel is not None:
93 result.extend(DataParser.encode_int8(data.wheel, signed=True))
95 return result