implement hyperion restart via webui (#242)

* first try

* implement hyperion restart. core function is good, but needs a beeter structuring- something for next refactoring session ;-)

* several fixes (including osx)
merge with upstream
some refactoring

* add some eye candy to webui
This commit is contained in:
redPanther 2016-09-15 20:42:58 +02:00 committed by GitHub
parent a04f34eab7
commit eeb9b0f7da
33 changed files with 197 additions and 71 deletions

View File

@ -8,7 +8,7 @@
<hr>
<div class="col-lg-12">
<div id='editor_container'></div>
<button id='btn_submit'>Submit (console.log)</button>
<button id='btn_submit' class="btn btn-danger">Submit - currently will destroy your config!</button>
</div>
</div>
</div>

View File

@ -61,3 +61,7 @@ table.borderless td,table.borderless th{border: none !important;}
top: 0;
z-index:99999;
}
#page-content {
padding-bottom:50px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -36,6 +36,11 @@
<script src="js/lib/jquery-lang.js" charset="utf-8" type="text/javascript"></script>
<script src="js/lib/js.cookie.js"></script>
<!-- jquery ui -->
<link href="css/jquery-ui/jquery-ui.min.css" rel="stylesheet">
<link href="css/jquery-ui/jquery-ui.structure.min.css" rel="stylesheet">
<link href="css/jquery-ui/jquery-ui.theme.min.css" rel="stylesheet">
<script type="text/javascript">
// Create language switcher instance
var lang = new Lang();
@ -195,7 +200,7 @@
</ul>
<!-- /.navbar-top-left -->
<div class="navbar-default sidebar" role="navigation">
<div id="main-nav" class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav" id="side-menu">
<li> <a class="active" id="load_dashboard"><i class="fa fa-dashboard fa-fw"></i><span lang="en" data-lang-token="main_menu_dashboard_token">Dashboard</span></a> </li>
@ -225,10 +230,10 @@
<!-- Page Content -->
<div id="page-wrapper" style="padding-top:10px">
<div id="hyperion_restart_notify" class="alert alert-warning" style="display:none;padding:10px;margin:0">
<div id="hyperion_reload_notify" class="alert alert-warning" style="display:none;padding:10px;margin:0">
<div class="panel-danger" style="text-align:right">
<div style="float:left">Hyperion Configuration is modified. To make it active, restart Hyperion.</div>
<button id="btn_hyperion_restart" class="btn btn-danger">Restart Hyperion</button>
<button id="btn_hyperion_reload" class="btn btn-danger">Restart Hyperion</button>
</div>
</div>
@ -264,6 +269,7 @@
<!-- Custom Theme JavaScript -->
<script src="/js/lib/sb-admin-2.js"></script>
<script src="/js/lib/jquery-ui.min.js"></script>
<script src="/js/content_index.js"></script>
</body>

View File

