diff --git a/.travis.yml b/.travis.yml index 2c3419c3..7fd8ec6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,6 @@ before_install: - ./.travis/travis_install.sh script: - ./.travis/travis_build.sh -after_success: - ./test/testrunner.sh +after_success: - ./.travis/travis_deploy.sh \ No newline at end of file diff --git a/.travis/travis_deploy.sh b/.travis/travis_deploy.sh index fc6eba6f..db7e398a 100644 --- a/.travis/travis_deploy.sh +++ b/.travis/travis_deploy.sh @@ -4,7 +4,7 @@ sf_upload() { /usr/bin/expect <<-EOD - spawn scp $1 $2 hyperionsf37@frs.sourceforge.net:/home/frs/project/hyperion-project/dev/$3 + spawn scp $1 hyperionsf37@frs.sourceforge.net:/home/frs/project/hyperion-project/dev/$2 expect "*(yes/no)*" send "yes\r" expect "*password:*" @@ -13,7 +13,7 @@ sf_upload() EOD } -deploylist="hyperion-2.0.0-Linux-x86.deb hyperion-2.0.0-Linux-x86.tar.gz" +deploylist="hyperion-2.0.0-Linux-x86.tar.gz" if [[ $TRAVIS_OS_NAME == 'linux' ]]; then cd $TRAVIS_BUILD_DIR/build diff --git a/assets/webconfig/content/connection_lost.html b/assets/webconfig/content/connection_lost.html index fa883b28..409a208f 100644 --- a/assets/webconfig/content/connection_lost.html +++ b/assets/webconfig/content/connection_lost.html @@ -1,15 +1,22 @@
- - -
-
-
Connection to Hyperion Service lost!
- -
+
+
+ Redefine ambient light! +
+

Lost connection to Hyperion service!

+
+

Possible reasons:

+

1. Hyperion restarts

+

2. You perform an update

+

3. An older browser session

+

3. Hyperion isn't running

+
+ +

This page will be automatically refreshed.

+

We reconnect again after Hyperion is available.

