I built usb-rea to take a USB capture of a vendor app, infer the protocol, and write a pyusb driver. The earlier posts in this series are about that second half: the overall pipeline, handshakes, named readings, image streams, and what a real AIO capture found in my tests.
This post is about a step I should have automated earlier. Before you capture anything, it is worth asking whether an open-source project already speaks that device's protocol. Until v0.7.12 I still did that by hand, grepping liquidctl and rivalcfg for a VID:PID. smichea suggested in Discussion #3 that usb-rea should do the lookup itself. So it does now.
usb-rea lookup
pip install -U usb-rea usb-rea lookup 1e71:170e 1e71:170e NZXT Kraken X (X42, X52, X62 or X72) (NZXT) protocol: documented - liquidctl: https://github.com/liquidctl/liquidctl/blob/main/liquidctl/driver/kraken2.py (fan/pump duty, lighting, status) - usb-rea validation: https://github.com/emirmrkaljevic/usb-rea/blob/main/docs/VALIDATION.md (fan duty command recovered byte for byte) note: A matching USB ID alone does not prove your unit speaks the same protocol; check the project's notes. That is the real output from 0.7.12, offline, no network involved. The ID is the NZXT Kraken X family. liquidctl already implements fan and pump duty, lighting and status in kraken2.py. usb-rea's own validation recovers that fan duty command byte for byte; the write-up is in docs/VALIDATION.md.
The note at the bottom is there on purpose. Vendors reuse IDs across revisions, and some IDs belong to a generic chip rather than one product. A matching ID is a hint to go read that project's notes, not proof that your unit speaks the same protocol.
What the offline table covers
The lookup is a curated table, not a search of every USB driver on GitHub. It covers the devices usb-rea is validated against, plus a few case studies:
- liquidctl: NZXT Kraken X/Z family, and Corsair Hydro Platinum / Pro XT / Elite
- rivalcfg: SteelSeries Rival 100 and 105
- turing-smart-screen-python
- linux-thermalright-lcd-control
- usb-rea's own case studies (Corsair Hydro Platinum, ASUS TUF Gaming LC III LCD)
Vendor and product names also come from the system's usb.ids when it is installed. --usb-ids points at a file if you want to override that. --json prints the same result as JSON.
Here is a case where the hint needs a closer look. 1a86:5722 is a generic WCH serial bridge ID:
usb-rea lookup 1a86:5722 1a86:5722 Turing Smart Screen 3.5" (rev A) / XuanFang (rev B) (QinHeng Electronics) protocol: documented - turing-smart-screen-python: https://github.com/mathoudebine/turing-smart-screen-python (serial protocol for revisions A and B) limits: generic WCH serial bridge ID; the project tells revisions apart by the USB serial number (USB35INCHIPSV2 is rev A), and unrelated devices use this ID too note: A matching USB ID alone does not prove your unit speaks the same protocol; check the project's notes. If your gadget only happens to use the same bridge chip, that project is not a driver for your device.
Look up every device in a capture
You can pass a pcap or pcapng instead of a VID:PID. usb-rea reads the IDs out of the device descriptors in the capture (the GET_DESCRIPTOR replies). I ran this on the trimmed ASUS TUF Gaming LC III capture that ships in the repo as tests/data/asus-tuf-lc3-lcd.pcap.gz. The command reads uncompressed pcap/pcapng, so I decompressed it first:
gunzip -c tests/data/asus-tuf-lc3-lcd.pcap.gz > asus-tuf-lc3-lcd.pcap usb-rea lookup asus-tuf-lc3-lcd.pcap 0b05:1c7b ASUS TUF Gaming LC III LCD (ASUSTek Computer, Inc.) protocol: documented - usb-rea case study: https://github.com/emirmrkaljevic/usb-rea/blob/main/docs/case-study-asus-tuf-lc3.md (brightness command and JPEG stream from one real capture; not a full driver) note: A matching USB ID alone does not prove your unit speaks the same protocol; check the project's notes. That is also honest: I have a case study of the brightness command and the JPEG stream from one real capture, not a full open driver. If a capture has no descriptor (you started recording after the device was already plugged in), lookup says so and asks you to pass the VID:PID from lsusb, or to replug while capturing.
Optional: ask OpenDeviceDB
Offline is the default. Nothing leaves your machine. --online also queries OpenDeviceDB's read-only API and sends only the VID:PID, never the capture. If there is no network, lookup prints the error and keeps the offline answer.
usb-rea lookup 1e71:170e --online 1e71:170e NZXT Kraken X (X42, X52, X62 or X72) (NZXT) protocol: documented - liquidctl: https://github.com/liquidctl/liquidctl/blob/main/liquidctl/driver/kraken2.py (fan/pump duty, lighting, status) - usb-rea validation: https://github.com/emirmrkaljevic/usb-rea/blob/main/docs/VALIDATION.md (fan duty command recovered byte for byte) OpenDeviceDB: NZXT Kraken X2, controllable (openrgb): https://opendevicedb.org/devices/nzxt/kraken-x2 - OpenRGB: https://gitlab.com/CalcProgrammer1/OpenRGB/-/tree/master/Controllers/NZXTKrakenController note: A matching USB ID alone does not prove your unit speaks the same protocol; check the project's notes. Same Kraken ID, plus a public OpenDeviceDB record that points at OpenRGB. An ID with nothing in the local table and nothing in OpenDeviceDB stays unknown. I tried a made-up NZXT product ID that is not in the table:
usb-rea lookup 1e71:9999 --online 1e71:9999 NZXT protocol: unknown other devices from this vendor are handled by: liquidctl OpenDeviceDB: no record for this ID usb-rea still tells you that other NZXT devices are handled by liquidctl, which is often the useful next place to look. A fully unknown ID, with no vendor in the table either, is just unknown device.
The same lookup is available as the MCP tool lookup_device when you run usb-rea mcp. It takes a device argument (a VID:PID or a capture path) and an optional online flag, and returns JSON.
When nothing exists yet
That is the case the rest of usb-rea is for. The built-in demo uses a synthetic fan controller with a fake ID, 1234:abcd. lookup leaves it unknown, then the pipeline infers the protocol from the labelled capture and writes a driver anyway:
usb-rea lookup 1234:abcd usb-rea demo --device fan 1234:abcd unknown device protocol: unknown nothing known offline; try --online (OpenDeviceDB) or capture it and find out captured 11 labelled actions, device 1234:abcd mode 100% 0x5a | counter (+1 per message) | 0x30 | lookup 1→0x11, 2→0x22, 3→0x47 | 0x00 | 0x00 | u16le crc16_modbus(bytes[0:6]) fan 100% 0x5a | counter (+1 per message) | 0x10 | u16be round(value * 1 + 0) | 0x00 | u16le crc16_modbus(bytes[0:6]) color 100% 0x5a | counter (+1 per message) | 0x20 | round(value * 1 + 0) | round(value[1] * 1 + 0) | round(value[2] * 1 + 0) | u16le crc16_modbus(bytes[0:6]) color only sent while mode 3 (3x; other commands ran in mode 1, 2): the driver sets it first handshake (3 messages sent once when the app opens the device): 1. 5a 00 01 00 00 00 0d 1d -> a5 01 ?? ?? ?? ?? ??, sent again 2x later (session reopened) 2. 5a 00 02 38 b4 e6 52 1e 1e -> a5 02 ?? ??, bytes 3-6 = token from step 1, sent again 2x later (session reopened) reply byte 2 = 01 (2x, most common): the steps below reply byte 2 = 02 (1x): 5a 00 04 01 00 00 5c 11 -> a5 04 01 ??, 5a 00 03 01 00 00 5d 65 -> a5 03 01 ?? 3. 5a 00 03 01 00 00 5d 65 -> a5 03 01 ??, sent again 1x later (session reopened) device replies: mode 6/6 0xa5 | = query byte 2 (0x30) | = query byte 1 | = query byte 3 | 0x00 | sum8(bytes[0:5]) fan 6/6 0xa5 | = query byte 2 (0x10) | = query byte 1 | u16le round(value * 1 + 0) | sum8(bytes[0:5]) color 6/6 0xa5 | = query byte 2 (0x20) | = query byte 1 | 0x00 | 0x00 | sum8(bytes[0:5]) poll 1 7/7 0xa5 | = query byte 2 (0x40) | u16le rpm = raw, seen 800..4011 | u8 temp = raw, seen 35..38 | mode: 0x11 = silent, 0x22 = balanced, 0x47 = turbo | sum8(bytes[0:6]) 24 of 24 'seen' observations matched a reply byte (mode, rpm, temp) wrote usb-rea-demo/report.md, usb-rea-demo/fan_driver.py and the Wireshark dissector usb-rea-demo/fan.lua On a real Windows-only gadget the flow is: capture the vendor app with usbmon or USBPcap, mark the clicks (or pass --labels if you only have a pcap), then:
usb-rea report lamp.pcapng -o report.md usb-rea scaffold lamp.pcapng -o lamp_driver.py If you have a device whose protocol is not in the lookup table, a capture is still the most useful thing you can send me. There is a device request thread. usb-rea is on GitHub and PyPI (v0.7.12).
I built usb-rea with help from AI coding tools; the inference itself is deterministic and every claim in its reports points at captured frames.
