-
-
Save andreldm/367f0dba38da9c59e9f169fe0dd570fd to your computer and use it in GitHub Desktop.
# Setup: | |
# python -m venv env | |
# source env/bin/activate | |
# pip install pyusb | |
# | |
# Execute with: sudo python test.py | |
# See udev rule present in (2) if you want to avoid running as root. | |
# | |
# References: | |
# 1. https://wiki.wireshark.org/CaptureSetup/USB | |
# 2. https://slomkowski.eu/tutorials/eavesdropping-usb-and-writing-driver-in-python | |
# 3. https://mvidner.blogspot.com/2017/01/usb-communication-with-python-and-pyusb.html | |
# 4. https://github.com/Benzhaomin/pyrmi/blob/master/main.py | |
import sys | |
import usb.core | |
import usb.util | |
import sched, time | |
VENDOR_ID = 0x1b1c | |
WIRED_DEVICE_ID = 0x1b5e | |
DONGLE_DEVICE_ID = 0x1b65 | |
WIRED = True | |
COMMAND_PREFIX = 0x08 | |
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=WIRED_DEVICE_ID) | |
if dev is None: | |
print("Wired default not found, looking for dongle...") | |
WIRED = False | |
COMMAND_PREFIX = 0x09 | |
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=DONGLE_DEVICE_ID) | |
assert dev | |
interface = 1 # INTERFACE 1: Human Interface Device | |
reader = dev[0][(interface, 0)][0] # ENDPOINT 0x84: Interrupt IN | |
writer = dev[0][(interface, 0)][1] # ENDPOINT 0x04: Interrupt OUT | |
def write(data): | |
padding = [0x0]*(64 - len(data)) | |
writer.write(data + padding, timeout=100) | |
def read(): | |
data = reader.read(reader.bEndpointAddress, reader.wMaxPacketSize) | |
return bytes(data).hex() | |
def send(data): | |
write(data) | |
print(read()) | |
def grab_device(): | |
if dev.is_kernel_driver_active(interface): | |
try: | |
dev.detach_kernel_driver(interface) | |
usb.util.claim_interface(dev, interface) | |
except usb.core.USBError as e: | |
sys.exit("Could not detach kernel driver: %s" % str(e)) | |
def ungrab_device(): | |
usb.util.release_interface(dev, interface) | |
dev.attach_kernel_driver(interface) | |
grab_device() | |
# Init | |
send([0x08, 0x01, 0x03, 0x00, 0x02]) | |
if WIRED: | |
send([0x08, 0x0d, 0x00, 0x01]) | |
else: | |
send([0x09, 0x01, 0x03, 0x00, 0x02]) | |
send([0x09, 0x0d, 0x00, 0x01]) | |
# Set my configuration | |
send([COMMAND_PREFIX, 0x06, # Command to manipulate LEDs ? | |
0x00, 0x06, # ? | |
0x00, 0x00, 0x00, # ? | |
0x00, # Indicator LED's red | |
0x00, # Main LED's red | |
0xff, # Indicator LED's green | |
0x00, # Main LED's green | |
0x00, # Indicator LED's blue | |
0x00 # Main LED's blue | |
]) | |
#send([COMMAND_PREFIX, 0x01, 0x20, 0x00, 0xe8, 0x03]) # Set DPI to 1000 (3e8 = 1000) | |
#send([COMMAND_PREFIX, 0x01, 0x20, 0x00, 0xb8, 0x0b]) # Set DPI to 3000 (b8b = 2955) | |
send([COMMAND_PREFIX, 0x01, 0x20, 0x00, 0x08, 0x70]) # Set DPI to 1800 (708 = 1800) | |
ungrab_device() | |
s = sched.scheduler(time.time, time.sleep) | |
def keep_alive(sc): | |
grab_device() | |
send([COMMAND_PREFIX, 0x12]) # Keep alive, has to be sent every minute or mouse will reset to defaults | |
ungrab_device() | |
s.enter(55, 1, keep_alive, (sc,)) | |
s.enter(55, 1, keep_alive, (s,)) | |
s.run() | |
# Packets sent when iCue is initialized (captured with Wireshark) | |
# | |
# WIRED | |
# | |
# send([0x08, 0x02, 0x03]) # ? | |
# send([0x08, 0x01, 0x03, 0x00, 0x02]) # REQUIRED, if executed alone, Main LED freezes | |
# send([0x08, 0x02, 0x03]) # ? | |
# send([0x08, 0x02, 0x5f]) # ? | |
# send([0x08, 0x02, 0x01]) # ? | |
# send([0x08, 0x02, 0x03]) # ? | |
# send([0x08, 0x02, 0x13]) # ? | |
# send([0x08, 0x0d, 0x00, 0x05]) # ? | |
# send([0x08, 0x09]) # ? | |
# send([0x08, 0x08]) # ? | |
# send([0x08, 0x05, 0x01]) # ? | |
# send([0x08, 0x0d, 0x00, 0x27]) # ? | |
# send([0x08, 0x01, 0x03, 0x00, 0x02]) # ? | |
# send([0x08, 0x0d, 0x00, 0x05]) # ? | |
# send([0x08, 0x09]) # ? | |
# send([0x08, 0x08]) # ? | |
# send([0x08, 0x05, 0x01]) # ? | |
# send([0x08, 0x02, 0x0f]) # ? | |
# send([0x08, 0x02, 0x10]) # ? | |
# send([0x08, 0x02, 0x0b]) # ? | |
# send([0x08, 0x02, 0x0d]) # ? | |
# send([0x08, 0x02, 0x0e]) # ? | |
# send([0x08, 0x01, 0x02, 0x00, 0xe8, 0x03]) # ? | |
# send([0x08, 0x02, 0x15]) # ? | |
# send([0x08, 0x01, 0x20, 0x00, 0x90, 0x01]) # ? | |
# send([0x08, 0x0d, 0x00, 0x02]) # ? | |
# send([0x08, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01]) # ? | |
# send([0x08, 0x05, 0x01]) # ? | |
# send([0x08, 0x0d, 0x00, 0x02]) # ? | |
# send([0x08, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01]) # ? | |
# send([0x08, 0x05, 0x01]) # ? | |
# send([0x08, 0x01, 0x07]) # ? | |
# send([0x08, 0x0d, 0x00, 0x01]) # REQUIRED, ? | |
# send([0x08, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xff, 0x13, 0x00, 0x00, 0x00, 0xc5]) # Sets DPI Indicator LED to red | |
# send([0x08, 0x02, 0x15]) # ? | |
# Makes DPI Indicator LED oscillate between green and off twice | |
# for x in [ | |
# 0x04, 0x08, 0x0c, 0x10, 0x19, 0x1d, 0x21, 0x2a, 0x2e, 0x33, 0x3b, 0x3f, | |
# 0x44, 0x48, 0x4c, 0x51, 0x55, 0x59, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x73, | |
# 0x77, 0x7b, 0x80, 0x7e, 0x7a, 0x76, 0x71, 0x6d, 0x69, 0x64, 0x60, 0x5c, | |
# 0x57, 0x53, 0x4f, 0x4b, 0x46, 0x42, 0x3e, 0x39, 0x35, 0x31, 0x2d, 0x29, | |
# 0x24, 0x1f, 0x1b, 0x17, 0x13, 0x0e, 0x0a, 0x06, 0x01, 0x00, 0x04, 0x08, | |
# 0x0d, 0x11, 0x15, 0x1a, 0x1e, 0x22, 0x27, 0x2b, 0x2f, 0x33, 0x38, 0x3c, | |
# 0x40, 0x44, 0x49, 0x4d, 0x51, 0x56, 0x5a, 0x5f, 0x63, 0x67, 0x6b, 0x70, | |
# 0x74, 0x78, 0x7c, 0x80, 0x7d, 0x79, 0x74, 0x70, 0x6c, 0x67, 0x63, 0x5f, | |
# 0x5a, 0x56, 0x52, 0x4e, 0x49, 0x45, 0x41, 0x3b, 0x37, 0x32, 0x2e, 0x2a, | |
# 0x26, 0x21, 0x1d, 0x19, 0x14, 0x10, 0x0c, 0x08, 0x03, 0x00]: | |
# send([0x08, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x13, x, 0x00, 0x00, 0xc5]) | |
# send([0x08, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xff, 0x13, 0x00, 0x00, 0x00, 0xc5]) # Sets DPI Indicator LED to red | |
# | |
# DONGLE | |
# | |
# send([0x08, 0x02, 0x03]) # ? | |
# send([0x08, 0x01, 0x03, 0x00, 0x02]) # REQUIRED, if executed alone, Main LED freezes | |
# send([0x08, 0x02, 0x03]) # ? | |
# send([0x08, 0x02, 0x5f]) # ? | |
# send([0x08, 0x02, 0x01]) # ? | |
# send([0x08, 0x02, 0x03]) # ? | |
# send([0x08, 0x02, 0x13]) # ? | |
# send([0x08, 0x02, 0x14]) # ?, this is different from the sequence of Wired initialization | |
# send([0x08, 0x0d, 0x00, 0x05]) # ? | |
# send([0x08, 0x09]) # ? | |
# send([0x08, 0x08]) # ? | |
# send([0x08, 0x05, 0x01]) # ? | |
# send([0x08, 0x0d, 0x00, 0x27]) # ? | |
# send([0x08, 0x01, 0x03, 0x00, 0x02]) # ? | |
# send([0x08, 0x0d, 0x00, 0x05]) # ? | |
# send([0x08, 0x09]) # ? | |
# send([0x08, 0x08]) # ? | |
# send([0x08, 0x05, 0x01]) # ? | |
# send([0x08, 0x02, 0x36]) # ?, at this point the sequence diverges from Wired initialization | |
# send([0x09, 0x02, 0x11]) # ? | |
# send([0x09, 0x02, 0x12]) # ? | |
# send([0x09, 0x02, 0x03]) # ? | |
# send([0x09, 0x01, 0x03, 0x00, 0x02]) # REQUIRED, ? | |
# send([0x09, 0x02, 0x03]) # ? | |
# send([0x09, 0x02, 0x5f]) # ? | |
# send([0x09, 0x02, 0x01]) # ? | |
# send([0x09, 0x02, 0x03]) # ? | |
# send([0x09, 0x02, 0x13]) # ? | |
# send([0x09, 0x02, 0x14]) # ? | |
# send([0x09, 0x0d, 0x00, 0x05]) # ? | |
# send([0x09, 0x09]) # ? | |
# send([0x09, 0x08]) # ? | |
# send([0x09, 0x05, 0x01]) # ? | |
# send([0x09, 0x0d, 0x00, 0x27]) # ? | |
# send([0x09, 0x0d, 0x00, 0x05]) # ? | |
# send([0x09, 0x09]) # ? | |
# send([0x09, 0x08]) # ? | |
# send([0x09, 0x05, 0x01]) # ? | |
# send([0x09, 0x02, 0x15]) # ? | |
# send([0x09, 0x02, 0x0f]) # ? | |
# send([0x09, 0x02, 0x10]) # ? | |
# send([0x09, 0x02, 0x0b]) # ? | |
# send([0x09, 0x02, 0x0d]) # ? | |
# send([0x09, 0x02, 0x0e]) # ? | |
# send([0x09, 0x01, 0x02, 0x00, 0xe8, 0x03]) # ? | |
# send([0x09, 0x02, 0x15]) # ? | |
# send([0x09, 0x0d, 0x00, 0x02]) # ? | |
# send([0x09, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01]) # ? | |
# send([0x09, 0x05, 0x01]) # ? | |
# send([0x09, 0x05, 0x07]) # ? | |
# send([0x09, 0x0d, 0x00, 0x02]) # ? | |
# send([0x09, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01]) # ? | |
# send([0x09, 0x05, 0x01]) # ? | |
# send([0x09, 0x02, 0x0f]) # ? | |
# send([0x09, 0x02, 0x10]) # ? | |
# send([0x09, 0x01, 0x02, 0x00, 0xe8, 0x03]) # ? | |
# send([0x09, 0x02, 0x15]) # ? | |
# send([0x09, 0x0d, 0x00, 0x02]) # ? | |
# send([0x09, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01]) # ? | |
# send([0x09, 0x05, 0x01]) # ? | |
# send([0x09, 0x01, 0x07]) # ? | |
# send([0x09, 0x0d, 0x00, 0x02]) # ? | |
# send([0x09, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01]) # ? | |
# send([0x09, 0x05, 0x01]) # ? | |
# send([0x09, 0x01, 0x20, 0x00, 0x40, 0x06]) # ? | |
# send([0x09, 0x02, 0x40]) # ? | |
# send([0x09, 0x02, 0x0b]) # ? | |
# send([0x09, 0x02, 0x0d]) # ? | |
# send([0x09, 0x02, 0x0e]) # ? | |
# send([0x09, 0x0d, 0x00, 0x01]) # REQUIRED, ? |
So how does one control the main led besides freezing?
I'm personally only interested in static colors, I didn't check yet how patterns are defined.
What's the pattern with the oscillation of the indicator led? Wanna try with other colors.
I think iCue does that either as a silly effect or as a way to check if responses are correct.
I'm personally only interested in static colors, I didn't check yet how patterns are defined.
I'm fine keeping it static, just not seeing how in the script minus the freezing which I would want to define a color.
I think iCue does that either as a silly effect or as a way to check if responses are correct.
Interesting, so the array is from them. Didn't know if you made it and it had a pattern or something I wasn't seeing. I tried with red but it went red to yellow. Very strange.
I'm fine keeping it static, just not seeing how in the script minus the freezing which I would want to define a color.
You can ignore that comment and interpret this packet as part of the initialization (which I think it's very likely)
Interesting, so the array is from them. Didn't know if you made it and it had a pattern or something I wasn't seeing. I tried with red but it went red to yellow. Very strange.
Up to # Custom packets crafted by me
all packets were sent by iCue, I kept them verbatim, commenting out the ones that don't seem necessary. Try tweaking values in send
of line 103.
@hockeymikey script updated to support dongle and stays running to send the keep alive packet every 55 seconds.
@hockeymikey script updated to support dongle and stays running to send the keep alive packet every 55 seconds.
Great work, you're amazing. Now if only I can figure out how to listen for the screen being dimmed on kde and I could incorporate that haha. Did you use W10 in a VM? I'm curious to figuring out the other unknown parts if I get the time.
Great work, you're amazing. Now if only I can figure out how to listen for the screen being dimmed on kde and I could incorporate that haha. Did you use W10 in a VM? I'm curious to figuring out the other unknown parts if I get the time.
Thanks, yes I used a Windows 10 VM (see references #1 and #2). As for preventing screen dimming, try caffeine.
By the way, I turned this gist into a project, so you won't updates here anymore.
Solid work
As for preventing screen dimming, try caffeine.
I wanna detect screen dimming with Python so I can dim the mouse with it and other things. Haven't been able to find anything either kde focused or in general.
So how does one control the main led besides freezing? What's the pattern with the oscillation of the indicator led? Wanna try with other colors.