Simple RGB LED controller for Mac OS X
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

caselights 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #!/usr/bin/env python3
  2. # CaseLights Linux Qt System Tray client
  3. # depends on:
  4. # - python-pyqt5
  5. # - python-pyserial
  6. import sys
  7. import os.path
  8. import threading
  9. import time
  10. import colorsys
  11. import serial, serial.tools, serial.tools.list_ports
  12. from PyQt5 import QtWidgets, QtGui, QtCore
  13. from PyQt5.QtWidgets import QSystemTrayIcon, QAction, QMenu
  14. from PyQt5.QtGui import QIcon, QPixmap
  15. from PyQt5.QtCore import QCoreApplication, QSettings
  16. class CaseLights():
  17. name = "CaseLights"
  18. vendor = "xythobuz"
  19. version = "0.1"
  20. iconPath = "/usr/share/pixmaps/"
  21. iconName = "caselights_icon.png"
  22. staticColors = [
  23. [ "Off", "0", "0", "0", None ],
  24. [ "Red", "255", "0", "0", None ],
  25. [ "Green", "0", "255", "0", None ],
  26. [ "Blue", "0", "0", "255", None ],
  27. [ "White", "255", "255", "255", None ],
  28. ]
  29. slowFadeUpdateFreq = 5
  30. fastFadeUpdateFreq = 20
  31. fadeSaturation = 1.0
  32. fadeValue = 1.0
  33. fadeHueCounter = 0
  34. usedPort = None
  35. serial = None
  36. animation = None
  37. animationRunning = False
  38. menu = None
  39. portMenu = None
  40. portActions = None
  41. refreshAction = None
  42. quitAction = None
  43. def __init__(self):
  44. app = QtWidgets.QApplication(sys.argv)
  45. QCoreApplication.setApplicationName(self.name)
  46. if not QSystemTrayIcon.isSystemTrayAvailable():
  47. print("System Tray is not available on this platform!")
  48. sys.exit(0)
  49. self.readSettings()
  50. if self.usedPort is not None:
  51. self.connect()
  52. self.menu = QMenu()
  53. colorMenu = QMenu("&Colors")
  54. for color in self.staticColors:
  55. color[4] = QAction(color[0])
  56. colorMenu.addAction(color[4])
  57. colorMenu.triggered.connect(self.setStaticColor)
  58. self.menu.addMenu(colorMenu)
  59. animMenu = QMenu("&Animations")
  60. noFadeAction = QAction("Off")
  61. noFadeAction.triggered.connect(self.animOff)
  62. animMenu.addAction(noFadeAction)
  63. slowFadeAction = QAction("Slow Fade")
  64. slowFadeAction.triggered.connect(self.slowFadeOn)
  65. animMenu.addAction(slowFadeAction)
  66. fastFadeAction = QAction("Fast Fade")
  67. fastFadeAction.triggered.connect(self.fastFadeOn)
  68. animMenu.addAction(fastFadeAction)
  69. self.menu.addMenu(animMenu)
  70. lightMenu = QMenu("&UV-Light")
  71. lightOnAction = QAction("O&n")
  72. lightOnAction.triggered.connect(self.lightsOn)
  73. lightMenu.addAction(lightOnAction)
  74. lightOffAction = QAction("O&ff")
  75. lightOffAction.triggered.connect(self.lightsOff)
  76. lightMenu.addAction(lightOffAction)
  77. self.menu.addMenu(lightMenu)
  78. self.refreshSerialPorts()
  79. self.quitAction = QAction("&Quit")
  80. self.quitAction.triggered.connect(self.exit)
  81. self.menu.addAction(self.quitAction)
  82. iconPathName = ""
  83. if os.path.isfile(self.iconName):
  84. iconPathName = self.iconName
  85. elif os.path.isfile(self.iconPath + self.iconName):
  86. iconPathName = self.iconPath + self.iconName
  87. else:
  88. print("no icon found")
  89. icon = QIcon()
  90. if iconPathName is not "":
  91. pic = QPixmap(32, 32)
  92. pic.load(iconPathName)
  93. icon = QIcon(pic)
  94. trayIcon = QSystemTrayIcon(icon)
  95. trayIcon.setToolTip(self.name + " " + self.version)
  96. trayIcon.setContextMenu(self.menu)
  97. trayIcon.setVisible(True)
  98. sys.exit(app.exec_())
  99. def exit(self):
  100. if self.serial is not None:
  101. if self.serial.is_open:
  102. print("stopping animations")
  103. self.animOff()
  104. print("turning off lights")
  105. self.serial.write(b'RGB 0 0 0\n')
  106. self.serial.write(b'UV 0\n')
  107. print("closing connection")
  108. self.serial.close()
  109. QCoreApplication.quit()
  110. def readSettings(self):
  111. settings = QSettings(self.vendor, self.name)
  112. self.usedPort = settings.value("serial_port")
  113. if self.usedPort is not None:
  114. print("serial port stored: " + self.usedPort)
  115. else:
  116. print("no serial port stored")
  117. def writeSettings(self):
  118. settings = QSettings(self.vendor, self.name)
  119. settings.setValue("serial_port", self.usedPort)
  120. if self.usedPort is not None:
  121. print("storing serial port: " + self.usedPort)
  122. else:
  123. print("not storing any serial port")
  124. del settings
  125. def refreshSerialPorts(self):
  126. self.portMenu = QMenu("Port")
  127. ports = serial.tools.list_ports.comports()
  128. self.portActions = []
  129. for port in ports:
  130. action = QAction(port.device)
  131. self.portActions.append(action)
  132. self.portMenu.addAction(action)
  133. self.portMenu.triggered.connect(self.selectSerialPort)
  134. if self.refreshAction == None:
  135. self.refreshAction = QAction("&Refresh")
  136. self.refreshAction.triggered.connect(self.refreshSerialPorts)
  137. self.portMenu.addAction(self.refreshAction)
  138. self.menu.insertMenu(self.quitAction, self.portMenu)
  139. def selectSerialPort(self, action):
  140. self.usedPort = action.text()
  141. self.writeSettings()
  142. if self.connect():
  143. self.portMenu.setActiveAction(action)
  144. def connect(self):
  145. if self.usedPort is None:
  146. print("not connecting to any serial port")
  147. return False
  148. if self.serial is not None:
  149. print("closing previous port")
  150. self.serial.close()
  151. self.serial = serial.Serial()
  152. self.serial.port = self.usedPort
  153. self.serial.baudrate = 115200
  154. self.serial.open()
  155. if self.serial.is_open:
  156. print("connected to: " + self.usedPort)
  157. else:
  158. print("error connecting to: " + self.usedPort)
  159. return self.serial.is_open
  160. def printRGBStrings(self, rs, gs, bs):
  161. if self.serial.is_open:
  162. r = str.encode(rs)
  163. g = str.encode(gs)
  164. b = str.encode(bs)
  165. rgb = b'RGB ' + r + b' ' + g + b' ' + b + b'\n'
  166. self.serial.write(rgb)
  167. else:
  168. print("not connected")
  169. def setStaticColor(self, action):
  170. self.animOff()
  171. for color in self.staticColors:
  172. if color[4] is action:
  173. self.printRGBStrings(color[1], color[2], color[3])
  174. return True
  175. print("color not found")
  176. return False
  177. def hsvToRgb(self, h, s, v):
  178. (r, g, b) = colorsys.hsv_to_rgb(h, s, v)
  179. return (round(r * 255), round(g * 255), round(b * 255))
  180. def fadeRunner(self, freq):
  181. while self.animationRunning is True:
  182. self.fadeHueCounter += 1
  183. if self.fadeHueCounter >= 360:
  184. self.fadeHueCounter = 0
  185. (r, g, b) = self.hsvToRgb(self.fadeHueCounter / 360.0, self.fadeSaturation, self.fadeValue)
  186. self.printRGBStrings(str(r), str(g), str(b))
  187. time.sleep(1.0 / freq)
  188. def slowFadeOn(self):
  189. self.animOff()
  190. self.animationRunning = True
  191. self.animation = threading.Thread(target=self.fadeRunner, args=[self.slowFadeUpdateFreq])
  192. self.animation.start()
  193. def fastFadeOn(self):
  194. self.animOff()
  195. self.animationRunning = True
  196. self.animation = threading.Thread(target=self.fadeRunner, args=[self.fastFadeUpdateFreq])
  197. self.animation.start()
  198. def animOff(self):
  199. self.animationRunning = False
  200. self.animation = None
  201. self.printRGBStrings("0", "0", "0")
  202. time.sleep(0.1)
  203. def lightsOn(self):
  204. if self.serial.is_open:
  205. self.serial.write(b'UV 1\n')
  206. else:
  207. print("not connected")
  208. def lightsOff(self):
  209. if self.serial.is_open:
  210. self.serial.write(b'UV 0\n')
  211. else:
  212. print("not connected")
  213. if __name__ == "__main__":
  214. tray = CaseLights()