From c47ae445dc62d5e5de2719b68853e28ce006ab3b Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 8 Dec 2013 12:46:14 +0100 Subject: [PATCH 1/5] Mood blobs effect added Former-commit-id: 4bae584f7fa0c688573ad6051458f99954ded686 --- effects/mood-blobs.py | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 effects/mood-blobs.py diff --git a/effects/mood-blobs.py b/effects/mood-blobs.py new file mode 100644 index 00000000..c9da30fe --- /dev/null +++ b/effects/mood-blobs.py @@ -0,0 +1,63 @@ +import hyperion +import time +import colorsys +import math + +# Get the parameters +rotationTime = hyperion.args.get('rotationTime', 20.0) +color = hyperion.args.get('color', (0,0,255)) +hueChange = hyperion.args.get('hueChange', 60.0) / 360.0 +blobs = hyperion.args.get('blobs', 5) +reverse = hyperion.args.get('reverse', False) +print color +print hyperion.args + +# Check parameters +rotationTime = max(0.1, rotationTime) +hueChange = max(0.0, min(abs(hueChange), .5)) +blobs = max(1, blobs) + +# Calculate the color data +baseHsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0) +colorData = bytearray() +for i in range(hyperion.ledCount): + hue = (baseHsv[0] + hueChange * math.sin(2*math.pi * i / hyperion.ledCount)) % 1.0 + rgb = colorsys.hsv_to_rgb(hue, baseHsv[1], baseHsv[2]) + colorData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))) + +# Calculate the increments +sleepTime = 0.1 +amplitudePhaseIncrement = blobs * math.pi * sleepTime / rotationTime +colorDataIncrement = 3 + +# Switch direction if needed +if reverse: + amplitudePhaseIncrement = -amplitudePhaseIncrement + colorDataIncrement = -colorDataIncrement + +# create a Array for the colors +colors = bytearray(hyperion.ledCount * (0,0,0)) + +# Start the write data loop +amplitudePhase = 0.0 +rotateColors = False +while not hyperion.abort(): + # Calculate new colors + for i in range(hyperion.ledCount): + amplitude = max(0.0, math.sin(-amplitudePhase + 2*math.pi * blobs * i / hyperion.ledCount)) + colors[3*i+0] = int(colorData[3*i+0] * amplitude) + colors[3*i+1] = int(colorData[3*i+1] * amplitude) + colors[3*i+2] = int(colorData[3*i+2] * amplitude) + + # set colors + hyperion.setColor(colors) + + # increment the phase + amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi) + + if rotateColors: + colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement] + rotateColors = not rotateColors + + # sleep for a while + time.sleep(sleepTime) From 000117e393854f5b87da92038275655cfaf5a385 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 8 Dec 2013 12:46:33 +0100 Subject: [PATCH 2/5] Some small optimizations in the effect engine Former-commit-id: ed35fc4ff0dc3afa133f584b6625227eb7b615dc --- effects/rainbow-mood.py | 2 +- libsrc/effectengine/Effect.cpp | 52 ++++++++++++---------------------- libsrc/effectengine/Effect.h | 3 ++ 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/effects/rainbow-mood.py b/effects/rainbow-mood.py index 622dfd8c..4f6c0255 100644 --- a/effects/rainbow-mood.py +++ b/effects/rainbow-mood.py @@ -3,7 +3,7 @@ import time import colorsys # Get the parameters -rotationTime = hyperion.args.get('rotation-time', 3.0) +rotationTime = hyperion.args.get('rotation-time', 30.0) brightness = hyperion.args.get('brightness', 1.0) saturation = hyperion.args.get('saturation', 1.0) reverse = hyperion.args.get('reverse', False) diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 730287eb..497ca700 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -29,8 +29,11 @@ Effect::Effect(int priority, int timeout, const std::string & script, const Json _endTime(-1), _interpreterThreadState(nullptr), _abortRequested(false), - _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()) + _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()), + _colors() { + _colors.resize(_imageProcessor->getLedCount(), ColorRgb::BLACK); + // connect the finished signal connect(this, SIGNAL(finished()), this, SLOT(effectFinished())); } @@ -170,7 +173,8 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args) ColorRgb color; if (PyArg_ParseTuple(args, "bbb", &color.red, &color.green, &color.blue)) { - effect->setColors(effect->_priority, std::vector(effect->_imageProcessor->getLedCount(), color), timeout); + std::fill(effect->_colors.begin(), effect->_colors.end(), color); + effect->setColors(effect->_priority, effect->_colors, timeout); return Py_BuildValue(""); } else @@ -189,16 +193,9 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args) size_t length = PyByteArray_Size(bytearray); if (length == 3 * effect->_imageProcessor->getLedCount()) { - std::vector colors(effect->_imageProcessor->getLedCount()); char * data = PyByteArray_AS_STRING(bytearray); - for (size_t i = 0; i < colors.size(); ++i) - { - ColorRgb & color = colors[i]; - color.red = data [3*i]; - color.green = data [3*i+1]; - color.blue = data [3*i+2]; - } - effect->setColors(effect->_priority, colors, timeout); + memcpy(effect->_colors.data(), data, length); + effect->setColors(effect->_priority, effect->_colors, timeout); return Py_BuildValue(""); } else @@ -265,21 +262,10 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args) { Image image(width, height); char * data = PyByteArray_AS_STRING(bytearray); - for (int y = 0; y < height; ++y) - { - for (int x = 0; x < width; ++x) - { - ColorRgb & color = image(x, y); - int index = x+width*y; - color.red = data [3*index]; - color.green = data [3*index+1]; - color.blue = data [3*index+2]; - } - } + memcpy(image.memptr(), data, length); - std::vector colors(effect->_imageProcessor->getLedCount()); - effect->_imageProcessor->process(image, colors); - effect->setColors(effect->_priority, colors, timeout); + effect->_imageProcessor->process(image, effect->_colors); + effect->setColors(effect->_priority, effect->_colors, timeout); return Py_BuildValue(""); } else @@ -307,13 +293,6 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args) PyObject* Effect::wrapAbort(PyObject *self, PyObject *) { Effect * effect = getEffect(self); - return Py_BuildValue("i", effect->_abortRequested ? 1 : 0); -} - -Effect * Effect::getEffect(PyObject *self) -{ - // Get the effect from the capsule in the self pointer - Effect * effect = reinterpret_cast(PyCapsule_GetPointer(self, nullptr)); // Test if the effect has reached it end time if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime) @@ -321,6 +300,11 @@ Effect * Effect::getEffect(PyObject *self) effect->_abortRequested = true; } - // return the effect - return effect; + return Py_BuildValue("i", effect->_abortRequested ? 1 : 0); +} + +Effect * Effect::getEffect(PyObject *self) +{ + // Get the effect from the capsule in the self pointer + return reinterpret_cast(PyCapsule_GetPointer(self, nullptr)); } diff --git a/libsrc/effectengine/Effect.h b/libsrc/effectengine/Effect.h index 765872b2..4f681a6e 100644 --- a/libsrc/effectengine/Effect.h +++ b/libsrc/effectengine/Effect.h @@ -61,4 +61,7 @@ private: /// The processor for translating images to led-values ImageProcessor * _imageProcessor; + + /// Buffer for colorData + std::vector _colors; }; From 9872d8f02bebc739e110e8938418231430488892 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 8 Dec 2013 16:23:01 +0100 Subject: [PATCH 3/5] Ensure types in effects to avoid integer calculations were floats were needed Former-commit-id: 84d8b281a544478bf14e88284cb600fdbb11f65c --- effects/knight-rider.py | 4 ++-- effects/mood-blobs.py | 10 ++++------ effects/rainbow-mood.py | 8 ++++---- effects/rainbow-swirl.py | 10 +++++----- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/effects/knight-rider.py b/effects/knight-rider.py index 1245336c..d2ff3ecc 100644 --- a/effects/knight-rider.py +++ b/effects/knight-rider.py @@ -3,8 +3,8 @@ import time import colorsys # Get the rotation time -speed = hyperion.args.get('speed', 1.0) -fadeFactor = hyperion.args.get('fadeFactor', 0.7) +speed = float(hyperion.args.get('speed', 1.0)) +fadeFactor = float(hyperion.args.get('fadeFactor', 0.7)) # Check parameters speed = max(0.0001, speed) diff --git a/effects/mood-blobs.py b/effects/mood-blobs.py index c9da30fe..c0ce3d15 100644 --- a/effects/mood-blobs.py +++ b/effects/mood-blobs.py @@ -4,13 +4,11 @@ import colorsys import math # Get the parameters -rotationTime = hyperion.args.get('rotationTime', 20.0) +rotationTime = float(hyperion.args.get('rotationTime', 20.0)) color = hyperion.args.get('color', (0,0,255)) -hueChange = hyperion.args.get('hueChange', 60.0) / 360.0 -blobs = hyperion.args.get('blobs', 5) -reverse = hyperion.args.get('reverse', False) -print color -print hyperion.args +hueChange = float(hyperion.args.get('hueChange', 60.0)) / 360.0 +blobs = int(hyperion.args.get('blobs', 5)) +reverse = bool(hyperion.args.get('reverse', False)) # Check parameters rotationTime = max(0.1, rotationTime) diff --git a/effects/rainbow-mood.py b/effects/rainbow-mood.py index 4f6c0255..2ce08edd 100644 --- a/effects/rainbow-mood.py +++ b/effects/rainbow-mood.py @@ -3,10 +3,10 @@ import time import colorsys # Get the parameters -rotationTime = hyperion.args.get('rotation-time', 30.0) -brightness = hyperion.args.get('brightness', 1.0) -saturation = hyperion.args.get('saturation', 1.0) -reverse = hyperion.args.get('reverse', False) +rotationTime = float(hyperion.args.get('rotation-time', 30.0)) +brightness = float(hyperion.args.get('brightness', 1.0)) +saturation = float(hyperion.args.get('saturation', 1.0)) +reverse = bool(hyperion.args.get('reverse', False)) # Check parameters rotationTime = max(0.1, rotationTime) diff --git a/effects/rainbow-swirl.py b/effects/rainbow-swirl.py index f8ac2522..ec623aa1 100644 --- a/effects/rainbow-swirl.py +++ b/effects/rainbow-swirl.py @@ -3,10 +3,10 @@ import time import colorsys # Get the parameters -rotationTime = hyperion.args.get('rotation-time', 3.0) -brightness = hyperion.args.get('brightness', 1.0) -saturation = hyperion.args.get('saturation', 1.0) -reverse = hyperion.args.get('reverse', False) +rotationTime = float(hyperion.args.get('rotation-time', 3.0)) +brightness = float(hyperion.args.get('brightness', 1.0)) +saturation = float(hyperion.args.get('saturation', 1.0)) +reverse = bool(hyperion.args.get('reverse', False)) # Check parameters rotationTime = max(0.1, rotationTime) @@ -31,7 +31,7 @@ increment %= hyperion.ledCount # Switch direction if needed if reverse: increment = -increment - + # Start the write data loop while not hyperion.abort(): hyperion.setColor(ledData) From f1acf5a9e42f95381fefea0c714d31c33e142012 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 8 Dec 2013 17:45:26 +0100 Subject: [PATCH 4/5] Clear effects if a setColor comes from another source Former-commit-id: 43084eeee26ee233ac1f0c71d6782b64b154f0da --- include/hyperion/Hyperion.h | 4 ++-- libsrc/effectengine/Effect.cpp | 6 +++--- libsrc/effectengine/Effect.h | 2 +- libsrc/effectengine/EffectEngine.cpp | 2 +- libsrc/hyperion/Hyperion.cpp | 12 +++++++++--- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index bf23cf5c..b4fc0f1c 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -105,7 +105,7 @@ public slots: /// @param[in] ledColor The color to write to the leds /// @param[in] timeout_ms The time the leds are set to the given color [ms] /// - void setColor(int priority, const ColorRgb &ledColor, const int timeout_ms); + void setColor(int priority, const ColorRgb &ledColor, const int timeout_ms, bool clearEffects = true); /// /// Writes the given colors to all leds for the given time and priority @@ -114,7 +114,7 @@ public slots: /// @param[in] ledColors The colors to write to the leds /// @param[in] timeout_ms The time the leds are set to the given colors [ms] /// - void setColors(int priority, const std::vector &ledColors, const int timeout_ms); + void setColors(int priority, const std::vector &ledColors, const int timeout_ms, bool clearEffects = true); /// /// Returns the list with unique transform identifiers diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 497ca700..e09f8979 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -174,7 +174,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args) if (PyArg_ParseTuple(args, "bbb", &color.red, &color.green, &color.blue)) { std::fill(effect->_colors.begin(), effect->_colors.end(), color); - effect->setColors(effect->_priority, effect->_colors, timeout); + effect->setColors(effect->_priority, effect->_colors, timeout, false); return Py_BuildValue(""); } else @@ -195,7 +195,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args) { char * data = PyByteArray_AS_STRING(bytearray); memcpy(effect->_colors.data(), data, length); - effect->setColors(effect->_priority, effect->_colors, timeout); + effect->setColors(effect->_priority, effect->_colors, timeout, false); return Py_BuildValue(""); } else @@ -265,7 +265,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args) memcpy(image.memptr(), data, length); effect->_imageProcessor->process(image, effect->_colors); - effect->setColors(effect->_priority, effect->_colors, timeout); + effect->setColors(effect->_priority, effect->_colors, timeout, false); return Py_BuildValue(""); } else diff --git a/libsrc/effectengine/Effect.h b/libsrc/effectengine/Effect.h index 4f681a6e..62c2ed75 100644 --- a/libsrc/effectengine/Effect.h +++ b/libsrc/effectengine/Effect.h @@ -29,7 +29,7 @@ public slots: signals: void effectFinished(Effect * effect); - void setColors(int priority, const std::vector &ledColors, const int timeout_ms); + void setColors(int priority, const std::vector &ledColors, const int timeout_ms, bool clearEffects); private slots: void effectFinished(); diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index b409540f..c01809b5 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -83,7 +83,7 @@ int EffectEngine::runEffectScript(const std::string &script, const Json::Value & // create the effect Effect * effect = new Effect(priority, timeout, script, args); - connect(effect, SIGNAL(setColors(int,std::vector,int)), _hyperion, SLOT(setColors(int,std::vector,int)), Qt::QueuedConnection); + connect(effect, SIGNAL(setColors(int,std::vector,int,bool)), _hyperion, SLOT(setColors(int,std::vector,int,bool)), Qt::QueuedConnection); connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*))); _activeEffects.push_back(effect); diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 54d75f0f..8e42ac05 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -392,17 +392,23 @@ unsigned Hyperion::getLedCount() const return _ledString.leds().size(); } -void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms) +void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms, bool clearEffects) { // create led output std::vector ledColors(_ledString.leds().size(), color); // set colors - setColors(priority, ledColors, timeout_ms); + setColors(priority, ledColors, timeout_ms, clearEffects); } -void Hyperion::setColors(int priority, const std::vector& ledColors, const int timeout_ms) +void Hyperion::setColors(int priority, const std::vector& ledColors, const int timeout_ms, bool clearEffects) { + // clear effects if this call does not come from an effect + if (clearEffects) + { + _effectEngine->channelCleared(priority); + } + if (timeout_ms > 0) { const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms; From 0537fdc741f04b51df7d94c643c9ef32c2752e48 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 11 Dec 2013 21:58:59 +0100 Subject: [PATCH 5/5] Moved effect configurations from the config file to effect directory Former-commit-id: b8db13f25b93a0007adf613f0310a1cfbb6b8224 --- config/hyperion.config.json | 765 +++++++++--------- effects/knight-rider.json | 9 + effects/mood-blobs-blue.json | 12 + effects/mood-blobs-green.json | 12 + effects/mood-blobs-red.json | 12 + effects/rainbow-mood.json | 10 + effects/rainbow-swirl-fast.json | 10 + effects/rainbow-swirl.json | 10 + include/effectengine/EffectEngine.h | 2 + libsrc/bootsequence/BootSequenceFactory.cpp | 17 +- libsrc/bootsequence/EffectBootSequence.cpp | 7 +- libsrc/bootsequence/EffectBootSequence.h | 8 +- libsrc/effectengine/CMakeLists.txt | 6 + .../effectengine/EffectDefinition.schema.json | 342 ++++++++ libsrc/effectengine/EffectEngine.cpp | 77 +- libsrc/effectengine/EffectEngine.qrc | 5 + libsrc/hyperion/hyperion.schema.json | 34 +- 17 files changed, 919 insertions(+), 419 deletions(-) create mode 100755 effects/knight-rider.json create mode 100755 effects/mood-blobs-blue.json create mode 100755 effects/mood-blobs-green.json create mode 100755 effects/mood-blobs-red.json create mode 100755 effects/rainbow-mood.json create mode 100755 effects/rainbow-swirl-fast.json create mode 100755 effects/rainbow-swirl.json create mode 100644 libsrc/effectengine/EffectDefinition.schema.json create mode 100644 libsrc/effectengine/EffectEngine.qrc diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 5f569a43..facc714e 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -2,399 +2,402 @@ // Generated by: HyperCon (The Hyperion deamon configuration file builder { - /// Device configuration contains the following fields: - /// * 'name' : The user friendly name of the device (only used for display purposes) - /// * 'type' : The type of the device or leds (known types for now are 'ws2801', 'ldp8806', - /// 'lpd6803', 'sedu', 'adalight', 'lightpack', 'test' and 'none') - /// * 'output' : The output specification depends on selected device. This can for example be the - /// device specifier, device serial number, or the output file name - /// * 'rate' : The baudrate of the output to the device - /// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.). - "device" : - { - "name" : "MyPi", - "type" : "ws2801", - "output" : "/dev/spidev0.0", - "rate" : 500000, - "colorOrder" : "rgb" - }, + /// Device configuration contains the following fields: + /// * 'name' : The user friendly name of the device (only used for display purposes) + /// * 'type' : The type of the device or leds (known types for now are 'ws2801', 'ldp8806', + /// 'lpd6803', 'sedu', 'adalight', 'lightpack', 'test' and 'none') + /// * 'output' : The output specification depends on selected device. This can for example be the + /// device specifier, device serial number, or the output file name + /// * 'rate' : The baudrate of the output to the device + /// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.). + "device" : + { + "name" : "MyPi", + "type" : "ws2801", + "output" : "/dev/spidev0.0", + "rate" : 500000, + "colorOrder" : "rgb" + }, - /// Color manipulation configuration used to tune the output colors to specific surroundings. Contains the following fields: - /// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following tuning parameters: - /// - 'saturationGain' The gain adjustement of the saturation - /// - 'valueGain' The gain adjustement of the value - /// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the following tuning parameters for each channel: - /// - 'threshold' The minimum required input value for the channel to be on (else zero) - /// - 'gamma' The gamma-curve correction factor - /// - 'blacklevel' The lowest possible value (when the channel is black) - /// - 'whitelevel' The highest possible value (when the channel is white) - /// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning parameters: - /// - 'type' The type of smoothing algorithm ('linear' or 'none') - /// - 'time_ms' The time constant for smoothing algorithm in milliseconds - /// - 'updateFrequency' The update frequency of the leds in Hz - "color" : - { - "hsv" : - { - "saturationGain" : 1.0000, - "valueGain" : 1.5000 - }, - "red" : - { - "threshold" : 0.1000, - "gamma" : 2.0000, - "blacklevel" : 0.0000, - "whitelevel" : 0.8000 - }, - "green" : - { - "threshold" : 0.1000, - "gamma" : 2.0000, - "blacklevel" : 0.0000, - "whitelevel" : 1.0000 - }, - "blue" : - { - "threshold" : 0.1000, - "gamma" : 2.0000, - "blacklevel" : 0.0000, - "whitelevel" : 1.0000 - }, - "smoothing" : - { - "type" : "none", - "time_ms" : 200, - "updateFrequency" : 20.0000 - } - }, + /// Color manipulation configuration used to tune the output colors to specific surroundings. Contains the following fields: + /// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following tuning parameters: + /// - 'saturationGain' The gain adjustement of the saturation + /// - 'valueGain' The gain adjustement of the value + /// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the following tuning parameters for each channel: + /// - 'threshold' The minimum required input value for the channel to be on (else zero) + /// - 'gamma' The gamma-curve correction factor + /// - 'blacklevel' The lowest possible value (when the channel is black) + /// - 'whitelevel' The highest possible value (when the channel is white) + /// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning parameters: + /// - 'type' The type of smoothing algorithm ('linear' or 'none') + /// - 'time_ms' The time constant for smoothing algorithm in milliseconds + /// - 'updateFrequency' The update frequency of the leds in Hz + "color" : + { + "hsv" : + { + "saturationGain" : 1.0000, + "valueGain" : 1.5000 + }, + "red" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 0.8000 + }, + "green" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "blue" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "smoothing" : + { + "type" : "none", + "time_ms" : 200, + "updateFrequency" : 20.0000 + } + }, - /// The configuration for each individual led. This contains the specification of the area - /// averaged of an input image for each led to determine its color. Each item in the list - /// contains the following fields: - /// * index: The index of the led. This determines its location in the string of leds; zero - /// being the first led. - /// * hscan: The fractional part of the image along the horizontal used for the averaging - /// (minimum and maximum inclusive) - /// * vscan: The fractional part of the image along the vertical used for the averaging - /// (minimum and maximum inclusive) - "leds" : - [ - { - "index" : 0, - "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 1, - "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 2, - "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 3, - "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 4, - "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 5, - "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 6, - "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 7, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 8, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 9, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } - }, - { - "index" : 10, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } - }, - { - "index" : 11, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } - }, - { - "index" : 12, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } - }, - { - "index" : 13, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } - }, - { - "index" : 14, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } - }, - { - "index" : 15, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } - }, - { - "index" : 16, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 17, - "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 18, - "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 19, - "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 20, - "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 21, - "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 22, - "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 23, - "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 24, - "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 25, - "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 26, - "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 27, - "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 28, - "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 29, - "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 30, - "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 31, - "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 32, - "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 33, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } - }, - { - "index" : 34, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } - }, - { - "index" : 35, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } - }, - { - "index" : 36, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } - }, - { - "index" : 37, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } - }, - { - "index" : 38, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } - }, - { - "index" : 39, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } - }, - { - "index" : 40, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } - }, - { - "index" : 41, - "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 42, - "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 43, - "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 44, - "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 45, - "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 46, - "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 47, - "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 48, - "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - }, - { - "index" : 49, - "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, - "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } - } - ], + /// The configuration for each individual led. This contains the specification of the area + /// averaged of an input image for each led to determine its color. Each item in the list + /// contains the following fields: + /// * index: The index of the led. This determines its location in the string of leds; zero + /// being the first led. + /// * hscan: The fractional part of the image along the horizontal used for the averaging + /// (minimum and maximum inclusive) + /// * vscan: The fractional part of the image along the vertical used for the averaging + /// (minimum and maximum inclusive) + "leds" : + [ + { + "index" : 0, + "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 1, + "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 2, + "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 3, + "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 4, + "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 5, + "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 6, + "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 7, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 8, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 9, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } + }, + { + "index" : 10, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } + }, + { + "index" : 11, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } + }, + { + "index" : 12, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } + }, + { + "index" : 13, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } + }, + { + "index" : 14, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } + }, + { + "index" : 15, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } + }, + { + "index" : 16, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 17, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 18, + "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 19, + "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 20, + "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 21, + "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 22, + "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 23, + "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 24, + "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 25, + "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 26, + "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 27, + "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 28, + "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 29, + "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 30, + "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 31, + "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 32, + "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 33, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 34, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } + }, + { + "index" : 35, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } + }, + { + "index" : 36, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } + }, + { + "index" : 37, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } + }, + { + "index" : 38, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } + }, + { + "index" : 39, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } + }, + { + "index" : 40, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } + }, + { + "index" : 41, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 42, + "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 43, + "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 44, + "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 45, + "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 46, + "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 47, + "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 48, + "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 49, + "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + } + ], - /// The black border configuration, contains the following items: - /// * enable : true if the detector should be activated - "blackborderdetector" : - { - "enable" : true - }, + "effects" : + { + "paths" : ["/home/pi/hyperion/effects"] + }, - /// The boot-sequence configuration, contains the following items: - /// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none') - /// * duration_ms : The length of the boot-sequence [ms] - "bootsequence" : - { - "type" : "Rainbow", - "duration_ms" : 3000 - }, + /// The black border configuration, contains the following items: + /// * enable : true if the detector should be activated + "blackborderdetector" : + { + "enable" : true + }, - /// The configuration for the frame-grabber, contains the following items: - /// * width : The width of the grabbed frames [pixels] - /// * height : The height of the grabbed frames [pixels] - /// * frequency_Hz : The frequency of the frame grab [Hz] - "framegrabber" : - { - "width" : 64, - "height" : 64, - "frequency_Hz" : 10.0 - }, + "bootsequence" : + { + "effect" : "rainbow-swirl-fast.json", + "path" : "/home/pi/hyperion/effects", + "duration_ms" : 3000 + }, - /// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: - /// * xbmcAddress : The IP address of the XBMC-host - /// * xbmcTcpPort : The TCP-port of the XBMC-server - /// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback - /// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show - /// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback - /// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu - "xbmcVideoChecker" : - { - "xbmcAddress" : "127.0.0.1", - "xbmcTcpPort" : 9090, - "grabVideo" : true, - "grabPictures" : true, - "grabAudio" : true, - "grabMenu" : false - }, + /// The configuration for the frame-grabber, contains the following items: + /// * width : The width of the grabbed frames [pixels] + /// * height : The height of the grabbed frames [pixels] + /// * frequency_Hz : The frequency of the frame grab [Hz] + "framegrabber" : + { + "width" : 64, + "height" : 64, + "frequency_Hz" : 10.0 + }, - /// The configuration of the Json server which enables the json remote interface - /// * port : Port at which the json server is started - "jsonServer" : - { - "port" : 19444 - }, + /// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: + /// * xbmcAddress : The IP address of the XBMC-host + /// * xbmcTcpPort : The TCP-port of the XBMC-server + /// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback + /// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show + /// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback + /// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu + "xbmcVideoChecker" : + { + "xbmcAddress" : "127.0.0.1", + "xbmcTcpPort" : 9090, + "grabVideo" : true, + "grabPictures" : true, + "grabAudio" : true, + "grabMenu" : false + }, - /// The configuration of the Proto server which enables the protobuffer remote interface - /// * port : Port at which the protobuffer server is started - "protoServer" : - { - "port" : 19445 - }, + /// The configuration of the Json server which enables the json remote interface + /// * port : Port at which the json server is started + "jsonServer" : + { + "port" : 19444 + }, - /// The configuration of the boblight server which enables the boblight remote interface - /// * port : Port at which the boblight server is started + /// The configuration of the Proto server which enables the protobuffer remote interface + /// * port : Port at which the protobuffer server is started + "protoServer" : + { + "port" : 19445 + }, + + /// The configuration of the boblight server which enables the boblight remote interface + /// * port : Port at which the boblight server is started // "boblightServer" : // { // "port" : 19333 // }, - "end-of-json" : "end-of-json" + "end-of-json" : "end-of-json" } diff --git a/effects/knight-rider.json b/effects/knight-rider.json new file mode 100755 index 00000000..bf83f897 --- /dev/null +++ b/effects/knight-rider.json @@ -0,0 +1,9 @@ +{ + "name" : "Knight rider", + "script" : "knight-rider.py", + "args" : + { + "speed" : 1.0, + "fadeFactor" : 0.7 + } +} diff --git a/effects/mood-blobs-blue.json b/effects/mood-blobs-blue.json new file mode 100755 index 00000000..1aa188ab --- /dev/null +++ b/effects/mood-blobs-blue.json @@ -0,0 +1,12 @@ +{ + "name" : "Blue mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [0,0,255], + "hueChange" : 60.0, + "blobs" : 5, + "reverse" : false + } +} diff --git a/effects/mood-blobs-green.json b/effects/mood-blobs-green.json new file mode 100755 index 00000000..c0c104fe --- /dev/null +++ b/effects/mood-blobs-green.json @@ -0,0 +1,12 @@ +{ + "name" : "Green mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [0,255,0], + "hueChange" : 60.0, + "blobs" : 5, + "reverse" : false + } +} diff --git a/effects/mood-blobs-red.json b/effects/mood-blobs-red.json new file mode 100755 index 00000000..3272dded --- /dev/null +++ b/effects/mood-blobs-red.json @@ -0,0 +1,12 @@ +{ + "name" : "Red mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [255,0,0], + "hueChange" : 60.0, + "blobs" : 5, + "reverse" : false + } +} diff --git a/effects/rainbow-mood.json b/effects/rainbow-mood.json new file mode 100755 index 00000000..fe754287 --- /dev/null +++ b/effects/rainbow-mood.json @@ -0,0 +1,10 @@ +{ + "name" : "Rainbow mood", + "script" : "rainbow-mood.py", + "args" : + { + "rotation-time" : 60.0, + "brightness" : 1.0, + "reverse" : false + } +} diff --git a/effects/rainbow-swirl-fast.json b/effects/rainbow-swirl-fast.json new file mode 100755 index 00000000..19fec89c --- /dev/null +++ b/effects/rainbow-swirl-fast.json @@ -0,0 +1,10 @@ +{ + "name" : "Rainbow swirl fast", + "script" : "rainbow-swirl.py", + "args" : + { + "rotation-time" : 3.0, + "brightness" : 1.0, + "reverse" : false + } +} diff --git a/effects/rainbow-swirl.json b/effects/rainbow-swirl.json new file mode 100755 index 00000000..3f7b7243 --- /dev/null +++ b/effects/rainbow-swirl.json @@ -0,0 +1,10 @@ +{ + "name" : "Rainbow swirl", + "script" : "rainbow-swirl.py", + "args" : + { + "rotation-time" : 20.0, + "brightness" : 1.0, + "reverse" : false + } +} diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 2bf73d6b..bf33ef3a 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -26,6 +26,8 @@ public: const std::list & getEffects() const; + static bool loadEffectDefinition(const std::string & path, const std::string & effectConfigFile, EffectDefinition &effectDefinition); + public slots: /// Run the specified effect on the given priority channel and optionally specify a timeout int runEffect(const std::string &effectName, int priority, int timeout = -1); diff --git a/libsrc/bootsequence/BootSequenceFactory.cpp b/libsrc/bootsequence/BootSequenceFactory.cpp index a7979a09..c319c884 100644 --- a/libsrc/bootsequence/BootSequenceFactory.cpp +++ b/libsrc/bootsequence/BootSequenceFactory.cpp @@ -5,13 +5,24 @@ // Bootsequence includes #include +// Effect engine includes +#include + // Local Bootsequence includes #include "EffectBootSequence.h" BootSequence * BootSequenceFactory::createBootSequence(Hyperion * hyperion, const Json::Value & jsonConfig) { - const std::string script = jsonConfig["script"].asString(); - const Json::Value args = jsonConfig.get("args", Json::Value(Json::objectValue)); + const std::string path = jsonConfig["path"].asString(); + const std::string effectFile = jsonConfig["effect"].asString(); const unsigned duration = jsonConfig["duration_ms"].asUInt(); - return new EffectBootSequence(hyperion, script, args, duration); + + EffectDefinition effect; + if (EffectEngine::loadEffectDefinition(path, effectFile, effect)) + { + return new EffectBootSequence(hyperion, effect, duration); + } + + std::cerr << "Boot sequence could not be loaded" << std::endl; + return nullptr; } diff --git a/libsrc/bootsequence/EffectBootSequence.cpp b/libsrc/bootsequence/EffectBootSequence.cpp index b40d8d02..9005a269 100644 --- a/libsrc/bootsequence/EffectBootSequence.cpp +++ b/libsrc/bootsequence/EffectBootSequence.cpp @@ -1,10 +1,9 @@ #include "EffectBootSequence.h" -EffectBootSequence::EffectBootSequence(Hyperion *hyperion, const std::string &script, const Json::Value &args, const unsigned duration_ms) : +EffectBootSequence::EffectBootSequence(Hyperion *hyperion, const EffectDefinition &effect, const unsigned duration_ms) : BootSequence(), _hyperion(hyperion), - _script(script), - _args(args), + _effect(effect), _duration_ms(duration_ms) { } @@ -15,5 +14,5 @@ EffectBootSequence::~EffectBootSequence() void EffectBootSequence::start() { - _hyperion->setEffectScript(_script, _args, 0, _duration_ms); + _hyperion->setEffectScript(_effect.script, _effect.args, 0, _duration_ms); } diff --git a/libsrc/bootsequence/EffectBootSequence.h b/libsrc/bootsequence/EffectBootSequence.h index 2d4be069..a0a9eae2 100644 --- a/libsrc/bootsequence/EffectBootSequence.h +++ b/libsrc/bootsequence/EffectBootSequence.h @@ -17,9 +17,10 @@ public: /// duration is the length the effect will run. /// /// @param[in] hyperion The Hyperion instance + /// @param[in] effect The effect definition /// @param[in] duration_ms The length of the sequence [ms] /// - EffectBootSequence(Hyperion * hyperion, const std::string & script, const Json::Value & args, const unsigned duration_ms); + EffectBootSequence(Hyperion * hyperion, const EffectDefinition & effect, const unsigned duration_ms); virtual ~EffectBootSequence(); virtual void start(); @@ -29,10 +30,7 @@ private: Hyperion * _hyperion; /// The script to execute - const std::string _script; - - /// The arguments of the script - const Json::Value _args; + const EffectDefinition _effect; /// Duration of the boot sequence const unsigned _duration_ms; diff --git a/libsrc/effectengine/CMakeLists.txt b/libsrc/effectengine/CMakeLists.txt index 92afce2e..98edfa5c 100644 --- a/libsrc/effectengine/CMakeLists.txt +++ b/libsrc/effectengine/CMakeLists.txt @@ -22,12 +22,18 @@ SET(EffectEngineSOURCES ${CURRENT_SOURCE_DIR}/Effect.cpp ) + +set(EffectEngine_RESOURCES ${CURRENT_SOURCE_DIR}/EffectEngine.qrc) + QT4_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS}) +qt4_add_resources(EffectEngine_RESOURCES_RCC ${EffectEngine_RESOURCES} OPTIONS "-no-compress") + add_library(effectengine ${EffectEngineHEADERS} ${EffectEngineQT_HEADERS} ${EffectEngineHEADERS_MOC} + ${EffectEngine_RESOURCES_RCC} ${EffectEngineSOURCES} ) diff --git a/libsrc/effectengine/EffectDefinition.schema.json b/libsrc/effectengine/EffectDefinition.schema.json new file mode 100644 index 00000000..44733252 --- /dev/null +++ b/libsrc/effectengine/EffectDefinition.schema.json @@ -0,0 +1,342 @@ +{ + "type" : "object", + "required" : true, + "properties" : { + "device" : { + "type" : "object", + "required" : true, + "properties" : { + "name" : { + "type" : "string", + "required" : true + }, + "type" : { + "type" : "string", + "required" : true + }, + "output" : { + "type" : "string", + "required" : true + }, + "rate" : { + "type" : "integer", + "required" : true, + "minimum" : 0 + }, + "colorOrder" : { + "type" : "string", + "required" : false + }, + "bgr-output" : { // deprecated + "type" : "boolean", + "required" : false + } + }, + "additionalProperties" : false + }, + "color": { + "type":"object", + "required":false, + "properties": { + "hsv" : { + "type" : "object", + "required" : false, + "properties" : { + "saturationGain" : { + "type" : "number", + "required" : false, + "minimum" : 0.0 + }, + "valueGain" : { + "type" : "number", + "required" : false, + "minimum" : 0.0 + } + }, + "additionalProperties" : false + }, + "red": { + "type":"object", + "required":false, + "properties":{ + "gamma": { + "type":"number", + "required":false + }, + "blacklevel": { + "type":"number", + "required":false + }, + "whitelevel": { + "type":"number", + "required":false + }, + "threshold": { + "type":"number", + "required":false, + "minimum" : 0.0, + "maximum" : 1.0 + } + }, + "additionalProperties" : false + }, + "green": { + "type":"object", + "required":false, + "properties":{ + "gamma": { + "type":"number", + "required":false + }, + "blacklevel": { + "type":"number", + "required":false + }, + "whitelevel": { + "type":"number", + "required":false + }, + "threshold": { + "type":"number", + "required":false, + "minimum" : 0.0, + "maximum" : 1.0 + } + }, + "additionalProperties" : false + }, + "blue": { + "type":"object", + "required":false, + "properties":{ + "gamma": { + "type":"number", + "required":false + }, + "whitelevel": { + "type":"number", + "required":false + }, + "blacklevel": { + "type":"number", + "required":false + }, + "threshold": { + "type":"number", + "required":false, + "minimum" : 0.0, + "maximum" : 1.0 + } + }, + "additionalProperties" : false + }, + "smoothing" : { + "type" : "object", + "required" : false, + "properties" : { + "type" : { + "type" : "enum", + "required" : true, + "values" : ["none", "linear"] + }, + "time_ms" : { + "type" : "integer", + "required" : false, + "minimum" : 10 + }, + "updateFrequency" : { + "type" : "number", + "required" : false, + "minimum" : 0.001 + } + }, + "additionalProperties" : false + } + + }, + "additionalProperties" : false + }, + "leds": { + "type":"array", + "required":true, + "items": { + "type":"object", + "properties": { + "index": { + "type":"integer", + "required":true + }, + "hscan": { + "type":"object", + "required":true, + "properties": { + "minimum": { + "type":"number", + "required":true + }, + "maximum": { + "type":"number", + "required":true + } + }, + "additionalProperties" : false + }, + "vscan": { + "type":"object", + "required":true, + "properties": { + "minimum": { + "type":"number", + "required":true + }, + "maximum": { + "type":"number", + "required":true + } + }, + "additionalProperties" : false + } + }, + "additionalProperties" : false + } + }, + "effects" : + { + "type" : "object", + "required" : false, + "properties" : { + "paths" : { + "type" : "array", + "required" : false, + "items" : { + "type" : "string" + } + } + }, + "additionalProperties" : false + }, + "blackborderdetector" : + { + "type" : "object", + "required" : false, + "properties" : { + "enable" : { + "type" : "boolean", + "required" : true + } + }, + "additionalProperties" : false + }, + "xbmcVideoChecker" : + { + "type" : "object", + "required" : false, + "properties" : { + "xbmcAddress" : { + "type" : "string", + "required" : true + }, + "xbmcTcpPort" : { + "type" : "integer", + "required" : true + }, + "grabVideo" : { + "type" : "boolean", + "required" : true + }, + "grabPictures" : { + "type" : "boolean", + "required" : true + }, + "grabAudio" : { + "type" : "boolean", + "required" : true + }, + "grabMenu" : { + "type" : "boolean", + "required" : true + } + }, + "additionalProperties" : false + }, + "bootsequence" : + { + "type" : "object", + "required" : false, + "properties" : { + "path" : { + "type" : "string", + "required" : true + }, + "effect" : { + "type" : "string", + "required" : true + } + }, + "additionalProperties" : false + }, + "framegrabber" : + { + "type" : "object", + "required" : false, + "properties" : { + "width" : { + "type" : "integer", + "required" : true + }, + "height" : { + "type" : "integer", + "required" : true + }, + "frequency_Hz" : { + "type" : "integer", + "required" : true + } + }, + "additionalProperties" : false + }, + "jsonServer" : + { + "type" : "object", + "required" : false, + "properties" : { + "port" : { + "type" : "integer", + "required" : true, + "minimum" : 0, + "maximum" : 65535 + } + }, + "additionalProperties" : false + }, + "protoServer" : + { + "type" : "object", + "required" : false, + "properties" : { + "port" : { + "type" : "integer", + "required" : true, + "minimum" : 0, + "maximum" : 65535 + } + }, + "additionalProperties" : false + }, + "boblightServer" : + { + "type" : "object", + "required" : false, + "properties" : { + "port" : { + "type" : "integer", + "required" : true, + "minimum" : 0, + "maximum" : 65535 + } + }, + "additionalProperties" : false + } + }, + "additionalProperties" : false +} diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index c01809b5..a343ae4e 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -1,8 +1,17 @@ // Python includes #include +// Stl includes +#include + // Qt includes +#include #include +#include +#include + +// hyperion util includes +#include // effect engine includes #include @@ -21,11 +30,26 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared())); // read all effects - std::vector effectNames = jsonEffectConfig.getMemberNames(); - for (const std::string & name : effectNames) + const Json::Value & paths = jsonEffectConfig["paths"]; + for (Json::UInt i = 0; i < paths.size(); ++i) { - const Json::Value & info = jsonEffectConfig[name]; - _availableEffects.push_back({name, info["script"].asString(), info["args"]}); + const std::string & path = paths[i].asString(); + QDir directory(QString::fromStdString(path)); + if (!directory.exists()) + { + std::cerr << "Effect directory can not be loaded: " << path << std::endl; + continue; + } + + QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); + foreach (const QString & filename, filenames) + { + EffectDefinition def; + if (loadEffectDefinition(path, filename.toStdString(), def)) + { + _availableEffects.push_back(def); + } + } } // initialize the python interpreter @@ -48,6 +72,51 @@ const std::list &EffectEngine::getEffects() const return _availableEffects; } +bool EffectEngine::loadEffectDefinition(const std::string &path, const std::string &effectConfigFile, EffectDefinition & effectDefinition) +{ + std::string fileName = path + QDir::separator().toAscii() + effectConfigFile; + std::ifstream file(fileName.c_str()); + + if (!file.is_open()) + { + std::cerr << "Effect file '" << fileName << "' could not be loaded" << std::endl; + return false; + } + + // Read the json config file + Json::Reader jsonReader; + Json::Value config; + if (!jsonReader.parse(file, config, false)) + { + std::cerr << "Error while reading effect '" << fileName << "': " << jsonReader.getFormattedErrorMessages() << std::endl; + return false; + } + + // Read the json schema file + QResource schemaData(":effect-schema"); + JsonSchemaChecker schemaChecker; + Json::Value schema; + Json::Reader().parse(reinterpret_cast(schemaData.data()), reinterpret_cast(schemaData.data()) + schemaData.size(), schema, false); + schemaChecker.setSchema(schema); + if (!schemaChecker.validate(config)) + { + const std::list & errors = schemaChecker.getMessages(); + foreach (const std::string & error, errors) { + std::cerr << "Error while checking '" << fileName << "':" << error << std::endl; + } + return false; + } + + // setup the definition + effectDefinition.name = config["name"].asString(); + effectDefinition.script = path + QDir::separator().toAscii() + config["script"].asString(); + effectDefinition.args = config["args"]; + + // return succes + std::cout << "Effect loaded: " + effectDefinition.name << std::endl; + return true; +} + int EffectEngine::runEffect(const std::string &effectName, int priority, int timeout) { return runEffect(effectName, Json::Value(Json::nullValue), priority, timeout); diff --git a/libsrc/effectengine/EffectEngine.qrc b/libsrc/effectengine/EffectEngine.qrc new file mode 100644 index 00000000..2f4ef03e --- /dev/null +++ b/libsrc/effectengine/EffectEngine.qrc @@ -0,0 +1,5 @@ + + + EffectDefinition.schema.json + + diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index 57d6b63f..44733252 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -204,22 +204,16 @@ { "type" : "object", "required" : false, - "additionalProperties" : - { - "type" : "object", - "required" : false, - "properties" : - { - "script" : { - "type" : "string", - "required" : true - }, - "args" : { - "type" : "object", - "required" : false + "properties" : { + "paths" : { + "type" : "array", + "required" : false, + "items" : { + "type" : "string" } } - } + }, + "additionalProperties" : false }, "blackborderdetector" : { @@ -270,17 +264,13 @@ "type" : "object", "required" : false, "properties" : { - "duration_ms" : { - "type" : "integer", - "required" : true - }, - "script" : { + "path" : { "type" : "string", "required" : true }, - "args" : { - "type" : "object", - "required" : false + "effect" : { + "type" : "string", + "required" : true } }, "additionalProperties" : false