From 4acc4faeaaed3fc0850070c5c55cbc403d39d1ea Mon Sep 17 00:00:00 2001 From: Andreas Wuerl Date: Sun, 3 Dec 2017 21:57:26 +0100 Subject: [PATCH 1/3] add some visualizations --- server/drivers/apa102.py | 30 +- server/lightshows/__active__.py | 22 +- server/lightshows/__init__.py | 23 +- server/lightshows/audio.py | 129 ++ server/lightshows/jump.py | 87 ++ server/lightshows/rgbtest.py | 11 +- server/lightshows/starlight.py | 50 + server/lightshows/templates/base.py | 1 + server/lightshows/theaterchase.py | 2 +- server/lightshows/twocolorblend.py | 1 - server/server.py | 8 + ui/nodered.json | 2106 ++++++++++++++++----------- 12 files changed, 1556 insertions(+), 914 deletions(-) create mode 100755 server/lightshows/audio.py create mode 100755 server/lightshows/jump.py create mode 100644 server/lightshows/starlight.py diff --git a/server/drivers/apa102.py b/server/drivers/apa102.py index bca592b..9b5a1ee 100644 --- a/server/drivers/apa102.py +++ b/server/drivers/apa102.py @@ -1,9 +1,8 @@ # Driver for APA102 LED strips (aka "DotStar") # (c) 2015 Martin Erzberger, 2016-2017 Simon Leiner # licensed under the GNU Public License, version 2 - -import spidev from multiprocessing import Array as SyncedArray +from typing import Callable, Tuple from drivers import LEDStrip from helpers.color import grayscale_correction @@ -61,9 +60,8 @@ def __init__(self, num_leds: int, max_clock_speed_hz: int = 4000000, max_global_ raise Exception("The APA102 LED driver does not support strips of more than 1024 LEDs") # SPI connection - self.spi = spidev.SpiDev() # Init the SPI device - self.spi.open(0, 1) # Open SPI port 0, slave device (CS) 1 - self.spi.max_speed_hz = self.max_clock_speed_hz # should not be higher than 8000000 + self.spi_transmit, self.spi_close = self.init_spi() + self.leds = [self.led_prefix(self._global_brightness), 0, 0, 0] * self.num_leds # 4 bytes per LED self.synced_buffer = SyncedArray('i', self.leds) @@ -71,6 +69,18 @@ def __init__(self, num_leds: int, max_clock_speed_hz: int = 4000000, max_global_ self.max_refresh_time_sec = 25E-6 * self.num_leds #: the maximum time the whole strip takes to refresh self.__sk9822_compatibility_mode = True #: be compatible with SK9822 chips? see: https://goo.gl/ePlcaI + def init_spi(self) -> Tuple[Callable, Callable]: + try: + import Adafruit_GPIO.SPI as SPI + spi = SPI.SpiDev(0, 0, self.max_clock_speed_hz) + return spi.write, spi.close + except ImportError: + import spidev + spi = spidev.SpiDev() # Init the SPI device + spi.open(0, 1) # Open SPI port 0, slave device (CS) 1 + spi.max_speed_hz = self.max_clock_speed_hz # should not be higher than 8000000 + return spi.xfer2, spi.close + def on_color_change(self, led_num, red: float, green: float, blue: float) -> None: """\ Changes the message buffer after a pixel was changed in the global color buffer. @@ -128,7 +138,7 @@ def led_prefix(cls, brightness: float) -> int: def close(self) -> None: """Closes the SPI connection to the strip.""" - self.spi.close() + self.spi_close() @staticmethod def spi_start_frame() -> list: @@ -141,11 +151,11 @@ def spi_start_frame() -> list: def show(self) -> None: """sends the buffered color and brightness values to the strip""" - self.spi.xfer2(self.spi_start_frame()) - self.spi.xfer2(self.leds) # SPI takes up to 4096 Integers. So we are fine for up to 1024 LEDs. + self.spi_transmit(self.spi_start_frame()) + self.spi_transmit(self.leds) # SPI takes up to 4096 Integers. So we are fine for up to 1024 LEDs. if self.__sk9822_compatibility_mode: - self.spi.xfer2(self.spi_start_frame()) - self.spi.xfer2(self.spi_end_frame(self.num_leds)) + self.spi_transmit(self.spi_start_frame()) + self.spi_transmit(self.spi_end_frame(self.num_leds)) @staticmethod def spi_end_frame(num_leds) -> list: diff --git a/server/lightshows/__active__.py b/server/lightshows/__active__.py index 7862b2c..60f591c 100644 --- a/server/lightshows/__active__.py +++ b/server/lightshows/__active__.py @@ -5,12 +5,16 @@ from lightshows import * -shows = {'christmas': christmas.Christmas, # A list of available shows - 'clear': clear.Clear, - 'rainbow': rainbow.Rainbow, - 'rgbtest': rgbtest.RGBTest, - 'spinthebottle': spinthebottle.SpinTheBottle, - 'solidcolor': solidcolor.SolidColor, - 'theaterchase': theaterchase.TheaterChase, - 'twocolorblend': twocolorblend.TwoColorBlend, - } +shows = { + 'christmas': christmas.Christmas, # A list of available shows + 'clear': clear.Clear, + 'rainbow': rainbow.Rainbow, + 'rgbtest': rgbtest.RGBTest, + 'spinthebottle': spinthebottle.SpinTheBottle, + 'solidcolor': solidcolor.SolidColor, + 'theaterchase': theaterchase.TheaterChase, + 'twocolorblend': twocolorblend.TwoColorBlend, + 'starlight': starlight.Starlight, + 'jump': jump.Jump, + 'audio_spectrum': audio.AudioSpectrum, +} diff --git a/server/lightshows/__init__.py b/server/lightshows/__init__.py index a891beb..08743dc 100644 --- a/server/lightshows/__init__.py +++ b/server/lightshows/__init__.py @@ -8,12 +8,17 @@ and displays animations on an LED strip. """ -__all__ = ['christmas', - 'clear', - 'rainbow', - 'rgbtest', - 'solidcolor', - 'spinthebottle', - 'strandtest', - 'theaterchase', - 'twocolorblend'] +__all__ = [ + 'audio', + 'christmas', + 'clear', + 'jump', + 'rainbow', + 'rgbtest', + 'solidcolor', + 'spinthebottle', + 'starlight', + 'strandtest', + 'theaterchase', + 'twocolorblend' +] diff --git a/server/lightshows/audio.py b/server/lightshows/audio.py new file mode 100755 index 0000000..bb3ad04 --- /dev/null +++ b/server/lightshows/audio.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +# 8 bar Audio equaliser using MCP2307 +import logging +from struct import unpack + +import alsaaudio as aa +import numpy as np + +from drivers import LEDStrip +from lightshows.templates.colorcycle import ColorCycle + +log = logging.getLogger(__name__) + + +def calculate_levels(data, chunk, sample_rate): + # Convert raw data to numpy array + data = unpack("%dh" % (len(data) / 2), data) + data = np.array(data, dtype='h') + # Apply FFT - real data so rfft used + fourier = np.fft.rfft(data) + # Remove last element in array to make it the same size as chunk + fourier = np.delete(fourier, len(fourier) - 1) + # Find amplitude + power = np.log10(np.abs(fourier)) ** 2 + # Araange array into 8 rows for the 8 bars on LED matrix + power = np.reshape(power, (1024, -1)) + matrix = np.int_(np.average(power, axis=1) / 4) + return matrix + + +def combine_color(red, green, blue, brightness: int): + brightness_factor = brightness / 100 + """Make one 3*8 byte color value.""" + + return (np.clip(red * brightness_factor, 0, 255), np.clip(green * brightness_factor, 0, 255), + np.clip(blue * brightness_factor, 0, 255)) + + +def wheel(value, threshold): + """Get a color from a color wheel; Green -> Red -> Blue -> Green""" + + wheel_pos = value - threshold + brightness = value if value > threshold else 0 + + if wheel_pos > 255: + wheel_pos = 255 # Safeguard + if wheel_pos < 85: # Red -> Green + return combine_color(255 - wheel_pos * 3, wheel_pos * 3, 0, brightness) + if wheel_pos < 170: # Green -> Blue + wheel_pos -= 85 + return combine_color(0, 255 - wheel_pos * 3, wheel_pos * 3, brightness) + # Blue -> Magenta + wheel_pos -= 170 + return combine_color(0, wheel_pos * 3, 255, brightness) + + +def brightness(value): + if value < threshold: + return 0 + if value < 4 * threshold: + return 1 + return min(value - threshold, 100) + + +threshold = 15 + + +class AudioSpectrum(ColorCycle): + + def __init__(self, strip: LEDStrip, parameters: dict): + super().__init__(strip, parameters) + self.data_in = None + self.chunk = 0 + self.sample_rate = 0 + self.first_run = None + + def init_parameters(self): + super().init_parameters() + self.set_parameter('num_steps_per_cycle', 255) + self.set_parameter('pause_sec', 0.001) + + def before_start(self) -> None: + log.info("set up audio") + # Set up audio + self.sample_rate = 44100 + no_channels = 2 + self.chunk = 2048 # Use a multiple of 8 + self.data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, cardindex=0) + self.data_in.setchannels(no_channels) + self.data_in.setrate(self.sample_rate) + self.data_in.setformat(aa.PCM_FORMAT_S16_LE) + self.data_in.setperiodsize(self.chunk) + + self.strip.clear_strip() + self.first_run = True + + def shutdown(self) -> None: + pass + + def update(self, current_step: int, current_cycle: int) -> bool: + if not self.first_run: + self.data_in.pause(0) # Resume capture + else: + self.first_run = False + + l, data = self.data_in.read() # Read data from device + + self.data_in.pause(1) # Pause capture whilst RPi processes data + + if l > 0: + try: + matrix = calculate_levels(data, self.chunk, self.sample_rate) + line = [int((1 << matrix[i]) - 1) for i in range(100)] + for index, value in enumerate(line): + rgb = wheel(value, threshold) + # brightness = int(max(1, math.log(value - threshold + 1))) if value >= threshold else 0 + # brightness = value - threshold + 1 if value >= threshold else 0 + self.strip.set_pixel(index, *rgb) + self.strip.set_pixel(self.strip.num_leds - index, *rgb) + except Exception as e: + if not hasattr(e, "message") or e.message != "not a whole number of frames": + raise e + log.info("frame error") + return False + return True + else: + print(f"skipping l: {l}, data: {data}") + return False diff --git a/server/lightshows/jump.py b/server/lightshows/jump.py new file mode 100755 index 0000000..6eb407d --- /dev/null +++ b/server/lightshows/jump.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +import logging +import math + +from drivers import LEDStrip +from lightshows.templates.colorcycle import ColorCycle + +log = logging.getLogger(__name__) + + +class Ball(object): + + def __init__(self, height, stripe, color): + self.height = height - stripe + self.stripe = stripe + self.width = 2 * math.sqrt(self.height) + self.center = self.width / 2.0 + self.color = color + self.period = 0 + self.next = False + + def get_pos(self, t): + current_period = int(math.floor(t / self.width)) + + if self.period != current_period: + self.period = current_period + self.next = True + + return int(self.height - (t % self.width - self.center) ** 2) + + def is_next(self): + if self.next: + log.debug("next %d", self.height) + self.next = False + return True + return False + + +class Jump(ColorCycle): + """\ + Rotates a rainbow color wheel around the strip. + + No parameters necessary + """ + + def __init__(self, strip: LEDStrip, parameters: dict): + super().__init__(strip, parameters) + self.state = {} + self.stripe = 1 + self.block = 99 + self.mirror = True + self.balls = () + self.spare_colors = [(0, 255, 255)] + + def init_parameters(self): + super().init_parameters() + self.set_parameter('num_steps_per_cycle', 255) + self.set_parameter('pause_sec', 0.005) + + def before_start(self): + self.balls = ( + Ball(self.block, self.stripe, (255, 0, 0)), + Ball(self.block * 0.5, self.stripe, (0, 255, 0)), + Ball(self.block * 0.75, self.stripe, (255, 255, 0)), + Ball(self.block * 0.88, self.stripe, (255, 0, 255)), + Ball(self.block * 0.66, self.stripe, (0, 0, 255)) + ) + + def update(self, current_step: int, current_cycle: int) -> bool: + t = (current_step + current_cycle * 256) * 0.1 + + self.strip.clear_buffer() + + for offset in range(0, self.stripe): + for ball in self.balls: + pos = ball.get_pos(t) + index = pos + offset + self.strip.set_pixel(index, *ball.color) + if self.mirror: + self.strip.set_pixel(self.strip.num_leds - index, *ball.color) + + if ball.is_next(): + self.spare_colors.insert(0, ball.color) + ball.color = self.spare_colors.pop() + + return True diff --git a/server/lightshows/rgbtest.py b/server/lightshows/rgbtest.py index 474b75a..0d47503 100644 --- a/server/lightshows/rgbtest.py +++ b/server/lightshows/rgbtest.py @@ -20,16 +20,19 @@ def check_runnable(self): def run(self): while True: + for pos in range(0, self.strip.num_leds): + self.strip.set_brightness(pos, 1.0) + # single leds - blend_whole_strip_to_color(self.strip, (255, 0, 0), fadetime_sec=0) + blend_whole_strip_to_color(self.strip, (255, 0, 0), fadetime_sec=5) self.sleep(10) - blend_whole_strip_to_color(self.strip, (0, 255, 0), fadetime_sec=0) + blend_whole_strip_to_color(self.strip, (0, 255, 0), fadetime_sec=5) self.sleep(10) - blend_whole_strip_to_color(self.strip, (0, 0, 255), fadetime_sec=0) + blend_whole_strip_to_color(self.strip, (0, 0, 255), fadetime_sec=5) self.sleep(10) # all leds together - blend_whole_strip_to_color(self.strip, (255, 255, 255), fadetime_sec=0) + blend_whole_strip_to_color(self.strip, (255, 255, 255), fadetime_sec=5) self.sleep(10) # clear strip diff --git a/server/lightshows/starlight.py b/server/lightshows/starlight.py new file mode 100644 index 0000000..d2029c7 --- /dev/null +++ b/server/lightshows/starlight.py @@ -0,0 +1,50 @@ +# Rainbow +# (c) 2015 Martin Erzberger, 2016-2017 Simon Leiner +# licensed under the GNU Public License, version 2 +import random + +from helpers.color import wheel +from lightshows.templates.colorcycle import * + + +class Starlight(ColorCycle): + """\ + Rotates a rainbow color wheel around the strip. + + No parameters necessary + """ + + def __init__(self, strip: LEDStrip, parameters: dict): + super().__init__(strip, parameters) + self.state = {} + self.length = 10 + self.color = (255, 180, 50) + + def init_parameters(self): + super().init_parameters() + self.set_parameter('num_steps_per_cycle', 255) + self.set_parameter('pause_sec', 0.02) + + def before_start(self): + pass + + def update(self, current_step: int, current_cycle: int) -> bool: + t = current_step + current_cycle * 256 + + if random.randint(0, 100) > 90: + self.state[random.randint(0, self.strip.num_leds - 1)] = t + self.length + + self.strip.clear_buffer() + + for pos, end in self.state.items(): + brightness = 1.0 / self.length * (end - t) + if brightness > 0.0: + self.strip.set_pixel(pos, *self.color) + self.strip.set_brightness(pos, brightness) + else: + self.strip.set_pixel(pos, 0, 0, 0) + self.strip.set_brightness(pos, 1.0) + + self.state = {pos: end for pos, end in self.state.items() if end > t} + + return True # All pixels are set in the buffer, so repaint the strip now diff --git a/server/lightshows/templates/base.py b/server/lightshows/templates/base.py index 7e1566a..36209f4 100644 --- a/server/lightshows/templates/base.py +++ b/server/lightshows/templates/base.py @@ -193,6 +193,7 @@ def apply_parameter_set(self, parameters: dict) -> None: self.set_parameter(param_name, value=parameters[param_name], send_mqtt_update=False) self.mqtt.send_current_parameter_state() else: + print("parameters", type(parameters)) raise InvalidParameters("Parameters payload must be given as JSON like this: " + "{\"param_name\": 42, \"param2_name\": [255,125,0]} " + "(instead received: " + str(parameters) + " ).") diff --git a/server/lightshows/theaterchase.py b/server/lightshows/theaterchase.py index ef74e03..f0a076a 100644 --- a/server/lightshows/theaterchase.py +++ b/server/lightshows/theaterchase.py @@ -19,7 +19,7 @@ def init_parameters(self): def before_start(self): pass - def update(self, current_step: int, current_cycle) -> bool: + def update(self, current_step: int, current_cycle: int) -> bool: # One cycle = One trip through the color wheel, 0..254 # Few cycles = quick transition, lots of cycles = slow transition # Note: For a smooth transition between cycles, numStepsPerCycle must be a multiple of 7 diff --git a/server/lightshows/twocolorblend.py b/server/lightshows/twocolorblend.py index e05844a..d74a6bc 100644 --- a/server/lightshows/twocolorblend.py +++ b/server/lightshows/twocolorblend.py @@ -1,7 +1,6 @@ # Two Color Blend # (c) 2016-2017 Simon Leiner # licensed under the GNU Public License, version 2 - from helpers.color import SmoothBlend, linear_dim, add_tuples from helpers.preprocessors import list_to_tuple from lightshows.templates.base import * diff --git a/server/server.py b/server/server.py index 3f0deb5..773b127 100644 --- a/server/server.py +++ b/server/server.py @@ -39,6 +39,14 @@ # configuration user_config = get_configuration() +# logging +root_logger = logging.getLogger() +fh = logging.FileHandler('102shows.log') +fh.setLevel(logging.INFO) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +fh.setFormatter(formatter) +root_logger.addHandler(fh) + # logger logger = logging.getLogger('102shows.server') coloredlogs.install(level=user_config.log_level) diff --git a/ui/nodered.json b/ui/nodered.json index 1a29fa4..7c79c84 100644 --- a/ui/nodered.json +++ b/ui/nodered.json @@ -1,32 +1,27 @@ [ { - "id": "6fda1b5e.3b8e6c", - "type": "tab", - "label": "LED Control" - }, - { - "id": "4c6d5c98.f195e4", + "id": "d15c800.af31a", "type": "subflow", - "name": "HEX to RGB", - "info": "convert a color HEX value (like #ff0080) to a rgb color tuple (like [255, 0, 128])", + "name": "RGB Joiner", + "info": "", "in": [ { - "x": 207, - "y": 96, + "x": 80, + "y": 100, "wires": [ { - "id": "6ff09576.26739c" + "id": "eb5767ba.336868" } ] } ], "out": [ { - "x": 534, - "y": 96, + "x": 440, + "y": 100, "wires": [ { - "id": "6ff09576.26739c", + "id": "2507f05e.330b78", "port": 0 } ] @@ -34,77 +29,78 @@ ] }, { - "id": "54ab6834.b684f", - "type": "subflow", - "name": "RGB to HEX", - "info": "convert an rgb color tuple (like [255, 0, 128]) to a color HEX value (like #ff0080)", - "in": [ - { - "x": 209.00001525878906, - "y": 136, - "wires": [ - { - "id": "a818f7ed.eaf208" - } - ] - } - ], - "out": [ - { - "x": 455, - "y": 137, - "wires": [ - { - "id": "a818f7ed.eaf208", - "port": 0 - } - ] - } + "id": "2507f05e.330b78", + "type": "function", + "z": "d15c800.af31a", + "name": "joiner", + "func": "var R = flow.get('R');\nvar G = flow.get('G');\nvar B = flow.get('B');\n\nmsg.topic = \"colour\";\nmsg.payload = [R, G, B];\n\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "x": 330, + "y": 100, + "wires": [ + [] ] }, { - "id": "79b7829.bf5367c", + "id": "eb5767ba.336868", + "type": "function", + "z": "d15c800.af31a", + "name": "buffer", + "func": "var valid = ['R', 'G', 'B'];\n\nif (valid.indexOf(msg.topic) !== -1) {\n flow.set(msg.topic, msg.payload);\n}\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "x": 198.99996948242188, + "y": 100, + "wires": [ + [ + "2507f05e.330b78" + ] + ] + }, + { + "id": "1919d466.09f77c", "type": "subflow", - "name": "RGB to HSV", - "info": "convert an RGB color tuple (like [128, 0, 128]) to H (hue), S (saturation) and V (brightness) values (like, 300, 1, 0.5).", + "name": "RGB Splitter", + "info": "", "in": [ { - "x": 144, - "y": 103, + "x": 360, + "y": 140, "wires": [ { - "id": "f5229fb5.8f5288" + "id": "99b2c1b8.b666d8" } ] } ], "out": [ { - "x": 474, - "y": 49, + "x": 700, + "y": 80, "wires": [ { - "id": "f5229fb5.8f5288", + "id": "11416ce1.833b7b", "port": 0 } ] }, { - "x": 472.9999694824219, - "y": 102, + "x": 700, + "y": 140, "wires": [ { - "id": "f5229fb5.8f5288", + "id": "11416ce1.833b7b", "port": 1 } ] }, { - "x": 473.9999694824219, - "y": 156, + "x": 700, + "y": 200, "wires": [ { - "id": "f5229fb5.8f5288", + "id": "11416ce1.833b7b", "port": 2 } ] @@ -112,7 +108,65 @@ ] }, { - "id": "b4110c92.379a4", + "id": "11416ce1.833b7b", + "type": "switch", + "z": "1919d466.09f77c", + "name": "", + "property": "parts.index", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "0", + "vt": "num" + }, + { + "t": "eq", + "v": "1", + "vt": "num" + }, + { + "t": "eq", + "v": "2", + "vt": "str" + } + ], + "checkall": "false", + "outputs": 3, + "x": 590, + "y": 140, + "wires": [ + [], + [], + [] + ], + "outputLabels": [ + "r", + "", + "b" + ] + }, + { + "id": "99b2c1b8.b666d8", + "type": "split", + "z": "1919d466.09f77c", + "name": "", + "splt": "\\n", + "spltType": "str", + "arraySplt": 1, + "arraySpltType": "len", + "stream": false, + "addname": "", + "x": 450, + "y": 140, + "wires": [ + [ + "11416ce1.833b7b" + ] + ] + }, + { + "id": "a79c1741.5ef28", "type": "subflow", "name": "HSV to RGB", "info": "cache all messages with topics H, S and V.\non any H, S or V message, the according RGB tuple is recalculated and sent out.", @@ -122,7 +176,7 @@ "y": 74.5, "wires": [ { - "id": "5d24ce5e.db9e78" + "id": "ff772c2b.e14458" } ] } @@ -133,7 +187,7 @@ "y": 74.66667175292969, "wires": [ { - "id": "5f5b597c.59f79", + "id": "b3d72a18.150118", "port": 0 } ] @@ -141,48 +195,78 @@ ] }, { - "id": "a51f5122.cdaca", + "id": "b3d72a18.150118", + "type": "function", + "z": "a79c1741.5ef28", + "name": "converter", + "func": "var H = flow.get('H');\nvar S = flow.get('S');\nvar V = flow.get('V');\n\n// conversion\n// algorithm: http://www.rapidtables.com/convert/color/hsv-to-rgb.htm\n\nvar C = V * S;\nvar X = C * (1 - Math.abs(((H / 60) % 2) - 1));\nvar m = V - C;\n\nvar R_, G_, B_;\nif (H < 0) {\n return;\n} else if (H < 60) {\n [R_, G_, B_] = [C, X, 0];\n} else if (H < 120) {\n [R_, G_, B_] = [X, C, 0];\n} else if (H < 180) {\n [R_, G_, B_] = [0, C, X];\n} else if (H < 240) {\n [R_, G_, B_] = [0, X, C];\n} else if (H < 300) {\n [R_, G_, B_] = [X, 0, C];\n} else if (H < 360) {\n [R_, G_, B_] = [C, 0, X];\n} else {\n return;\n}\n\nvar R, G, B;\n[R, G, B] = [(R_ + m) * 255, (G_ + m) * 255, (B_ + m) * 255];\n[R, G, B] = [Math.round(R), Math.round(G), Math.round(B)];\n\nmsg.topic = \"colour\";\nmsg.payload = [R, G, B];\n\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "x": 341.06671142578125, + "y": 74.38334655761719, + "wires": [ + [] + ] + }, + { + "id": "ff772c2b.e14458", + "type": "function", + "z": "a79c1741.5ef28", + "name": "buffer", + "func": "var valid = ['H', 'S', 'V'];\n\nif (valid.indexOf(msg.topic) !== -1) {\n flow.set(msg.topic, msg.payload);\n}\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "x": 200.06668090820312, + "y": 74.38334655761719, + "wires": [ + [ + "b3d72a18.150118" + ] + ] + }, + { + "id": "793be9a5.d2c558", "type": "subflow", - "name": "RGB Splitter", - "info": "", + "name": "RGB to HSV", + "info": "convert an RGB color tuple (like [128, 0, 128]) to H (hue), S (saturation) and V (brightness) values (like, 300, 1, 0.5).", "in": [ { - "x": 360, - "y": 140, + "x": 144, + "y": 103, "wires": [ { - "id": "b19dcdab.93aec8" + "id": "8f7a013f.29663" } ] } ], "out": [ { - "x": 700, - "y": 80, + "x": 474, + "y": 49, "wires": [ { - "id": "97d1d54c.8f5fa8", + "id": "8f7a013f.29663", "port": 0 } ] }, { - "x": 700, - "y": 140, + "x": 472.9999694824219, + "y": 102, "wires": [ { - "id": "97d1d54c.8f5fa8", + "id": "8f7a013f.29663", "port": 1 } ] }, { - "x": 700, - "y": 200, + "x": 473.9999694824219, + "y": 156, "wires": [ { - "id": "97d1d54c.8f5fa8", + "id": "8f7a013f.29663", "port": 2 } ] @@ -190,28 +274,44 @@ ] }, { - "id": "7ea41ffb.7ca8f", + "id": "8f7a013f.29663", + "type": "function", + "z": "793be9a5.d2c558", + "name": "converter", + "func": "var r = msg.payload[0];\nvar g = msg.payload[1];\nvar b = msg.payload[2];\n\n// algorithm: http://www.rapidtables.com/convert/color/rgb-to-hsv.htm\n\n// helper variables\nvar R_ = r / 255;\nvar G_ = g / 255;\nvar B_ = b / 255;\n\nvar C_max = Math.max(R_, G_, B_);\nvar C_min = Math.min(R_, G_, B_);\nvar Delta = C_max - C_min;\n\n// Hue\nvar H;\nif (Delta === 0) {\n H = 0;\n} else if (C_max === R_) {\n H = 60 * ( ( (G_ - B_) / Delta ) % 6 );\n if (H < 0) {\n H = H + 360;\n }\n} else if (C_max === G_) {\n H = 60 * ( ( (B_ - R_) / Delta) + 2 );\n} else if (C_max === B_) {\n H = 60 * ( ( (R_ - G_) / Delta) + 4 );\n}\n\n// Saturation\nvar S;\nif (C_max === 0) {\n S = 0;\n} else{\n S = Delta / C_max;\n}\n\n// Value\nvar V = C_max;\n\n// output messages\nvar msg_h = {\"topic\": \"H\", \"payload\": H};\nvar msg_s = {\"topic\": \"S\", \"payload\": S};\nvar msg_v = {\"topic\": \"V\", \"payload\": V};\n\nreturn [msg_h, msg_s, msg_v];", + "outputs": "3", + "noerr": 0, + "x": 273.73333740234375, + "y": 103.7166748046875, + "wires": [ + [], + [], + [] + ] + }, + { + "id": "93f4131.fbfd2f", "type": "subflow", - "name": "RGB Joiner", - "info": "", + "name": "HEX to RGB", + "info": "convert a color HEX value (like #ff0080) to a rgb color tuple (like [255, 0, 128])", "in": [ { - "x": 80, - "y": 100, + "x": 207, + "y": 96, "wires": [ { - "id": "b29e8c7a.7f7bc" + "id": "5c79cf54.3c5ed" } ] } ], "out": [ { - "x": 440, - "y": 100, + "x": 534, + "y": 96, "wires": [ { - "id": "4eceadda.d6b5d4", + "id": "5c79cf54.3c5ed", "port": 0 } ] @@ -219,271 +319,142 @@ ] }, { - "id": "1adc485c.1d2ee", - "type": "ui_base", - "theme": { - "name": "theme-dark", - "lightTheme": { - "default": "#0094CE", - "baseColor": "#0094CE", - "baseFont": "Helvetica Neue", - "edited": true, - "reset": false - }, - "darkTheme": { - "default": "#097479", - "baseColor": "#097479", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", - "edited": true, - "reset": false - }, - "customTheme": { - "name": "Untitled Theme 1", - "default": "#4B7930", - "baseColor": "#4B7930", - "baseFont": "Helvetica Neue" - }, - "themeState": { - "base-color": { - "default": "#097479", - "value": "#097479", - "edited": false - }, - "page-titlebar-backgroundColor": { - "value": "#097479", - "edited": false - }, - "page-backgroundColor": { - "value": "#111111", - "edited": false - }, - "page-sidebar-backgroundColor": { - "value": "#000000", - "edited": false - }, - "group-textColor": { - "value": "#0eb8c0", - "edited": false - }, - "group-borderColor": { - "value": "#555555", - "edited": false - }, - "group-backgroundColor": { - "value": "#333333", - "edited": false - }, - "widget-textColor": { - "value": "#eeeeee", - "edited": false - }, - "widget-backgroundColor": { - "value": "#097479", - "edited": false - }, - "widget-borderColor": { - "value": "#333333", - "edited": false - }, - "base-font": { - "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" - } - } - }, - "site": { - "name": "Node-RED Dashboard", - "hideToolbar": "false", - "allowSwipe": "false", - "dateFormat": "DD. MM. YYYY", - "sizes": { - "sx": 48, - "sy": 48, - "gx": 6, - "gy": 6, - "cx": 6, - "cy": 6, - "px": 0, - "py": 0 - } - } + "id": "5c79cf54.3c5ed", + "type": "function", + "z": "93f4131.fbfd2f", + "name": "HEX to RGB", + "func": "var split_str = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(msg.payload);\n\nr = parseInt(split_str[1], 16);\ng = parseInt(split_str[2], 16);\nb = parseInt(split_str[3], 16)\n\nvar rgb = [r,g,b];\nmsg.payload = rgb;\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 379.90000915527344, + "y": 96.39999389648438, + "wires": [ + [] + ] }, { - "id": "33da3f6.4497c4", - "type": "mqtt-broker", - "z": "", - "broker": "localhost", - "port": "1883", - "clientid": "", - "usetls": false, - "compatmode": true, - "keepalive": "60", - "cleansession": true, - "willTopic": "", - "willQos": "0", - "willPayload": "", - "birthTopic": "", - "birthQos": "0", - "birthPayload": "" + "id": "49e83dbb.1a9224", + "type": "subflow", + "name": "RGB to HEX", + "info": "convert an rgb color tuple (like [255, 0, 128]) to a color HEX value (like #ff0080)", + "in": [ + { + "x": 209.00001525878906, + "y": 136, + "wires": [ + { + "id": "bd378a2d.22a2d" + } + ] + } + ], + "out": [ + { + "x": 455, + "y": 137, + "wires": [ + { + "id": "bd378a2d.22a2d", + "port": 0 + } + ] + } + ] }, { - "id": "b84cfb29.211978", - "type": "ui_group", - "z": "", - "name": "Spin the Bottle", - "tab": "b37678f7.74443", - "order": 3, - "disp": true, - "width": "6" - }, - { - "id": "b37678f7.74443", - "type": "ui_tab", - "z": "", - "name": "MyLED", - "icon": "lightbulb_outline", - "order": 2 - }, - { - "id": "cc4dd42c.7e74b8", - "type": "ui_base", - "name": "LED Control", - "theme": "theme-light" - }, - { - "id": "94adec20.46bd2", - "type": "ui_group", - "z": "", - "name": "etc", - "tab": "b37678f7.74443", - "order": 5, - "disp": false, - "width": "6" - }, - { - "id": "d07e03c3.642f38", - "type": "ui_group", - "z": "", - "name": "Color", - "tab": "b37678f7.74443", - "order": 1, - "disp": true, - "width": "6" - }, - { - "id": "4af0450a.2775c4", - "type": "ui_group", - "z": "", - "name": "Color Blend", - "tab": "b37678f7.74443", - "order": 2, - "disp": true, - "width": "6" - }, - { - "id": "5b37eb93.64dcc4", - "type": "ui_group", - "z": "", - "name": "Animations", - "tab": "b37678f7.74443", - "order": 4, - "disp": true, - "width": "6" - }, - { - "id": "82c0ad11.edeb3", - "type": "ui_group", - "z": "", - "name": "Christmas Show", - "tab": "b37678f7.74443", - "order": 6, - "disp": true, - "width": "6" - }, - { - "id": "6ff09576.26739c", + "id": "bd378a2d.22a2d", "type": "function", - "z": "4c6d5c98.f195e4", - "name": "HEX to RGB", - "func": "var split_str = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(msg.payload);\n\nr = parseInt(split_str[1], 16);\ng = parseInt(split_str[2], 16);\nb = parseInt(split_str[3], 16)\n\nvar rgb = [r,g,b];\nmsg.payload = rgb;\n\nreturn msg;", + "z": "49e83dbb.1a9224", + "name": "RGB to HEX", + "func": "r = msg.payload[0];\ng = msg.payload[1];\nb = msg.payload[2];\n\nvar rgb = b | (g << 8) | (r << 16);\nmsg.payload = '#' + (0x1000000 + rgb).toString(16).slice(1);\n\nreturn msg;", "outputs": 1, "noerr": 0, - "x": 379.90000915527344, - "y": 96.39999389648438, + "x": 326.20001220703125, + "y": 142.1999969482422, "wires": [ [] ] }, { - "id": "634ef0bd.a10dc8", + "id": "2fc32e47.9732e2", + "type": "tab", + "label": "LED Control" + }, + { + "id": "83a0d3d7.f8db4", "type": "mqtt in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "topic": "+/+/show/+/parameters/current", "qos": "2", - "broker": "33da3f6.4497c4", + "broker": "4dc2b995.13d0f8", + "inputs": 0, "x": 1329.64306640625, "y": 384.7143249511719, "wires": [ [ - "9f7e462e.a18a08" + "7a345317.9fe64c" ] ] }, { - "id": "3108eab5.770626", + "id": "c10cd6b4.0850b", "type": "inject", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "INIT", - "topic": "", - "payload": "", - "payloadType": "date", "repeat": "", "crontab": "", "once": true, + "topic": "", + "payload": "", + "payloadType": "date", "x": 345.14288330078125, "y": 71.42855834960938, "wires": [ [ - "efabcb19.2f2e88" + "52f20add.55f74c" ] ] }, { - "id": "efabcb19.2f2e88", + "id": "52f20add.55f74c", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "GLOBAL SETTINGS", - "func": "flow.set(\"mqtt_prefix\", \"sled\");\nflow.set(\"mqtt_sys_name\", \"MyLED\");\nreturn msg;", + "func": "flow.set(\"mqtt_prefix\", \"led\");\nflow.set(\"mqtt_sys_name\", \"ariana\");\nreturn msg;", "outputs": 1, "noerr": 0, "x": 579.1428833007812, "y": 71.85713195800781, "wires": [ [ - "63e917fd.6459a8" + "ac3d5427.2d9a88" ] ] }, { - "id": "49c71781.469648", - "type": "subflow:54ab6834.b684f", - "z": "6fda1b5e.3b8e6c", + "id": "f8ef4130.151bd", + "type": "subflow:49e83dbb.1a9224", + "z": "2fc32e47.9732e2", "name": "", "x": 3092.8575134277344, "y": 521.1190643310547, "wires": [ [ - "e0a98fbc.94f48" + "1d3e77bb.8aff08" ] ] }, { - "id": "e0a98fbc.94f48", + "id": "1d3e77bb.8aff08", "type": "ui_text_input", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "spot", - "group": "b84cfb29.211978", + "group": "222a3f19.b90e9", "order": 1, "width": "2", "height": "1", @@ -495,71 +466,74 @@ "y": 521.1190795898438, "wires": [ [ - "d95f2c5e.c2e5b8" + "bc934c7f.93176" ] ] }, { - "id": "d95f2c5e.c2e5b8", - "type": "subflow:4c6d5c98.f195e4", - "z": "6fda1b5e.3b8e6c", + "id": "bc934c7f.93176", + "type": "subflow:93f4131.fbfd2f", + "z": "2fc32e47.9732e2", "name": "", "x": 3481.8574829101562, "y": 521.3690490722656, "wires": [ [ - "8cd4ae2f.4dfc18" + "cb573b2e.c83878" ] ] }, { - "id": "99893d67.54c708", + "id": "3ec3076f.74f948", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "add global prefix to topic (destrip)", "func": "var command = msg.topic;\n\nif (msg.topic == \"start\") {\n msg.topic = flow.get(\"mqtt_prefix\") + \"/\" + flow.get(\"mqtt_sys_name\") +\n \"/show/start\";\n \n var parameters = msg.payload;\n \n msg.payload = {\n \"name\": msg.show,\n \"parameters\": parameters\n }\n}\n\nelse {\n var parameter_name = msg.topic;\n var parameter_value = msg.payload;\n \n msg.topic = flow.get(\"mqtt_prefix\") + \"/\" + flow.get(\"mqtt_sys_name\") +\n \"/show/\" + msg.show + \"/parameters/set\";\n \n msg.payload = {parameter_name: parameter_value};\n}\n\nreturn msg;", "outputs": 1, "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], "x": 4255.327392578125, "y": 704.0831298828125, "wires": [ [ - "54d0a6c3.f8c01" + "feb57140.68cd68" ] ] }, { - "id": "b29039b2.4d1188", + "id": "c264f0a4.a5008", "type": "mqtt out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "topic": "", "qos": "1", "retain": "", - "broker": "33da3f6.4497c4", + "broker": "4dc2b995.13d0f8", "x": 4770, "y": 700, "wires": [] }, { - "id": "e415e03b.86a3f", - "type": "subflow:54ab6834.b684f", - "z": "6fda1b5e.3b8e6c", + "id": "3b6fa06d.71cb3", + "type": "subflow:49e83dbb.1a9224", + "z": "2fc32e47.9732e2", "x": 3094.2742309570312, "y": 570.1190643310547, "wires": [ [ - "19e6fee3.7c6251" + "9e6af63c.381b8" ] ] }, { - "id": "19e6fee3.7c6251", + "id": "9e6af63c.381b8", "type": "ui_text_input", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "background color", - "group": "b84cfb29.211978", + "group": "222a3f19.b90e9", "order": 2, "width": "2", "height": "1", @@ -571,63 +545,49 @@ "y": 570.6190490722656, "wires": [ [ - "5542de52.b25e" + "a8465acb.fdca68" ] ] }, { - "id": "5542de52.b25e", - "type": "subflow:4c6d5c98.f195e4", - "z": "6fda1b5e.3b8e6c", + "id": "a8465acb.fdca68", + "type": "subflow:93f4131.fbfd2f", + "z": "2fc32e47.9732e2", "name": "", "x": 3483.0242309570312, "y": 570.6189880371094, "wires": [ [ - "8cd4ae2f.4dfc18" + "cb573b2e.c83878" ] ] }, { - "id": "a818f7ed.eaf208", - "type": "function", - "z": "54ab6834.b684f", - "name": "RGB to HEX", - "func": "r = msg.payload[0];\ng = msg.payload[1];\nb = msg.payload[2];\n\nvar rgb = b | (g << 8) | (r << 16);\nmsg.payload = '#' + (0x1000000 + rgb).toString(16).slice(1);\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 326.20001220703125, - "y": 142.1999969482422, - "wires": [ - [] - ] - }, - { - "id": "39fb791e.fa9cb6", + "id": "2bf3642a.dfeb6c", "type": "inject", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "trigger reset to presets", - "topic": "", - "payload": "", - "payloadType": "date", "repeat": "", "crontab": "", "once": false, + "topic": "", + "payload": "", + "payloadType": "date", "x": 834.390625, "y": 115.05706787109375, "wires": [ [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ] ] }, { - "id": "1a7479b9.4a4476", + "id": "ee809af1.8df16", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "fadeout", "label": "", - "group": "b84cfb29.211978", + "group": "222a3f19.b90e9", "order": 3, "width": "2", "height": "1", @@ -646,17 +606,17 @@ "y": 345.8572082519531, "wires": [ [ - "8cd4ae2f.4dfc18" + "cb573b2e.c83878" ] ] }, { - "id": "50718828.214f7", + "id": "58ab9704.81bcb8", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "time_sec", "label": "", - "group": "b84cfb29.211978", + "group": "222a3f19.b90e9", "order": 5, "width": "3", "height": "1", @@ -669,16 +629,16 @@ "y": 449.7620544433594, "wires": [ [ - "1b79bcb7.898613", - "8cd4ae2f.4dfc18" + "ed9a055d.67389", + "cb573b2e.c83878" ] ] }, { - "id": "1b79bcb7.898613", + "id": "ed9a055d.67389", "type": "ui_text", - "z": "6fda1b5e.3b8e6c", - "group": "b84cfb29.211978", + "z": "2fc32e47.9732e2", + "group": "222a3f19.b90e9", "order": 6, "width": "2", "height": "1", @@ -691,69 +651,72 @@ "wires": [] }, { - "id": "85c91225.eb8dd", - "type": "subflow:54ab6834.b684f", - "z": "6fda1b5e.3b8e6c", + "id": "13fba410.b8d01c", + "type": "subflow:49e83dbb.1a9224", + "z": "2fc32e47.9732e2", "name": "", "x": 2990, "y": 820, "wires": [ [ - "55f8e26c.17c65c" + "969f60d4.9e711" ] ] }, { - "id": "13eab3fb.19d10c", - "type": "subflow:4c6d5c98.f195e4", - "z": "6fda1b5e.3b8e6c", + "id": "5604be7e.4db068", + "type": "subflow:93f4131.fbfd2f", + "z": "2fc32e47.9732e2", "name": "", "x": 3334.8568725585938, "y": 820.2501220703125, "wires": [ [ - "efc351d7.30ddc" + "5d817da7.2113cc" ] ] }, { - "id": "5fd0b2e8.946cf4", + "id": "569f7e7f.3c1e68", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "assemble START command", "func": "// input:\n// msg.topic: parameter name => \"color\"\n// msg.payload: color tuple => [255, 0, 128]\nvar parameters = {};\nparameters[msg.topic] = msg.payload;\n\n// assemble start command\nmsg.topic = \"start\";\nmsg.payload = parameters;\n\nreturn msg;", "outputs": 1, "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], "x": 3620, "y": 720, "wires": [ [ - "4af54fa2.f9dcd8" + "7da6d993.633d28" ] ] }, { - "id": "51bb22c7.fd896c", + "id": "451f5f90.8cd6f", "type": "link out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "led_presets", "links": [ - "e6959cc8.e5f9a", - "8f572820.57b5b", - "55382540.869f5c", + "fbf9b80.e353648", + "c4643763.41c148", + "bcd696ca.ca9c8", "101849bd.cce656", "cee502fc.be451", - "5395b965.d1dd", - "50d2486d.e6243" + "9df74816.b1fcc8", + "756ae94f.b7242" ], "x": 990.881103515625, "y": 113.6953125, "wires": [] }, { - "id": "7e0f234b.ce19c4", + "id": "33f12004.31f1c8", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "preset for solidcolor", "func": "var show_name = \"solidcolor\";\n\nflow.set(\"parameters_\" + show_name , {});\n\nvar color = {\"topic\": \"color\", \"payload\": [30, 200, 40]};\n\nvar msg_queue = [color];\n\nmsg_queue.forEach(function(msg) {\n msg.show = show_name;\n});\n\nreturn [msg_queue];\n", "outputs": 1, @@ -762,87 +725,92 @@ "y": 855.7271728515625, "wires": [ [ - "2f16dd9c.bf8dba" + "a9af69bf.8e2aa" ] ] }, { - "id": "8f572820.57b5b", + "id": "c4643763.41c148", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "led_presets", "links": [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ], "x": 2222.9334716796875, "y": 856.0606079101562, "wires": [ [ - "7e0f234b.ce19c4" + "33f12004.31f1c8" ] ] }, { - "id": "49677f6a.541ec", + "id": "9432c02d.2fc698", "type": "link out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "cache_solidcolor", "links": [ - "214117af.69c508" + "57127376.359c3c" ], - "x": 2814.9170532226562, - "y": 791.0000610351562, + "x": 2876.9169921875, + "y": 663.0000610351562, "wires": [] }, { - "id": "c91ed422.b28188", + "id": "3232186.264b6e8", "type": "link out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "cache_spinthebottle", "links": [ - "214117af.69c508" + "57127376.359c3c" ], "x": 3006.9166259765625, "y": 317.5000915527344, "wires": [] }, { - "id": "214117af.69c508", + "id": "57127376.359c3c", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "cache", "links": [ - "c91ed422.b28188", - "49677f6a.541ec", - "201446e0.aaab7a", - "4a114481.ef5414" + "3232186.264b6e8", + "9432c02d.2fc698", + "e49e1dd7.1016f8", + "0555ed834494cbab", + "1ee7c9a6620d3859", + "2499e0f9.91c298" ], - "x": 4109.750244140625, - "y": 394.5001220703125, + "x": 4115, + "y": 380, "wires": [ [ - "a91d993c.b311d8" + "621db8de.2c88f8" ] ] }, { - "id": "a91d993c.b311d8", + "id": "621db8de.2c88f8", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "cache in flow context", - "func": "var paramstore_name = \"parameters_\" + msg.show;\n\nvar show_parameters = flow.get(paramstore_name);\nshow_parameters[msg.topic] = msg.payload;\nflow.set(paramstore_name, show_parameters);\n\nreturn msg;", + "func": "var paramstore_name = \"parameters_\" + msg.show;\n\nvar show_parameters = flow.get(paramstore_name);\nshow_parameters[msg.topic] = msg.payload;\nnode.debug(\"update cache\"+paramstore_name+show_parameters)\nflow.set(paramstore_name, show_parameters);\n\nreturn msg;", "outputs": 1, "noerr": 0, - "x": 4287.999755859375, - "y": 392.16680908203125, + "initialize": "", + "finalize": "", + "libs": [], + "x": 4280, + "y": 380, "wires": [ [] ] }, { - "id": "8cd4ae2f.4dfc18", + "id": "cb573b2e.c83878", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.show => spinthebottle", "rules": [ { @@ -862,14 +830,14 @@ "y": 375.3334045410156, "wires": [ [ - "99893d67.54c708" + "3ec3076f.74f948" ] ] }, { - "id": "e6c1abf1.b7836", + "id": "da1a805f.2a69d8", "type": "switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "select command", "property": "topic", "propertyType": "msg", @@ -896,33 +864,34 @@ } ], "checkall": "false", + "repair": false, "outputs": 4, "x": 2845.666748046875, "y": 359.9999084472656, "wires": [ [ - "1a7479b9.4a4476", - "c91ed422.b28188" + "ee809af1.8df16", + "3232186.264b6e8" ], [ - "50718828.214f7", - "c91ed422.b28188", - "1b79bcb7.898613" + "58ab9704.81bcb8", + "3232186.264b6e8", + "ed9a055d.67389" ], [ - "49c71781.469648", - "c91ed422.b28188" + "f8ef4130.151bd", + "3232186.264b6e8" ], [ - "e415e03b.86a3f", - "c91ed422.b28188" + "3b6fa06d.71cb3", + "3232186.264b6e8" ] ] }, { - "id": "2f16dd9c.bf8dba", + "id": "a9af69bf.8e2aa", "type": "switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "select command", "property": "topic", "propertyType": "msg", @@ -939,17 +908,17 @@ "y": 859.9998779296875, "wires": [ [ - "49677f6a.541ec", - "85c91225.eb8dd", - "65d31e50.cba11", - "4e0b8077.7f6048" + "9432c02d.2fc698", + "13fba410.b8d01c", + "1dc1d8c7.b1662f", + "90223fcd.1047c8" ] ] }, { - "id": "4af54fa2.f9dcd8", + "id": "7da6d993.633d28", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.show => solidcolor", "rules": [ { @@ -969,16 +938,16 @@ "y": 720, "wires": [ [ - "99893d67.54c708" + "3ec3076f.74f948" ] ] }, { - "id": "cd9ea25d.9c70f", + "id": "f5142577.ef282", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "START: spinthebottle", - "group": "b84cfb29.211978", + "group": "222a3f19.b90e9", "order": 7, "width": "0", "height": "0", @@ -988,18 +957,18 @@ "payload": "spinthebottle", "payloadType": "str", "topic": "start", - "x": 3103.4857177734375, - "y": 228.37155151367188, + "x": 3120, + "y": 220, "wires": [ [ - "2fef367f.182a6a" + "c11a8382.00e4d8" ] ] }, { - "id": "2fef367f.182a6a", + "id": "c11a8382.00e4d8", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "collect parameters for \"spinthebottle\"", "func": "msg.payload = flow.get(\"parameters_spinthebottle\");\nreturn msg;", "outputs": 1, @@ -1008,14 +977,14 @@ "y": 214.26681518554688, "wires": [ [ - "8cd4ae2f.4dfc18" + "cb573b2e.c83878" ] ] }, { - "id": "558b4933.d1441", + "id": "91aff95b.f4e928", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "presets for spinthebottle", "func": "var show_name = \"spinthebottle\";\n\nflow.set(\"parameters_\" + show_name , {});\n\nvar highlight_color = {\"topic\": \"highlight_color\", \"payload\": [200, 100, 0]};\nvar background_color = {\"topic\": \"background_color\", \"payload\": [0, 0, 0]};\nvar fadeout = {\"topic\": \"fadeout\", \"payload\": true};\nvar time_sec = {\"topic\": \"time_sec\", \"payload\": 10};\n\nvar msg_queue = [highlight_color, background_color, fadeout, time_sec];\n\nmsg_queue.forEach(function(msg) {\n msg.show = show_name;\n});\n\nreturn [msg_queue];", "outputs": 1, @@ -1024,30 +993,30 @@ "y": 333.89996337890625, "wires": [ [ - "e6c1abf1.b7836" + "da1a805f.2a69d8" ] ] }, { - "id": "e6959cc8.e5f9a", + "id": "fbf9b80.e353648", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "led_presets", "links": [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ], "x": 2448.666748046875, "y": 333.8999938964844, "wires": [ [ - "558b4933.d1441" + "91aff95b.f4e928" ] ] }, { - "id": "a1b62d23.6d9bb", + "id": "f34afbce.9f5eb", "type": "switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "select command", "property": "topic", "propertyType": "msg", @@ -1064,99 +1033,104 @@ } ], "checkall": "false", + "repair": false, "outputs": 2, "x": 2809.66650390625, "y": 1376.4998779296875, "wires": [ [ - "201446e0.aaab7a", - "2ccef3f6.72247c" + "845b563.6862ca8", + "2499e0f9.91c298" ], [ - "201446e0.aaab7a", - "6967b376.c9726c" + "6116c978.6b3cc", + "2499e0f9.91c298" ] ] }, { - "id": "201446e0.aaab7a", + "id": "2499e0f9.91c298", "type": "link out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "cache_twocolorblend", + "mode": "link", "links": [ - "214117af.69c508" + "57127376.359c3c" ], - "x": 2951.0665283203125, - "y": 1326.7999267578125, + "x": 2955, + "y": 1300, "wires": [] }, { - "id": "2ccef3f6.72247c", - "type": "subflow:54ab6834.b684f", - "z": "6fda1b5e.3b8e6c", + "id": "845b563.6862ca8", + "type": "subflow:49e83dbb.1a9224", + "z": "2fc32e47.9732e2", "name": "", - "x": 3101.809326171875, - "y": 1349.428466796875, + "x": 3150, + "y": 1340, "wires": [ [ - "9a7936f0.6bf0a8" + "6a6eeb89.65f4c4" ] ] }, { - "id": "9a7936f0.6bf0a8", + "id": "6a6eeb89.65f4c4", "type": "ui_text_input", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "color1", "label": "", - "group": "4af0450a.2775c4", + "tooltip": "", + "group": "e2aca116.33ad2", "order": 1, "width": "3", "height": "1", - "passthru": false, + "passthru": true, "mode": "color", "delay": 300, "topic": "color1", - "x": 3275.9759216308594, - "y": 1349.428482055664, + "x": 3290, + "y": 1340, "wires": [ [ - "6d3980a3.057e88" + "90a89be1.2aa74" ] ] }, { - "id": "6d3980a3.057e88", - "type": "subflow:4c6d5c98.f195e4", - "z": "6fda1b5e.3b8e6c", + "id": "90a89be1.2aa74", + "type": "subflow:93f4131.fbfd2f", + "z": "2fc32e47.9732e2", "name": "", - "x": 3490.809295654297, - "y": 1349.678451538086, + "env": [], + "x": 3430, + "y": 1340, "wires": [ [ - "c5e92e87.c40f68" + "68c7a793eaa9294d" ] ] }, { - "id": "6967b376.c9726c", - "type": "subflow:54ab6834.b684f", - "z": "6fda1b5e.3b8e6c", - "x": 3103.226043701172, - "y": 1398.428466796875, + "id": "6116c978.6b3cc", + "type": "subflow:49e83dbb.1a9224", + "z": "2fc32e47.9732e2", + "x": 3150, + "y": 1380, "wires": [ [ - "6bb5263e.9075d8" + "a3507107.89ba6" ] ] }, { - "id": "6bb5263e.9075d8", + "id": "a3507107.89ba6", "type": "ui_text_input", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "color2", "label": "", - "group": "4af0450a.2775c4", + "tooltip": "", + "group": "e2aca116.33ad2", "order": 2, "width": "3", "height": "1", @@ -1164,31 +1138,31 @@ "mode": "color", "delay": 300, "topic": "color2", - "x": 3274.9524536132812, - "y": 1398.9285278320312, + "x": 3290, + "y": 1380, "wires": [ [ - "d0c98a96.7eda3" + "7caed831.88ab9" ] ] }, { - "id": "d0c98a96.7eda3", - "type": "subflow:4c6d5c98.f195e4", - "z": "6fda1b5e.3b8e6c", + "id": "7caed831.88ab9", + "type": "subflow:93f4131.fbfd2f", + "z": "2fc32e47.9732e2", "name": "", - "x": 3491.976043701172, - "y": 1398.9283905029297, + "x": 3430, + "y": 1380, "wires": [ [ - "c5e92e87.c40f68" + "5c2e39584ad8b2f5" ] ] }, { - "id": "c5e92e87.c40f68", + "id": "4a0170a0.88db3", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.show => twocolorblend", "rules": [ { @@ -1204,18 +1178,21 @@ "from": "", "to": "", "reg": false, - "x": 3805.8094482421875, - "y": 1322.2853393554688, + "x": 3640, + "y": 1220, "wires": [ [ - "99893d67.54c708" + "3ec3076f.74f948" ] + ], + "inputLabels": [ + "asdf" ] }, { - "id": "a04bf73a.e6bf98", + "id": "37c7a7f5.f95858", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "preset for twocolorblend", "func": "var show_name = \"twocolorblend\";\n\nflow.set(\"parameters_\" + show_name , {});\n\nvar color1 = {\"topic\": \"color1\", \"payload\": [200, 0, 0]};\nvar color2 = {\"topic\": \"color2\", \"payload\": [0, 0, 255]};\n\nvar msg_queue = [color1, color2];\n\nmsg_queue.forEach(function(msg) {\n msg.show = show_name;\n});\n\nreturn [msg_queue];\n", "outputs": 1, @@ -1224,30 +1201,30 @@ "y": 1378.857177734375, "wires": [ [ - "a1b62d23.6d9bb" + "f34afbce.9f5eb" ] ] }, { - "id": "55382540.869f5c", + "id": "bcd696ca.ca9c8", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "led_presets", "links": [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ], "x": 2297.50439453125, "y": 1378.8572082519531, "wires": [ [ - "a04bf73a.e6bf98" + "37c7a7f5.f95858" ] ] }, { - "id": "9ab01e6c.35685", + "id": "7f23b14c.f99218", "type": "comment", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "README", "info": "# Configure me!\n\nIn order to use this flow correctly you must configure the following nodes:\n\n- the assigned __MQTT broker node__ (host, port, credentials,..)\n- the __\"GLOBAL SETTINGS\" node__ (next to this one) according to the settings made in your _server/config.py_\n - ```prefix```\n - ```sys_name```\n- It is recommended that you also change the name of the UI tab.", "x": 124.14285278320312, @@ -1255,50 +1232,56 @@ "wires": [] }, { - "id": "a4914748.1a6a7", + "id": "597ffc2a.b603fc", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "START: twocolorblend", - "group": "4af0450a.2775c4", + "group": "e2aca116.33ad2", "order": 0, "width": "0", "height": "0", + "passthru": false, "label": "Show me! ✨", + "tooltip": "", "color": "", + "bgcolor": "", "icon": "", "payload": "twocolorblend", "payloadType": "str", "topic": "start", - "x": 3081.238037109375, - "y": 1233.9998779296875, + "x": 3020, + "y": 1220, "wires": [ [ - "d1b9cd49.6fc678" + "305c823d.2e4e76" ] ] }, { - "id": "d1b9cd49.6fc678", + "id": "305c823d.2e4e76", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "collect parameters for \"twocolorblend\"", "func": "msg.payload = flow.get(\"parameters_twocolorblend\");\nreturn msg;", "outputs": 1, "noerr": 0, - "x": 3409.39990234375, - "y": 1233.8951416015625, + "initialize": "", + "finalize": "", + "libs": [], + "x": 3350, + "y": 1220, "wires": [ [ - "c5e92e87.c40f68" + "4a0170a0.88db3" ] ] }, { - "id": "a671956a.f4cbf8", + "id": "e8ecd711.e66e6", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", - "group": "5b37eb93.64dcc4", + "group": "57eea257.4cfba4", "order": 0, "width": 0, "height": 0, @@ -1314,14 +1297,14 @@ "y": 1968.76220703125, "wires": [ [ - "198e8662.12b6a2" + "52bddc0e.6541f4" ] ] }, { - "id": "198e8662.12b6a2", + "id": "52bddc0e.6541f4", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.show => theaterchase", "rules": [ { @@ -1341,16 +1324,16 @@ "y": 1969.09521484375, "wires": [ [ - "99893d67.54c708" + "3ec3076f.74f948" ] ] }, { - "id": "cff5ad09.cecd4", + "id": "f2026a5a.a9e608", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", - "group": "5b37eb93.64dcc4", + "group": "57eea257.4cfba4", "order": 0, "width": 0, "height": 0, @@ -1366,14 +1349,14 @@ "y": 2035.76220703125, "wires": [ [ - "e7be5b72.1d31b8" + "97be11e2.a190b8" ] ] }, { - "id": "e7be5b72.1d31b8", + "id": "97be11e2.a190b8", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.show => rainbow", "rules": [ { @@ -1393,17 +1376,17 @@ "y": 2036.0954284667969, "wires": [ [ - "99893d67.54c708" + "3ec3076f.74f948" ] ] }, { - "id": "9416d131.c04a18", + "id": "54cf3bac.d4f274", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "brightness", "label": "", - "group": "94adec20.46bd2", + "group": "da52ac1d.f09478", "order": 2, "width": "4", "height": "1", @@ -1416,17 +1399,17 @@ "y": 98.5714340209961, "wires": [ [ - "ea7594f7.7fcb58" + "4a369efd.12b948" ] ] }, { - "id": "66149301.8d45ac", + "id": "b1686e7d.af7c3", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "reset time (sec)", "label": "", - "group": "b84cfb29.211978", + "group": "222a3f19.b90e9", "order": 4, "width": "1", "height": "1", @@ -1445,17 +1428,17 @@ "y": 410.0000305175781, "wires": [ [ - "8cd4ae2f.4dfc18" + "cb573b2e.c83878" ] ] }, { - "id": "4a869317.21f41c", + "id": "65e1aac1.63585c", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "minimize brightness", "label": "", - "group": "94adec20.46bd2", + "group": "da52ac1d.f09478", "order": 1, "width": "1", "height": "1", @@ -1474,17 +1457,17 @@ "y": 140, "wires": [ [ - "ea7594f7.7fcb58" + "4a369efd.12b948" ] ] }, { - "id": "6349402a.6e89a", + "id": "73700cf5.5d486c", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "maximize brightness", "label": "", - "group": "94adec20.46bd2", + "group": "da52ac1d.f09478", "order": 3, "width": "1", "height": "1", @@ -1503,30 +1486,31 @@ "y": 60, "wires": [ [ - "ea7594f7.7fcb58" + "4a369efd.12b948" ] ] }, { - "id": "c7a1aeb2.92ebf", + "id": "aab4302e.c133a", "type": "mqtt in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "topic": "+/+/notification", "qos": "2", - "broker": "33da3f6.4497c4", + "broker": "4dc2b995.13d0f8", + "inputs": 0, "x": 277.90020751953125, "y": 358.01434326171875, "wires": [ [ - "bbec2399.b8b62" + "9e0c09d8.6eb0b8" ] ] }, { - "id": "bbec2399.b8b62", + "id": "9e0c09d8.6eb0b8", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "prefix and sys_name filter", "func": "// get to work\nvar topic_path = msg.topic.split(\"/\");\n\nvar mqtt_prefix = topic_path[0];\nvar mqtt_sys_name = topic_path[1];\n\n// filters\nif (mqtt_prefix !== flow.get(\"mqtt_prefix\")) {\n return;\n}\nif (mqtt_sys_name !== flow.get(\"mqtt_sys_name\")) {\n return;\n}\n\nreturn msg;", "outputs": 1, @@ -1535,25 +1519,26 @@ "y": 358.38189697265625, "wires": [ [ - "d5a33e0a.968908" + "95dff5fa.f06ac" ] ] }, { - "id": "a03c597a.472908", + "id": "1f5832f8.f7e15d", "type": "ui_toast", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "position": "top right", "displayTime": "5", + "outputs": 0, "name": "", "x": 888.63623046875, "y": 357.8908386230469, "wires": [] }, { - "id": "d5a33e0a.968908", + "id": "95dff5fa.f06ac", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "rules": [ { @@ -1571,16 +1556,16 @@ "y": 358.727294921875, "wires": [ [ - "a03c597a.472908" + "1f5832f8.f7e15d" ] ] }, { - "id": "1ade6665.4c2ffa", + "id": "f0defa9a.2b171", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", - "group": "82c0ad11.edeb3", + "group": "f7b943a7.14f418", "order": 0, "width": 0, "height": 0, @@ -1594,14 +1579,14 @@ "y": 1577.4601135253906, "wires": [ [ - "bd0632ad.413938" + "c0f9d636.c0ae18" ] ] }, { - "id": "cb884988.95d3f8", + "id": "5ff4847d.a8513c", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.show => christmas", "rules": [ { @@ -1621,14 +1606,14 @@ "y": 1706.8949584960938, "wires": [ [ - "99893d67.54c708" + "3ec3076f.74f948" ] ] }, { - "id": "636ba7b3.f3885", + "id": "e6671e8e.787948", "type": "switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "select command", "property": "topic", "propertyType": "msg", @@ -1660,50 +1645,55 @@ "y": 1750.273681640625, "wires": [ [ - "feea0191.76cd78", - "4a114481.ef5414" + "158d6749.b5ff71", + "e49e1dd7.1016f8" ], [ - "f01958ec.c38778", - "4a114481.ef5414" + "731f4b91.7100ec", + "e49e1dd7.1016f8" ], [ - "2ada3dc7.8d7f7a", - "4a114481.ef5414" + "b99b695b.b906d8", + "e49e1dd7.1016f8" ], [ - "b35a1a47.ed5e08", - "4a114481.ef5414" + "1233c41d.482bb4", + "e49e1dd7.1016f8" ] ] }, { - "id": "bd0632ad.413938", + "id": "c0f9d636.c0ae18", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "collect parameters for \"christmas\"", "func": "msg.payload = flow.get(\"parameters_christmas\");\nreturn msg;", "outputs": 1, "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], "x": 3433.190185546875, "y": 1576.952392578125, "wires": [ [ - "cb884988.95d3f8" + "5ff4847d.a8513c" ] ] }, { - "id": "feea0191.76cd78", + "id": "158d6749.b5ff71", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "velocity", - "group": "82c0ad11.edeb3", + "tooltip": "", + "group": "f7b943a7.14f418", "order": 0, "width": 0, "height": 0, "passthru": false, + "outs": "all", "topic": "velocity", "min": "1", "max": 10, @@ -1712,17 +1702,18 @@ "y": 1679.6665649414062, "wires": [ [ - "cb884988.95d3f8" + "5ff4847d.a8513c" ] ] }, { - "id": "f01958ec.c38778", + "id": "731f4b91.7100ec", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "Merry Go Round", - "group": "82c0ad11.edeb3", + "tooltip": "", + "group": "f7b943a7.14f418", "order": 0, "width": 0, "height": 0, @@ -1741,17 +1732,18 @@ "y": 1722.8094482421875, "wires": [ [ - "cb884988.95d3f8" + "5ff4847d.a8513c" ] ] }, { - "id": "2ada3dc7.8d7f7a", + "id": "b99b695b.b906d8", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "Chunk Blendover", - "group": "82c0ad11.edeb3", + "tooltip": "", + "group": "f7b943a7.14f418", "order": 0, "width": 0, "height": 0, @@ -1770,17 +1762,18 @@ "y": 1765.3807373046875, "wires": [ [ - "cb884988.95d3f8" + "5ff4847d.a8513c" ] ] }, { - "id": "b35a1a47.ed5e08", + "id": "1233c41d.482bb4", "type": "ui_switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "Whole Blendover", - "group": "82c0ad11.edeb3", + "tooltip": "", + "group": "f7b943a7.14f418", "order": 0, "width": 0, "height": 0, @@ -1799,14 +1792,14 @@ "y": 1806.809326171875, "wires": [ [ - "cb884988.95d3f8" + "5ff4847d.a8513c" ] ] }, { - "id": "63e917fd.6459a8", + "id": "ac3d5427.2d9a88", "type": "delay", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "wait for mqtt connection", "pauseType": "delay", "timeout": "7", @@ -1818,21 +1811,22 @@ "randomLast": "5", "randomUnits": "seconds", "drop": false, + "outputs": 1, "x": 826.9000244140625, "y": 72.39999389648438, "wires": [ [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ] ] }, { - "id": "55f8e26c.17c65c", + "id": "969f60d4.9e711", "type": "ui_colour_picker", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "color picker", "label": "", - "group": "d07e03c3.642f38", + "group": "abc9273f.36d778", "format": "hex", "outformat": "string", "showSwatch": false, @@ -1849,51 +1843,35 @@ "y": 820.0852661132812, "wires": [ [ - "13eab3fb.19d10c" + "5604be7e.4db068" ] ] }, { - "id": "f5229fb5.8f5288", - "type": "function", - "z": "79b7829.bf5367c", - "name": "converter", - "func": "var r = msg.payload[0];\nvar g = msg.payload[1];\nvar b = msg.payload[2];\n\n// algorithm: http://www.rapidtables.com/convert/color/rgb-to-hsv.htm\n\n// helper variables\nvar R_ = r / 255;\nvar G_ = g / 255;\nvar B_ = b / 255;\n\nvar C_max = Math.max(R_, G_, B_);\nvar C_min = Math.min(R_, G_, B_);\nvar Delta = C_max - C_min;\n\n// Hue\nvar H;\nif (Delta === 0) {\n H = 0;\n} else if (C_max === R_) {\n H = 60 * ( ( (G_ - B_) / Delta ) % 6 );\n if (H < 0) {\n H = H + 360;\n }\n} else if (C_max === G_) {\n H = 60 * ( ( (B_ - R_) / Delta) + 2 );\n} else if (C_max === B_) {\n H = 60 * ( ( (R_ - G_) / Delta) + 4 );\n}\n\n// Saturation\nvar S;\nif (C_max === 0) {\n S = 0;\n} else{\n S = Delta / C_max;\n}\n\n// Value\nvar V = C_max;\n\n// output messages\nvar msg_h = {\"topic\": \"H\", \"payload\": H};\nvar msg_s = {\"topic\": \"S\", \"payload\": S};\nvar msg_v = {\"topic\": \"V\", \"payload\": V};\n\nreturn [msg_h, msg_s, msg_v];", - "outputs": "3", - "noerr": 0, - "x": 273.73333740234375, - "y": 103.7166748046875, - "wires": [ - [], - [], - [] - ] - }, - { - "id": "65d31e50.cba11", - "type": "subflow:79b7829.bf5367c", - "z": "6fda1b5e.3b8e6c", + "id": "1dc1d8c7.b1662f", + "type": "subflow:793be9a5.d2c558", + "z": "2fc32e47.9732e2", "x": 3010, "y": 960, "wires": [ [ - "5e0aac3e.4cbba4" + "bcd9110.2b30bf" ], [ - "1fa03f3a.d14299" + "90654081.fab4a" ], [ - "e0aefdc0.559e28" + "ffc03869.e56db8" ] ] }, { - "id": "5e0aac3e.4cbba4", + "id": "bcd9110.2b30bf", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "H", - "group": "d07e03c3.642f38", + "group": "abc9273f.36d778", "order": 3, "width": "0", "height": "0", @@ -1906,17 +1884,17 @@ "y": 929.0000305175781, "wires": [ [ - "bc8dc0dc.a410d" + "2b9ae18a.a9465e" ] ] }, { - "id": "1fa03f3a.d14299", + "id": "90654081.fab4a", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "S", - "group": "d07e03c3.642f38", + "group": "abc9273f.36d778", "order": 4, "width": "0", "height": "0", @@ -1929,17 +1907,17 @@ "y": 959.25, "wires": [ [ - "bc8dc0dc.a410d" + "2b9ae18a.a9465e" ] ] }, { - "id": "e0aefdc0.559e28", + "id": "ffc03869.e56db8", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "V", - "group": "d07e03c3.642f38", + "group": "abc9273f.36d778", "order": 5, "width": "0", "height": "0", @@ -1952,56 +1930,26 @@ "y": 990.0833435058594, "wires": [ [ - "bc8dc0dc.a410d" - ] - ] - }, - { - "id": "5f5b597c.59f79", - "type": "function", - "z": "b4110c92.379a4", - "name": "converter", - "func": "var H = flow.get('H');\nvar S = flow.get('S');\nvar V = flow.get('V');\n\n// conversion\n// algorithm: http://www.rapidtables.com/convert/color/hsv-to-rgb.htm\n\nvar C = V * S;\nvar X = C * (1 - Math.abs(((H / 60) % 2) - 1));\nvar m = V - C;\n\nvar R_, G_, B_;\nif (H < 0) {\n return;\n} else if (H < 60) {\n [R_, G_, B_] = [C, X, 0];\n} else if (H < 120) {\n [R_, G_, B_] = [X, C, 0];\n} else if (H < 180) {\n [R_, G_, B_] = [0, C, X];\n} else if (H < 240) {\n [R_, G_, B_] = [0, X, C];\n} else if (H < 300) {\n [R_, G_, B_] = [X, 0, C];\n} else if (H < 360) {\n [R_, G_, B_] = [C, 0, X];\n} else {\n return;\n}\n\nvar R, G, B;\n[R, G, B] = [(R_ + m) * 255, (G_ + m) * 255, (B_ + m) * 255];\n[R, G, B] = [Math.round(R), Math.round(G), Math.round(B)];\n\nmsg.topic = \"colour\";\nmsg.payload = [R, G, B];\n\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 341.06671142578125, - "y": 74.38334655761719, - "wires": [ - [] - ] - }, - { - "id": "5d24ce5e.db9e78", - "type": "function", - "z": "b4110c92.379a4", - "name": "buffer", - "func": "var valid = ['H', 'S', 'V'];\n\nif (valid.indexOf(msg.topic) !== -1) {\n flow.set(msg.topic, msg.payload);\n}\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 200.06668090820312, - "y": 74.38334655761719, - "wires": [ - [ - "5f5b597c.59f79" + "2b9ae18a.a9465e" ] ] }, { - "id": "bc8dc0dc.a410d", - "type": "subflow:b4110c92.379a4", - "z": "6fda1b5e.3b8e6c", + "id": "2b9ae18a.a9465e", + "type": "subflow:a79c1741.5ef28", + "z": "2fc32e47.9732e2", "x": 3311.0164794921875, "y": 962.9834594726562, "wires": [ [ - "efc351d7.30ddc" + "5d817da7.2113cc" ] ] }, { - "id": "9062f53f.f8367", + "id": "9eee0521.d59178", "type": "change", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "msg.topic => color", "rules": [ { @@ -2017,18 +1965,18 @@ "from": "", "to": "", "reg": false, - "x": 3210, + "x": 3170, "y": 720, "wires": [ [ - "ffaa4a73.bbfde" + "d08e9516.95f93" ] ] }, { - "id": "8424c6f9.b362b", + "id": "af165c9a.745a4", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "preset for \"christmas\"", "func": "var show_name = \"christmas\";\n\nflow.set(\"parameters_\" + show_name , {});\n\nvar velocity = {\"topic\": \"velocity\", \"payload\": 5};\nvar merry_go_round = {\"topic\": \"merry_go_round\", \"payload\": 3};\nvar chunk_blendover = {\"topic\": \"chunk_blendover\", \"payload\": 5};\nvar whole_blendover = {\"topic\": \"whole_blendover\", \"payload\": 5};\n\nvar msg_queue = [velocity, merry_go_round, chunk_blendover, whole_blendover];\n\nmsg_queue.forEach(function(msg) {\n msg.show = show_name;\n});\n\nreturn [msg_queue];\n", "outputs": 1, @@ -2037,42 +1985,42 @@ "y": 1745.60009765625, "wires": [ [ - "636ba7b3.f3885" + "e6671e8e.787948" ] ] }, { - "id": "5395b965.d1dd", + "id": "9df74816.b1fcc8", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "led_presets", "links": [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ], "x": 2440.5425415039062, "y": 1746.0286254882812, "wires": [ [ - "8424c6f9.b362b" + "af165c9a.745a4" ] ] }, { - "id": "4a114481.ef5414", + "id": "e49e1dd7.1016f8", "type": "link out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "cache_christmas", "links": [ - "214117af.69c508" + "57127376.359c3c" ], "x": 2976, "y": 1683.5999755859375, "wires": [] }, { - "id": "1a3dc865.b7d26", + "id": "71875ea9.c4d728", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "set msg.show", "func": "// split the topic tree\nvar topic_path = msg.topic.split(\"/\");\n\n// set \"show\" attribute\nmsg.show = topic_path[3];\n\nreturn msg;\n", "outputs": 1, @@ -2081,43 +2029,49 @@ "y": 383.5999450683594, "wires": [ [ - "f9799ed.185d9e" + "7ffe1ae4.4a371c" ] ] }, { - "id": "f9799ed.185d9e", + "id": "7ffe1ae4.4a371c", "type": "json", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", + "property": "payload", + "action": "", + "pretty": false, "x": 1984.199951171875, "y": 384.3999938964844, "wires": [ [ - "a408a14a.f41cd" + "ba691aee.84ab9" ] ] }, { - "id": "a408a14a.f41cd", + "id": "ba691aee.84ab9", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "serialize", "func": "var properties = msg.payload;\nvar msg_queue = []\n\nfor (var property_name in properties) {\n var property_msg = {\"show\": msg.show};\n property_msg.topic = property_name;\n property_msg.payload = properties[property_name];\n msg_queue.push(property_msg);\n}\n\nreturn [msg_queue];", "outputs": 1, "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], "x": 2117.142822265625, "y": 384.4285583496094, "wires": [ [ - "9869a888.163a" + "adc70530.f083a8" ] ] }, { - "id": "9869a888.163a", + "id": "adc70530.f083a8", "type": "switch", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "select show", "property": "show", "propertyType": "msg", @@ -2152,40 +2106,43 @@ "y": 384.5713806152344, "wires": [ [ - "e6c1abf1.b7836" + "da1a805f.2a69d8" ], [ - "2f16dd9c.bf8dba" + "a9af69bf.8e2aa" ], [ - "a1b62d23.6d9bb" + "f34afbce.9f5eb" ], [ - "636ba7b3.f3885" + "e6671e8e.787948" ], [] ] }, { - "id": "9f7e462e.a18a08", + "id": "7a345317.9fe64c", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "filter for prefixes", "func": "// get to work\nvar topic_path = msg.topic.split(\"/\");\n\nvar mqtt_prefix = topic_path[0];\nvar mqtt_sys_name = topic_path[1];\n\n// filters\nif (mqtt_prefix !== flow.get(\"mqtt_prefix\")) {\n return;\n}\nif (mqtt_sys_name !== flow.get(\"mqtt_sys_name\")) {\n return;\n}\n\nreturn msg;", "outputs": 1, "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], "x": 1618.8887939453125, "y": 383.4444580078125, "wires": [ [ - "1a3dc865.b7d26" + "71875ea9.c4d728" ] ] }, { - "id": "c4558aab.3971f", + "id": "34e9a58e.c238ca", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "filter for prefix", "func": "// get to work\nvar topic_path = msg.topic.split(\"/\");\n\nvar mqtt_prefix = topic_path[0];\nvar mqtt_sys_name = topic_path[1];\n\n// filters\nif (mqtt_prefix !== flow.get(\"mqtt_prefix\")) {\n return;\n}\nif (mqtt_sys_name !== flow.get(\"mqtt_sys_name\")) {\n return;\n}\n\nreturn msg;", "outputs": 1, @@ -2194,30 +2151,31 @@ "y": 99.77774810791016, "wires": [ [ - "5e0ce140.05a188" + "5839804a.83d6c8" ] ] }, { - "id": "8ee0ec3f.3ba43", + "id": "c68b5b70.cd789", "type": "mqtt in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "topic": "+/+/global-brightness/current", "qos": "2", - "broker": "33da3f6.4497c4", + "broker": "4dc2b995.13d0f8", + "inputs": 0, "x": 2621.333251953125, "y": 99.77774810791016, "wires": [ [ - "c4558aab.3971f" + "34e9a58e.c238ca" ] ] }, { - "id": "5e0ce140.05a188", + "id": "5839804a.83d6c8", "type": "range", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "minin": "0", "maxin": "1", "minout": "0", @@ -2229,33 +2187,33 @@ "y": 99.33330535888672, "wires": [ [ - "9416d131.c04a18" + "54cf3bac.d4f274" ] ] }, { - "id": "ea7594f7.7fcb58", + "id": "4a369efd.12b948", "type": "range", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "minin": "0", "maxin": "100", "minout": "0", "maxout": "1", "action": "scale", "round": false, - "name": "rescale: 100 => 0.1", + "name": "rescale: 100 => 1.0", "x": 3758.6663818359375, "y": 103.6666488647461, "wires": [ [ - "ed6241fe.5b34f8" + "d813152e.271448" ] ] }, { - "id": "ed6241fe.5b34f8", + "id": "d813152e.271448", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "set topic", "func": "msg.topic = flow.get(\"mqtt_prefix\") + \"/\" + flow.get(\"mqtt_sys_name\") +\n \"/global-brightness/set\";\n\nreturn msg;", "outputs": 1, @@ -2264,28 +2222,28 @@ "y": 103.66666412353516, "wires": [ [ - "8713f774.b40a48" + "c788fb1.4677a88" ] ] }, { - "id": "8713f774.b40a48", + "id": "c788fb1.4677a88", "type": "mqtt out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "topic": "", "qos": "1", "retain": "", - "broker": "33da3f6.4497c4", + "broker": "4dc2b995.13d0f8", "x": 4102.50048828125, "y": 104.6666488647461, "wires": [] }, { - "id": "99f7f97e.a2bf8", + "id": "9bce6ffd.0090c8", "type": "ui_text", - "z": "6fda1b5e.3b8e6c", - "group": "94adec20.46bd2", + "z": "2fc32e47.9732e2", + "group": "da52ac1d.f09478", "order": 4, "width": 0, "height": 0, @@ -2298,25 +2256,26 @@ "wires": [] }, { - "id": "4bcc53ed.8cd964", + "id": "78c7cb51.41f2bc", "type": "mqtt in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "topic": "+/+/show/current", "qos": "2", - "broker": "33da3f6.4497c4", + "broker": "4dc2b995.13d0f8", + "inputs": 0, "x": 2464.25, "y": 2179.5000610351562, "wires": [ [ - "710071a5.0830b" + "db9a4792.81212" ] ] }, { - "id": "710071a5.0830b", + "id": "db9a4792.81212", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "filter for prefixes", "func": "// get to work\nvar topic_path = msg.topic.split(\"/\");\n\nvar mqtt_prefix = topic_path[0];\nvar mqtt_sys_name = topic_path[1];\n\n// filters\nif (mqtt_prefix !== flow.get(\"mqtt_prefix\")) {\n return;\n}\nif (mqtt_sys_name !== flow.get(\"mqtt_sys_name\")) {\n return;\n}\n\nreturn msg;", "outputs": 1, @@ -2325,14 +2284,14 @@ "y": 2178.5, "wires": [ [ - "99f7f97e.a2bf8" + "9bce6ffd.0090c8" ] ] }, { - "id": "2a527977.91f54e", + "id": "96df562.6b7a828", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "brightness preset", "func": "msg.topic = \"brightness\";\nmsg.payload = 0.5;\n\nreturn msg;", "outputs": 1, @@ -2341,59 +2300,60 @@ "y": 50.33332061767578, "wires": [ [ - "5e0ce140.05a188" + "5839804a.83d6c8" ] ] }, { - "id": "50d2486d.e6243", + "id": "756ae94f.b7242", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "led_presets", "links": [ - "51bb22c7.fd896c" + "451f5f90.8cd6f" ], "x": 2706.15478515625, "y": 50.33335876464844, "wires": [ [ - "2a527977.91f54e" + "96df562.6b7a828" ] ] }, { - "id": "faf2574a.df3c5", + "id": "1cbaddbc.a3913a", "type": "debug", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "active": false, "console": "false", - "complete": "false", + "complete": "payload", "x": 4770, "y": 760, "wires": [] }, { - "id": "54d0a6c3.f8c01", + "id": "feb57140.68cd68", "type": "json", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "pretty": false, "x": 4570, "y": 700, "wires": [ [ - "b29039b2.4d1188", - "faf2574a.df3c5" + "c264f0a4.a5008", + "1cbaddbc.a3913a", + "27f04f00.27cdfa" ] ] }, { - "id": "d20c1eef.24e6f", + "id": "ff55be04.7f292", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", - "group": "94adec20.46bd2", + "group": "da52ac1d.f09478", "order": 6, "width": 0, "height": 0, @@ -2409,16 +2369,16 @@ "y": 640, "wires": [ [ - "e45228ca.653e1" + "66090b14.cac814" ] ] }, { - "id": "2f5aa845.ab2b4", + "id": "637c9514.ed369c", "type": "ui_button", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", - "group": "94adec20.46bd2", + "group": "da52ac1d.f09478", "order": 5, "width": 0, "height": 0, @@ -2434,14 +2394,14 @@ "y": 600, "wires": [ [ - "e45228ca.653e1" + "66090b14.cac814" ] ] }, { - "id": "e45228ca.653e1", + "id": "66090b14.cac814", "type": "function", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "add prefix and sys_name", "func": "msg.topic = flow.get(\"mqtt_prefix\") + \"/\" + flow.get(\"mqtt_sys_name\") + \"/\" + msg.topic;\n\nreturn msg;", "outputs": 1, @@ -2450,20 +2410,20 @@ "y": 624.7858276367188, "wires": [ [ - "54d0a6c3.f8c01" + "feb57140.68cd68" ] ] }, { - "id": "742a15b9.710024", + "id": "77ba146c.b47924", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "R", - "group": "d07e03c3.642f38", + "group": "abc9273f.36d778", "order": 7, - "width": "5", - "height": "1", + "width": "0", + "height": "0", "passthru": false, "topic": "R", "min": 0, @@ -2473,20 +2433,20 @@ "y": 1100, "wires": [ [ - "d19b25eb.348ea" + "d9c76ab3.29ae8" ] ] }, { - "id": "e708580f.3be6b", + "id": "99930617.1f588", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "G", - "group": "d07e03c3.642f38", + "group": "abc9273f.36d778", "order": 8, - "width": "5", - "height": "1", + "width": "0", + "height": "0", "passthru": false, "topic": "G", "min": 0, @@ -2496,21 +2456,23 @@ "y": 1130.2499694824219, "wires": [ [ - "d19b25eb.348ea" + "d9c76ab3.29ae8" ] ] }, { - "id": "8b4b2b7a.332b48", + "id": "34ca452c.044d62", "type": "ui_slider", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "label": "B", - "group": "d07e03c3.642f38", + "tooltip": "", + "group": "abc9273f.36d778", "order": 9, - "width": "5", - "height": "1", + "width": "0", + "height": "0", "passthru": false, + "outs": "all", "topic": "B", "min": 0, "max": "255", @@ -2519,15 +2481,15 @@ "y": 1161.0833129882812, "wires": [ [ - "d19b25eb.348ea" + "d9c76ab3.29ae8" ] ] }, { - "id": "b69af97.17f7288", + "id": "9ce1d5bd.d6eae", "type": "ui_text", - "z": "6fda1b5e.3b8e6c", - "group": "d07e03c3.642f38", + "z": "2fc32e47.9732e2", + "group": "abc9273f.36d778", "order": 6, "width": "0", "height": "0", @@ -2540,127 +2502,39 @@ "wires": [] }, { - "id": "4e0b8077.7f6048", - "type": "subflow:a51f5122.cdaca", - "z": "6fda1b5e.3b8e6c", + "id": "90223fcd.1047c8", + "type": "subflow:1919d466.09f77c", + "z": "2fc32e47.9732e2", "x": 3010.3465270996094, "y": 1121.0833129882812, "wires": [ [ - "742a15b9.710024" + "77ba146c.b47924" ], [ - "e708580f.3be6b" + "99930617.1f588" ], [ - "8b4b2b7a.332b48" - ] - ] - }, - { - "id": "97d1d54c.8f5fa8", - "type": "switch", - "z": "a51f5122.cdaca", - "name": "", - "property": "parts.index", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "0", - "vt": "num" - }, - { - "t": "eq", - "v": "1", - "vt": "num" - }, - { - "t": "eq", - "v": "2", - "vt": "str" - } - ], - "checkall": "false", - "outputs": 3, - "x": 590, - "y": 140, - "wires": [ - [], - [], - [] - ], - "outputLabels": [ - "r", - "", - "b" - ] - }, - { - "id": "b19dcdab.93aec8", - "type": "split", - "z": "a51f5122.cdaca", - "name": "", - "splt": "\\n", - "spltType": "str", - "arraySplt": 1, - "arraySpltType": "len", - "stream": false, - "addname": "", - "x": 450, - "y": 140, - "wires": [ - [ - "97d1d54c.8f5fa8" - ] - ] - }, - { - "id": "4eceadda.d6b5d4", - "type": "function", - "z": "7ea41ffb.7ca8f", - "name": "joiner", - "func": "var R = flow.get('R');\nvar G = flow.get('G');\nvar B = flow.get('B');\n\nmsg.topic = \"colour\";\nmsg.payload = [R, G, B];\n\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 330, - "y": 100, - "wires": [ - [] - ] - }, - { - "id": "b29e8c7a.7f7bc", - "type": "function", - "z": "7ea41ffb.7ca8f", - "name": "buffer", - "func": "var valid = ['R', 'G', 'B'];\n\nif (valid.indexOf(msg.topic) !== -1) {\n flow.set(msg.topic, msg.payload);\n}\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 198.99996948242188, - "y": 100, - "wires": [ - [ - "4eceadda.d6b5d4" + "34ca452c.044d62" ] ] }, { - "id": "d19b25eb.348ea", - "type": "subflow:7ea41ffb.7ca8f", - "z": "6fda1b5e.3b8e6c", + "id": "d9c76ab3.29ae8", + "type": "subflow:d15c800.af31a", + "z": "2fc32e47.9732e2", "x": 3295.8465118408203, "y": 1130.0832977294922, "wires": [ [ - "efc351d7.30ddc" + "5d817da7.2113cc" ] ] }, { - "id": "ffaa4a73.bbfde", + "id": "d08e9516.95f93", "type": "delay", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "", "pauseType": "rate", "timeout": "5", @@ -2672,66 +2546,67 @@ "randomLast": "5", "randomUnits": "seconds", "drop": true, + "outputs": 1, "x": 3390, "y": 720, "wires": [ [ - "5fd0b2e8.946cf4" + "569f7e7f.3c1e68" ] ] }, { - "id": "efc351d7.30ddc", + "id": "5d817da7.2113cc", "type": "link out", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "solidcolor_color_setter_output", "links": [ - "15f49275.5f4906", - "4782629f.d703cc" + "15119dc.8ce47e2", + "d63511da.4405a8" ], "x": 3455, "y": 940, "wires": [] }, { - "id": "15f49275.5f4906", + "id": "15119dc.8ce47e2", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "solidcolor_color_mqtt_outlet", "links": [ - "efc351d7.30ddc" + "5d817da7.2113cc" ], - "x": 3075, + "x": 3035, "y": 720, "wires": [ [ - "9062f53f.f8367" + "9eee0521.d59178" ] ] }, { - "id": "4782629f.d703cc", + "id": "d63511da.4405a8", "type": "link in", - "z": "6fda1b5e.3b8e6c", + "z": "2fc32e47.9732e2", "name": "solidcolor_color_setter_input", "links": [ - "efc351d7.30ddc" + "5d817da7.2113cc" ], "x": 2795, "y": 940, "wires": [ [ - "85c91225.eb8dd", - "65d31e50.cba11", - "4e0b8077.7f6048" + "13fba410.b8d01c", + "1dc1d8c7.b1662f", + "90223fcd.1047c8" ] ] }, { - "id": "b7945f63.9ce58", + "id": "394e8ec8.f81c42", "type": "ui_text", - "z": "6fda1b5e.3b8e6c", - "group": "d07e03c3.642f38", + "z": "2fc32e47.9732e2", + "group": "abc9273f.36d778", "order": 2, "width": "0", "height": "0", @@ -2742,5 +2617,476 @@ "x": 3150, "y": 880, "wires": [] + }, + { + "id": "774d66c.3826518", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "57eea257.4cfba4", + "order": 0, + "width": 0, + "height": 0, + "passthru": false, + "label": "RGBTest", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "{}", + "payloadType": "json", + "topic": "start", + "x": 3570.625, + "y": 2108.75, + "wires": [ + [ + "1bea695d.8a1867" + ] + ] + }, + { + "id": "1bea695d.8a1867", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => rgbtest", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "rgbtest", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3798.125, + "y": 2107.5, + "wires": [ + [ + "3ec3076f.74f948" + ] + ] + }, + { + "id": "27f04f00.27cdfa", + "type": "ui_text", + "z": "2fc32e47.9732e2", + "group": "da52ac1d.f09478", + "order": 0, + "width": 0, + "height": 0, + "name": "", + "label": "text", + "format": "{{msg.payload}}", + "layout": "row-spread", + "x": 4777, + "y": 884, + "wires": [] + }, + { + "id": "c561213d.313758", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "57eea257.4cfba4", + "order": 0, + "width": 0, + "height": 0, + "passthru": false, + "label": "Starlight", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "{}", + "payloadType": "json", + "topic": "start", + "x": 3496.999999999999, + "y": 2175.9999999999995, + "wires": [ + [ + "e182af98.87405" + ] + ] + }, + { + "id": "e182af98.87405", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => starlight", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "starlight", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3780.999999999999, + "y": 2181.9999999999995, + "wires": [ + [ + "3ec3076f.74f948" + ] + ] + }, + { + "id": "ddf6bc75.ad0428", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => jump", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "jump", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3802.999999999999, + "y": 2255.9999999999995, + "wires": [ + [ + "3ec3076f.74f948" + ] + ] + }, + { + "id": "6e95c051.38c64", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "57eea257.4cfba4", + "order": 0, + "width": 0, + "height": 0, + "passthru": false, + "label": "Jump!", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "{}", + "payloadType": "json", + "topic": "start", + "x": 3510, + "y": 2260, + "wires": [ + [ + "ddf6bc75.ad0428" + ] + ] + }, + { + "id": "149826ee.cb6201", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "abc9273f.36d778", + "order": 0, + "width": 0, + "height": 0, + "passthru": false, + "label": "Warm white", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "[241,178,60]", + "payloadType": "json", + "topic": "start", + "x": 3564.999999999999, + "y": 1051.9999999999998, + "wires": [ + [ + "5d817da7.2113cc" + ] + ] + }, + { + "id": "b492d144.1b9b08", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "abc9273f.36d778", + "order": 0, + "width": 0, + "height": 0, + "passthru": false, + "label": "White", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "[255,255,255]", + "payloadType": "json", + "topic": "", + "x": 3544.5, + "y": 1088, + "wires": [ + [ + "5d817da7.2113cc" + ] + ] + }, + { + "id": "94200d9a63f133ed", + "type": "link in", + "z": "2fc32e47.9732e2", + "name": "twocolorblend_color1_input", + "links": [ + "0555ed834494cbab" + ], + "x": 3035, + "y": 1300, + "wires": [ + [ + "845b563.6862ca8" + ] + ] + }, + { + "id": "0555ed834494cbab", + "type": "link out", + "z": "2fc32e47.9732e2", + "name": "twocolorblend_color1_output", + "mode": "link", + "links": [ + "57127376.359c3c", + "94200d9a63f133ed" + ], + "x": 3795, + "y": 1340, + "wires": [] + }, + { + "id": "b2529fb843960528", + "type": "link in", + "z": "2fc32e47.9732e2", + "name": "twocolorblend_color2_input", + "links": [ + "1ee7c9a6620d3859" + ], + "x": 3035, + "y": 1420, + "wires": [ + [ + "6116c978.6b3cc" + ] + ] + }, + { + "id": "1ee7c9a6620d3859", + "type": "link out", + "z": "2fc32e47.9732e2", + "name": "twocolorblend_color2_output", + "mode": "link", + "links": [ + "57127376.359c3c", + "b2529fb843960528" + ], + "x": 3795, + "y": 1380, + "wires": [] + }, + { + "id": "5c2e39584ad8b2f5", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => twocolorblend", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "twocolorblend", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3640, + "y": 1380, + "wires": [ + [ + "1ee7c9a6620d3859" + ] + ] + }, + { + "id": "68c7a793eaa9294d", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => twocolorblend", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "twocolorblend", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3640, + "y": 1340, + "wires": [ + [ + "0555ed834494cbab" + ] + ] + }, + { + "id": "36653f0a33615a9a", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "57eea257.4cfba4", + "order": 11, + "width": 0, + "height": 0, + "passthru": false, + "label": "Audio spectrum", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "{}", + "payloadType": "json", + "topic": "start", + "x": 3530, + "y": 2340, + "wires": [ + [ + "663dd5d378ba6465" + ] + ] + }, + { + "id": "663dd5d378ba6465", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => audio_spectrum", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "audio_spectrum", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3860, + "y": 2340, + "wires": [ + [ + "3ec3076f.74f948" + ] + ] + }, + { + "id": "4dc2b995.13d0f8", + "type": "mqtt-broker", + "broker": "localhost", + "port": "1883", + "clientid": "", + "usetls": false, + "compatmode": true, + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "willTopic": "", + "willQos": "0", + "willPayload": "" + }, + { + "id": "222a3f19.b90e9", + "type": "ui_group", + "name": "Spin the Bottle", + "tab": "c7886a8d.764f4", + "order": 3, + "disp": true, + "width": "6" + }, + { + "id": "e2aca116.33ad2", + "type": "ui_group", + "name": "Color Blend", + "tab": "c7886a8d.764f4", + "order": 2, + "disp": true, + "width": "6" + }, + { + "id": "57eea257.4cfba4", + "type": "ui_group", + "name": "Animations", + "tab": "c7886a8d.764f4", + "order": 4, + "disp": true, + "width": "6" + }, + { + "id": "da52ac1d.f09478", + "type": "ui_group", + "name": "etc", + "tab": "c7886a8d.764f4", + "order": 5, + "disp": false, + "width": "6" + }, + { + "id": "f7b943a7.14f418", + "type": "ui_group", + "name": "Christmas Show", + "tab": "c7886a8d.764f4", + "order": 6, + "disp": true, + "width": "6" + }, + { + "id": "abc9273f.36d778", + "type": "ui_group", + "name": "Color", + "tab": "c7886a8d.764f4", + "order": 1, + "disp": true, + "width": "6" + }, + { + "id": "c7886a8d.764f4", + "type": "ui_tab", + "name": "MyLED", + "icon": "lightbulb_outline", + "order": 2 } ] \ No newline at end of file From 322327b70e7d04b3922c8e0b80ea532137c84c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20W=C3=BCrl?= <8093294-wuerla@users.noreply.gitlab.com> Date: Wed, 2 Mar 2022 22:12:52 +0100 Subject: [PATCH 2/3] due to reasons ... --- server/helpers/layout.py | 25 + server/lightshows/__active__.py | 1 + server/lightshows/__init__.py | 3 +- server/lightshows/audio.py | 6 +- server/lightshows/jump.py | 17 +- server/lightshows/templates/base.py | 2 + server/lightshows/ukraine.py | 43 ++ ui/nodered.json | 773 +++++++++++++++++----------- 8 files changed, 551 insertions(+), 319 deletions(-) create mode 100644 server/helpers/layout.py create mode 100755 server/lightshows/ukraine.py diff --git a/server/helpers/layout.py b/server/helpers/layout.py new file mode 100644 index 0000000..644ee83 --- /dev/null +++ b/server/helpers/layout.py @@ -0,0 +1,25 @@ +import logging + +from drivers import LEDStrip + +log = logging.getLogger(__name__) + +class Layout: + + def __init__(self, strip: LEDStrip, dead=102, mirror=True): + self.strip = strip + self.length = strip.num_leds + self.dead = dead + self.mirror = mirror + + @property + def block(self): + return int((self.length - self.dead) / (2 if self.mirror else 1)) + + def set_pixel(self, led_num: int, red: float, green: float, blue: float) -> None: + self.set_pixel_func(self.strip.set_pixel, led_num, red, green, blue) + + def set_pixel_func(self, func, led_num: int, red: float, green: float, blue: float) -> None: + func(led_num, red, green, blue) + if self.mirror: + func(self.length -1 - led_num, red, green, blue) diff --git a/server/lightshows/__active__.py b/server/lightshows/__active__.py index 60f591c..b453f11 100644 --- a/server/lightshows/__active__.py +++ b/server/lightshows/__active__.py @@ -17,4 +17,5 @@ 'starlight': starlight.Starlight, 'jump': jump.Jump, 'audio_spectrum': audio.AudioSpectrum, + 'ukraine': ukraine.Ukraine, } diff --git a/server/lightshows/__init__.py b/server/lightshows/__init__.py index 08743dc..54dae45 100644 --- a/server/lightshows/__init__.py +++ b/server/lightshows/__init__.py @@ -20,5 +20,6 @@ 'starlight', 'strandtest', 'theaterchase', - 'twocolorblend' + 'twocolorblend', + 'ukraine', ] diff --git a/server/lightshows/audio.py b/server/lightshows/audio.py index bb3ad04..663c582 100755 --- a/server/lightshows/audio.py +++ b/server/lightshows/audio.py @@ -113,11 +113,7 @@ def update(self, current_step: int, current_cycle: int) -> bool: matrix = calculate_levels(data, self.chunk, self.sample_rate) line = [int((1 << matrix[i]) - 1) for i in range(100)] for index, value in enumerate(line): - rgb = wheel(value, threshold) - # brightness = int(max(1, math.log(value - threshold + 1))) if value >= threshold else 0 - # brightness = value - threshold + 1 if value >= threshold else 0 - self.strip.set_pixel(index, *rgb) - self.strip.set_pixel(self.strip.num_leds - index, *rgb) + self.layout.set_pixel(index, *(wheel(value, threshold))) except Exception as e: if not hasattr(e, "message") or e.message != "not a whole number of frames": raise e diff --git a/server/lightshows/jump.py b/server/lightshows/jump.py index 6eb407d..80180bb 100755 --- a/server/lightshows/jump.py +++ b/server/lightshows/jump.py @@ -4,6 +4,7 @@ import math from drivers import LEDStrip +from helpers.layout import Layout from lightshows.templates.colorcycle import ColorCycle log = logging.getLogger(__name__) @@ -48,8 +49,6 @@ def __init__(self, strip: LEDStrip, parameters: dict): super().__init__(strip, parameters) self.state = {} self.stripe = 1 - self.block = 99 - self.mirror = True self.balls = () self.spare_colors = [(0, 255, 255)] @@ -60,11 +59,11 @@ def init_parameters(self): def before_start(self): self.balls = ( - Ball(self.block, self.stripe, (255, 0, 0)), - Ball(self.block * 0.5, self.stripe, (0, 255, 0)), - Ball(self.block * 0.75, self.stripe, (255, 255, 0)), - Ball(self.block * 0.88, self.stripe, (255, 0, 255)), - Ball(self.block * 0.66, self.stripe, (0, 0, 255)) + Ball(self.layout.block, self.stripe, (255, 0, 0)), + Ball(self.layout.block * 0.5, self.stripe, (0, 255, 0)), + Ball(self.layout.block * 0.75, self.stripe, (255, 255, 0)), + Ball(self.layout.block * 0.88, self.stripe, (255, 0, 255)), + Ball(self.layout.block * 0.66, self.stripe, (0, 0, 255)) ) def update(self, current_step: int, current_cycle: int) -> bool: @@ -76,9 +75,7 @@ def update(self, current_step: int, current_cycle: int) -> bool: for ball in self.balls: pos = ball.get_pos(t) index = pos + offset - self.strip.set_pixel(index, *ball.color) - if self.mirror: - self.strip.set_pixel(self.strip.num_leds - index, *ball.color) + self.layout.set_pixel(index, *ball.color) if ball.is_next(): self.spare_colors.insert(0, ball.color) diff --git a/server/lightshows/templates/base.py b/server/lightshows/templates/base.py index 36209f4..60d3f5e 100644 --- a/server/lightshows/templates/base.py +++ b/server/lightshows/templates/base.py @@ -16,6 +16,7 @@ from drivers import LEDStrip from helpers.configparser import get_configuration from helpers.exceptions import * +from helpers.layout import Layout class LightshowParameters: @@ -64,6 +65,7 @@ def __init__(self, strip: LEDStrip, parameters: dict): # Parameters self.p = LightshowParameters() self.strip = strip + self.layout = Layout(strip) self.init_parameters() # let the child class set its own default parameters # override with any directly given parameters diff --git a/server/lightshows/ukraine.py b/server/lightshows/ukraine.py new file mode 100755 index 0000000..1b2e0b3 --- /dev/null +++ b/server/lightshows/ukraine.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import logging + +from drivers import LEDStrip +from helpers import verify +from helpers.color import SmoothBlend +from helpers.exceptions import InvalidStrip +from lightshows.templates.base import Lightshow + +log = logging.getLogger(__name__) + + +class Ukraine(Lightshow): + """\ + Rotates a rainbow color wheel around the strip. + + No parameters necessary + """ + + def __init__(self, strip: LEDStrip, parameters: dict): + super().__init__(strip, parameters) + self.state = {} + self.stripe = 1 + self.balls = () + self.spare_colors = [(0, 255, 255)] + + def init_parameters(self): + pass + + def run(self): + transition = SmoothBlend(self.strip) + yellow_pixels = self.layout.block // 2 + for index in range(yellow_pixels): + self.layout.set_pixel_func(transition.set_pixel, index, 255, 255, 0.0) + for index in range(yellow_pixels, self.layout.block): + self.layout.set_pixel_func(transition.set_pixel, index, 0, 0, 255) + transition.blend() + + def check_runnable(self): + # do we have at least one LED? + if self.strip.num_leds < 1: + raise InvalidStrip("This show needs a strip of at least {} LEDs to run!".format(self.strip.num_leds)) diff --git a/ui/nodered.json b/ui/nodered.json index 7c79c84..bc49aef 100644 --- a/ui/nodered.json +++ b/ui/nodered.json @@ -1,27 +1,32 @@ [ { - "id": "d15c800.af31a", + "id": "2fc32e47.9732e2", + "type": "tab", + "label": "LED Control" + }, + { + "id": "93f4131.fbfd2f", "type": "subflow", - "name": "RGB Joiner", - "info": "", + "name": "HEX to RGB", + "info": "convert a color HEX value (like #ff0080) to a rgb color tuple (like [255, 0, 128])", "in": [ { - "x": 80, - "y": 100, + "x": 207, + "y": 96, "wires": [ { - "id": "eb5767ba.336868" + "id": "5c79cf54.3c5ed" } ] } ], "out": [ { - "x": 440, - "y": 100, + "x": 534, + "y": 96, "wires": [ { - "id": "2507f05e.330b78", + "id": "5c79cf54.3c5ed", "port": 0 } ] @@ -29,33 +34,110 @@ ] }, { - "id": "2507f05e.330b78", - "type": "function", - "z": "d15c800.af31a", - "name": "joiner", - "func": "var R = flow.get('R');\nvar G = flow.get('G');\nvar B = flow.get('B');\n\nmsg.topic = \"colour\";\nmsg.payload = [R, G, B];\n\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 330, - "y": 100, - "wires": [ - [] + "id": "49e83dbb.1a9224", + "type": "subflow", + "name": "RGB to HEX", + "info": "convert an rgb color tuple (like [255, 0, 128]) to a color HEX value (like #ff0080)", + "in": [ + { + "x": 209.00001525878906, + "y": 136, + "wires": [ + { + "id": "bd378a2d.22a2d" + } + ] + } + ], + "out": [ + { + "x": 455, + "y": 137, + "wires": [ + { + "id": "bd378a2d.22a2d", + "port": 0 + } + ] + } ] }, { - "id": "eb5767ba.336868", - "type": "function", - "z": "d15c800.af31a", - "name": "buffer", - "func": "var valid = ['R', 'G', 'B'];\n\nif (valid.indexOf(msg.topic) !== -1) {\n flow.set(msg.topic, msg.payload);\n}\n\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 198.99996948242188, - "y": 100, - "wires": [ - [ - "2507f05e.330b78" - ] + "id": "793be9a5.d2c558", + "type": "subflow", + "name": "RGB to HSV", + "info": "convert an RGB color tuple (like [128, 0, 128]) to H (hue), S (saturation) and V (brightness) values (like, 300, 1, 0.5).", + "in": [ + { + "x": 144, + "y": 103, + "wires": [ + { + "id": "8f7a013f.29663" + } + ] + } + ], + "out": [ + { + "x": 474, + "y": 49, + "wires": [ + { + "id": "8f7a013f.29663", + "port": 0 + } + ] + }, + { + "x": 472.9999694824219, + "y": 102, + "wires": [ + { + "id": "8f7a013f.29663", + "port": 1 + } + ] + }, + { + "x": 473.9999694824219, + "y": 156, + "wires": [ + { + "id": "8f7a013f.29663", + "port": 2 + } + ] + } + ] + }, + { + "id": "a79c1741.5ef28", + "type": "subflow", + "name": "HSV to RGB", + "info": "cache all messages with topics H, S and V.\non any H, S or V message, the according RGB tuple is recalculated and sent out.", + "in": [ + { + "x": 95.33334350585938, + "y": 74.5, + "wires": [ + { + "id": "ff772c2b.e14458" + } + ] + } + ], + "out": [ + { + "x": 448.33331298828125, + "y": 74.66667175292969, + "wires": [ + { + "id": "b3d72a18.150118", + "port": 0 + } + ] + } ] }, { @@ -108,92 +190,273 @@ ] }, { - "id": "11416ce1.833b7b", - "type": "switch", - "z": "1919d466.09f77c", - "name": "", - "property": "parts.index", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "0", - "vt": "num" - }, - { - "t": "eq", - "v": "1", - "vt": "num" - }, - { - "t": "eq", - "v": "2", - "vt": "str" - } - ], - "checkall": "false", - "outputs": 3, - "x": 590, - "y": 140, - "wires": [ - [], - [], - [] - ], - "outputLabels": [ - "r", - "", - "b" - ] - }, - { - "id": "99b2c1b8.b666d8", - "type": "split", - "z": "1919d466.09f77c", - "name": "", - "splt": "\\n", - "spltType": "str", - "arraySplt": 1, - "arraySpltType": "len", - "stream": false, - "addname": "", - "x": 450, - "y": 140, - "wires": [ - [ - "11416ce1.833b7b" - ] - ] - }, - { - "id": "a79c1741.5ef28", + "id": "d15c800.af31a", "type": "subflow", - "name": "HSV to RGB", - "info": "cache all messages with topics H, S and V.\non any H, S or V message, the according RGB tuple is recalculated and sent out.", + "name": "RGB Joiner", + "info": "", "in": [ { - "x": 95.33334350585938, - "y": 74.5, + "x": 80, + "y": 100, "wires": [ { - "id": "ff772c2b.e14458" + "id": "eb5767ba.336868" } ] } ], "out": [ { - "x": 448.33331298828125, - "y": 74.66667175292969, + "x": 440, + "y": 100, "wires": [ { - "id": "b3d72a18.150118", + "id": "2507f05e.330b78", "port": 0 } ] } ] }, + { + "id": "311dcc49.0aed54", + "type": "ui_base", + "theme": { + "name": "theme-light", + "lightTheme": { + "default": "#0094CE", + "baseColor": "#0094CE", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": true, + "reset": false + }, + "darkTheme": { + "default": "#097479", + "baseColor": "#097479", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": false + }, + "customTheme": { + "name": "Untitled Theme 1", + "default": "#4B7930", + "baseColor": "#4B7930", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + }, + "themeState": { + "base-color": { + "default": "#0094CE", + "value": "#0094CE", + "edited": false + }, + "page-titlebar-backgroundColor": { + "value": "#0094CE", + "edited": false + }, + "page-backgroundColor": { + "value": "#fafafa", + "edited": false + }, + "page-sidebar-backgroundColor": { + "value": "#ffffff", + "edited": false + }, + "group-textColor": { + "value": "#1bbfff", + "edited": false + }, + "group-borderColor": { + "value": "#ffffff", + "edited": false + }, + "group-backgroundColor": { + "value": "#ffffff", + "edited": false + }, + "widget-textColor": { + "value": "#111111", + "edited": false + }, + "widget-backgroundColor": { + "value": "#0094ce", + "edited": false + }, + "widget-borderColor": { + "value": "#ffffff", + "edited": false + }, + "base-font": { + "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + } + }, + "angularTheme": { + "primary": "indigo", + "accents": "blue", + "warn": "red", + "background": "grey" + } + }, + "site": { + "name": "Node-RED Dashboard", + "hideToolbar": "false", + "allowSwipe": "false", + "dateFormat": "DD/MM/YYYY", + "sizes": { + "sx": 48, + "sy": 48, + "gx": 6, + "gy": 6, + "cx": 6, + "cy": 6, + "px": 0, + "py": 0 + } + } + }, + { + "id": "b944567b.d15278", + "type": "mqtt-broker", + "name": "", + "broker": "localhost", + "port": "1883", + "clientid": "", + "usetls": false, + "compatmode": true, + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "closeTopic": "", + "closePayload": "", + "willTopic": "", + "willQos": "0", + "willPayload": "" + }, + { + "id": "4dc2b995.13d0f8", + "type": "mqtt-broker", + "broker": "localhost", + "port": "1883", + "clientid": "", + "usetls": false, + "compatmode": true, + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "willTopic": "", + "willQos": "0", + "willPayload": "" + }, + { + "id": "222a3f19.b90e9", + "type": "ui_group", + "name": "Spin the Bottle", + "tab": "c7886a8d.764f4", + "order": 3, + "disp": true, + "width": "6" + }, + { + "id": "c7886a8d.764f4", + "type": "ui_tab", + "name": "MyLED", + "icon": "lightbulb_outline", + "order": 2 + }, + { + "id": "da52ac1d.f09478", + "type": "ui_group", + "name": "etc", + "tab": "c7886a8d.764f4", + "order": 5, + "disp": false, + "width": "6" + }, + { + "id": "abc9273f.36d778", + "type": "ui_group", + "name": "Color", + "tab": "c7886a8d.764f4", + "order": 1, + "disp": true, + "width": "6" + }, + { + "id": "e2aca116.33ad2", + "type": "ui_group", + "name": "Color Blend", + "tab": "c7886a8d.764f4", + "order": 2, + "disp": true, + "width": "6" + }, + { + "id": "57eea257.4cfba4", + "type": "ui_group", + "name": "Animations", + "tab": "c7886a8d.764f4", + "order": 4, + "disp": true, + "width": "6" + }, + { + "id": "f7b943a7.14f418", + "type": "ui_group", + "name": "Christmas Show", + "tab": "c7886a8d.764f4", + "order": 6, + "disp": true, + "width": "6" + }, + { + "id": "5c79cf54.3c5ed", + "type": "function", + "z": "93f4131.fbfd2f", + "name": "HEX to RGB", + "func": "var split_str = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(msg.payload);\n\nr = parseInt(split_str[1], 16);\ng = parseInt(split_str[2], 16);\nb = parseInt(split_str[3], 16)\n\nvar rgb = [r,g,b];\nmsg.payload = rgb;\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 379.90000915527344, + "y": 96.39999389648438, + "wires": [ + [] + ] + }, + { + "id": "bd378a2d.22a2d", + "type": "function", + "z": "49e83dbb.1a9224", + "name": "RGB to HEX", + "func": "r = msg.payload[0];\ng = msg.payload[1];\nb = msg.payload[2];\n\nvar rgb = b | (g << 8) | (r << 16);\nmsg.payload = '#' + (0x1000000 + rgb).toString(16).slice(1);\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "x": 326.20001220703125, + "y": 142.1999969482422, + "wires": [ + [] + ] + }, + { + "id": "8f7a013f.29663", + "type": "function", + "z": "793be9a5.d2c558", + "name": "converter", + "func": "var r = msg.payload[0];\nvar g = msg.payload[1];\nvar b = msg.payload[2];\n\n// algorithm: http://www.rapidtables.com/convert/color/rgb-to-hsv.htm\n\n// helper variables\nvar R_ = r / 255;\nvar G_ = g / 255;\nvar B_ = b / 255;\n\nvar C_max = Math.max(R_, G_, B_);\nvar C_min = Math.min(R_, G_, B_);\nvar Delta = C_max - C_min;\n\n// Hue\nvar H;\nif (Delta === 0) {\n H = 0;\n} else if (C_max === R_) {\n H = 60 * ( ( (G_ - B_) / Delta ) % 6 );\n if (H < 0) {\n H = H + 360;\n }\n} else if (C_max === G_) {\n H = 60 * ( ( (B_ - R_) / Delta) + 2 );\n} else if (C_max === B_) {\n H = 60 * ( ( (R_ - G_) / Delta) + 4 );\n}\n\n// Saturation\nvar S;\nif (C_max === 0) {\n S = 0;\n} else{\n S = Delta / C_max;\n}\n\n// Value\nvar V = C_max;\n\n// output messages\nvar msg_h = {\"topic\": \"H\", \"payload\": H};\nvar msg_s = {\"topic\": \"S\", \"payload\": S};\nvar msg_v = {\"topic\": \"V\", \"payload\": V};\n\nreturn [msg_h, msg_s, msg_v];", + "outputs": "3", + "noerr": 0, + "x": 273.73333740234375, + "y": 103.7166748046875, + "wires": [ + [], + [], + [] + ] + }, { "id": "b3d72a18.150118", "type": "function", @@ -225,164 +488,93 @@ ] }, { - "id": "793be9a5.d2c558", - "type": "subflow", - "name": "RGB to HSV", - "info": "convert an RGB color tuple (like [128, 0, 128]) to H (hue), S (saturation) and V (brightness) values (like, 300, 1, 0.5).", - "in": [ - { - "x": 144, - "y": 103, - "wires": [ - { - "id": "8f7a013f.29663" - } - ] - } - ], - "out": [ + "id": "11416ce1.833b7b", + "type": "switch", + "z": "1919d466.09f77c", + "name": "", + "property": "parts.index", + "propertyType": "msg", + "rules": [ { - "x": 474, - "y": 49, - "wires": [ - { - "id": "8f7a013f.29663", - "port": 0 - } - ] + "t": "eq", + "v": "0", + "vt": "num" }, { - "x": 472.9999694824219, - "y": 102, - "wires": [ - { - "id": "8f7a013f.29663", - "port": 1 - } - ] + "t": "eq", + "v": "1", + "vt": "num" }, { - "x": 473.9999694824219, - "y": 156, - "wires": [ - { - "id": "8f7a013f.29663", - "port": 2 - } - ] + "t": "eq", + "v": "2", + "vt": "str" } - ] - }, - { - "id": "8f7a013f.29663", - "type": "function", - "z": "793be9a5.d2c558", - "name": "converter", - "func": "var r = msg.payload[0];\nvar g = msg.payload[1];\nvar b = msg.payload[2];\n\n// algorithm: http://www.rapidtables.com/convert/color/rgb-to-hsv.htm\n\n// helper variables\nvar R_ = r / 255;\nvar G_ = g / 255;\nvar B_ = b / 255;\n\nvar C_max = Math.max(R_, G_, B_);\nvar C_min = Math.min(R_, G_, B_);\nvar Delta = C_max - C_min;\n\n// Hue\nvar H;\nif (Delta === 0) {\n H = 0;\n} else if (C_max === R_) {\n H = 60 * ( ( (G_ - B_) / Delta ) % 6 );\n if (H < 0) {\n H = H + 360;\n }\n} else if (C_max === G_) {\n H = 60 * ( ( (B_ - R_) / Delta) + 2 );\n} else if (C_max === B_) {\n H = 60 * ( ( (R_ - G_) / Delta) + 4 );\n}\n\n// Saturation\nvar S;\nif (C_max === 0) {\n S = 0;\n} else{\n S = Delta / C_max;\n}\n\n// Value\nvar V = C_max;\n\n// output messages\nvar msg_h = {\"topic\": \"H\", \"payload\": H};\nvar msg_s = {\"topic\": \"S\", \"payload\": S};\nvar msg_v = {\"topic\": \"V\", \"payload\": V};\n\nreturn [msg_h, msg_s, msg_v];", - "outputs": "3", - "noerr": 0, - "x": 273.73333740234375, - "y": 103.7166748046875, + ], + "checkall": "false", + "outputs": 3, + "x": 590, + "y": 140, "wires": [ [], [], [] + ], + "outputLabels": [ + "r", + "", + "b" ] }, { - "id": "93f4131.fbfd2f", - "type": "subflow", - "name": "HEX to RGB", - "info": "convert a color HEX value (like #ff0080) to a rgb color tuple (like [255, 0, 128])", - "in": [ - { - "x": 207, - "y": 96, - "wires": [ - { - "id": "5c79cf54.3c5ed" - } - ] - } - ], - "out": [ - { - "x": 534, - "y": 96, - "wires": [ - { - "id": "5c79cf54.3c5ed", - "port": 0 - } - ] - } + "id": "99b2c1b8.b666d8", + "type": "split", + "z": "1919d466.09f77c", + "name": "", + "splt": "\\n", + "spltType": "str", + "arraySplt": 1, + "arraySpltType": "len", + "stream": false, + "addname": "", + "x": 450, + "y": 140, + "wires": [ + [ + "11416ce1.833b7b" + ] ] }, { - "id": "5c79cf54.3c5ed", + "id": "2507f05e.330b78", "type": "function", - "z": "93f4131.fbfd2f", - "name": "HEX to RGB", - "func": "var split_str = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(msg.payload);\n\nr = parseInt(split_str[1], 16);\ng = parseInt(split_str[2], 16);\nb = parseInt(split_str[3], 16)\n\nvar rgb = [r,g,b];\nmsg.payload = rgb;\n\nreturn msg;", + "z": "d15c800.af31a", + "name": "joiner", + "func": "var R = flow.get('R');\nvar G = flow.get('G');\nvar B = flow.get('B');\n\nmsg.topic = \"colour\";\nmsg.payload = [R, G, B];\n\n\nreturn msg;", "outputs": 1, "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 379.90000915527344, - "y": 96.39999389648438, + "x": 330, + "y": 100, "wires": [ [] ] }, { - "id": "49e83dbb.1a9224", - "type": "subflow", - "name": "RGB to HEX", - "info": "convert an rgb color tuple (like [255, 0, 128]) to a color HEX value (like #ff0080)", - "in": [ - { - "x": 209.00001525878906, - "y": 136, - "wires": [ - { - "id": "bd378a2d.22a2d" - } - ] - } - ], - "out": [ - { - "x": 455, - "y": 137, - "wires": [ - { - "id": "bd378a2d.22a2d", - "port": 0 - } - ] - } - ] - }, - { - "id": "bd378a2d.22a2d", + "id": "eb5767ba.336868", "type": "function", - "z": "49e83dbb.1a9224", - "name": "RGB to HEX", - "func": "r = msg.payload[0];\ng = msg.payload[1];\nb = msg.payload[2];\n\nvar rgb = b | (g << 8) | (r << 16);\nmsg.payload = '#' + (0x1000000 + rgb).toString(16).slice(1);\n\nreturn msg;", + "z": "d15c800.af31a", + "name": "buffer", + "func": "var valid = ['R', 'G', 'B'];\n\nif (valid.indexOf(msg.topic) !== -1) {\n flow.set(msg.topic, msg.payload);\n}\n\nreturn msg;", "outputs": 1, "noerr": 0, - "x": 326.20001220703125, - "y": 142.1999969482422, + "x": 198.99996948242188, + "y": 100, "wires": [ - [] + [ + "2507f05e.330b78" + ] ] }, - { - "id": "2fc32e47.9732e2", - "type": "tab", - "label": "LED Control" - }, { "id": "83a0d3d7.f8db4", "type": "mqtt in", @@ -3003,7 +3195,7 @@ "from": "", "to": "", "reg": false, - "x": 3860, + "x": 3830, "y": 2340, "wires": [ [ @@ -3012,81 +3204,56 @@ ] }, { - "id": "4dc2b995.13d0f8", - "type": "mqtt-broker", - "broker": "localhost", - "port": "1883", - "clientid": "", - "usetls": false, - "compatmode": true, - "keepalive": "60", - "cleansession": true, - "birthTopic": "", - "birthQos": "0", - "birthPayload": "", - "willTopic": "", - "willQos": "0", - "willPayload": "" - }, - { - "id": "222a3f19.b90e9", - "type": "ui_group", - "name": "Spin the Bottle", - "tab": "c7886a8d.764f4", - "order": 3, - "disp": true, - "width": "6" - }, - { - "id": "e2aca116.33ad2", - "type": "ui_group", - "name": "Color Blend", - "tab": "c7886a8d.764f4", - "order": 2, - "disp": true, - "width": "6" - }, - { - "id": "57eea257.4cfba4", - "type": "ui_group", - "name": "Animations", - "tab": "c7886a8d.764f4", - "order": 4, - "disp": true, - "width": "6" - }, - { - "id": "da52ac1d.f09478", - "type": "ui_group", - "name": "etc", - "tab": "c7886a8d.764f4", - "order": 5, - "disp": false, - "width": "6" - }, - { - "id": "f7b943a7.14f418", - "type": "ui_group", - "name": "Christmas Show", - "tab": "c7886a8d.764f4", - "order": 6, - "disp": true, - "width": "6" - }, - { - "id": "abc9273f.36d778", - "type": "ui_group", - "name": "Color", - "tab": "c7886a8d.764f4", - "order": 1, - "disp": true, - "width": "6" + "id": "676f680930b90f84", + "type": "ui_button", + "z": "2fc32e47.9732e2", + "name": "", + "group": "abc9273f.36d778", + "order": 11, + "width": 0, + "height": 0, + "passthru": false, + "label": "Ukraine", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "{}", + "payloadType": "json", + "topic": "start", + "x": 3530, + "y": 2420, + "wires": [ + [ + "9e6bf7b4155daf0f" + ] + ] }, { - "id": "c7886a8d.764f4", - "type": "ui_tab", - "name": "MyLED", - "icon": "lightbulb_outline", - "order": 2 + "id": "9e6bf7b4155daf0f", + "type": "change", + "z": "2fc32e47.9732e2", + "name": "msg.show => ukraine", + "rules": [ + { + "t": "set", + "p": "show", + "pt": "msg", + "to": "ukraine", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 3800, + "y": 2420, + "wires": [ + [ + "3ec3076f.74f948" + ] + ] } ] \ No newline at end of file From 8e67772e21b798056984bcf285174417790d015a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20W=C3=BCrl?= Date: Sun, 11 Dec 2022 14:27:39 +0100 Subject: [PATCH 3/3] revert strip fix --- server/drivers/apa102.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/server/drivers/apa102.py b/server/drivers/apa102.py index 9b5a1ee..44c947c 100644 --- a/server/drivers/apa102.py +++ b/server/drivers/apa102.py @@ -2,7 +2,7 @@ # (c) 2015 Martin Erzberger, 2016-2017 Simon Leiner # licensed under the GNU Public License, version 2 from multiprocessing import Array as SyncedArray -from typing import Callable, Tuple +import spidev from drivers import LEDStrip from helpers.color import grayscale_correction @@ -60,7 +60,9 @@ def __init__(self, num_leds: int, max_clock_speed_hz: int = 4000000, max_global_ raise Exception("The APA102 LED driver does not support strips of more than 1024 LEDs") # SPI connection - self.spi_transmit, self.spi_close = self.init_spi() + self.spi = spidev.SpiDev() # Init the SPI device + self.spi.open(0, 1) # Open SPI port 0, slave device (CS) 1 + self.spi.max_speed_hz = self.max_clock_speed_hz # should not be higher than 8000000 self.leds = [self.led_prefix(self._global_brightness), 0, 0, 0] * self.num_leds # 4 bytes per LED self.synced_buffer = SyncedArray('i', self.leds) @@ -69,18 +71,6 @@ def __init__(self, num_leds: int, max_clock_speed_hz: int = 4000000, max_global_ self.max_refresh_time_sec = 25E-6 * self.num_leds #: the maximum time the whole strip takes to refresh self.__sk9822_compatibility_mode = True #: be compatible with SK9822 chips? see: https://goo.gl/ePlcaI - def init_spi(self) -> Tuple[Callable, Callable]: - try: - import Adafruit_GPIO.SPI as SPI - spi = SPI.SpiDev(0, 0, self.max_clock_speed_hz) - return spi.write, spi.close - except ImportError: - import spidev - spi = spidev.SpiDev() # Init the SPI device - spi.open(0, 1) # Open SPI port 0, slave device (CS) 1 - spi.max_speed_hz = self.max_clock_speed_hz # should not be higher than 8000000 - return spi.xfer2, spi.close - def on_color_change(self, led_num, red: float, green: float, blue: float) -> None: """\ Changes the message buffer after a pixel was changed in the global color buffer. @@ -138,7 +128,7 @@ def led_prefix(cls, brightness: float) -> int: def close(self) -> None: """Closes the SPI connection to the strip.""" - self.spi_close() + self.spi.close() @staticmethod def spi_start_frame() -> list: @@ -151,11 +141,11 @@ def spi_start_frame() -> list: def show(self) -> None: """sends the buffered color and brightness values to the strip""" - self.spi_transmit(self.spi_start_frame()) - self.spi_transmit(self.leds) # SPI takes up to 4096 Integers. So we are fine for up to 1024 LEDs. + self.spi.xfer2(self.spi_start_frame()) + self.spi.xfer2(self.leds.copy()) # SPI takes up to 4096 Integers. So we are fine for up to 1024 LEDs. if self.__sk9822_compatibility_mode: - self.spi_transmit(self.spi_start_frame()) - self.spi_transmit(self.spi_end_frame(self.num_leds)) + self.spi.xfer2(self.spi_start_frame()) + self.spi.xfer(self.spi_end_frame(self.num_leds)) @staticmethod def spi_end_frame(num_leds) -> list: