Coverage for src / bluetooth_sig / gatt / characteristics / boot_keyboard_input_report.py: 100%
37 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"""Boot Keyboard Input Report characteristic implementation."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ...types.gatt_enums import CharacteristicRole
10from ..constants import SIZE_UINT16
11from ..context import CharacteristicContext
12from .base import BaseCharacteristic
15class KeyboardModifiers(IntFlag):
16 """Keyboard modifier keys bitmap."""
18 LEFT_CTRL = 0x01
19 LEFT_SHIFT = 0x02
20 LEFT_ALT = 0x04
21 LEFT_GUI = 0x08
22 RIGHT_CTRL = 0x10
23 RIGHT_SHIFT = 0x20
24 RIGHT_ALT = 0x40
25 RIGHT_GUI = 0x80
28class BootKeyboardInputReportData(msgspec.Struct, frozen=True, kw_only=True):
29 """Boot keyboard input report data.
31 Attributes:
32 modifiers: Modifier keys state bitmap
33 reserved: Reserved byte (always 0)
34 keycodes: Up to 6 simultaneous key codes (HID usage IDs)
35 """
37 modifiers: KeyboardModifiers
38 reserved: int
39 keycodes: tuple[int, ...]
42class BootKeyboardInputReportCharacteristic(BaseCharacteristic[BootKeyboardInputReportData]):
43 """Boot Keyboard Input Report characteristic (0x2A22).
45 org.bluetooth.characteristic.boot_keyboard_input_report
47 Contains keyboard input report data in boot mode following USB HID boot protocol.
48 Format: 1-8 bytes - modifier(1) + [reserved(1) + keycodes(0-6)].
50 Spec Reference:
51 USB HID Specification v1.11, Appendix B - Boot Interface Descriptors
52 """
54 _manual_role = CharacteristicRole.INFO
55 min_length = 1
56 max_length = 8
57 allow_variable_length = True
59 def _decode_value(
60 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
61 ) -> BootKeyboardInputReportData:
62 """Parse HID keyboard report.
64 Args:
65 data: Raw bytearray from BLE characteristic (1-8 bytes).
66 ctx: Optional CharacteristicContext.
67 validate: Whether to validate ranges (default True)
69 Returns:
70 BootKeyboardInputReportData with parsed keyboard state.
71 """
72 modifiers = KeyboardModifiers(data[0])
73 reserved = data[1] if len(data) >= SIZE_UINT16 else 0
74 keycodes = tuple(data[i] for i in range(2, len(data)))
76 return BootKeyboardInputReportData(
77 modifiers=modifiers,
78 reserved=reserved,
79 keycodes=keycodes,
80 )
82 def _encode_value(self, data: BootKeyboardInputReportData) -> bytearray:
83 """Encode keyboard report to bytes.
85 Args:
86 data: BootKeyboardInputReportData to encode
88 validate: Whether to validate ranges (default True)
90 Returns:
91 Encoded bytes (8 bytes total)
92 """
93 result = bytearray(8)
94 result[0] = int(data.modifiers)
95 result[1] = data.reserved
97 # Encode up to 6 keycodes
98 for i, keycode in enumerate(data.keycodes[:6]):
99 result[2 + i] = keycode
101 return result