-
-
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, ? |
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.
I'm personally only interested in static colors, I didn't check yet how patterns are defined.
I think iCue does that either as a silly effect or as a way to check if responses are correct.