Coverage for src / bluetooth_sig / gatt / characteristics / boot_keyboard_input_report.py: 100%
35 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 Keyboard Input Report characteristic implementation."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
13class KeyboardModifiers(IntFlag):
14 """Keyboard modifier keys bitmap."""
16 LEFT_CTRL = 0x01
17 LEFT_SHIFT = 0x02
18 LEFT_ALT = 0x04
19 LEFT_GUI = 0x08
20 RIGHT_CTRL = 0x10
21 RIGHT_SHIFT = 0x20
22 RIGHT_ALT = 0x40
23 RIGHT_GUI = 0x80
26class BootKeyboardInputReportData(msgspec.Struct, frozen=True, kw_only=True):
27 """Boot keyboard input report data.
29 Attributes:
30 modifiers: Modifier keys state bitmap
31 reserved: Reserved byte (always 0)
32 keycodes: Up to 6 simultaneous key codes (HID usage IDs)
33 """
35 modifiers: KeyboardModifiers
36 reserved: int
37 keycodes: tuple[int, ...]
40class BootKeyboardInputReportCharacteristic(BaseCharacteristic[BootKeyboardInputReportData]):
41 """Boot Keyboard Input Report characteristic (0x2A22).
43 org.bluetooth.characteristic.boot_keyboard_input_report
45 Contains keyboard input report data in boot mode following USB HID boot protocol.
46 Format: 1-8 bytes - modifier(1) + [reserved(1) + keycodes(0-6)].
48 Spec Reference:
49 USB HID Specification v1.11, Appendix B - Boot Interface Descriptors
50 """
52 _manual_value_type = "BootKeyboardInputReportData"
54 min_length = 1
55 max_length = 8
56 allow_variable_length = True
58 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> BootKeyboardInputReportData:
59 """Parse HID keyboard report.
61 Args:
62 data: Raw bytearray from BLE characteristic (1-8 bytes).
63 ctx: Optional CharacteristicContext.
65 Returns:
66 BootKeyboardInputReportData with parsed keyboard state.
67 """
68 modifiers = KeyboardModifiers(data[0])
69 reserved = data[1] if len(data) >= 2 else 0
70 keycodes = tuple(data[i] for i in range(2, len(data)))
72 return BootKeyboardInputReportData(
73 modifiers=modifiers,
74 reserved=reserved,
75 keycodes=keycodes,
76 )
78 def _encode_value(self, data: BootKeyboardInputReportData) -> bytearray:
79 """Encode keyboard report to bytes.
81 Args:
82 data: BootKeyboardInputReportData to encode
84 Returns:
85 Encoded bytes (8 bytes total)
86 """
87 result = bytearray(8)
88 result[0] = int(data.modifiers)
89 result[1] = data.reserved
91 # Encode up to 6 keycodes
92 for i, keycode in enumerate(data.keycodes[:6]):
93 result[2 + i] = keycode
95 return result