+
+
+
+
+
+
+
+
+
+
diff --git a/assets/webconfig/js/hyperion.js b/assets/webconfig/js/hyperion.js
index 19e2ce74..b5931152 100644
--- a/assets/webconfig/js/hyperion.js
+++ b/assets/webconfig/js/hyperion.js
@@ -30,50 +30,130 @@ var parsedServerInfoJSON;
var parsedUpdateJSON;
var parsedConfSchemaJSON;
var hyperionport = 19444;
+var websocket = null;
+var hyperion = {};
+var wsTan = 1;
+var cronId = 0;
-function button_reloaddata(){
- hyperionport = $("#json_port").val();
- loaddata();
- };
+//
+function cron()
+{
+ requestServerInfo();
+}
-function loaddata() {
+// init websocket to hyperion and bind socket events to jquery events of $(hyperion) object
+function initWebSocket()
+{
+ if ("WebSocket" in window)
+ {
+ if (websocket == null)
+ {
+ $.ajax({ url: "/cgi/cfg_jsonserver" }).done(function(data) {
+ hyperionport = data.substr(1);
+ websocket = new WebSocket('ws://'+document.location.hostname+data);
- webSocket = new WebSocket('ws://'+document.location.hostname+':'+hyperionport);
+ websocket.onopen = function (event) {
+ $(hyperion).trigger({type:"open"});
+ cronId = window.setInterval(cron,3000);
+ };
- webSocket.onerror = function(event) {
- $('#con_error_modal').modal('show');
- };
-
- webSocket.onopen = function(event) {
- webSocket.send('{"command":"serverinfo"}');
- };
-
- webSocket.onmessage = function(response){
- parsedServerInfoJSON = JSON.parse(response.data );
- currentVersion = parsedServerInfoJSON.info.hyperion[0].version;
- cleanCurrentVersion = currentVersion.replace(/\./g, '');
- // get active led device
- var leddevice = parsedServerInfoJSON.info.ledDevices.active;
- $('#dash_leddevice').html(leddevice);
- // get host
- var hostname = parsedServerInfoJSON.info.hostname;
- $('#dash_systeminfo').html(hostname+':'+hyperionport);
-
- $.get( "https://raw.githubusercontent.com/hyperion-project/hyperion.ng/master/version.json", function( data ) {
- parsedUpdateJSON = JSON.parse(data);
- latestVersion = parsedUpdateJSON[0].versionnr;
- cleanLatestVersion = latestVersion.replace(/\./g, '');
-
- $('#currentversion').html(' V'+currentVersion);
- $('#latestversion').html(' V'+latestVersion);
-
- if ( cleanCurrentVersion < cleanLatestVersion ) {
- $('#versioninforesult').html('
A newer version of Hyperion is available!
');
+ websocket.onclose = function (event) {
+ // See http://tools.ietf.org/html/rfc6455#section-7.4.1
+ var reason;
+ switch(event.code)
+ {
+ case 1000: reason = "Normal closure, meaning that the purpose for which the connection was established has been fulfilled."; break;
+ case 1001: reason = "An endpoint is \"going away\", such as a server going down or a browser having navigated away from a page."; break;
+ case 1002: reason = "An endpoint is terminating the connection due to a protocol error"; break;
+ case 1003: reason = "An endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message)."; break;
+ case 1004: reason = "Reserved. The specific meaning might be defined in the future."; break;
+ case 1005: reason = "No status code was actually present."; break;
+ case 1006: reason = "The connection was closed abnormally, e.g., without sending or receiving a Close control frame"; break;
+ case 1007: reason = "An endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [http://tools.ietf.org/html/rfc3629] data within a text message)."; break;
+ case 1008: reason = "An endpoint is terminating the connection because it has received a message that \"violates its policy\". This reason is given either if there is no other sutible reason, or if there is a need to hide specific details about the policy."; break;
+ case 1009: reason = "An endpoint is terminating the connection because it has received a message that is too big for it to process."; break;
+ case 1010: reason = "An endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake.
Specifically, the extensions that are needed are: " + event.reason; break;
+ case 1011: reason = "A server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request."; break;
+ case 1015: reason = "The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified)."; break;
+ default: reason = "Unknown reason";
}
- else{
- $('#versioninforesult').html('
You run the latest version of Hyperion.
');
+ $(hyperion).trigger({type:"close", reason:reason});
+ };
+
+ websocket.onmessage = function (event) {
+ try
+ {
+ response = JSON.parse(event.data);
+ success = response.success;
+ cmd = response.command;
+ if (success)
+ {
+ $(hyperion).trigger({type:"cmd-"+cmd, response:response});
+ }
+ else
+ {
+ error = response.hasOwnProperty("error")? response.error : "unknown";
+ $(hyperion).trigger({type:"error",reason:error});
+ console.log("[websocket::onmessage] "+error)
+ }
}
+ catch(exception_error)
+ {
+ $(hyperion).trigger({type:"error",reason:exception_error});
+ console.log("[websocket::onmessage] "+exception_error)
+ }
+ };
+
+ websocket.onerror = function (error) {
+ $(hyperion).trigger({type:"error",reason:error});
+ console.log("[websocket::onerror] "+error)
+ };
});
+ }
+ }
+ else
+ {
+ $(hyperion).trigger("error");
+ alert("Websocket is not supported by your browser");
+ return;
+ }
+}
+
+// -----------------------------------------------------------
+// wrapped server commands
+
+function requestServerInfo() {
+ websocket.send('{"command":"serverinfo", "tan":'+wsTan+'}');
+}
+
+function requestServerConfigSchema() {
+ websocket.send('{"command":"config", "tan":'+wsTan+',"subcommand":"getschema"}');
+}
+
+function requestPriorityClear() {
+ websocket.send('{"command":"clear", "tan":'+wsTan+', "priority":1}');
+}
+
+function requestPlayEffect(effectName) {
+ websocket.send('{"command":"effect", "tan":'+wsTan+',"effect":{"name":"'+effectName+'"},"priority":1}');
+}
+
+function requestSetColor(r,g,b) {
+ websocket.send('{"command":"color", "tan":'+wsTan+', "color":['+r+','+g+','+b+'], "priority":1}');
+}
+
+
+function requestSetComponentState(comp, state){
+ state_str = state?"true":"false";
+ websocket.send('{"command":"componentstate", "tan":'+wsTan+',"componentstate":{"component":"'+comp+'","state":'+state_str+'}}');
+ console.log(comp+' state: '+state_str);
+}
+
+function requestSetSource( prio )
+{
+ if ( prio == "auto" )
+ websocket.send('{"command":"sourceselect", "tan":'+wsTan+', "auto" : true}');
+ else
+ websocket.send('{"command":"sourceselect", "tan":'+wsTan+', "priority" : '+prio+'}');
+}
- };
-};
diff --git a/assets/webconfig/js/ui_utils.js b/assets/webconfig/js/ui_utils.js
new file mode 100644
index 00000000..e89eed01
--- /dev/null
+++ b/assets/webconfig/js/ui_utils.js
@@ -0,0 +1,12 @@
+
+function bindNavToContent(containerId, fileName, loadNow=false)
+{
+ $("#page-wrapper").off();
+ $(containerId).on("click", function() {
+ $("#page-wrapper").load("/content/"+fileName+".html");
+ });
+ if (loadNow)
+ {
+ $("#page-wrapper").load("/content/"+fileName+".html");
+ }
+}
diff --git a/assets/webconfig/remote_components.html b/assets/webconfig/remote_components.html
deleted file mode 100644
index 6cbc7c81..00000000
--- a/assets/webconfig/remote_components.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
The components remote enables you to disable and enable certain components of Hyperion during runtime. Keep in mind this persist just until the next reboot! To enable/disable components permament, use the configuration section.
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/assets/webconfig/select/index.html b/assets/webconfig/select/index.html
deleted file mode 100644
index 82924b24..00000000
--- a/assets/webconfig/select/index.html
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
-
-
-
-
Hyperion web control
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/assets/webconfig/select/starter-template.css b/assets/webconfig/select/starter-template.css
deleted file mode 100644
index e83f68ad..00000000
--- a/assets/webconfig/select/starter-template.css
+++ /dev/null
@@ -1,8 +0,0 @@
-body {
- padding-top: 50px;
-}
-.starter-template {
- padding: 40px 15px;
- text-align: center;
-}
-
diff --git a/assets/webconfig/server_scripts/demo.py b/assets/webconfig/server_scripts/demo.py
new file mode 100644
index 00000000..a7d90ed8
--- /dev/null
+++ b/assets/webconfig/server_scripts/demo.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+print ("hello world");
+
+
diff --git a/assets/webconfig/server_scripts/demo.sh b/assets/webconfig/server_scripts/demo.sh
new file mode 100644
index 00000000..adfa2ccd
--- /dev/null
+++ b/assets/webconfig/server_scripts/demo.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+echo "hello world"
+pwd
+
diff --git a/assets/webconfig/update.html b/assets/webconfig/update.html
deleted file mode 100644
index ba6b31ef..00000000
--- a/assets/webconfig/update.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
This page gives you an overview of all Hyperion versions available. On top you could update or downgrade your version of Hyperion whenever you want. Sorted from newest to oldest
-
-
-
-
-
-
-
-
-
-
-
diff --git a/include/utils/FileUtils.h b/include/utils/FileUtils.h
new file mode 100644
index 00000000..d3f488dc
--- /dev/null
+++ b/include/utils/FileUtils.h
@@ -0,0 +1,8 @@
+#include
+
+namespace FileUtils {
+
+std::string getBaseName( std::string sourceFile);
+std::string command_exec(const char* cmd);
+
+};
diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp
index efe4676d..dbaec2ea 100644
--- a/libsrc/effectengine/EffectEngine.cpp
+++ b/libsrc/effectengine/EffectEngine.cpp
@@ -12,6 +12,7 @@
// hyperion util includes
#include
+#include
// effect engine includes
#include
@@ -181,7 +182,7 @@ int EffectEngine::runEffectScript(const std::string &script, const Json::Value &
_activeEffects.push_back(effect);
// start the effect
- _hyperion->registerPriority("EFFECT: "+script, priority);
+ _hyperion->registerPriority("EFFECT: "+FileUtils::getBaseName(script), priority);
effect->start();
return 0;
diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json
index 56c36688..6c2f0f94 100644
--- a/libsrc/hyperion/hyperion.schema.json
+++ b/libsrc/hyperion/hyperion.schema.json
@@ -684,6 +684,30 @@
"maximum" : 1.0,
"default" : 0.05
},
+ "unknownFrameCnt" :
+ {
+ "type" : "number",
+ "minimum" : 0,
+ "advanced" : true
+ },
+ "borderFrameCnt" :
+ {
+ "type" : "number",
+ "minimum" : 0,
+ "advanced" : true
+ },
+ "maxInconsistentCnt" :
+ {
+ "type" : "number",
+ "minimum" : 0,
+ "advanced" : true
+ },
+ "blurRemoveCnt" :
+ {
+ "type" : "number",
+ "minimum" : 0,
+ "advanced" : true
+ },
"mode" :
{
"type" : "string",
diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp
index 527aaa34..ba961e0e 100644
--- a/libsrc/jsonserver/JsonClientConnection.cpp
+++ b/libsrc/jsonserver/JsonClientConnection.cpp
@@ -852,21 +852,22 @@ void JsonClientConnection::handleSourceSelectCommand(const Json::Value & message
void JsonClientConnection::handleConfigCommand(const Json::Value & message, const std::string &command, const int tan)
{
std::string subcommand = message.get("subcommand","").asString();
+ std::string full_command = command + "-" + subcommand;
if (subcommand == "getschema")
{
- handleSchemaGetCommand(message, command, tan);
+ handleSchemaGetCommand(message, full_command, tan);
}
else if (subcommand == "getconfig")
{
- handleConfigGetCommand(message, command, tan);
+ handleConfigGetCommand(message, full_command, tan);
}
else if (subcommand == "setconfig")
{
- handleConfigSetCommand(message, command, tan);
+ handleConfigSetCommand(message, full_command, tan);
}
else
{
- sendErrorReply("unknown or missing subcommand", command, tan);
+ sendErrorReply("unknown or missing subcommand", full_command, tan);
}
}
diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt
index 203dddc0..334fa667 100644
--- a/libsrc/utils/CMakeLists.txt
+++ b/libsrc/utils/CMakeLists.txt
@@ -19,6 +19,8 @@ add_library(hyperion-utils
${CURRENT_SOURCE_DIR}/ColorRgbw.cpp
${CURRENT_HEADER_DIR}/Image.h
${CURRENT_HEADER_DIR}/Sleep.h
+ ${CURRENT_HEADER_DIR}/FileUtils.h
+ ${CURRENT_SOURCE_DIR}/FileUtils.cpp
${CURRENT_HEADER_DIR}/Logger.h
${CURRENT_SOURCE_DIR}/Logger.cpp
diff --git a/libsrc/utils/FileUtils.cpp b/libsrc/utils/FileUtils.cpp
new file mode 100644
index 00000000..9cd54338
--- /dev/null
+++ b/libsrc/utils/FileUtils.cpp
@@ -0,0 +1,35 @@
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace FileUtils {
+
+std::string getBaseName( std::string sourceFile)
+{
+ QFileInfo fi( sourceFile.c_str() );
+ return fi.fileName().toStdString();
+}
+
+std::string command_exec(const char* cmd)
+{
+ char buffer[128];
+ std::string result = "";
+ std::shared_ptr pipe(popen(cmd, "r"), pclose);
+ if (pipe)
+ {
+ while (!feof(pipe.get()))
+ {
+ if (fgets(buffer, 128, pipe.get()) != NULL)
+ result += buffer;
+ }
+ }
+
+ return result;
+}
+
+};
\ No newline at end of file
diff --git a/libsrc/utils/Logger.cpp b/libsrc/utils/Logger.cpp
index d833cbd2..7fe003de 100644
--- a/libsrc/utils/Logger.cpp
+++ b/libsrc/utils/Logger.cpp
@@ -1,4 +1,5 @@
-#include "utils/Logger.h"
+#include
+#include
#include
#include
@@ -7,11 +8,6 @@
#include
#include
-std::string getBaseName( std::string sourceFile)
-{
- QFileInfo fi( sourceFile.c_str() );
- return fi.fileName().toStdString();
-}
static const char * LogLevelStrings[] = { "", "DEBUG", "INFO", "WARNING", "ERROR" };
static const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR };
@@ -134,7 +130,7 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u
std::string function(func);
if ( level == Logger::DEBUG )
{
- location = "<" + getBaseName(sourceFile) + ":" + QString::number(line).toStdString()+":"+ function + "()> ";
+ location = "<" + FileUtils::getBaseName(sourceFile) + ":" + QString::number(line).toStdString()+":"+ function + "()> ";
}
std::cout
diff --git a/libsrc/utils/Profiler.cpp b/libsrc/utils/Profiler.cpp
index 2c076c43..7f4773c0 100644
--- a/libsrc/utils/Profiler.cpp
+++ b/libsrc/utils/Profiler.cpp
@@ -1,5 +1,6 @@
#include "HyperionConfig.h"
-#include "utils/Profiler.h"
+#include
+#include
#include
#include
@@ -15,21 +16,11 @@ static unsigned int blockCounter = 0;
static std::map GlobalProfilerMap;
Logger* Profiler::_logger = nullptr;
-
-
-std::string profiler_getBaseName( std::string sourceFile)
-{
- QFileInfo fi( sourceFile.c_str() );
- return fi.fileName().toStdString();
-}
-
double getClockDelta(clock_t start)
{
return ((double)(clock() - start) / CLOCKS_PER_SEC) ;
}
-
-
Profiler::Profiler(const char* sourceFile, const char* func, unsigned int line) :
_file(sourceFile),
_func(func),
@@ -71,7 +62,7 @@ void Profiler::TimerStart(const std::string timerName, const char* sourceFile, c
else
{
_logger->Message(Logger::DEBUG, sourceFile, func, line, "ERROR timer '%s' started in multiple locations. First occurence %s:%d:%s()",
- timerName.c_str(), profiler_getBaseName(ret.first->second.sourceFile).c_str(), ret.first->second.line, ret.first->second.func );
+ timerName.c_str(), FileUtils::getBaseName(ret.first->second.sourceFile).c_str(), ret.first->second.line, ret.first->second.func );
}
}
else
@@ -88,7 +79,7 @@ void Profiler::TimerGetTime(const std::string timerName, const char* sourceFile,
if (ret != GlobalProfilerMap.end())
{
_logger->Message(Logger::DEBUG, sourceFile, func, line, "timer '%s' started at %s:%d:%s() took %f s execution time until here", timerName.c_str(),
- profiler_getBaseName(ret->second.sourceFile).c_str(), ret->second.line, ret->second.func, getClockDelta(ret->second.startTime) );
+ FileUtils::getBaseName(ret->second.sourceFile).c_str(), ret->second.line, ret->second.func, getClockDelta(ret->second.startTime) );
}
else
{
diff --git a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp
index 0b0b5651..6b78e5aa 100644
--- a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp
+++ b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp
@@ -108,7 +108,7 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
; // nothing to do. value is present so always oke
else if (attribute == "id")
; // references have already been collected
- else if (attribute == "title" || attribute == "description" || attribute == "default" || attribute == "format")
+ else if (attribute == "title" || attribute == "description" || attribute == "default" || attribute == "format" || attribute == "advanced")
; // nothing to do.
else
{
diff --git a/libsrc/webconfig/CgiHandler.cpp b/libsrc/webconfig/CgiHandler.cpp
index 90341027..ff8e48c1 100644
--- a/libsrc/webconfig/CgiHandler.cpp
+++ b/libsrc/webconfig/CgiHandler.cpp
@@ -5,11 +5,13 @@
#include "CgiHandler.h"
#include "QtHttpHeader.h"
+#include
-CgiHandler::CgiHandler (Hyperion * hyperion, QObject * parent)
+CgiHandler::CgiHandler (Hyperion * hyperion, QString baseUrl, QObject * parent)
: QObject(parent)
, _hyperion(hyperion)
, _hyperionConfig(_hyperion->getJsonConfig())
+ , _baseUrl(baseUrl)
{
}
@@ -26,6 +28,7 @@ void CgiHandler::exec(const QStringList & args, QtHttpRequest * request, QtHttpR
cmd_cfg_jsonserver(args,reply);
cmd_cfg_hyperion(args,reply);
+ cmd_runscript(args,reply);
throw 1;
}
catch(int e)
@@ -71,3 +74,34 @@ void CgiHandler::cmd_cfg_hyperion(const QStringList & args, QtHttpReply * reply)
throw 0;
}
}
+
+void CgiHandler::cmd_runscript(const QStringList & args, QtHttpReply * reply)
+{
+ if ( args.at(0) == "run" )
+ {
+ QStringList scriptFilePathList(args);
+ scriptFilePathList.removeAt(0);
+
+ QString scriptFilePath = scriptFilePathList.join('/');
+ // relative path not allowed
+ if (scriptFilePath.indexOf("..") >=0)
+ {
+ throw 1;
+ }
+
+ scriptFilePath = _baseUrl+"/server_scripts/"+scriptFilePath;
+ QString interpreter = "";
+ if (scriptFilePath.endsWith(".sh")) interpreter = "sh";
+ if (scriptFilePath.endsWith(".py")) interpreter = "python";
+
+ if (QFile::exists(scriptFilePath) && !interpreter.isEmpty())
+ {
+ QByteArray data = FileUtils::command_exec(QString(interpreter + " " + scriptFilePath).toUtf8().constData()).c_str();
+
+ reply->addHeader ("Content-Type", "text/plain");
+ reply->appendRawData (data);
+ throw 0;
+ }
+ throw 1;
+ }
+}
diff --git a/libsrc/webconfig/CgiHandler.h b/libsrc/webconfig/CgiHandler.h
index 38d6feb9..c3735d8d 100644
--- a/libsrc/webconfig/CgiHandler.h
+++ b/libsrc/webconfig/CgiHandler.h
@@ -15,7 +15,7 @@ class CgiHandler : public QObject {
Q_OBJECT
public:
- CgiHandler (Hyperion * hyperion, QObject * parent = NULL);
+ CgiHandler (Hyperion * hyperion, QString baseUrl, QObject * parent = NULL);
virtual ~CgiHandler (void);
void exec(const QStringList & args,QtHttpRequest * request, QtHttpReply * reply);
@@ -23,11 +23,13 @@ public:
// cgi commands
void cmd_cfg_jsonserver(const QStringList & args, QtHttpReply * reply);
void cmd_cfg_hyperion (const QStringList & args, QtHttpReply * reply);
+ void cmd_runscript (const QStringList & args, QtHttpReply * reply);
private:
Hyperion* _hyperion;
QtHttpReply * _reply;
const Json::Value &_hyperionConfig;
+ const QString _baseUrl;
};
#endif // CGIHANDLER_H
diff --git a/libsrc/webconfig/StaticFileServing.cpp b/libsrc/webconfig/StaticFileServing.cpp
index 341b0492..78ac2bb7 100644
--- a/libsrc/webconfig/StaticFileServing.cpp
+++ b/libsrc/webconfig/StaticFileServing.cpp
@@ -12,7 +12,7 @@ StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint
: QObject (parent)
, _hyperion(hyperion)
, _baseUrl (baseUrl)
- , _cgi(hyperion, this)
+ , _cgi(hyperion, baseUrl, this)
, _log(Logger::getInstance("WEBSERVER"))
{
_mimeDb = new QMimeDatabase;
@@ -71,7 +71,7 @@ void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpRepl
}
catch(...)
{
- printErrorToReply (reply, "cgi script failed (" % path % ")");
+ printErrorToReply (reply, "script failed (" % path % ")");
}
return;
}
diff --git a/libsrc/webconfig/StaticFileServing.h b/libsrc/webconfig/StaticFileServing.h
index d0a3bcd2..99daf833 100644
--- a/libsrc/webconfig/StaticFileServing.h
+++ b/libsrc/webconfig/StaticFileServing.h
@@ -33,7 +33,7 @@ private:
QtHttpServer * _server;
QMimeDatabase * _mimeDb;
CgiHandler _cgi;
- Logger * _log;
+ Logger * _log;
};