razercfg 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. #!/usr/bin/env python3
  2. #
  3. # Razer device commandline configuration tool
  4. #
  5. # Copyright (C) 2007-2011 Michael Buesch <m@bues.ch>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. import sys
  17. import os
  18. import getopt
  19. import time
  20. import re
  21. from pyrazer import *
  22. from configparser import *
  23. try:
  24. PYRAZER_SETUP_PY == True
  25. except:
  26. print("ERROR: Found an old 'pyrazer' module.")
  27. print("You should uninstall razercfg from the system (see README)")
  28. print("and re-install it properly.")
  29. sys.exit(1)
  30. razer = None
  31. def getRazer():
  32. global razer
  33. if razer is None:
  34. razer = Razer()
  35. return razer
  36. class Operation:
  37. def parseProfileValueStr(self, parameter, idstr, nrValues=1):
  38. # Parse profile:value[:value]... string. Default to active
  39. # profile, if not given. May raise ValueError.
  40. split = parameter.split(":")
  41. if len(split) == nrValues:
  42. profile = None
  43. values = split
  44. elif len(split) == nrValues + 1:
  45. profile = int(split[0].strip())
  46. values = split[1:]
  47. if profile == 0:
  48. profile = getRazer().getActiveProfile(idstr) + 1
  49. if profile < 0:
  50. raise ValueError
  51. else:
  52. raise ValueError
  53. return (profile, values)
  54. class OpSleep(Operation):
  55. def __init__(self, seconds):
  56. self.seconds = seconds
  57. def run(self, idstr):
  58. time.sleep(self.seconds)
  59. class OpScan(Operation):
  60. def run(self, idstr):
  61. scanDevices()
  62. class OpReconfigure(Operation):
  63. def run(self, idstr):
  64. reconfigureDevices()
  65. class OpGetFwVer(Operation):
  66. def run(self, idstr):
  67. verTuple = getRazer().getFwVer(idstr)
  68. print("%s: Firmware version %d.%02d" %\
  69. (idstr, verTuple[0], verTuple[1]))
  70. class OpGetProfile(Operation):
  71. def run(self, idstr):
  72. profileId = getRazer().getActiveProfile(idstr)
  73. print("Active profile: %u" % (profileId + 1))
  74. class OpGetFreq(Operation):
  75. def run(self, idstr):
  76. freqs = getRazer().getSupportedFreqs(idstr)
  77. minfo = getRazer().getMouseInfo(idstr)
  78. curProf = getRazer().getActiveProfile(idstr)
  79. if minfo & Razer.MOUSEINFOFLG_GLOBAL_FREQ:
  80. curFreq = getRazer().getCurrentFreq(idstr)
  81. self.printFreq(freqs, curFreq)
  82. if minfo & Razer.MOUSEINFOFLG_PROFILE_FREQ:
  83. profiles = getRazer().getProfiles(idstr)
  84. for profile in profiles:
  85. sys.stdout.write("Profile %2u%s: " %\
  86. (profile + 1,
  87. "*" if profile == curProf else " "))
  88. curFreq = getRazer().getCurrentFreq(idstr, profile)
  89. self.printFreq(freqs, curFreq)
  90. def printFreq(self, freqs, curFreq):
  91. output = []
  92. for freq in freqs:
  93. pfx = "*" if freq == curFreq else " "
  94. output.append("%s%u Hz" % (pfx, freq))
  95. print(", ".join(output))
  96. class OpGetRes(Operation):
  97. def run(self, idstr):
  98. mappings = getRazer().getSupportedDpiMappings(idstr)
  99. profiles = getRazer().getProfiles(idstr)
  100. curProf = getRazer().getActiveProfile(idstr)
  101. for profile in profiles:
  102. sys.stdout.write("Profile %2u%s: " %\
  103. (profile + 1,
  104. "*" if profile == curProf else " "))
  105. curMapping = getRazer().getDpiMapping(idstr, profile)
  106. output = []
  107. pm = [m for m in mappings if m.profileMask == 0 or\
  108. m.profileMask & (1 << profile)]
  109. for mapping in pm:
  110. pfx = "*" if mapping.id == curMapping else " "
  111. r = [ "%u" % r for r in mapping.res if r is not None ]
  112. rStr = "/".join(r)
  113. output.append("%s%u (%s DPI)" % (pfx, mapping.id + 1, rStr))
  114. print(", ".join(output))
  115. class OpPrintLeds(Operation):
  116. def run(self, idstr):
  117. minfo = getRazer().getMouseInfo(idstr)
  118. if minfo & Razer.MOUSEINFOFLG_GLOBAL_LEDS:
  119. sys.stdout.write("Global LEDs: ")
  120. leds = getRazer().getLeds(idstr)
  121. self.printLeds(leds)
  122. if minfo & Razer.MOUSEINFOFLG_PROFILE_LEDS:
  123. profiles = getRazer().getProfiles(idstr)
  124. curProf = getRazer().getActiveProfile(idstr)
  125. for profile in profiles:
  126. sys.stdout.write("Profile %2u%s LEDs: " %\
  127. (profile + 1,
  128. "*" if profile == curProf else " "))
  129. leds = getRazer().getLeds(idstr, profile)
  130. self.printLeds(leds)
  131. def printLeds(self, leds):
  132. output = []
  133. for led in leds:
  134. state = "on" if led.state else "off"
  135. mode = led.mode.toString()
  136. color = ""
  137. if led.color is not None:
  138. color = "color#%02X%02X%02X" %\
  139. (led.color.r, led.color.g, led.color.b)
  140. output.append("%s => %s/%s/%s" % (led.name, state, mode, color))
  141. print(", ".join(output))
  142. class OpSetProfile(Operation):
  143. def __init__(self, param):
  144. self.param = param
  145. def run(self, idstr):
  146. try:
  147. profileId = int(self.param) - 1
  148. error = getRazer().setActiveProfile(idstr, profileId)
  149. if error:
  150. raise RazerEx("Failed to set active profile (%s)" %\
  151. Razer.strerror(error))
  152. except (ValueError) as e:
  153. raise RazerEx("Invalid parameter to --profile option")
  154. class OpSetLedState(Operation):
  155. def __init__(self, param):
  156. self.param = param
  157. def setLed(self, idstr, led, state):
  158. led.state = state
  159. error = getRazer().setLed(idstr, led)
  160. if error:
  161. raise RazerEx("Failed to set LED state (%s)" %\
  162. Razer.strerror(error))
  163. def run(self, idstr):
  164. try:
  165. (profile, config) = self.parseProfileValueStr(self.param, idstr, 2)
  166. ledName = config[0].strip().lower()
  167. newState = razer_str2bool(config[1])
  168. if profile is None:
  169. profile = Razer.PROFILE_INVALID
  170. else:
  171. profile -= 1
  172. leds = getRazer().getLeds(idstr, profile)
  173. if 'all' == ledName:
  174. for led in leds:
  175. self.setLed(idstr, led, newState)
  176. else:
  177. led = [led for led in leds if led.name.lower() == ledName.lower()][0]
  178. self.setLed(idstr, led, newState)
  179. except (IndexError, ValueError):
  180. raise RazerEx("Invalid parameter to --setled option")
  181. class OpSetLedColor(Operation):
  182. def __init__(self, param):
  183. self.param = param
  184. def run(self, idstr):
  185. try:
  186. (profile, config) = self.parseProfileValueStr(self.param, idstr, 2)
  187. ledName = config[0].strip()
  188. newColor = RazerRGB.fromString(config[1])
  189. if profile is None:
  190. profile = Razer.PROFILE_INVALID
  191. else:
  192. profile -= 1
  193. leds = getRazer().getLeds(idstr, profile)
  194. led = [led for led in leds if led.name.lower() == ledName.lower()][0]
  195. led.color = newColor
  196. error = getRazer().setLed(idstr, led)
  197. if error:
  198. raise RazerEx("Failed to set LED color (%s)" %\
  199. Razer.strerror(error))
  200. except (IndexError, ValueError):
  201. raise RazerEx("Invalid parameter to --setledcolor option")
  202. class OpSetLedMode(Operation):
  203. def __init__(self, param):
  204. self.param = param
  205. def run(self, idstr):
  206. try:
  207. (profile, config) = self.parseProfileValueStr(self.param, idstr, 2)
  208. ledName = config[0].strip().lower()
  209. newMode = RazerLEDMode.fromString(config[1].lower())
  210. if profile is None:
  211. profile = Razer.PROFILE_INVALID
  212. else:
  213. profile -= 1
  214. leds = getRazer().getLeds(idstr, profile)
  215. led = [led for led in leds if led.name.lower() == ledName.lower()][0]
  216. led.mode = newMode
  217. error = getRazer().setLed(idstr, led)
  218. if error:
  219. raise RazerEx("Failed to set LED mode (%s)" %\
  220. Razer.strerror(error))
  221. except (KeyError, IndexError, ValueError):
  222. raise RazerEx("Invalid parameter to --setledmode option")
  223. class OpSetRes(Operation):
  224. def __init__(self, param):
  225. self.param = param
  226. def setDpiMapping(self, idstr, profile, axisId, value):
  227. # Get profile mappings.
  228. mappings = [m for m in getRazer().getSupportedDpiMappings(idstr) \
  229. if m.profileMask == 0 or m.profileMask & (1 << profile)]
  230. if value >= 100:
  231. # Value is in DPI.
  232. mappings = [m for m in mappings if value in m.res]
  233. else:
  234. # Value is a mapping ID.
  235. mappings = [m for m in mappings if (value - 1) == m.id]
  236. try:
  237. mappingId = mappings[0].id
  238. except IndexError:
  239. raise RazerEx("Invalid resolution %d" % value)
  240. error = getRazer().setDpiMapping(idstr, profile - 1, mappingId, axisId=axisId)
  241. if error:
  242. raise RazerEx("Failed to set resolution to %u (%s)" %\
  243. (mappingId, Razer.strerror(error)))
  244. def run(self, idstr):
  245. try:
  246. (profile, values) = self.parseProfileValueStr(self.param, idstr)
  247. resolutions = []
  248. for arg in values[0].split(','):
  249. m = re.match('^\d+$', arg)
  250. if m is not None:
  251. resolutions.append((None, int(arg)))
  252. continue
  253. m = re.match('^(\d+)x(\d+)$', arg)
  254. if m is not None:
  255. resolutions.append((0, int(m.group(1))))
  256. resolutions.append((1, int(m.group(2))))
  257. continue
  258. raise ValueError
  259. except ValueError:
  260. raise RazerEx("Invalid parameter to --res option")
  261. if profile is None:
  262. # No profile number was specified. Get the current one.
  263. profile = getRazer().getActiveProfile(idstr) + 1
  264. for axisId, value in resolutions:
  265. self.setDpiMapping(idstr, profile, axisId, value)
  266. class OpSetFreq(Operation):
  267. def __init__(self, param):
  268. self.param = param
  269. def run(self, idstr):
  270. try:
  271. (profile, freq) = self.parseProfileValueStr(self.param, idstr)
  272. freq = int(freq[0])
  273. except ValueError:
  274. raise RazerEx("Invalid parameter to --freq option")
  275. if profile is None:
  276. profile = Razer.PROFILE_INVALID
  277. else:
  278. profile -= 1
  279. error = getRazer().setFrequency(idstr, profile, freq)
  280. if error:
  281. raise RazerEx("Failed to set frequency to %d Hz (%s)" %\
  282. (freq, Razer.strerror(error)))
  283. class OpFlashFw(Operation):
  284. def __init__(self, filename):
  285. self.filename = filename
  286. def run(self, idstr):
  287. p = RazerFirmwareParser(self.filename)
  288. data = p.getImage()
  289. print("Flashing firmware on %s ..." % idstr)
  290. print("!!! DO NOT DISCONNECT ANY DEVICE !!!")
  291. print("Sending %d bytes..." % len(data))
  292. error = getRazer().flashFirmware(idstr, data)
  293. if error:
  294. raise RazerEx("Failed to flash firmware (%s)" % Razer.strerror(error))
  295. print("Firmware successfully flashed.")
  296. # List of operations
  297. class DevOps:
  298. def __init__(self, idstr):
  299. self.idstr = idstr
  300. self.ops = []
  301. def add(self, op):
  302. self.ops.append(op)
  303. def runAll(self):
  304. for op in self.ops:
  305. op.run(self.idstr)
  306. def scanDevices():
  307. getRazer().rescanMice()
  308. mice = getRazer().getMice()
  309. for mouse in mice:
  310. print(mouse)
  311. def reconfigureDevices():
  312. getRazer().rescanDevices()
  313. getRazer().reconfigureDevices()
  314. def exit(exitcode):
  315. sys.exit(exitcode)
  316. def prVersion():
  317. print("Razer device configuration tool")
  318. print("Version", RAZER_VERSION)
  319. def usage():
  320. prVersion()
  321. print("")
  322. print("Usage: razercfg [OPTIONS] [-d DEV DEVOPS] [-d DEV DEVOPS]...")
  323. print("")
  324. print("-h|--help Print this help text")
  325. print("-v|--version Print the program version number")
  326. print("-B|--background Fork into the background")
  327. print("-s|--scan Scan for devices and print the bus IDs")
  328. print("-K|--reconfigure Force-reconfigure all detected devices")
  329. print("")
  330. print("-d|--device DEV Selects the device with the bus ID \"DEV\"")
  331. print(" Use the special value \"mouse\" for DEV to select")
  332. print(" the first razer mouse device found in the system.")
  333. print(" If this option is omitted, the first Razer device found is selected.")
  334. print("")
  335. print("-S|--sleep SECS Sleep SECS seconds.")
  336. print("")
  337. print("Device operations (DEVOPS):")
  338. print("These options apply to the device that is specified with -d")
  339. print("")
  340. print("Options for mice:")
  341. print("-V|--fwver Print the firmware version number")
  342. print("-p|--profile PROF Changes the active profile")
  343. print("-P|--getprofile Prints the active profile")
  344. print("-r|--res [PROF:]RES[xRES] Changes the scan resolution")
  345. print("-R|--getres Prints the resolutions")
  346. print("-f|--freq [PROF:]FREQ Changes the scan frequency")
  347. print("-F|--getfreq Prints the frequencies")
  348. print("-L|--leds List the device supported LED identifiers")
  349. print("-l|--setled [PROF:]LED:(on|off) Toggle the LED with the identifier \"LED\"")
  350. print(" Use the special identifier \"all\"")
  351. print(" to toggle all supported LEDs.")
  352. print("-c|--setledcolor [PROF:]LED:rrggbb Set LED color to RGB 'rrggbb'")
  353. print("-m|--setledmode [PROF:]LED:MODE Set LED mode to MODE ('static', 'spectrum'")
  354. print(" or 'breathing')")
  355. print("")
  356. print("-X|--flashfw FILE Flash a firmware image to the device")
  357. print("")
  358. print("The profile number \"PROF\" may be 0 for the current profile. If omitted,")
  359. print("the global settings are changed (not possible for every device).")
  360. def findDevice(deviceType=None):
  361. if deviceType is None or deviceType == "mouse":
  362. getRazer().rescanMice()
  363. mice = getRazer().getMice()
  364. if mice:
  365. return mice[0] # Return the first idstr
  366. if deviceType:
  367. raise RazerEx("No Razer mouse found in the system")
  368. raise RazerEx("No Razer device found in the system")
  369. def parse_args():
  370. devOpsList = []
  371. currentDevOps = None
  372. try:
  373. (opts, args) = getopt.getopt(sys.argv[1:],
  374. "hvBsKd:r:Rf:FLl:VS:X:c:p:Pm:",
  375. [ "help", "version", "background",
  376. "scan", "reconfigure", "device=", "res=",
  377. "getres", "freq=", "getfreq", "leds", "setled=",
  378. "fwver", "config=", "sleep=", "flashfw=",
  379. "setledcolor=", "setledmode=",
  380. "profile=", "getprofile", ])
  381. except getopt.GetoptError:
  382. usage()
  383. exit(1)
  384. for (o, v) in opts:
  385. if o in ("-h", "--help"):
  386. usage()
  387. exit(0)
  388. if o in ("-v", "--version"):
  389. prVersion()
  390. exit(0)
  391. if o in ("-B", "--background"):
  392. if os.fork() != 0:
  393. exit(0) # Exit parent
  394. if o in ("-s", "--scan"):
  395. ops = currentDevOps
  396. if not currentDevOps:
  397. ops = DevOps(None)
  398. ops.add(OpScan())
  399. if not currentDevOps:
  400. devOpsList.append(ops)
  401. continue
  402. if o in ("-K", "--reconfigure"):
  403. ops = currentDevOps
  404. if not currentDevOps:
  405. ops = DevOps(None)
  406. ops.add(OpReconfigure())
  407. if not currentDevOps:
  408. devOpsList.append(ops)
  409. continue
  410. if o in ("-d", "--device"):
  411. if v == "mouse": # magic; select the first mouse
  412. v = findDevice("mouse")
  413. if currentDevOps and currentDevOps.ops:
  414. devOpsList.append(currentDevOps)
  415. currentDevOps = DevOps(v)
  416. continue
  417. if o in ("-p", "--profile"):
  418. if not currentDevOps:
  419. currentDevOps = DevOps(findDevice())
  420. currentDevOps.add(OpSetProfile(v))
  421. continue
  422. if o in ("-P", "--getprofile"):
  423. if not currentDevOps:
  424. currentDevOps = DevOps(findDevice())
  425. currentDevOps.add(OpGetProfile())
  426. continue
  427. if o in ("-r", "--res"):
  428. if not currentDevOps:
  429. currentDevOps = DevOps(findDevice())
  430. currentDevOps.add(OpSetRes(v))
  431. continue
  432. if o in ("-R", "--getres"):
  433. if not currentDevOps:
  434. currentDevOps = DevOps(findDevice())
  435. currentDevOps.add(OpGetRes())
  436. continue
  437. if o in ("-f", "--freq"):
  438. if not currentDevOps:
  439. currentDevOps = DevOps(findDevice())
  440. currentDevOps.add(OpSetFreq(v))
  441. continue
  442. if o in ("-F", "--getfreq"):
  443. if not currentDevOps:
  444. currentDevOps = DevOps(findDevice())
  445. currentDevOps.add(OpGetFreq())
  446. continue
  447. if o in ("-L", "--leds"):
  448. if not currentDevOps:
  449. currentDevOps = DevOps(findDevice())
  450. currentDevOps.add(OpPrintLeds())
  451. continue
  452. if o in ("-l", "--setled"):
  453. if not currentDevOps:
  454. currentDevOps = DevOps(findDevice())
  455. currentDevOps.add(OpSetLedState(v))
  456. continue
  457. if o in ("-c", "--setledcolor"):
  458. if not currentDevOps:
  459. currentDevOps = DevOps(findDevice())
  460. currentDevOps.add(OpSetLedColor(v))
  461. continue
  462. if o in ("-m", "--setledmode"):
  463. if not currentDevOps:
  464. currentDevOps = DevOps(findDevice())
  465. currentDevOps.add(OpSetLedMode(v))
  466. continue
  467. if o in ("-V", "--fwver"):
  468. if not currentDevOps:
  469. currentDevOps = DevOps(findDevice())
  470. currentDevOps.add(OpGetFwVer())
  471. continue
  472. if o in ("-S", "--sleep"):
  473. ops = currentDevOps
  474. if not currentDevOps:
  475. ops = DevOps(None)
  476. try:
  477. v = float(v)
  478. except ValueError:
  479. raise RazerEx("Value for -S|--sleep must be a floating point value")
  480. ops.add(OpSleep(v))
  481. if not currentDevOps:
  482. devOpsList.append(ops)
  483. continue
  484. if o in ("-X", "--flashfw"):
  485. if not currentDevOps:
  486. raise RazerEx("Must specify a device (-d) before -X|--flashfw")
  487. currentDevOps.add(OpFlashFw(v))
  488. continue
  489. if currentDevOps and currentDevOps.ops:
  490. devOpsList.append(currentDevOps)
  491. if not devOpsList:
  492. usage()
  493. exit(1)
  494. return devOpsList
  495. def main():
  496. try:
  497. devOpsList = parse_args()
  498. for devOps in devOpsList:
  499. devOps.runAll()
  500. except (RazerEx) as e:
  501. print(e)
  502. return 1
  503. return 0
  504. if __name__ == "__main__":
  505. exit(main())