def _write_colors(self, cid, mode, colors, sval, direction='forward',): mval, mod3, movingFlag, mincolors, maxcolors = self._COLOR_MODES[mode] color_count = len(colors) if maxcolors == 40: led_padding = [0x00, 0x00, 0x00]*(maxcolors - color_count) # turn off remaining LEDs leds = list(itertools.chain(*colors)) + led_padding self._write([0x22, 0x10, cid, 0x00] + leds[0:60]) # send first 20 colors to device (3 bytes per color) self._write([0x22, 0x11, cid, 0x00] + leds[60:]) # send remaining colors to device self._write([0x22, 0xa0, cid, 0x00, mval, mod3, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x32, 0x00, 0x00, 0x01]) elif mode == 'wings': # wings requires special handling for [g, r, b] in colors: self._write([0x22, 0x10, cid]) # clear out all independent LEDs self._write([0x22, 0x11, cid]) # clear out all independent LEDs color_lists = [] * 3 color_lists[0] = [g, r, b] * 8 color_lists[1] = [int(x // 2.5) for x in color_lists[0]] color_lists[2] = [int(x // 4) for x in color_lists[1]] for i in range(8): # send color scheme first, before enabling wings mode mod = 0x05 if i in [3, 7] else 0x01 msg = ([0x22, 0x20, cid, i, 0x04, 0x39, 0x00, mod, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x05, 0x85, 0x05, 0x85, 0x05, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) self._write(msg + color_lists[i % 4]) self._write([0x22, 0x03, cid, 0x08]) # this actually enables wings mode else: byte7 = movingFlag # sets 'moving' flag for moving alternating modes byte8 = map_direction(direction, 0, 1) # sets 'backward' flag byte9 = mod3 if mval == 0x03 else color_count # specifies 'marquee' LED size byte10 = mod3 if mval == 0x05 else 0x00 # specifies LED size for 'alternating' modes header = [0x28, 0x03, cid, 0x00, mval, sval, byte7, byte8, byte9, byte10] self._write(header + list(itertools.chain(*colors)))
def _write_colors(self, cid, mode, colors, sval, direction='forward'): mval, mod3, mod4, _, _ = self._COLOR_MODES[mode] # generate steps from mode and colors: usually each color set by the user generates # one step, where it is specified to all leds and the device handles the animation; # but in super mode there is a single step and each color directly controls a led mod3 += map_direction(direction, 0, 0x10) if 'super' in mode: steps = [list(itertools.chain(*colors))] else: steps = [color * 40 for color in colors] for i, leds in enumerate(steps): seq = i << 5 byte4 = sval | seq | mod4 self._write([0x2, 0x4b, mval, mod3, byte4] + leds[0:57]) self._write([0x3] + leds[57:])
def set_color(self, channel, mode, colors, speed='normal', direction='forward', **kwargs): """Set the color mode for a specific channel.""" if not self.supports_lighting: raise NotSupportedByDevice() if mode == 'super': _LOGGER.warning( 'deprecated mode, move to super-fixed, super-breathing or super-wave' ) mode = 'super-fixed' if 'backwards' in mode: _LOGGER.warning( 'deprecated mode, move to direction=backward option') mode = mode.replace('backwards-', '') direction = 'backward' mval, mod2, mod4, mincolors, maxcolors, ringonly = _COLOR_MODES[mode] mod2 += map_direction(direction, 0, 0x10) if ringonly and channel != 'ring': _LOGGER.warning( 'mode=%s unsupported with channel=%s, dropping to ring', mode, channel) channel = 'ring' steps = self._generate_steps(colors, mincolors, maxcolors, mode, ringonly) sval = _ANIMATION_SPEEDS[speed] byte2 = mod2 | _COLOR_CHANNELS[channel] for i, leds in enumerate(steps): seq = i << 5 byte4 = sval | seq | mod4 logo = [leds[0][1], leds[0][0], leds[0][2]] ring = list(itertools.chain(*leds[1:])) self._write([0x2, 0x4c, byte2, mval, byte4] + logo + ring)
def set_color(self, channel, mode, colors, direction='forward', speed='medium', start_led=1, maximum_leds=1, **kwargs): """Set the color of each LED. The table bellow summarizes the available channels, modes, and their associated maximum number of colors for each device family. | Channel | Mode | Num colors | | -------- | ----------- | ---------- | | led | off | 0 | | led | fixed | 1 | | led | color_shift | 2 | | led | color_pulse | 2 | | led | color_wave | 2 | | led | visor | 2 | | led | blink | 2 | | led | marquee | 1 | | led | sequential | 1 | | led | rainbow | 0 | | led | rainbow2 | 0 | """ # a special mode to clear the current led settings. # this is usefull if the the user wants to use a led mode for multiple devices if mode == 'clear': self._data.store('saved_effects', None) return colors = list(colors) expanded = colors[:3] c = itertools.chain(*((r, g, b) for r, g, b in expanded)) colors = list(c) direction = map_direction(direction, _LED_DIRECTION_FORWARD, _LED_DIRECTION_BACKWARD) speed = _LED_SPEED_SLOW if speed == 'slow' else _LED_SPEED_FAST if speed == 'fast' else _LED_SPEED_MEDIUM start_led = clamp(start_led, 1, 204) - 1 num_leds = clamp(maximum_leds, 1, 204 - start_led - 1) random_colors = 0x00 if mode == 'off' or len(colors) != 0 else 0x01 mode_val = _MODES.get(mode, -1) if mode_val == -1: raise ValueError(f'mode "{mode}" is not valid') # FIXME clears on 'off', while the docs only mention this behavior for 'clear' saved_effects = [] if mode == 'off' else self._data.load( 'saved_effects', default=[]) for led_channel in self._get_hw_led_channels(channel): lighting_effect = { 'channel': led_channel, 'start_led': start_led, 'num_leds': num_leds, 'mode': mode_val, 'speed': speed, 'direction': direction, 'random_colors': random_colors, 'colors': colors } saved_effects += [lighting_effect] # check to make sure that too many LED effects are not being sent. # the max seems to be 8 as found here https://github.com/liquidctl/liquidctl/issues/154#issuecomment-762372583 if len(saved_effects) > 8: _LOGGER.warning( f'too many lighting effects. Run `liquidctl set {channel} color clear` to reset the effect' ) return # start sending the led commands self._send_command(_CMD_RESET_LED_CHANNEL, [led_channel]) self._send_command(_CMD_BEGIN_LED_EFFECT, [led_channel]) self._send_command(_CMD_SET_LED_CHANNEL_STATE, [led_channel, 0x01]) # FIXME clears on 'off', while the docs only mention this behavior for 'clear' self._data.store('saved_effects', None if mode == 'off' else saved_effects) for effect in saved_effects: config = [ effect.get('channel'), effect.get('start_led'), effect.get('num_leds'), effect.get('mode'), effect.get('speed'), effect.get('direction'), effect.get('random_colors'), 0xff ] + effect.get('colors') self._send_command(_CMD_LED_EFFECT, config) self._send_command(_CMD_LED_COMMIT, [0xff])
def _write_colors(self, cid, mode, colors, sval, direction): mval, size_variant, speed_scale, mincolors, maxcolors = _COLOR_MODES[mode] color_count = len(colors) if 'super-fixed' == mode or 'super-breathing' == mode: color = list(itertools.chain(*colors)) + [0x00, 0x00, 0x00] * (maxcolors - color_count) speed_value = _SPEED_VALUE[speed_scale][sval] self._write([0x22, 0x10, cid, 0x00] + color) self._write([0x22, 0x11, cid, 0x00]) self._write([0x22, 0xa0, cid, 0x00, mval] + speed_value + [0x08, 0x00, 0x00, 0x80, 0x00, 0x32, 0x00, 0x00, 0x01]) elif mode == 'wings': # wings requires special handling self._write([0x22, 0x10, cid]) # clear out all independent LEDs self._write([0x22, 0x11, cid]) # clear out all independent LEDs color_lists = {} color_lists[0] = colors[0] * 2 color_lists[1] = [int(x // 2.5) for x in color_lists[0]] color_lists[2] = [int(x // 4) for x in color_lists[1]] color_lists[3] = [0x00] * 8 speed_value = _SPEED_VALUE[speed_scale][sval] for i in range(8): # send color scheme first, before enabling wings mode mod = 0x05 if i in [3, 7] else 0x01 alt = [0x04, 0x84] if i // 4 == 0 else [0x84, 0x04] msg = ([0x22, 0x20, cid, i, 0x04] + speed_value + [mod] + [0x00] * 7 + [0x02] + alt + [0x00] * 10) self._write(msg + color_lists[i % 4]) self._write([0x22, 0x03, cid, 0x08]) # this actually enables wings mode else: opcode = [0x2a, 0x04] address = [cid, cid] speed_value = _SPEED_VALUE[speed_scale][sval] header = opcode + address + [mval] + speed_value color = list(itertools.chain(*colors)) + [0, 0, 0] * (16 - color_count) if 'marquee' in mode: backward_byte = 0x04 elif mode == 'starry-night' or 'moving-alternating' in mode: backward_byte = 0x01 else: backward_byte = 0x00 backward_byte += map_direction(direction, 0, 0x02) if mode == 'fading' or mode == 'pulse' or mode == 'breathing': mode_related = 0x08 elif mode == 'tai-chi': mode_related = 0x05 elif mode == 'water-cooler': mode_related = 0x05 color_count = 0x01 elif mode == 'loading': mode_related = 0x04 else: mode_related = 0x00 static_byte = _STATIC_VALUE[cid] led_size = size_variant if mval == 0x03 or mval == 0x05 else 0x03 footer = [backward_byte, color_count, mode_related, static_byte, led_size] self._write(header + color + footer)