1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
# SPDX-FileCopyrightText: 2017 Dan Halbert for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_hid.keyboard.Keyboard`
====================================================
* Author(s): Scott Shawcroft, Dan Halbert
"""
import time
from micropython import const
from .keycode import Keycode
from . import find_device
try:
from typing import Sequence
import usb_hid
except ImportError:
pass
_MAX_KEYPRESSES = const(6)
class Keyboard:
"""Send HID keyboard reports."""
LED_NUM_LOCK = 0x01
"""LED Usage ID for Num Lock"""
LED_CAPS_LOCK = 0x02
"""LED Usage ID for Caps Lock"""
LED_SCROLL_LOCK = 0x04
"""LED Usage ID for Scroll Lock"""
LED_COMPOSE = 0x08
"""LED Usage ID for Compose"""
# No more than _MAX_KEYPRESSES regular keys may be pressed at once.
def __init__(self, devices: Sequence[usb_hid.Device]) -> None:
"""Create a Keyboard object that will send keyboard HID reports.
Devices can be a sequence of devices that includes a keyboard device or a keyboard device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._keyboard_device = find_device(devices, usage_page=0x1, usage=0x06)
# Reuse this bytearray to send keyboard reports.
self.report = bytearray(8)
# report[0] modifiers
# report[1] unused
# report[2:8] regular key presses
# View onto byte 0 in report.
self.report_modifier = memoryview(self.report)[0:1]
# List of regular keys currently pressed.
# View onto bytes 2-7 in report.
self.report_keys = memoryview(self.report)[2:]
# Do a no-op to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self.release_all()
except OSError:
time.sleep(1)
self.release_all()
def press(self, *keycodes: int) -> None:
"""Send a report indicating that the given keys have been pressed.
:param keycodes: Press these keycodes all at once.
:raises ValueError: if more than six regular keys are pressed.
Keycodes may be modifiers or regular keys.
No more than six regular keys may be pressed simultaneously.
Examples::
from adafruit_hid.keycode import Keycode
# Press ctrl-x.
kbd.press(Keycode.LEFT_CONTROL, Keycode.X)
# Or, more conveniently, use the CONTROL alias for LEFT_CONTROL:
kbd.press(Keycode.CONTROL, Keycode.X)
# Press a, b, c keys all at once.
kbd.press(Keycode.A, Keycode.B, Keycode.C)
"""
for keycode in keycodes:
self._add_keycode_to_report(keycode)
self._keyboard_device.send_report(self.report)
def release(self, *keycodes: int) -> None:
"""Send a USB HID report indicating that the given keys have been released.
:param keycodes: Release these keycodes all at once.
If a keycode to be released was not pressed, it is ignored.
Example::
# release SHIFT key
kbd.release(Keycode.SHIFT)
"""
for keycode in keycodes:
self._remove_keycode_from_report(keycode)
self._keyboard_device.send_report(self.report)
def release_all(self) -> None:
"""Release all pressed keys."""
for i in range(8):
self.report[i] = 0
self._keyboard_device.send_report(self.report)
def send(self, *keycodes: int) -> None:
"""Press the given keycodes and then release all pressed keys.
:param keycodes: keycodes to send together
"""
self.press(*keycodes)
self.release_all()
def _add_keycode_to_report(self, keycode: int) -> None:
"""Add a single keycode to the USB HID report."""
modifier = Keycode.modifier_bit(keycode)
if modifier:
# Set bit for this modifier.
self.report_modifier[0] |= modifier
else:
# Don't press twice.
# (I'd like to use 'not in self.report_keys' here, but that's not implemented.)
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
# Already pressed.
return
# Put keycode in first empty slot.
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == 0:
self.report_keys[i] = keycode
return
# All slots are filled.
raise ValueError("Trying to press more than six keys at once.")
def _remove_keycode_from_report(self, keycode: int) -> None:
"""Remove a single keycode from the report."""
modifier = Keycode.modifier_bit(keycode)
if modifier:
# Turn off the bit for this modifier.
self.report_modifier[0] &= ~modifier
else:
# Check all the slots, just in case there's a duplicate. (There should not be.)
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
self.report_keys[i] = 0
@property
def led_status(self) -> bytes:
"""Returns the last received report"""
return self._keyboard_device.last_received_report
def led_on(self, led_code: int) -> bool:
"""Returns whether an LED is on based on the led code
Examples::
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import time
# Initialize Keybaord
kbd = Keyboard(usb_hid.devices)
# Press and release CapsLock.
kbd.press(Keycode.CAPS_LOCK)
time.sleep(.09)
kbd.release(Keycode.CAPS_LOCK)
# Check status of the LED_CAPS_LOCK
print(kbd.led_on(Keyboard.LED_CAPS_LOCK))
"""
return bool(self.led_status[0] & led_code)
|