171 lines
5.5 KiB
Python
171 lines
5.5 KiB
Python
from greendeck.lib.hidapi.library import DeviceInfo
|
|
from greendeck.lib.hidapi.library import Handle
|
|
from greendeck.lib.hidapi.library import hid_close_device
|
|
from greendeck.lib.hidapi.library import hid_enumerate
|
|
from greendeck.lib.hidapi.library import hid_get_feature_report
|
|
from greendeck.lib.hidapi.library import hid_open_device
|
|
from greendeck.lib.hidapi.library import hid_read
|
|
from greendeck.lib.hidapi.library import hid_send_feature_report
|
|
from greendeck.lib.hidapi.library import hid_write
|
|
|
|
__all__ = ["Device", "DeviceInfo", "enumerate_devices"]
|
|
|
|
|
|
class Device:
|
|
device_info: DeviceInfo
|
|
device_handle: Handle | None
|
|
|
|
def __init__(self, device_info: DeviceInfo):
|
|
"""
|
|
Creates a new HID device instance, used to send and receive HID
|
|
reports from/to an attached USB HID device.
|
|
|
|
:param dict() device_info: Device information dictionary describing
|
|
a single unique attached USB HID device.
|
|
"""
|
|
self.device_info = device_info
|
|
self.device_handle = None
|
|
|
|
async def open(self) -> None:
|
|
"""
|
|
Opens the HID device for input/output. This must be called prior to
|
|
sending or receiving any HID reports.
|
|
|
|
.. seealso:: See :func:`~HID.Device.close` for the corresponding
|
|
close method.
|
|
"""
|
|
|
|
if self.device_handle:
|
|
return
|
|
|
|
self.device_handle = await hid_open_device(self.device_info.path)
|
|
|
|
async def close(self) -> None:
|
|
"""
|
|
Closes the HID device for input/output.
|
|
|
|
.. seealso:: See :func:`~~HID.Device.open` for the corresponding
|
|
open method.
|
|
"""
|
|
|
|
if self.device_handle:
|
|
self.device_handle, device_handle = None, self.device_handle
|
|
|
|
await hid_close_device(device_handle)
|
|
|
|
async def is_open(self) -> bool:
|
|
"""
|
|
Indicates if the physical device object this instance is attached
|
|
to has been opened by the host.
|
|
|
|
:rtype: bool
|
|
:return: `True` if the device is open, `False` otherwise.
|
|
"""
|
|
|
|
return self.device_handle is not None
|
|
|
|
async def connected(self) -> bool:
|
|
"""
|
|
Indicates if the physical HID device this instance is attached to
|
|
is still connected to the host.
|
|
|
|
:rtype: bool
|
|
:return: `True` if the device is still connected, `False` otherwise.
|
|
"""
|
|
|
|
return any([d.path == self.path for d in await hid_enumerate()])
|
|
|
|
@property
|
|
def path(self) -> str:
|
|
"""
|
|
Retrieves the logical path of the attached HID device within the
|
|
current system. This can be used to differentiate one HID device
|
|
from another.
|
|
|
|
:rtype: str
|
|
:return: Logical device path for the attached device.
|
|
"""
|
|
return self.device_info.path
|
|
|
|
@property
|
|
def serial_number(self) -> int:
|
|
return self.device_info.serial_number
|
|
|
|
@property
|
|
def vendor_id(self) -> int:
|
|
return self.device_info.vendor_id
|
|
|
|
@property
|
|
def product_id(self) -> int:
|
|
return self.device_info.product_id
|
|
|
|
async def write_feature(self, payload: bytes) -> int:
|
|
"""
|
|
Sends a HID Feature report to the open HID device.
|
|
|
|
:param enumerable() payload: Enumerate list of bytes to send to the
|
|
device, as a feature report. The first
|
|
byte of the report should be the Report
|
|
ID of the report being sent.
|
|
|
|
:rtype: int
|
|
:return: Number of bytes successfully sent to the device.
|
|
"""
|
|
|
|
return await hid_send_feature_report(self.device_handle, payload)
|
|
|
|
async def read_feature(self, report_id: int, length: int) -> bytes:
|
|
"""
|
|
Reads a HID Feature report from the open HID device.
|
|
|
|
:param int report_id: Report ID of the report being read.
|
|
:param int length: Maximum length of the Feature report to read..
|
|
|
|
:rtype: bytes
|
|
:return: List of bytes containing the read Feature report. The
|
|
first byte of the report will be the Report ID of the
|
|
report that was read.
|
|
"""
|
|
|
|
return await hid_get_feature_report(self.device_handle, report_id, length)
|
|
|
|
async def write(self, payload: bytes) -> int:
|
|
"""
|
|
Sends a HID Out report to the open HID device.
|
|
|
|
:param bytes payload: bytes to send to the
|
|
device, as an Out report. The first
|
|
byte of the report should be the Report
|
|
ID of the report being sent.
|
|
|
|
:rtype: int
|
|
:return: Number of bytes successfully sent to the device.
|
|
"""
|
|
|
|
return await hid_write(self.device_handle, payload)
|
|
|
|
async def read(self, length: int) -> bytes | None:
|
|
"""
|
|
Performs a non-blocking read of a HID In report from the open HID device.
|
|
|
|
:param int length: Maximum length of the In report to read.
|
|
|
|
:rtype: bytes
|
|
:return: bytes containing the read In report. The first byte
|
|
of the report will be the Report ID of the report that was
|
|
read.
|
|
"""
|
|
|
|
return await hid_read(self.device_handle, length)
|
|
|
|
|
|
async def enumerate_devices(
|
|
vendor_id: int | None = None, product_id: int | None = None
|
|
) -> list[Device]:
|
|
return [
|
|
Device(device_info)
|
|
for device_info in await hid_enumerate(
|
|
vendor_id=vendor_id, product_id=product_id
|
|
)
|
|
]
|