Skip to content

Instantly share code, notes, and snippets.

@pklaus
Last active June 2, 2020 22:36
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pklaus/56ee78d26593d33d906c to your computer and use it in GitHub Desktop.
Save pklaus/56ee78d26593d33d906c to your computer and use it in GitHub Desktop.
Easterntimes Tech Wired Gaming Mouse T1

Easterntimes Tech - Wired Gaming Mouse T1

A script to change settings of the Wired Gaming Mouse T1 when running Linux.

#!/usr/bin/env python
"""
Script to control the
Easterntimes Tech
Wired Gaming Mouse T1
via PyUSB.
"""
import sys, argparse, textwrap, logging
import usb.core, usb.util
logger = logging.getLogger(name=__name__)
class WiredGamingMouseT1(object):
idVendor = 0x04d9
idProduct = 0xfc07
brightness_map = {'bright': 3, 'medium': 2, 'dim': 1, 'off': 0}
breathing_map = {'fast': 1, 'medium': 3, 'slow': 6, 'off': 0}
def __init__(self):
# Device Initialization
dev = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
try:
if dev.is_kernel_driver_active(2):
dev.detach_kernel_driver(2)
except usb.core.USBError as e:
msg = """
Could not interact with the device. This tool needs to be run as root
or you need to set the right permissions on the usb device:
sudo chmod 666 /dev/bus/usb/{bus:03d}/{address:03d}
Or you can let udev configure the permissions automatically by putting this rule:
SUBSYSTEM=="usb", ATTR{{idVendor}}=="04d9", ATTR{{idProduct}}=="fc07", MODE="0666", SYMLINK+="wgmt1"
into the file /etc/udev/rules.d/99-wgmt1.rules.
"""
sys.exit(textwrap.dedent(msg.format(bus=dev.bus, address=dev.address)))
usb.util.claim_interface(dev, 2)
self.dev = dev
def send_ctrl_msg(self, data, big=False):
logger.debug('sending: ' + repr(data))
val = 0x0303 if big else 0x0302
self.dev.ctrl_transfer(0x21, 9, val, 2, data)
def set_color(self, rgb, brightness='bright', breathing='off'):
""" send a URB message via PyUSB and change the LED color """
r, g, b = (255 - col for col in rgb)
brightness, breathing = self.sanitize_brightness_breathing(brightness, breathing)
msg = bytes([0x2, 0x4, r, g, b, brightness, breathing, 0, 0, 0, 0, 0, 0, 0, 0, 0])
self.send_ctrl_msg(msg)
def set_profile(self, profile):
""" send a URB message via PyUSB and change the profile """
assert profile in range(0, 5)
msg = bytes([2,2,0x43,0,1,0,0xfa,0xfa,profile,0,0,0,0,0,0,0])
self.send_ctrl_msg(msg)
msg = bytes([2,1,1,profile,0,0,0,0,0,0,0,0,0,0,0,0])
self.send_ctrl_msg(msg)
def set_cpi(self, profile, cpi_steps):
"""
cpi_steps must be an iterable of length 5
each consisting of a number going from 0 to 16
"""
assert len(cpi_steps) == 5
msg = [3,2,0x4f,2,0x2a,0,0xfa,0xfa,5,profile]
for cpi_step in cpi_steps:
if cpi_step in range(0, 17):
enable = 1
else:
enable = 0
cpi_step = 0
msg += [enable,cpi_step,0,cpi_step,0,0,0,0]
msg += [0] * 14
msg = bytes(msg)
self.send_ctrl_msg(msg, big=True)
logger.info('You need to switch to the profile now to enable the new settings.')
def sanitize_brightness_breathing(self, brightness, breathing):
if type(brightness) == str:
brightness = self.brightness_map[brightness]
if type(breathing) == str:
breathing = self.breathing_map[breathing]
assert brightness in (0,1,2,3)
assert breathing in (0,1,3,6)
return brightness, breathing
def set_brightness_breathing(self, profile, brightness, breathing):
""" """
brightness, breathing = self.sanitize_brightness_breathing(brightness, breathing)
msg = bytes([2,2,0xf1,profile,6,0,0xfa,0xfa,0xf1,0xf0,0,brightness,breathing,0,0,0])
logger.debug('Sending this now: ' + repr(msg))
self.send_ctrl_msg(msg)
logger.info('You need to switch to the profile now to enable the new settings.')
def cpi_steps(string):
"""
argparse type definition to enter CPI steps.
"""
try:
steps = [int(part) for part in string.split(',')]
assert len(steps) == 5
return steps
except:
raise argparse.ArgumentTypeError('Not a proper CPI steps value.')
def hex_rgb(string):
"""
argparse type definition to enter RGB
values as a 3- or 6- digit hex number
"""
try:
if len(string) == 3:
string = string[0] * 2 + string[1] * 2 + string[2] * 2
assert len(string) == 6
r, g, b = string[0:2], string[2:4], string[4:6]
return (int(col, 16) for col in (r, g, b))
except:
raise argparse.ArgumentTypeError('Not a proper RGB hex value.')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--debug', action='store_true')
profile_parser = argparse.ArgumentParser(add_help=False)
profile_parser.add_argument('profile', type=int, choices=range(0,5), help='Profile')
subparsers = parser.add_subparsers(dest='action', metavar='<action>', help="Action to perform:")
# set-color
action_desc = 'Set the LED color of the mouse'
set_color_parser = subparsers.add_parser('col',
description=action_desc, help=action_desc)
set_color_parser.add_argument('color', type=hex_rgb, metavar='RRGGBB', help='New LED color')
set_color_parser.add_argument('--brightness', choices=WiredGamingMouseT1.brightness_map.keys(), default='bright', help='Brightness')
set_color_parser.add_argument('--breathing', choices=WiredGamingMouseT1.breathing_map.keys(), default='off', help='Breathing')
# switch-profile
action_desc = 'Switch to a different mouse profile'
switch_profile_parser = subparsers.add_parser('sp',
description=action_desc, parents=[profile_parser], help=action_desc)
# cpi-steps
action_desc = 'Set the CPI (counts-per-inch) steps'
switch_profile_parser = subparsers.add_parser('cs',
description=action_desc, parents=[profile_parser], help=action_desc)
switch_profile_parser.add_argument('steps', type=cpi_steps,
help='Comma separated list of CPI steps for the specified profile. Default: 2,3,6,13,16. Use -1 to disable a step.')
# brightness-breathing
action_desc = 'Change the brightness & breathing settings'
brightness_breathing_parser = subparsers.add_parser('bb',
description=action_desc, parents=[profile_parser], help=action_desc)
brightness_breathing_parser.add_argument('brightness', choices=WiredGamingMouseT1.brightness_map.keys(), help='Brightness')
brightness_breathing_parser.add_argument('breathing', choices=WiredGamingMouseT1.breathing_map.keys(), help='Breathing')
args = parser.parse_args()
if not args.action: parser.error("Please choose and action.")
level = logging.DEBUG if args.debug else logging.INFO
logging.basicConfig(format='%(levelname)s: %(message)s', level=level)
t1 = WiredGamingMouseT1()
if args.action == 'col': t1.set_color(args.color, args.brightness, args.breathing)
if args.action == 'sp': t1.set_profile(args.profile)
if args.action == 'cs': t1.set_cpi(args.profile, args.steps)
if args.action == 'bb': t1.set_brightness_breathing(args.profile, args.brightness, args.breathing)
if __name__ == "__main__": main()
@Conobi
Copy link

Conobi commented Jun 30, 2016

image
I get these errors when i try the script, i don't understand why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment