diff --git a/adafruit_hid/__init__.py b/adafruit_hid/__init__.py index 532d31a..f4d8f76 100644 --- a/adafruit_hid/__init__.py +++ b/adafruit_hid/__init__.py @@ -19,6 +19,12 @@ # imports from __future__ import annotations +import time + +try: + import supervisor +except ImportError: + supervisor = None try: from typing import Sequence @@ -31,17 +37,46 @@ def find_device( - devices: Sequence[usb_hid.Device], *, usage_page: int, usage: int + devices: Sequence[usb_hid.Device], + *, + usage_page: int, + usage: int, + timeout: int = None, ) -> usb_hid.Device: """Search through the provided sequence of devices to find the one with the matching - usage_page and usage.""" + usage_page and usage. + + :param timeout: Time in seconds to wait for USB to become ready before timing out. + Defaults to None to wait indefinitely.""" + if hasattr(devices, "send_report"): devices = [devices] # type: ignore - for device in devices: + device = None + for dev in devices: if ( - device.usage_page == usage_page - and device.usage == usage - and hasattr(device, "send_report") + dev.usage_page == usage_page + and dev.usage == usage + and hasattr(dev, "send_report") ): - return device - raise ValueError("Could not find matching HID device.") + device = dev + break + if device is None: + raise ValueError("Could not find matching HID device.") + + if supervisor is None: + # Blinka doesn't have supervisor (see issue Adafruit_Blinka#711), so wait + # one second for USB to become ready + time.sleep(1.0) + elif timeout is None: + # default behavior: wait indefinitely for USB to become ready + while not supervisor.runtime.usb_connected: + time.sleep(1.0) + else: + # wait up to timeout seconds for USB to become ready + for _ in range(timeout): + if supervisor.runtime.usb_connected: + return device + time.sleep(1.0) + raise OSError("Failed to initialize HID device. Is USB connected?") + + return device diff --git a/adafruit_hid/consumer_control.py b/adafruit_hid/consumer_control.py index d26f116..e501040 100644 --- a/adafruit_hid/consumer_control.py +++ b/adafruit_hid/consumer_control.py @@ -18,7 +18,6 @@ # pylint: disable=wrong-import-position import struct -import time from . import find_device try: @@ -31,26 +30,23 @@ class ConsumerControl: """Send ConsumerControl code reports, used by multimedia keyboards, remote controls, etc.""" - def __init__(self, devices: Sequence[usb_hid.Device]) -> None: + def __init__(self, devices: Sequence[usb_hid.Device], timeout: int = None) -> None: """Create a ConsumerControl object that will send Consumer Control Device HID reports. + :param timeout: Time in seconds to wait for USB to become ready before timing out. + Defaults to None to wait indefinitely. + Devices can be a sequence of devices that includes a Consumer Control device or a CC device itself. A device is any object that implements ``send_report()``, ``usage_page`` and ``usage``. """ - self._consumer_device = find_device(devices, usage_page=0x0C, usage=0x01) + self._consumer_device = find_device( + devices, usage_page=0x0C, usage=0x01, timeout=timeout + ) # Reuse this bytearray to send consumer reports. self._report = bytearray(2) - # Do a no-op to test if HID device is ready. - # If not, wait a bit and try once more. - try: - self.send(0x0) - except OSError: - time.sleep(1) - self.send(0x0) - def send(self, consumer_code: int) -> None: """Send a report to do the specified consumer control action, and then stop the action (so it will not repeat). diff --git a/adafruit_hid/keyboard.py b/adafruit_hid/keyboard.py index c8bbbab..282d545 100644 --- a/adafruit_hid/keyboard.py +++ b/adafruit_hid/keyboard.py @@ -9,7 +9,6 @@ * Author(s): Scott Shawcroft, Dan Halbert """ -import time from micropython import const import usb_hid @@ -39,14 +38,19 @@ class Keyboard: # No more than _MAX_KEYPRESSES regular keys may be pressed at once. - def __init__(self, devices: Sequence[usb_hid.Device]) -> None: + def __init__(self, devices: Sequence[usb_hid.Device], timeout: int = None) -> None: """Create a Keyboard object that will send keyboard HID reports. + :param timeout: Time in seconds to wait for USB to become ready before timing out. + Defaults to None to wait indefinitely. + 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) + self._keyboard_device = find_device( + devices, usage_page=0x1, usage=0x06, timeout=timeout + ) # Reuse this bytearray to send keyboard reports. self.report = bytearray(8) @@ -65,14 +69,6 @@ def __init__(self, devices: Sequence[usb_hid.Device]) -> None: # No keyboard LEDs on. self._led_status = b"\x00" - # 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. diff --git a/adafruit_hid/mouse.py b/adafruit_hid/mouse.py index 3b324db..014b778 100644 --- a/adafruit_hid/mouse.py +++ b/adafruit_hid/mouse.py @@ -8,8 +8,6 @@ * Author(s): Dan Halbert """ -import time - from . import find_device try: @@ -29,14 +27,19 @@ class Mouse: MIDDLE_BUTTON = 4 """Middle mouse button.""" - def __init__(self, devices: Sequence[usb_hid.Device]): + def __init__(self, devices: Sequence[usb_hid.Device], timeout: int = None) -> None: """Create a Mouse object that will send USB mouse HID reports. + :param timeout: Time in seconds to wait for USB to become ready before timing out. + Defaults to None to wait indefinitely. + 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._mouse_device = find_device(devices, usage_page=0x1, usage=0x02) + self._mouse_device = find_device( + devices, usage_page=0x1, usage=0x02, timeout=timeout + ) # Reuse this bytearray to send mouse reports. # report[0] buttons pressed (LEFT, MIDDLE, RIGHT) @@ -45,14 +48,6 @@ def __init__(self, devices: Sequence[usb_hid.Device]): # report[3] wheel movement self.report = bytearray(4) - # Do a no-op to test if HID device is ready. - # If not, wait a bit and try once more. - try: - self._send_no_move() - except OSError: - time.sleep(1) - self._send_no_move() - def press(self, buttons: int) -> None: """Press the given mouse buttons.