@ -50,19 +50,19 @@ $(hyperion).one("cmd-config-getschema", function(event) {
schema: {
title:'',
properties: {
/*blackborderdetector,
blackborderdetector,
color,
effects,
forwarder,
initialEffect,
kodiVideoChecker,
smoothing,*/
logger//,
/*jsonServer,
smoothing,
logger,
jsonServer,
protoServer,
boblightServer,
udpListener,
webConfig*/
webConfig
}
}
});
@ -74,10 +74,10 @@ $(hyperion).one("cmd-config-getschema", function(event) {
$('#editor_container h3').first().remove();
//Called everytime a Input Field is changed = No need for save button
general_conf_editor.off().on('change',function() {
console.log(JSON.stringify(general_conf_editor.getValue()));
requestWriteConfig(general_conf_editor.getValue());
});
// general_conf_editor.off().on('change',function() {
// console.log(JSON.stringify(general_conf_editor.getValue()));
// requestWriteConfig(general_conf_editor.getValue());
// });
//Alternative Function with submit button to get Values
$('btn_submit').off().on('click',function() {

View File

@ -1,4 +1,6 @@
$(document).ready( function() {
$("#main-nav").hide();
$("#page-content").hide();
$("#loading_overlay").addClass("overlay");
loadContentTo("#container_connection_lost","connection_lost");
initWebSocket();
@ -22,9 +24,9 @@ $(document).ready( function() {
cleanCurrentVersion = currentVersion.replace(/\./g, '');
if (parsedServerInfoJSON.info.hyperion[0].config_modified)
$("#hyperion_restart_notify").fadeIn("fast");
$("#hyperion_reload_notify").fadeIn("fast");
else
$("#hyperion_restart_notify").fadeOut("fast");
$("#hyperion_reload_notify").fadeOut("fast");
// get active led device
var leddevice = parsedServerInfoJSON.info.ledDevices.active;
@ -59,6 +61,9 @@ $(document).ready( function() {
}
});
$("#loading_overlay").removeClass("overlay");
$("#main-nav").show('slide', {direction: 'left'}, 1000);
$("#page-content").show('slide', {direction: 'down'}, 2000);
}); // end cmd-serverinfo
$(hyperion).one("cmd-config-getschema", function(event) {
@ -75,6 +80,13 @@ $(document).ready( function() {
requestServerInfo();
});
$("#btn_hyperion_reload").on("click", function(){
$(hyperion).off();
requestServerConfigReload();
watchdog = 1;
$("#wrapper").fadeOut("slow");
cron();
});
});
$(function(){

View File

@ -0,0 +1 @@

View File

@ -1,6 +1,21 @@
var ledsCustomCfgInitialized = false;
function get_hue_lights(){
$.ajax({
type: "GET",
url: 'http://'+$("#ip").val()+'/api/'+$("#user").val()+'/lights',
processData: false,
contentType: 'application/json',
success: function(r) {
for(var lightid in r){
//console.log(r[lightid].name);
$('#hue_lights').append('ID: '+lightid+' Name: '+r[lightid].name+'<br />');
}
}
});
}
$(document).ready(function() {
// ------------------------------------------------------------------
@ -157,7 +172,7 @@ $(document).ready(function() {
if (isCurrentDevice)
{
specificOptions_val = grabber_conf_editor.getEditor("root.specificOptions").getValue()
for(var key in grabber_conf_editor.getEditor("root.specificOptions").getValue()){
for(var key in specificOptions_val){
values_specific[key] = (key in parsedConfJSON.device) ? parsedConfJSON.device[key] : specificOptions_val[key];
};

View File

@ -155,6 +155,10 @@ function requestServerConfig() {
websocket.send('{"command":"config", "tan":'+wsTan+',"subcommand":"getconfig"}');
}
function requestServerConfigReload() {
websocket.send('{"command":"config", "tan":'+wsTan+',"subcommand":"reload"}');
}
function requestLedColorsStart() {
ledStreamActive=true;
websocket.send('{"command":"ledcolors", "tan":'+wsTan+',"subcommand":"ledstream-start"}');
@ -197,18 +201,3 @@ function requestWriteConfig(config, create, overwrite)
var overwrite = (typeof overwrite !== 'undefined') ? overwrite : false;
websocket.send('{"command":"config","subcommand":"setconfig", "tan":'+wsTan+', "config":'+JSON.stringify(config)+',"create":'+create+', "overwrite":'+overwrite+'}');
}
function get_hue_lights(){
$.ajax({
type: "GET",
url: 'http://'+$("#ip").val()+'/api/'+$("#user").val()+'/lights',
processData: false,
contentType: 'application/json',
success: function(r) {
for(var lightid in r){
//console.log(r[lightid].name);
$('#hue_lights').append('ID: '+lightid+' Name: '+r[lightid].name+'<br />');
}
}
});
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@ function bindNavToContent(containerId, fileName, loadNow)
});
if (loadNow)
{
$("#page-content").load("/content/"+fileName+".html");
$(containerId).trigger("click");
}
}

View File

@ -3,6 +3,6 @@
namespace FileUtils {
std::string getBaseName( std::string sourceFile);
std::string command_exec(const char* cmd);
};

8
include/utils/Process.h Normal file
View File

@ -0,0 +1,8 @@
#include <string>
namespace Process {
void restartHyperion(bool asNewProcess=false);
std::string command_exec(const char* cmd);
};

View File

@ -657,24 +657,26 @@ unsigned Hyperion::getLedCount() const
bool Hyperion::configModified()
{
bool isModified = false;
QFile f(_configFile.c_str());
if (f.open(QFile::ReadOnly))
{
QCryptographicHash hash(QCryptographicHash::Sha1);
if (hash.addData(&f))
if (hash.addData(&f))
{
if (_configHash.size() == 0)
{
if (_configHash.size() == 0)
{
_configHash = hash.result();
qDebug(_configHash.toHex());
return false;
}
return _configHash != hash.result();
_configHash = hash.result();
}
else
{
isModified = _configHash != hash.result();
}
}
}
f.close();
return false;
return isModified;
}
void Hyperion::registerPriority(const std::string name, const int priority)

View File

@ -1057,7 +1057,15 @@
{
"paths" :
{
"type" : "array"
"type" : "array",
"title" : "List of folders to additional effects",
"propertyOrder" : 1
},
"disable" :
{
"type" : "array",
"title" : "List of disabled effects",
"propertyOrder" : 2
}
},
"additionalProperties" : false

View File

@ -2,6 +2,7 @@
#include <stdexcept>
#include <cassert>
#include <iomanip>
#include <unistd.h>
// stl includes
#include <iostream>
@ -15,6 +16,7 @@
#include <QHostInfo>
#include <QString>
#include <QFile>
#include <QCoreApplication>
// hyperion util includes
#include <hyperion/ImageProcessorFactory.h>
@ -27,6 +29,7 @@
#include <leddevice/LedDevice.h>
#include <HyperionConfig.h>
#include <utils/jsonschema/JsonFactory.h>
#include <utils/Process.h>
// project includes
#include "JsonClientConnection.h"
@ -883,6 +886,12 @@ void JsonClientConnection::handleConfigCommand(const Json::Value & message, cons
{
handleConfigSetCommand(message, full_command, tan);
}
else if (subcommand == "reload")
{
// restart hyperion, this code must be put in some own class ...
Process::restartHyperion();
sendErrorReply("failed to restart hyperion", full_command, tan);
}
else
{
sendErrorReply("unknown or missing subcommand", full_command, tan);

View File

@ -10,7 +10,7 @@
"subcommand": {
"type" : "string",
"required" : true,
"enum" : ["getconfig","setconfig","getschema"]
"enum" : ["getconfig","setconfig","getschema","reload"]
},
"tan" : {
"type" : "integer"

View File

@ -54,6 +54,9 @@
"type" : "array",
"propertyOrder" : 9,
"default" : [1.0,1.0,1.0],
"maxItems" : 3,
"minItems" : 3,
"format" : "table",
"items" : {
"type" : "number",
"minimum" : 0.0,

View File

@ -21,6 +21,8 @@ add_library(hyperion-utils
${CURRENT_HEADER_DIR}/Sleep.h
${CURRENT_HEADER_DIR}/FileUtils.h
${CURRENT_SOURCE_DIR}/FileUtils.cpp
${CURRENT_HEADER_DIR}/Process.h
${CURRENT_SOURCE_DIR}/Process.cpp
${CURRENT_HEADER_DIR}/Logger.h
${CURRENT_SOURCE_DIR}/Logger.cpp

View File

@ -1,10 +1,5 @@
#include <utils/FileUtils.h>
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <QFileInfo>
namespace FileUtils {
@ -15,21 +10,5 @@ std::string getBaseName( std::string sourceFile)
return fi.fileName().toStdString();
}
std::string command_exec(const char* cmd)
{
char buffer[128];
std::string result = "";
std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
if (pipe)
{
while (!feof(pipe.get()))
{
if (fgets(buffer, 128, pipe.get()) != NULL)
result += buffer;
}
}
return result;
}
};

57
libsrc/utils/Process.cpp Normal file
View File

@ -0,0 +1,57 @@
#include <utils/Process.h>
#include <utils/Logger.h>
#include <QCoreApplication>
#include <QStringList>
#include <unistd.h>
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
namespace Process {
void restartHyperion(bool asNewProcess)
{
Logger* log = Logger::getInstance("Process");
std::cout << std::endl
<< " *******************************************" << std::endl
<< " * hyperion will restart now *" << std::endl
<< " *******************************************" << std::endl << std::endl;
QStringList qargs = QCoreApplication::arguments();
int size = qargs.size();
char *args[size+1];
args[size] = nullptr;
for(int i=0; i<size; i++)
{
int str_size = qargs[i].toLocal8Bit().size();
args[i] = new char[str_size+1];
strncpy(args[i], qargs[i].toLocal8Bit().constData(),str_size );
args[i][str_size] = '\0';
}
execv(args[0],args);
Error(log, "error while restarting hyperion");
}
std::string command_exec(const char* cmd)
{
char buffer[128];
std::string result = "";
std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
if (pipe)
{
while (!feof(pipe.get()))
{
if (fgets(buffer, 128, pipe.get()) != NULL)
result += buffer;
}
}
return result;
}
};

View File

@ -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 == "defaultProperties" || attribute == "propertyOrder")
; // nothing to do.
else

View File

@ -6,6 +6,7 @@
#include "CgiHandler.h"
#include "QtHttpHeader.h"
#include <utils/FileUtils.h>
#include <utils/Process.h>
CgiHandler::CgiHandler (Hyperion * hyperion, QString baseUrl, QObject * parent)
: QObject(parent)
@ -96,7 +97,7 @@ void CgiHandler::cmd_runscript(const QStringList & args, QtHttpReply * reply)
if (QFile::exists(scriptFilePath) && !interpreter.isEmpty())
{
QByteArray data = FileUtils::command_exec(QString(interpreter + " " + scriptFilePath).toUtf8().constData()).c_str();
QByteArray data = Process::command_exec(QString(interpreter + " " + scriptFilePath).toUtf8().constData()).c_str();
reply->addHeader ("Content-Type", "text/plain");
reply->appendRawData (data);

View File

@ -6,12 +6,12 @@
WebConfig::WebConfig(QObject * parent)
: QObject(parent)
, _hyperion(Hyperion::getInstance())
, _port(WEBCONFIG_DEFAULT_PORT)
, _server(nullptr)
{
_baseUrl = WEBCONFIG_DEFAULT_PATH;
const Json::Value &config = _hyperion->getJsonConfig();
Logger* log = Logger::getInstance("WEBSERVER");
_port = WEBCONFIG_DEFAULT_PORT;
_baseUrl = WEBCONFIG_DEFAULT_PATH;
const Json::Value &config = _hyperion->getJsonConfig();
bool webconfigEnable = true;
@ -19,7 +19,7 @@ WebConfig::WebConfig(QObject * parent)
{
const Json::Value & webconfigConfig = config["webConfig"];
webconfigEnable = webconfigConfig.get("enable", true).asBool();
_port = webconfigConfig.get("port", WEBCONFIG_DEFAULT_PORT).asUInt();
_port = webconfigConfig.get("port", _port).asUInt();
_baseUrl = QString::fromStdString( webconfigConfig.get("document_root", _baseUrl.toStdString()).asString() );
}