我正在尝试与无线鼠标Ninjutso Sora V2通信,以使用pyusb获取充电信息。我之前为 Razer 鼠标做过类似的东西 - Razer 托盘。我想为 Sora 鼠标制作类似的脚本。
此鼠标使用基于网络的软件和 webhid API 来更改设置并获取状态信息。它被称为NinjaForce。我不懂 JS,但我在代码中找到了这个函数,并想用 pyusb 实现它的一部分:
async function _(e) {
const t = new Uint8Array(31);
t[0] = 13,
t[3] = 1,
t[6] = 22,
await e.sendFeatureReport(5, t),
await a(90);
let r = await e.receiveFeatureReport(5);
K.profile = r.getUint8(9)
}
const l = async e=>{
let t = new Uint8Array(31);
t[0] = 9,
t[3] = 1,
await e.sendFeatureReport(5, t),
await a(90);
let r = await e.receiveFeatureReport(5);
K.version = [...Array(4)].map(((e,t)=>r.getUint8(12 - t).toString(16).padStart(2, "0"))).join("")
}
, d = async e=>{
const t = new Uint8Array(31);
t[0] = 21,
t[3] = 1,
await a(90),
await e.sendFeatureReport(5, t),
await a(90);
let r = await e.receiveFeatureReport(5);
K.battery = r.getUint8(9),
K.charging = r.getUint8(10),
K.fullCharge = r.getUint8(11),
K.online = r.getUint8(12)
}
, p = async e=>{
let t = e.productId;
if (44572 === t || 44684 === t) {
const r = new Uint8Array(31);
r[0] = 34,
r[3] = 1,
r[6] = 22,
await e.sendFeatureReport(5, r),
await a(90);
let n = await e.receiveFeatureReport(5);
t = n.getUint8(10) << 8 | n.getUint8(9)
}
K.deviceColor = {
44561: "black",
44562: "white",
44563: "pink",
44564: "red",
44565: "#1e22aa",
44566: "transparent"
}[t]
}
我对这部分感兴趣:
, d = async e=>{
const t = new Uint8Array(31);
t[0] = 21,
t[3] = 1,
await a(90),
await e.sendFeatureReport(5, t),
await a(90);
let r = await e.receiveFeatureReport(5);
K.battery = r.getUint8(9),
K.charging = r.getUint8(10),
K.fullCharge = r.getUint8(11),
K.online = r.getUint8(12)
我用 Wireshark 跟踪 USB 并发现了此控制传输: 。 我猜测报告 0x05 的第一个字节是报告号 5。
所以,这是我使用 pyusb 的实现:
import time
import usb.core
import usb.util
from usb.backend import libusb1
VID = 0x1915
PID = 0xAE1C
def send_feature_report(device, feature_report_data):
device.ctrl_transfer(
bmRequestType=0x21,
bRequest=0x09,
wValue=0x305,
wIndex=1,
data_or_wLength=feature_report_data)
def get_feature_report(device, report_length):
feature_report = device.ctrl_transfer(
bmRequestType=0xA1,
bRequest=0x01,
wValue=0x305,
wIndex=1,
data_or_wLength=report_length)
return feature_report
backend = libusb1.get_backend(find_library=lambda x: R".\libusb-1.0.dll")
dev = usb.core.find(idVendor=VID, idProduct=PID, backend=backend)
print(f"dev: {dev}")
dev.set_configuration()
usb.util.claim_interface(dev, 1)
report = bytearray(32)
report[0] = 5
report[1] = 21
report[4] = 1
print(f"report: {report}")
time.sleep(0.09)
send_feature_report(dev, report)
time.sleep(0.09)
result = get_feature_report(dev, 32)
usb.util.dispose_resources(dev)
usb.util.release_interface(dev, 1)
print(f"result: {result}")
输出:
dev: DEVICE ID 1915:ae1c on Bus 001 Address 010 =================
bLength : 0x12 (18 bytes)
bDescriptorType : 0x1 Device
bcdUSB : 0x200 USB 2.0
bDeviceClass : 0x0 Specified at interface
bDeviceSubClass : 0x0
bDeviceProtocol : 0x0
bMaxPacketSize0 : 0x40 (64 bytes)
idVendor : 0x1915
idProduct : 0xae1c
bcdDevice : 0x200 Device 2.0
iManufacturer : 0x1 Ninjutso
iProduct : 0x2 Ninjutso Sora V2
iSerialNumber : 0x3 000000000000
bNumConfigurations : 0x1
CONFIGURATION 1: 500 mA ==================================
bLength : 0x9 (9 bytes)
bDescriptorType : 0x2 Configuration
wTotalLength : 0x42 (66 bytes)
bNumInterfaces : 0x2
bConfigurationValue : 0x1
iConfiguration : 0x4 Default configuration
bmAttributes : 0xe0 Self Powered, Remote Wakeup
bMaxPower : 0xfa (500 mA)
INTERFACE 0: Human Interface Device ====================
bLength : 0x9 (9 bytes)
bDescriptorType : 0x4 Interface
bInterfaceNumber : 0x0
bAlternateSetting : 0x0
bNumEndpoints : 0x1
bInterfaceClass : 0x3 Human Interface Device
bInterfaceSubClass : 0x1
bInterfaceProtocol : 0x2
iInterface : 0x0
ENDPOINT 0x81: Interrupt IN ==========================
bLength : 0x7 (7 bytes)
bDescriptorType : 0x5 Endpoint
bEndpointAddress : 0x81 IN
bmAttributes : 0x3 Interrupt
wMaxPacketSize : 0x40 (64 bytes)
bInterval : 0x1
INTERFACE 1: Human Interface Device ====================
bLength : 0x9 (9 bytes)
bDescriptorType : 0x4 Interface
bInterfaceNumber : 0x1
bAlternateSetting : 0x0
bNumEndpoints : 0x2
bInterfaceClass : 0x3 Human Interface Device
bInterfaceSubClass : 0x1
bInterfaceProtocol : 0x0
iInterface : 0x0
ENDPOINT 0x82: Interrupt IN ==========================
bLength : 0x7 (7 bytes)
bDescriptorType : 0x5 Endpoint
bEndpointAddress : 0x82 IN
bmAttributes : 0x3 Interrupt
wMaxPacketSize : 0x40 (64 bytes)
bInterval : 0x1
ENDPOINT 0x2: Interrupt OUT ==========================
bLength : 0x7 (7 bytes)
bDescriptorType : 0x5 Endpoint
bEndpointAddress : 0x2 OUT
bmAttributes : 0x3 Interrupt
wMaxPacketSize : 0x40 (64 bytes)
bInterval : 0x1
report: bytearray(b'\x05\x15\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
Traceback (most recent call last):
File "c:\Users\xxxx\Documents\Python\sorav2_tray\usb1.py", line 40, in <module>
send_feature_report(dev, report)
File "c:\Users\xxxx\Documents\Python\sorav2_tray\usb1.py", line 10, in send_feature_report
device.ctrl_transfer(
File "C:\Users\xxxx\Documents\Python\sorav2_tray\.venv\Lib\site-packages\usb\core.py", line 1082, in ctrl_transfer
ret = self._ctx.backend.ctrl_transfer(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\xxxx\Documents\Python\sorav2_tray\.venv\Lib\site-packages\usb\backend\libusb1.py", line 893, in ctrl_transfer
ret = _check(self.lib.libusb_control_transfer(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\xxxx\Documents\Python\sorav2_tray\.venv\Lib\site-packages\usb\backend\libusb1.py", line 604, in _check
raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 5] Input/Output Error
无论如何我都会
[Errno 5] Input/Output Error
。也许,我需要实现链中主功能的所有请求?我对 webhid 函数 sendFeatureReport
和 receiveFeatureReport
的实现是否正确?
我需要建议。
此错误可能是由于发送了意外大小的报告而引起的。
USB HID 控制传输将报告 ID 放入
wValue
。这意味着您不应将其作为报告缓冲区的第一个字节。报告大小是报告的长度(以字节为单位),如果设备使用报告 ID,则不包括报告 ID 字节。
从您的代码来看,
wValue
始终是0x305
(功能报告5)。相反,请将低字节设置为报告 ID,并且不要将该字节包含在传递给 control_transfer
的报告数据中。
请参阅HID 的设备类定义中的7.2 类特定请求。