/
ctrl.py
executable file
·155 lines (124 loc) · 4.87 KB
/
ctrl.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import hid
import math
from enum import Enum
class LedChannel(Enum):
R_STATIC = 0x00
R_BREATHE = 0x01
R_CYCLE = 0x02
LOGO = 0x05
FAN = 0x06
R_RAINBOW = 0x07
R_SWIRL = 0x0A
OFF = 0xFE
class LedMode(Enum):
OFF = 0x00
STATIC = 0x01
CYCLE = 0x02
BREATHE = 0x03
R_RAINBOW = 0x05
R_SWIRL = 0x4a
R_DEFAULT = 0xFF
class CMRGBController:
VENDOR_ID = 0x2516
PRODUCT_ID = 0x0051
PRODUCT_STR = 'CYRM02p0303h00E0r0100'
IFACE_NUM = 1
@staticmethod
def new_packet(fill, *args):
pkt = bytearray([fill] * 65)
pkt[0] = 0 # Report ID
for i, v in enumerate(args):
pkt[i + 1] = v
return pkt
# 52 get / 51 SET - looks like even numbers are getters and odd setters
P_POWER_ON = new_packet.__func__(0, 0x41, 0x80)
P_POWER_OFF = new_packet.__func__(0, 0x41, 0x03)
P_RESTORE = new_packet.__func__(0, 0x41)
P_LED_LOAD = new_packet.__func__(0, 0x50)
P_LED_SAVE = new_packet.__func__(0, 0x50, 0x55)
P_APPLY = new_packet.__func__(0, 0x51, 0x28, 0x00, 0x00, 0xe0)
P_MAGIC_2 = new_packet.__func__(0, 0x51, 0x96)
P_MIRAGE_OFF = new_packet.__func__(0, 0x51, 0x71, 0x00, 0x00, 0x01, 0x00, 0xFF, 0x4A, 0x02, 0x00, 0xFF, 0x4A, 0x03,
0x00, 0xFF, 0x4A, 0x04, 0x00, 0xFF, 0x4A)
P_GET_VER = new_packet.__func__(0, 0x12, 0x20)
def __init__(self):
self.__init_hid_device()
self.__init_controller()
def __init_hid_device(self):
device_list = [x for x in hid.enumerate(self.VENDOR_ID, self.PRODUCT_ID)
if x['interface_number'] == self.IFACE_NUM]
if len(device_list) == 0:
raise Exception("No devices found. See: https://github.com/gfduszynski/cm-rgb/issues/9")
self.device = hid.device()
try:
self.device.open_path(device_list[0]["path"])
except OSError as err:
print("Failed to access usb device. See: https://github.com/gfduszynski/cm-rgb/wiki/1.-Installation-&-Configuration#3-configuration")
print("Also check if other process is not using the device.\n")
raise
def __init_controller(self):
# Without this controller wont accept changes
self.send_packet(self.P_POWER_ON)
# No idea what this does but it's in original startup sequence
self.send_packet(self.P_MAGIC_2)
# Some sort of apply / flush op
self.apply()
def send_packet(self, packet):
self.device.write(packet)
return self.device.read(64)
def apply(self):
return self.send_packet(self.P_APPLY)
def save(self):
return self.send_packet(self.P_LED_SAVE)
def restore(self):
self.send_packet(self.enableMirage(0,0,0))
self.send_packet(self.P_LED_LOAD)
self.send_packet(self.P_POWER_OFF)
self.send_packet(self.P_RESTORE)
self.apply()
def disableMirage(self):
self.send_packet(self.enableMirage(0,0,0))
def enableMirage(self, rHz, gHz, bHz):
def hzToBytes(hz):
if hz == 0:
return [0x00, 0xFF, 0x4A]
v = 187498.0 / hz
vMul = math.floor(v/256.0)
vRem = v / (vMul + 1)
return [min(vMul,255), math.floor(vRem % 1 * 256), math.floor(vRem)]
rBytes = hzToBytes(rHz)
gBytes = hzToBytes(gHz)
bBytes = hzToBytes(bHz)
pkt = self.new_packet(0, 0x51, 0x71, 0x00, 0x00,
0x01, 0x00, 0xFF, 0x4A, # This part is probably for white LED's that did not found their way into final cooler
0x02, rBytes[0], rBytes[1], rBytes[2],
0x03, gBytes[0], gBytes[1], gBytes[2],
0x04, bBytes[0], bBytes[1], bBytes[2])
return self.send_packet(pkt)
def getVersion(self):
reply = self.send_packet(self.P_GET_VER)
fv = bytearray(16)
i = 0
while i < 16 :
if reply[i + 0x08] != 0 :
fv[int(i / 2)] = fv[int(i / 2)] + reply[i + 0x08]
else:
break
i+=2
return fv.decode("utf-8")
# color_source 0x20 takes supplied color for breathe mode
def set_channel(self, channel, mode, brightness, r, g, b, speed=0xff, color_source=0x20):
pkt = self.new_packet(0xff, 0x51, 0x2C, 0x01, 0x0, channel.value, speed, color_source, mode.value, 0xFF,
brightness, r, g, b, 0x00, 0x00, 0x00)
return self.send_packet(pkt)
def assign_leds_to_channels(self, logo, fan, *ring):
pkt = self.new_packet(0x00, 0x51, 0xA0, 0x01, 0, 0, 0x03, 0, 0, logo.value, fan.value)
j = 0
# Ring LED's
for i in range(11, 26):
if j < len(ring):
pkt[i] = ring[j].value
j += 1
else:
pkt[i] = LedChannel.OFF.value
return self.send_packet(pkt)