使用Python和pyusb库从HID设备(USB无线鼠标)获取数据(充电信息)

问题描述 投票:0回答:1

我正在尝试与无线鼠标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 并发现了此控制传输: frame。 我猜测报告 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
的实现是否正确? 我需要建议。

python hid libusb pyusb webhid
1个回答
0
投票

此错误可能是由于发送了意外大小的报告而引起的。

USB HID 控制传输将报告 ID 放入

wValue
。这意味着您不应将其作为报告缓冲区的第一个字节。报告大小是报告的长度(以字节为单位),如果设备使用报告 ID,则不包括报告 ID 字节。

从您的代码来看,

wValue
始终是
0x305
(功能报告5)。相反,请将低字节设置为报告 ID,并且不要将该字节包含在传递给
control_transfer
的报告数据中。

请参阅HID 的设备类定义中的7.2 类特定请求

© www.soinside.com 2019 - 2024. All rights reserved.