+ If not, click me +
+
diff --git a/assets/webconfig/css/hyperion.css b/assets/webconfig/css/hyperion.css index 4c04f22a..392ff51e 100644 --- a/assets/webconfig/css/hyperion.css +++ b/assets/webconfig/css/hyperion.css @@ -49,8 +49,15 @@ table.borderless td,table.borderless th{border: none !important;} .introd{padding-left:14px;border-left:5px solid #0088cc;} .introd h4{line-height:25px;} +/*backported bootstrap 4 forms-label alignment*/ +.col-form-label { + padding-top: .5rem; + padding-bottom: .5rem; + margin-bottom: 0; +} + .overlay { - background-image: url('/img/hyperion/hyperionlogo.png'); + background-image: url('/img/hyperion/hyperionwhitelogo.png'); background-repeat: no-repeat; background-position: center center; background-color: rgba(1, 1, 1, 0.7); diff --git a/assets/webconfig/img/hyperion/hyperionlostconnection.png b/assets/webconfig/img/hyperion/hyperionlostconnection.png new file mode 100644 index 00000000..3b899029 Binary files /dev/null and b/assets/webconfig/img/hyperion/hyperionlostconnection.png differ diff --git a/assets/webconfig/img/hyperion/hyperionwhitelogo.png b/assets/webconfig/img/hyperion/hyperionwhitelogo.png new file mode 100644 index 00000000..65338f68 Binary files /dev/null and b/assets/webconfig/img/hyperion/hyperionwhitelogo.png differ diff --git a/assets/webconfig/index.html b/assets/webconfig/index.html index 935ec129..fbadfd00 100644 --- a/assets/webconfig/index.html +++ b/assets/webconfig/index.html @@ -98,6 +98,7 @@ Redefine ambient light! + Redefine ambient light!
diff --git a/assets/webconfig/js/content_generalconf.js b/assets/webconfig/js/content_generalconf.js index 1341ba56..26f67a94 100644 --- a/assets/webconfig/js/content_generalconf.js +++ b/assets/webconfig/js/content_generalconf.js @@ -23,20 +23,6 @@ $(hyperion).one("cmd-config-getschema", function(event) { parsedConfSchemaJSON = event.response.result; schema = parsedConfSchemaJSON.properties; - blackborderdetector = schema.blackborderdetector; - color = schema.color; - effects = schema.effects; - forwarder = schema.forwarder; - initialEffect = schema.initialEffect; - kodiVideoChecker = schema.kodiVideoChecker; - smoothing = schema.smoothing; - logger = schema.logger; - jsonServer = schema.jsonServer; - protoServer = schema.protoServer; - boblightServer = schema.boblightServer; - udpListener = schema.udpListener; - webConfig = schema.webConfig; - var element = document.getElementById('editor_container'); var general_conf_editor = new JSONEditor(element,{ @@ -50,19 +36,19 @@ $(hyperion).one("cmd-config-getschema", function(event) { schema: { title:'', properties: { - blackborderdetector, - color, - effects, - forwarder, - initialEffect, - kodiVideoChecker, - smoothing, - logger, - jsonServer, - protoServer, - boblightServer, - udpListener, - webConfig + blackborderdetector: schema.blackborderdetector, + color : schema.color, + effects : schema.effects, + forwarder : schema.forwarder, + initialEffect : schema.initialEffect, + kodiVideoChecker : schema.kodiVideoChecker, + smoothing : schema.smoothing, + logger : schema.logger, + jsonServer : schema.jsonServer, + protoServer : schema.protoServer, + boblightServer : schema.boblightServer, + udpListener : schema.udpListener, + webConfig : schema.webConfig } } }); @@ -80,7 +66,7 @@ $(hyperion).one("cmd-config-getschema", function(event) { // }); //Alternative Function with submit button to get Values - $('btn_submit').off().on('click',function() { + $('#btn_submit').off().on('click',function() { console.log(general_conf_editor.getValue()); }); diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index f6d2234b..6d442464 100644 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -20,15 +20,14 @@ function removeAdvanced(obj,searchStack) } */ +var grabber_conf_editor = null; $(hyperion).one("cmd-config-getschema", function(event) { parsedConfSchemaJSON = event.response.result; schema = parsedConfSchemaJSON.properties; - schema_framegrabber = schema.framegrabber; - schema_grabberv4l2 = schema["grabber-v4l2"]; var element = document.getElementById('editor_container'); - var grabber_conf_editor = new JSONEditor(element,{ + grabber_conf_editor = new JSONEditor(element,{ theme: 'bootstrap3', iconlib: "fontawesome4", disable_collapse: 'true', @@ -39,8 +38,8 @@ $(hyperion).one("cmd-config-getschema", function(event) { schema: { title:'', properties: { - schema_framegrabber, - schema_grabberv4l2, + framegrabber: schema.framegrabber, + grabberV4L2 : schema["grabberV4L2"] } } }); @@ -59,7 +58,7 @@ $(document).ready( function() { document.getElementById('btn_submit').addEventListener('click',function() { // Get the value from the editor - //console.log(general_conf_editor.getValue()); + console.log(grabber_conf_editor.getValue()); }); // $("[type='checkbox']").bootstrapSwitch(); }); diff --git a/assets/webconfig/js/content_index.js b/assets/webconfig/js/content_index.js index fc90b20b..6d14d88a 100644 --- a/assets/webconfig/js/content_index.js +++ b/assets/webconfig/js/content_index.js @@ -1,6 +1,5 @@ $(document).ready( function() { $("#main-nav").hide(); - $("#page-content").hide(); $("#loading_overlay").addClass("overlay"); loadContentTo("#container_connection_lost","connection_lost"); initWebSocket(); @@ -62,7 +61,6 @@ $(document).ready( function() { }); $("#loading_overlay").removeClass("overlay"); $("#main-nav").show('slide', {direction: 'left'}, 1000); - $("#page-content").show('slide', {direction: 'down'}, 2000); }); // end cmd-serverinfo diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 2330136b..24bb7557 100644 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -153,8 +153,8 @@ $(document).ready(function() { schema: { title:' ', properties: { - generalOptions, - specificOptions, + generalOptions : generalOptions, + specificOptions : specificOptions, } } }); diff --git a/assets/webconfig/js/content_remote.js b/assets/webconfig/js/content_remote.js index 85fc680e..ac9b551b 100644 --- a/assets/webconfig/js/content_remote.js +++ b/assets/webconfig/js/content_remote.js @@ -96,6 +96,7 @@ $(document).ready(function() { $('#cp2').colorpicker().on('changeColor', function(e) { color = e.color.toRGB(); + $("#effect_select").val("__none__"); requestSetColor(color.r, color.g, color.b); }); }); @@ -106,7 +107,10 @@ $(document).ready(function() { efx = $(this).val(); if(efx != "__none__") { - requestPlayEffect(efx); + requestPriorityClear(); + $(hyperion).one("cmd-clear", function(event) { + setTimeout(function() {requestPlayEffect(efx)}, 100); + }); } }); diff --git a/assets/webconfig/js/lib/jsoneditor.js b/assets/webconfig/js/lib/jsoneditor.js index 8b81c0ff..6ddc4869 100755 --- a/assets/webconfig/js/lib/jsoneditor.js +++ b/assets/webconfig/js/lib/jsoneditor.js @@ -1938,6 +1938,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({ var self = this, i; if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); + if(this.schema.append) this.append = this.theme.getFormInputAppend(this.schema.append); this.format = this.schema.format; if(!this.format && this.schema.media && this.schema.media.type) { @@ -2122,7 +2123,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({ if(this.format) this.input.setAttribute('data-schemaformat',this.format); - this.control = this.theme.getFormControl(this.label, this.input, this.description); + this.control = this.theme.getFormControl(this.label, this.input, this.description, this.append); this.container.appendChild(this.control); // Any special formatting that needs to happen after the input is added to the dom @@ -2716,6 +2717,12 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ this.description = this.theme.getDescription(this.schema.description); this.container.appendChild(this.description); } + + // Appends + if(this.schema.append) { + this.append = this.theme.getAppend(this.schema.append); + this.container.appendChild(this.append); + } // Validation error placeholder area this.error_holder = document.createElement('div'); @@ -3313,6 +3320,10 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ if(this.schema.description) { this.description = this.theme.getDescription(this.schema.description); this.container.appendChild(this.description); + } + if(this.schema.append) { + this.append = this.theme.getAppend(this.schema.append); + this.container.appendChild(this.append); } this.error_holder = document.createElement('div'); this.container.appendChild(this.error_holder); @@ -3988,6 +3999,10 @@ JSONEditor.defaults.editors.table = JSONEditor.defaults.editors.array.extend({ if(this.schema.description) { this.description = this.theme.getDescription(this.schema.description); this.container.appendChild(this.description); + } + if(this.schema.append) { + this.append = this.theme.getAppend(this.schema.append); + this.container.appendChild(this.append); } this.panel = this.theme.getIndentedPanel(); this.container.appendChild(this.panel); @@ -4974,7 +4989,8 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({ var self = this; if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); - + if(this.schema.append) this.append = this.theme.getFormInputAppend(this.schema.append); + if(this.options.compact) this.container.className += ' compact'; this.input = this.theme.getSelectInput(this.enum_options); @@ -5320,6 +5336,7 @@ JSONEditor.defaults.editors.selectize = JSONEditor.AbstractEditor.extend({ var self = this; if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); + if(this.schema.append) this.append = this.theme.getFormInputAppend(this.schema.append); if(this.options.compact) this.container.className += ' compact'; @@ -5547,6 +5564,7 @@ JSONEditor.defaults.editors.multiselect = JSONEditor.AbstractEditor.extend({ var self = this, i; if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); + if(this.schema.append) this.append = this.theme.getFormInputAppend(this.schema.append); if((!this.schema.format && this.option_keys.length < 8) || this.schema.format === "checkbox") { this.input_type = 'checkboxes'; @@ -6025,6 +6043,9 @@ JSONEditor.defaults.editors.arraySelectize = JSONEditor.AbstractEditor.extend({ if(this.schema.description) { this.description = this.theme.getDescription(this.schema.description); } + if(this.schema.append) { + this.append = this.theme.getAppend(this.schema.append); + } this.input = document.createElement('select'); this.input.setAttribute('multiple', 'multiple'); @@ -6307,6 +6328,11 @@ JSONEditor.AbstractTheme = Class.extend({ el.innerHTML = text; return el; }, + getAppend: function(text) { + var el = document.createElement('p'); + el.innerHTML = text; + return el; + }, getCheckboxDescription: function(text) { return this.getDescription(text); }, @@ -6518,7 +6544,13 @@ JSONEditor.defaults.themes.bootstrap2 = JSONEditor.AbstractTheme.extend({ el.textContent = text; return el; }, - getFormControl: function(label, input, description) { + getFormInputAppend: function(text) { + var el = document.createElement('div'); + el.className = 'input-group-addon'; + el.textContent = text; + return el; + }, + getFormControl: function(label, input, description, append) { var ret = document.createElement('div'); ret.className = 'control-group'; @@ -6676,26 +6708,35 @@ JSONEditor.defaults.themes.bootstrap3 = JSONEditor.AbstractTheme.extend({ } return el; }, - getFormControl: function(label, input, description) { + getFormControl: function(label, input, description, append) { var group = document.createElement('div'); + var subgroup = document.createElement('div'); - if(label && input.type === 'checkbox') { - group.className += ' checkbox'; - label.appendChild(input); - label.style.fontSize = '14px'; - group.style.marginTop = '0'; - group.appendChild(label); - input.style.position = 'relative'; - input.style.cssFloat = 'left'; - } + if (append){ + group.className += ' form-group'; + if(label) { + label.className += ' col-form-label col-md-2'; + group.appendChild(label); + } + group.appendChild(subgroup); + subgroup.className += 'col-md-10 input-group'; + subgroup.appendChild(input); + subgroup.appendChild(append); + } else { group.className += ' form-group'; if(label) { - label.className += ' control-label'; + label.className += ' col-form-label col-md-2'; group.appendChild(label); } - group.appendChild(input); - } + group.appendChild(subgroup); + subgroup.className += ' input-group col-md-10'; + subgroup.appendChild(input); + if (input.type === 'checkbox'){ + label.style.fontWeight = 'bold'; + } + } + if(description) group.appendChild(description); @@ -6713,6 +6754,12 @@ JSONEditor.defaults.themes.bootstrap3 = JSONEditor.AbstractTheme.extend({ el.innerHTML = text; return el; }, + getFormInputAppend: function(text) { + var el = document.createElement('div'); + el.className = 'input-group-addon'; + el.textContent = text; + return el; + }, getHeaderButtonHolder: function() { var el = this.getButtonHolder(); el.style.marginLeft = '10px'; diff --git a/bin/copy_binaries_to_deploy.sh b/bin/copy_binaries_to_deploy.sh index 82dc5032..6452c2c7 100755 --- a/bin/copy_binaries_to_deploy.sh +++ b/bin/copy_binaries_to_deploy.sh @@ -16,8 +16,6 @@ echo create $outfile tar --create --verbose --gzip --absolute-names --show-transformed-names \ --file "$outfile" \ --transform "s:$builddir/bin/:hyperion/bin/:" \ - --transform "s:$repodir/effects/:hyperion/effects/:" \ - --transform "s:$repodir/config/:hyperion/config/:" \ --transform "s:$repodir/bin/hyperion.init.sh:hyperion/init.d/hyperion.init.sh:" \ --transform "s://:/:g" \ "$builddir/bin/hyperiond" \ @@ -25,6 +23,4 @@ tar --create --verbose --gzip --absolute-names --show-transformed-names \ "$builddir/bin/hyperion-v4l2" \ "$builddir/bin/gpio2spi" \ "$builddir/bin/dispmanx2png" \ - "$repodir/effects/"* \ "$repodir/bin/hyperion.init.sh" \ - "$repodir/config/hyperion.config.json" diff --git a/cmake/debian/postinst b/cmake/debian/postinst index f7e4c2e1..7431bb5c 100644 --- a/cmake/debian/postinst +++ b/cmake/debian/postinst @@ -20,8 +20,12 @@ install_file() echo "--- hyperion ambient light postinstall ---" echo "- install configuration template" mkdir -p /etc/hyperion -install_file /usr/share/hyperion/config/hyperion.config.json.default /etc/hyperion/hyperion.config.json +if [ ! -e "/etc/hyperion/hyperion.config.json" ] +then + echo "install default config to /etc/hyperion" + /usr/bin/hyperiond --export-config /etc/hyperion/hyperion.config.json +fi HYPERION_RUNNING=false pgrep hyperiond > /dev/null 2>&1 && HYPERION_RUNNING=true diff --git a/config/hyperion.config.json.commented b/config/hyperion.config.json.commented index a8bed600..7a0b1a6c 100644 --- a/config/hyperion.config.json.commented +++ b/config/hyperion.config.json.commented @@ -168,7 +168,7 @@ /// * redSignalThreshold : Signal threshold for the red channel between 0.0 and 1.0 [default=0.0] /// * greenSignalThreshold : Signal threshold for the green channel between 0.0 and 1.0 [default=0.0] /// * blueSignalThreshold : Signal threshold for the blue channel between 0.0 and 1.0 [default=0.0] - "grabber-v4l2" : + "grabberV4L2" : [ { "enable" : false, @@ -364,7 +364,7 @@ [ "/storage/hyperion/effects", "/usr/share/hyperion/effects" - ] + ], "disable" : [ "Rainbow swirl", diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 41f9cbca..1747b422 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -96,7 +96,7 @@ "continuousOutput" : true }, - "grabber-v4l2" : + "grabberV4L2" : [ { "enable" : false, @@ -194,7 +194,7 @@ "webConfig" : { - "enable" : true, + "enable" : true }, "effects" : diff --git a/effects/rainbow-swirl-fast.json b/effects/rainbow-swirl-fast.json index 88e8d79d..e3bec7ca 100644 --- a/effects/rainbow-swirl-fast.json +++ b/effects/rainbow-swirl-fast.json @@ -3,8 +3,9 @@ "script" : "rainbow-swirl.py", "args" : { - "rotation-time" : 3.0, - "brightness" : 1.0, + "rotation-time" : 4.0, + "center_x" : 0.5, + "center_y" : 0.5, "reverse" : false } } diff --git a/effects/rainbow-swirl.json b/effects/rainbow-swirl.json index 43a80a8d..fb952d38 100644 --- a/effects/rainbow-swirl.json +++ b/effects/rainbow-swirl.json @@ -4,7 +4,8 @@ "args" : { "rotation-time" : 20.0, - "brightness" : 1.0, + "center_x" : 0.5, + "center_y" : 0.5, "reverse" : false } } diff --git a/effects/rainbow-swirl.py b/effects/rainbow-swirl.py index ec623aa1..6aeaaa56 100644 --- a/effects/rainbow-swirl.py +++ b/effects/rainbow-swirl.py @@ -1,39 +1,37 @@ -import hyperion -import time -import colorsys +import hyperion, time # Get the parameters 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)) +reverse = bool(hyperion.args.get('reverse', False)) +centerX = float(hyperion.args.get('center_x', 0.5)) +centerY = float(hyperion.args.get('center_y', 0.5)) -# Check parameters -rotationTime = max(0.1, rotationTime) -brightness = max(0.0, min(brightness, 1.0)) -saturation = max(0.0, min(saturation, 1.0)) +sleepTime = max(0.1, rotationTime) / 360 +angle = 0 +centerX = int(round(hyperion.imageWidth)*centerX) +centerY = int(round(float(hyperion.imageHeight)*centerY)) +increment = -1 if reverse else 1 -# Initialize the led data -ledData = bytearray() -for i in range(hyperion.ledCount): - hue = float(i)/hyperion.ledCount - rgb = colorsys.hsv_to_rgb(hue, saturation, brightness) - ledData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))) +# table of stop colors for rainbow gradient, first is the position, next rgb, all values 0-255 +rainbowColors = bytearray([ + 0 ,255,0 ,0, + 25 ,255,230,0, + 63 ,255,255,0, + 100,0 ,255,0, + 127,0 ,255,200, + 159,0 ,255,255, + 191,0 ,0 ,255, + 224,255,0 ,255, + 255,255,0 ,127, +]) -# Calculate the sleep time and rotation increment -increment = 3 -sleepTime = rotationTime / hyperion.ledCount -while sleepTime < 0.05: - increment *= 2 - sleepTime *= 2 -increment %= hyperion.ledCount - -# Switch direction if needed -if reverse: - increment = -increment - -# Start the write data loop +# effect loop while not hyperion.abort(): - hyperion.setColor(ledData) - ledData = ledData[-increment:] + ledData[:-increment] + angle += increment + if angle > 360: angle=0 + if angle < 0: angle=360 + + hyperion.imageCanonicalGradient(centerX, centerY, angle, rainbowColors) + + hyperion.imageShow() time.sleep(sleepTime) diff --git a/include/commandline/Option.h b/include/commandline/Option.h index d58bc4fe..722a002f 100644 --- a/include/commandline/Option.h +++ b/include/commandline/Option.h @@ -42,6 +42,7 @@ public: QString value(Parser &parser); std::string getStdString(Parser &parser); std::wstring getStdWString(Parser &parser); + const char* getCString(Parser &parser); }; } diff --git a/include/commandline/Parser.h b/include/commandline/Parser.h index a9876e87..3c2c7848 100644 --- a/include/commandline/Parser.h +++ b/include/commandline/Parser.h @@ -1,5 +1,4 @@ -#ifndef HYPERION_COMMANDLINEPARSER_H -#define HYPERION_COMMANDLINEPARSER_H +#pragma once #include #include @@ -20,101 +19,126 @@ namespace commandline class Parser : public QObject { protected: - QHash _options; - QString _errorText; - /* No public inheritance because we need to modify a few methods */ - QCommandLineParser _parser; + QHash _options; + QString _errorText; + /* No public inheritance because we need to modify a few methods */ + QCommandLineParser _parser; - QStringList _getNames(const char shortOption, const QString longOption); - QString _getDescription(const QString description, const QString default_=QString()); + QStringList _getNames(const char shortOption, const QString longOption); + QString _getDescription(const QString description, const QString default_=QString()); public: - bool parse(const QStringList &arguments); - void process(const QStringList &arguments); - void process(const QCoreApplication &app); - QString errorText() const; + bool parse(const QStringList &arguments); + void process(const QStringList &arguments); + void process(const QCoreApplication &app); + QString errorText() const; - template - OptionT &add( - const char shortOption, - const QString longOption, + template + OptionT &add( + const char shortOption, + const QString longOption, const QString description, const QString default_, Args ... args) - { - OptionT * option = new OptionT( - _getNames(shortOption, longOption), - _getDescription(description, default_), - longOption, - default_, - args...); - addOption(option); - return *option; - } + { + OptionT * option = new OptionT( + _getNames(shortOption, longOption), + _getDescription(description, default_), + longOption, + default_, + args...); + addOption(option); + return *option; + } - /* gcc does not support default arguments for variadic templates which - * makes this method necessary */ - template - OptionT &add( - const char shortOption, + /* gcc does not support default arguments for variadic templates which + * makes this method necessary */ + template + OptionT &add( + const char shortOption, const QString longOption, const QString description, const QString default_ = QString()) - { - OptionT * option = new OptionT( - _getNames(shortOption, longOption), - _getDescription(description, default_), - longOption, - default_); - addOption(option); - return *option; - } + { + OptionT * option = new OptionT( + _getNames(shortOption, longOption), + _getDescription(description, default_), + longOption, + default_); + addOption(option); + return *option; + } - Parser(QString description=QString()) - {if(description.size())setApplicationDescription(description);}; - QCommandLineOption addHelpOption() - { return _parser.addHelpOption(); } - bool addOption(Option &option); - bool addOption(Option *option); - void addPositionalArgument(const QString &name, const QString &description, const QString &syntax = QString()) - { _parser.addPositionalArgument(name, description, syntax); } - QCommandLineOption addVersionOption() - { return _parser.addVersionOption(); } - QString applicationDescription() const - { return _parser.applicationDescription(); } - void clearPositionalArguments() - { _parser.clearPositionalArguments(); } - QString helpText() const - { return _parser.helpText(); } - bool isSet(const QString &name) const - { return _parser.isSet(name); } - bool isSet(const Option &option) const - { return _parser.isSet(option); } - bool isSet(const Option *option) const - { return _parser.isSet(*option); } - QStringList optionNames() const - { return _parser.optionNames(); } - QStringList positionalArguments() const - { return _parser.positionalArguments(); } - void setApplicationDescription(const QString &description) - { _parser.setApplicationDescription(description); } - void setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode) - { _parser.setSingleDashWordOptionMode(singleDashWordOptionMode); } - void showHelp(int exitCode = 0) - { _parser.showHelp(exitCode); } - QStringList unknownOptionNames() const - { return _parser.unknownOptionNames(); } - QString value(const QString &optionName) const - { return _parser.value(optionName); } - QString value(const Option &option) const - { return _parser.value(option); } - QStringList values(const QString &optionName) const - { return _parser.values(optionName); } - QStringList values(const Option &option) const - { return _parser.values(option); } + Parser(QString description=QString()) + { + if(description.size())setApplicationDescription(description); + }; + + QCommandLineOption addHelpOption() + { + return _parser.addHelpOption(); + }; + + bool addOption(Option &option); + bool addOption(Option *option); + void addPositionalArgument(const QString &name, const QString &description, const QString &syntax = QString()) + { + _parser.addPositionalArgument(name, description, syntax); + }; + + QCommandLineOption addVersionOption() + { + return _parser.addVersionOption(); + }; + + QString applicationDescription() const + { return _parser.applicationDescription(); } + + void clearPositionalArguments() + { _parser.clearPositionalArguments(); } + + QString helpText() const + { return _parser.helpText(); } + + bool isSet(const QString &name) const + { return _parser.isSet(name); } + + bool isSet(const Option &option) const + { return _parser.isSet(option); } + + bool isSet(const Option *option) const + { return _parser.isSet(*option); } + + QStringList optionNames() const + { return _parser.optionNames(); } + + QStringList positionalArguments() const + { return _parser.positionalArguments(); } + + void setApplicationDescription(const QString &description) + { _parser.setApplicationDescription(description); } + + void setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode) + { _parser.setSingleDashWordOptionMode(singleDashWordOptionMode); } + + void showHelp(int exitCode = 0) + { _parser.showHelp(exitCode); } + + QStringList unknownOptionNames() const + { return _parser.unknownOptionNames(); } + + QString value(const QString &optionName) const + { return _parser.value(optionName); } + + QString value(const Option &option) const + { return _parser.value(option); } + + QStringList values(const QString &optionName) const + { return _parser.values(optionName); } + + QStringList values(const Option &option) const + { return _parser.values(option); } }; } - -#endif //HYPERION_COMMANDLINEPARSER_H diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index d93096b1..9f706f60 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -7,6 +7,7 @@ // QT includes #include #include +#include // hyperion-utils includes #include @@ -85,7 +86,9 @@ public: /// Returns the number of attached leds /// unsigned getLedCount() const; - + + QSize getLedGridSize() const { return _ledGridSize; } + /// /// Returns the current priority /// @@ -292,7 +295,8 @@ public: static LinearColorSmoothing * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice); static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig); - + static QSize getLedLayoutGridSize(const Json::Value& ledsConfig); + signals: /// Signal which is emitted when a priority channel is actively cleared /// This signal will not be emitted when a priority channel time out @@ -390,4 +394,6 @@ private: int _currentSourcePriority; QByteArray _configHash; + + QSize _ledGridSize; }; diff --git a/include/utils/FileUtils.h b/include/utils/FileUtils.h index 8d04dc1f..bda77bc4 100644 --- a/include/utils/FileUtils.h +++ b/include/utils/FileUtils.h @@ -1,8 +1,9 @@ +#pragma once + #include namespace FileUtils { std::string getBaseName( std::string sourceFile); - }; diff --git a/include/utils/Process.h b/include/utils/Process.h index 3e76607d..dd1055c9 100644 --- a/include/utils/Process.h +++ b/include/utils/Process.h @@ -1,3 +1,5 @@ +#pragma once + #include namespace Process { diff --git a/libsrc/commandline/Option.cpp b/libsrc/commandline/Option.cpp index d8e733e8..abc7892a 100644 --- a/libsrc/commandline/Option.cpp +++ b/libsrc/commandline/Option.cpp @@ -24,3 +24,8 @@ std::wstring Option::getStdWString(Parser &parser) return value(parser).toStdWString(); } +const char* Option::getCString(Parser &parser) +{ + return value(parser).toLocal8Bit().constData(); +} + diff --git a/libsrc/effectengine/CMakeLists.txt b/libsrc/effectengine/CMakeLists.txt index 53714ee3..7ae723b4 100644 --- a/libsrc/effectengine/CMakeLists.txt +++ b/libsrc/effectengine/CMakeLists.txt @@ -48,6 +48,8 @@ add_library(effectengine ${EffectEngineSOURCES} ) +qt5_use_modules(effectengine Core Gui) + target_link_libraries(effectengine hyperion jsoncpp diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 405557d1..d5707e5d 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -8,16 +8,27 @@ // Qt includes #include #include +#include +#include +#include +#include +#include // effect engin eincludes #include "Effect.h" #include +#include // Python method table PyMethodDef Effect::effectMethods[] = { - {"setColor", Effect::wrapSetColor, METH_VARARGS, "Set a new color for the leds."}, - {"setImage", Effect::wrapSetImage, METH_VARARGS, "Set a new image to process and determine new led colors."}, - {"abort", Effect::wrapAbort, METH_NOARGS, "Check if the effect should abort execution."}, + {"setColor", Effect::wrapSetColor, METH_VARARGS, "Set a new color for the leds."}, + {"setImage", Effect::wrapSetImage, METH_VARARGS, "Set a new image to process and determine new led colors."}, + {"abort", Effect::wrapAbort, METH_NOARGS, "Check if the effect should abort execution."}, + {"imageShow", Effect::wrapImageShow, METH_NOARGS, "set current effect image to hyperion core."}, + {"imageCanonicalGradient", Effect::wrapImageCanonicalGradient, METH_VARARGS, ""}, + {"imageRadialGradient" , Effect::wrapImageRadialGradient, METH_VARARGS, ""}, +// {"imageSetPixel",Effect::wrapImageShow, METH_VARARGS, "set pixel color of image"}, +// {"imageGetPixel",Effect::wrapImageShow, METH_VARARGS, "get pixel color of image"}, {NULL, NULL, 0, NULL} }; @@ -69,6 +80,12 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const // disable the black border detector for effects _imageProcessor->enableBlackBorderDetector(false); + + // init effect image for image based effects, size is based on led layout + _imageSize = Hyperion::getInstance()->getLedGridSize(); + _image = new QImage(_imageSize, QImage::Format_ARGB32_Premultiplied); + _image->fill(Qt::black); + _painter = new QPainter(_image); // connect the finished signal connect(this, SIGNAL(finished()), this, SLOT(effectFinished())); @@ -77,6 +94,8 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const Effect::~Effect() { + delete _painter; + delete _image; } void Effect::run() @@ -96,6 +115,12 @@ void Effect::run() // add ledCount variable to the interpreter PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount())); + // add imageWidth variable to the interpreter + PyObject_SetAttrString(module, "imageWidth", Py_BuildValue("i", _imageSize.width())); + + // add imageHeight variable to the interpreter + PyObject_SetAttrString(module, "imageHeight", Py_BuildValue("i", _imageSize.height())); + // add a args variable to the interpreter PyObject_SetAttrString(module, "args", json2python(_args)); @@ -357,6 +382,207 @@ PyObject* Effect::wrapAbort(PyObject *self, PyObject *) return Py_BuildValue("i", effect->_abortRequested ? 1 : 0); } + +PyObject* Effect::wrapImageShow(PyObject *self, PyObject *args) +{ + Effect * effect = getEffect(); + + // determine the timeout + int timeout = effect->_timeout; + if (timeout > 0) + { + timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch(); + + // we are done if the time has passed + if (timeout <= 0) + { + return Py_BuildValue(""); + } + } + + int width = effect->_imageSize.width(); + int height = effect->_imageSize.height(); + QImage * qimage = effect->_image; + + Image image(width, height); + QByteArray binaryImage; + + for (int i = 0; i height(); ++i) + { + const QRgb * scanline = reinterpret_cast(qimage->scanLine(i)); + for (int j = 0; j < qimage->width(); ++j) + { + binaryImage.append((char) qRed(scanline[j])); + binaryImage.append((char) qGreen(scanline[j])); + binaryImage.append((char) qBlue(scanline[j])); + } + } + + memcpy(image.memptr(), binaryImage.data(), binaryImage.size()); + effect->_imageProcessor->process(image, effect->_colors); + effect->setColors(effect->_priority, effect->_colors, timeout, false); + + return Py_BuildValue(""); +} + + +PyObject* Effect::wrapImageCanonicalGradient(PyObject *self, PyObject *args) +{ + Effect * effect = getEffect(); + + int argCount = PyTuple_Size(args); + PyObject * bytearray = nullptr; + int centerX, centerY, angle; + int startX = 0; + int startY = 0; + int width = effect->_imageSize.width(); + int height = effect->_imageSize.height(); + + bool argsOK = false; + + if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &angle, &bytearray) ) + { + argsOK = true; + } + if ( argCount == 4 && PyArg_ParseTuple(args, "iiiO", ¢erX, ¢erY, &angle, &bytearray) ) + { + argsOK = true; + } + angle = std::max(std::min(angle,360),0); + + if (argsOK) + { + if (PyByteArray_Check(bytearray)) + { + int length = PyByteArray_Size(bytearray); + if (length % 4 == 0) + { + + QPainter * painter = effect->_painter; + QRect myQRect(startX,startY,width,height); + QConicalGradient gradient(QPoint(centerX,centerY), angle ); + char * data = PyByteArray_AS_STRING(bytearray); + + for (int idx=0; idxfillRect(myQRect, gradient); + + return Py_BuildValue(""); + } + else + { + PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should multiple of 4"); + return nullptr; + } + } + else + { + PyErr_SetString(PyExc_RuntimeError, "Argument 8 is not a bytearray"); + return nullptr; + } + } + else + { + return nullptr; + } +} + + +PyObject* Effect::wrapImageRadialGradient(PyObject *self, PyObject *args) +{ + Effect * effect = getEffect(); + + int argCount = PyTuple_Size(args); + PyObject * bytearray = nullptr; + int centerX, centerY, radius, focalX, focalY, focalRadius; + int startX = 0; + int startY = 0; + int width = effect->_imageSize.width(); + int height = effect->_imageSize.height(); + + bool argsOK = false; + + if ( argCount == 11 && PyArg_ParseTuple(args, "iiiiiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray) ) + { + argsOK = true; + } + if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &bytearray) ) + { + argsOK = true; + focalX = centerX; + focalY = centerY; + focalRadius = radius; + } + if ( argCount == 7 && PyArg_ParseTuple(args, "iiiiiiO", ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray) ) + { + argsOK = true; + } + if ( argCount == 4 && PyArg_ParseTuple(args, "iiiO", ¢erX, ¢erY, &radius, &bytearray) ) + { + argsOK = true; + focalX = centerX; + focalY = centerY; + focalRadius = radius; + } + + if (argsOK) + { + if (PyByteArray_Check(bytearray)) + { + int length = PyByteArray_Size(bytearray); + if (length % 4 == 0) + { + + QPainter * painter = effect->_painter; + QRect myQRect(startX,startY,width,height); + QRadialGradient gradient(QPoint(centerX,centerY), std::max(radius,0) ); + char * data = PyByteArray_AS_STRING(bytearray); + + for (int idx=0; idxfillRect(myQRect, gradient); + + return Py_BuildValue(""); + } + else + { + PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should multiple of 4"); + return nullptr; + } + } + else + { + PyErr_SetString(PyExc_RuntimeError, "Last argument is not a bytearray"); + return nullptr; + } + } + else + { + return nullptr; + } +} + + Effect * Effect::getEffect() { // extract the module from the runtime diff --git a/libsrc/effectengine/Effect.h b/libsrc/effectengine/Effect.h index 68c25518..12672f26 100644 --- a/libsrc/effectengine/Effect.h +++ b/libsrc/effectengine/Effect.h @@ -5,6 +5,9 @@ // Qt includes #include +#include +#include +#include // Hyperion includes #include @@ -48,21 +51,25 @@ private: PyObject * json2python(const Json::Value & json) const; // Wrapper methods for Python interpreter extra buildin methods - static PyMethodDef effectMethods[]; - static PyObject* wrapSetColor(PyObject *self, PyObject *args); + static PyMethodDef effectMethods[]; + static PyObject* wrapSetColor(PyObject *self, PyObject *args); static PyObject* wrapSetImage(PyObject *self, PyObject *args); static PyObject* wrapAbort(PyObject *self, PyObject *args); - static Effect * getEffect(); + static PyObject* wrapImageShow(PyObject *self, PyObject *args); + static PyObject* wrapImageCanonicalGradient(PyObject *self, PyObject *args); + static PyObject* wrapImageRadialGradient(PyObject *self, PyObject *args); + + static Effect * getEffect(); #if PY_MAJOR_VERSION >= 3 - static struct PyModuleDef moduleDef; - static PyObject* PyInit_hyperion(); + static struct PyModuleDef moduleDef; + static PyObject* PyInit_hyperion(); #else - static void PyInit_hyperion(); + static void PyInit_hyperion(); #endif private: - PyThreadState * _mainThreadState; + PyThreadState * _mainThreadState; const int _priority; @@ -84,5 +91,10 @@ private: /// Buffer for colorData std::vector _colors; + + QSize _imageSize; + + QImage * _image; + QPainter * _painter; }; diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 9ff8e867..a44942ca 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -423,6 +423,7 @@ LedString Hyperion::createLedString(const Json::Value& ledsConfig, const ColorOr LedString ledString; const std::string deviceOrderStr = colorOrderToString(deviceOrder); int maxLedId = ledsConfig.size(); + for (const Json::Value& ledConfig : ledsConfig) { Led led; @@ -499,6 +500,51 @@ LedString Hyperion::createLedStringClone(const Json::Value& ledsConfig, const Co return ledString; } +QSize Hyperion::getLedLayoutGridSize(const Json::Value& ledsConfig) +{ + std::vector midPointsX; + std::vector midPointsY; + + for (const Json::Value& ledConfig : ledsConfig) + { + if ( ledConfig.get("clone",-1).asInt() < 0 ) + { + const Json::Value& hscanConfig = ledConfig["hscan"]; + const Json::Value& vscanConfig = ledConfig["vscan"]; + double minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].asDouble())); + double maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].asDouble())); + double minY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].asDouble())); + double maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].asDouble())); + // Fix if the user swapped min and max + if (minX_frac > maxX_frac) + { + std::swap(minX_frac, maxX_frac); + } + if (minY_frac > maxY_frac) + { + std::swap(minY_frac, maxY_frac); + } + + // calculate mid point and make grid calculation + midPointsX.push_back( int(1000.0*(minX_frac + maxX_frac) / 2.0) ); + midPointsY.push_back( int(1000.0*(minY_frac + maxY_frac) / 2.0) ); + } + } + + // remove duplicates + std::sort(midPointsX.begin(), midPointsX.end()); + midPointsX.erase(std::unique(midPointsX.begin(), midPointsX.end()), midPointsX.end()); + std::sort(midPointsY.begin(), midPointsY.end()); + midPointsY.erase(std::unique(midPointsY.begin(), midPointsY.end()), midPointsY.end()); + + QSize gridSize( midPointsX.size(), midPointsY.size() ); + Debug(Logger::getInstance("Core"), "led layout grid: %dx%d", gridSize.width(), gridSize.height()); + + return gridSize; +} + + + LinearColorSmoothing * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice) { Logger * log = Logger::getInstance("Core"); @@ -578,6 +624,7 @@ Hyperion::Hyperion(const Json::Value &jsonConfig, const std::string configFile) , _hwLedCount(_ledString.leds().size()) , _sourceAutoSelectEnabled(true) , _configHash() + , _ledGridSize(getLedLayoutGridSize(jsonConfig["leds"])) { registerPriority("Off", PriorityMuxer::LOWEST_PRIORITY); diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index e10ae6a4..195bc739 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -30,6 +30,7 @@ "name" : { "type" : "string", + "title" : "Configuration name", "required" : true, "propertyOrder" : 1 }, @@ -48,6 +49,7 @@ "colorOrder" : { "type" : "string", + "title" : "RGB byte order", "enum" : ["rgb", "bgr", "rbg", "brg", "gbr", "grb"], "propertyOrder" : 3 } @@ -397,6 +399,7 @@ "minimum" : 25, "maximum": 600, "default" : 200, + "append" : "ms", "propertyOrder" : 3 }, "updateFrequency" : @@ -406,6 +409,7 @@ "minimum" : 1.000, "maximum" : 100.000, "default" : 25.000, + "append" : "Hz", "propertyOrder" : 4 }, "updateDelay" : @@ -415,6 +419,7 @@ "minimum" : 0, "maximum": 2048, "default" : 0, + "append" : "ms", "propertyOrder" : 5 }, "continuousOutput" : @@ -427,12 +432,14 @@ }, "additionalProperties" : false }, - "grabber-v4l2" : + "grabberV4L2" : { "type":"array", + "title" : "USB Grabber", "items": { "type" : "object", + "title" : "USB Grabber", "properties" : { "enable" : @@ -469,6 +476,7 @@ "type" : "integer", "title" : "Width", "default" : -1, + "append" : "Pixel", "propertyOrder" : 5 }, "height" : @@ -476,6 +484,7 @@ "type" : "integer", "title" : "Height", "default" : -1, + "append" : "Pixel", "propertyOrder" : 6 }, "frameDecimation" : @@ -520,6 +529,7 @@ "title" : "Crop left", "minimum" : 0, "default" : 0, + "append" : "Pixel", "propertyOrder" : 12 }, "cropRight" : @@ -528,6 +538,7 @@ "title" : "Crop right", "minimum" : 0, "default" : 0, + "append" : "Pixel", "propertyOrder" : 13 }, "cropTop" : @@ -536,6 +547,7 @@ "title" : "Crop top", "minimum" : 0, "default" : 0, + "append" : "Pixel", "propertyOrder" : 14 }, "cropBottom" : @@ -544,6 +556,7 @@ "title" : "Crop bottom", "minimum" : 0, "default" : 0, + "append" : "Pixel", "propertyOrder" : 15 }, "redSignalThreshold" : @@ -553,6 +566,7 @@ "minimum" : 0.0, "maximum" : 1.0, "default" : 0.1, + "append" : "%", "propertyOrder" : 16 }, "greenSignalThreshold" : @@ -562,6 +576,7 @@ "minimum" : 0.0, "maximum" : 1.0, "default" : 0.1, + "append" : "%", "propertyOrder" : 17 }, "blueSignalThreshold" : @@ -571,6 +586,7 @@ "minimum" : 0.0, "maximum" : 1.0, "default" : 0.1, + "append" : "%", "propertyOrder" : 18 } }, @@ -601,20 +617,23 @@ { "type" : "integer", "title" : "Width", - "default" : 96 + "default" : 96, + "append" : "Pixel" }, "height" : { "type" : "integer", "title" : "Height", - "default" : 96 + "default" : 96, + "append" : "Pixel" }, "frequency_Hz" : { "type" : "integer", "title" : "Frequency", "minimum" : 0, - "default" : 10 + "default" : 10, + "append" : "Hz" }, "priority" : { @@ -628,28 +647,32 @@ "type" : "integer", "title" : "Crop left", "minimum" : 0, - "default" : 0 + "default" : 0, + "append" : "Pixel" }, "cropRight" : { "type" : "integer", "title" : "Crop right", "minimum" : 0, - "default" : 0 + "default" : 0, + "append" : "Pixel" }, "cropTop" : { "type" : "integer", "title" : "Crop top", "minimum" : 0, - "default" : 0 + "default" : 0, + "append" : "Pixel" }, "cropBottom" : { "type" : "integer", "title" : "Crop bottom", "minimum" : 0, - "default" : 0 + "default" : 0, + "append" : "Pixel" }, "useXGetImage" : { @@ -707,6 +730,7 @@ "minimum" : 0.0, "maximum" : 1.0, "default" : 0.05, + "append" : "%", "propertyOrder" : 2 }, "unknownFrameCnt" : diff --git a/libsrc/hyperion/resource.qrc b/libsrc/hyperion/resource.qrc index 4181fbe8..efc3930b 100644 --- a/libsrc/hyperion/resource.qrc +++ b/libsrc/hyperion/resource.qrc @@ -1,5 +1,6 @@ hyperion.schema.json + ../../config/hyperion.config.json.default diff --git a/libsrc/leddevice/schemas/schema-adalight.json b/libsrc/leddevice/schemas/schema-adalight.json index 4755a475..8dacc228 100644 --- a/libsrc/leddevice/schemas/schema-adalight.json +++ b/libsrc/leddevice/schemas/schema-adalight.json @@ -17,6 +17,7 @@ "type": "integer", "title":"Delay after connect", "default": 250, + "append" : "ms", "propertyOrder" : 3 } }, diff --git a/libsrc/leddevice/schemas/schema-adalightapa102.json b/libsrc/leddevice/schemas/schema-adalightapa102.json index 4755a475..8dacc228 100644 --- a/libsrc/leddevice/schemas/schema-adalightapa102.json +++ b/libsrc/leddevice/schemas/schema-adalightapa102.json @@ -17,6 +17,7 @@ "type": "integer", "title":"Delay after connect", "default": 250, + "append" : "ms", "propertyOrder" : 3 } }, diff --git a/libsrc/leddevice/schemas/schema-atmo.json b/libsrc/leddevice/schemas/schema-atmo.json index 4755a475..8dacc228 100644 --- a/libsrc/leddevice/schemas/schema-atmo.json +++ b/libsrc/leddevice/schemas/schema-atmo.json @@ -17,6 +17,7 @@ "type": "integer", "title":"Delay after connect", "default": 250, + "append" : "ms", "propertyOrder" : 3 } }, diff --git a/libsrc/leddevice/schemas/schema-philipshue.json b/libsrc/leddevice/schemas/schema-philipshue.json index 8c48bafa..8b338e59 100644 --- a/libsrc/leddevice/schemas/schema-philipshue.json +++ b/libsrc/leddevice/schemas/schema-philipshue.json @@ -23,8 +23,9 @@ }, "transitiontime": { "type": "integer", - "title":"Transistion time (x100ms)", + "title":"Transistion time", "default" : 1, + "append" : "x100ms", "propertyOrder" : 4 }, "switchOffOnBlack": { diff --git a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp index eea0dfa5..3eeea2ad 100644 --- a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp +++ b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp @@ -109,7 +109,7 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s else if (attribute == "id") ; // references have already been collected else if (attribute == "title" || attribute == "description" || attribute == "default" || attribute == "format" - || attribute == "defaultProperties" || attribute == "propertyOrder") + || attribute == "defaultProperties" || attribute == "propertyOrder" || attribute == "append") ; // nothing to do. else { diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index cc91a63e..ab113efe 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -34,14 +34,14 @@ int main(int argc, char ** argv) // create the option parser and initialize all parser Parser parser("AmLogic capture application for Hyperion"); - IntOption & argFps = parser.add ('f', "framerate", "Capture frame rate [default: %1]", "10"); - IntOption & argWidth = parser.add (0x0, "width", "Width of the captured image [default: %1]", "160", 160, 4096); - IntOption & argHeight = parser.add (0x0, "height", "Height of the captured image [default: %1]", "160", 160, 4096); - BooleanOption & argScreenshot = parser.add (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); - Option & argAddress = parser.add