refactoring of webui and event based websocket layer (#219)

* make hyperion websocket api event based

* implement new websocket handling for generalconf

* migrate all webui stuff to new event based websocket api
some cleanup ... now all html templates are in content
refactoring of web stuff

* add hyperionport to global
start impl. removing advanced key

* separate dashboard
serverinfo is updated every 3 seconds automatily
add input selection
cleanup and remove not needed stuff

* prepare infrastructure for server sided file execution

* webui minor fixes

* fix compile
This commit is contained in:
redPanther 2016-09-03 15:54:33 +02:00 committed by GitHub
parent c17c3bd273
commit 4a27f3d43e
35 changed files with 842 additions and 865 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ CMakeCache.txt
*.log
/HyperionConfig.h
/lib
.directory

View File

@ -0,0 +1,91 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">Dashboard</h1>
<div class="introd">
<h4 lang="en" data-lang-token="dashboard_label_intro">The dashboard give you a quick overview about the status of Hyperion and show you the latest news of the Hyperion Blog.</h4>
</div>
<hr>
<div class="col-lg-4">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-info-circle fa-fw"></i>
<span lang="en" data-lang-token="dashboard_label_infobox_title">Information</span>
</div>
<div class="panel-body">
<table class="table borderless">
<tbody>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_currenthyp">Your Hyperion version:</td>
<td id="currentversion">unknown</td>
</tr>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_latesthyp">Latest version:</td>
<td id="latestversion">unknown</td>
</tr>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_leddevice">LED type:</td>
<td id="dash_leddevice">unknown</td>
</tr>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_device">Device:</td>
<td id="dash_systeminfo"></td>
</tr>
</tbody>
</table>
<hr>
<span id="versioninforesult"></span>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-eye fa-fw"></i>
<span lang="en" data-lang-token="dashboard_label_componentbox_title">Components status</span>
</div>
<div class="panel-body">
<table class="table">
<thead>
<tr>
<th>Component</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kodi Watch</td>
<td><i class="fa fa-circle component-on"></i></td>
</tr>
<tr>
<td>Blackborder</td>
<td><i class="fa fa-circle component-off"></i></td>
</tr>
<tr>
<td>Booteffect</td>
<td><i class="fa fa-circle component-off"></i></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-newspaper-o fa-fw"></i>
<span lang="en" data-lang-token="dashboard_label_newsbox_title">Latest blog posts</span>
</div>
<div class="panel-body">
</div>
</div>
</div>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->

View File

@ -0,0 +1,25 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_effects_token">Effects</h1>
<div class="introd">
<h4 lang="en" data-lang-token="remote_effects_intro">The Effects remote enables you to set an effect for testing or demonstration purposes. Don't forget to stop it afterwards.</h4>
</div>
<hr>
<div class="col-lg-12" id="buttondiv">
<button type="button" class="btn btn-danger" id="effect_stop" onclick="requestPriorityClear()"><i class="fa fa-stop"></i></button><span lang="en" data-lang-token="remote_effects_label_stopeffect">Stop Effect</span><br />
</div>
</div>
</div>
</div>
<script>
$(document).ready( function() {
for(i = 0; i < parsedServerInfoJSON.info.effects.length; i++) {
//console.log(parsedServerInfoJSON.info.effects[i].name);
var effectName = parsedServerInfoJSON.info.effects[i].name;
$('#buttondiv').append('<button type="button" class="btn btn-success" onclick="requestPlayEffect(\''+effectName+'\')"><i class="fa fa-play"></i></button> '+effectName+'<br />');
}
});
</script>

View File

@ -0,0 +1,106 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_general_conf_token">General</h1>
<div class="introd">
<h4 lang="en" data-lang-token="remote_general_conf_intro">You can edit the general configuration here.</h4>
</div>
<hr>
<div class="col-lg-12">
<form id="generalConfForm"></form>
<div id='editor_holder'></div>
<button id='submit'>Submit (console.log)</button>
</div>
</div>
</div>
</div>
<script>
function removeAdvanced(obj,searchStack = [])
{
$.each(obj, function(key, val) {
if ( typeof(val) == 'object' )
{
searchStack.push(key);
if (! removeAdvanced(val,searchStack) )
searchStack.pop();
}
else if ( key == "advanced" && val == true )
{
console.log(searchStack);
return true;
}
});
return false;
}
$(hyperion).on("cmd-config-getschema", function(event) {
parsedConfSchemaJSON = event.response.result;
// remove all "advanced" options from schema
//removeAdvanced(parsedConfSchemaJSON); // not working atm
//console.log(JSON.stringify(parsedConfSchemaJSON));
schema_smoothing = parsedConfSchemaJSON.properties.smoothing;
schema_kodiVideoChecker = parsedConfSchemaJSON.properties.kodiVideoChecker;
schema_initialEffect = parsedConfSchemaJSON.properties.initialEffect;
//schema_grabber-v4l2 = parsedConfSchemaJSON.properties.grabber-v4l2;
schema_framegrabber = parsedConfSchemaJSON.properties.framegrabber;
schema_forwarder = parsedConfSchemaJSON.properties.forwarder;
schema_effects = parsedConfSchemaJSON.properties.effects;
schema_device = parsedConfSchemaJSON.properties.device;
schema_color = parsedConfSchemaJSON.properties.color;
schema_blackborderdetector = parsedConfSchemaJSON.properties.blackborderdetector;
schema_logger = parsedConfSchemaJSON.properties.logger;
schema_jsonServer = parsedConfSchemaJSON.properties.jsonServer;
schema_protoServer = parsedConfSchemaJSON.properties.protoServer;
schema_boblightServer = parsedConfSchemaJSON.properties.boblightServer;
schema_udpListener = parsedConfSchemaJSON.properties.udpListener;
schema_webConfig = parsedConfSchemaJSON.properties.webConfig;
var element = document.getElementById('editor_holder');
//JSONEditor.defaults.options.theme = 'bootstrap3';
var general_conf_editor = new JSONEditor(element,{
theme: 'bootstrap3',
disable_collapse: 'true',
form_name_root: 'sa',
disable_edit_json: 'true',
disable_properties: 'true',
no_additional_properties: 'true',
schema: {
title:' ',
properties: {
schema_blackborderdetector,
schema_color,
schema_device,
schema_effects,
schema_forwarder,
schema_framegrabber,
//schema_grabber-v4l2,
schema_initialEffect,
schema_kodiVideoChecker,
schema_smoothing,
schema_logger,
schema_jsonServer,
schema_protoServer,
schema_boblightServer,
schema_udpListener,
schema_webConfig
}
}
});
});
$(document).ready( function() {
requestServerConfigSchema();
document.getElementById('submit').addEventListener('click',function() {
// Get the value from the editor
console.log(general_conf_editor.getValue());
});
// $("[type='checkbox']").bootstrapSwitch();
});
</script>

View File

@ -0,0 +1,34 @@
<div style="text-align:center;margin:auto;padding:auto">
<div id="hyperion_inputs" />
</div>
<script>
$(hyperion).on("cmd-serverinfo", function(event) {
var data = "";
var i;
for(i = 0; i < parsedServerInfoJSON.info.priorities.length; i++) {
var owner = parsedServerInfoJSON.info.priorities[i].owner;
var active = parsedServerInfoJSON.info.priorities[i].active;
var visible = parsedServerInfoJSON.info.priorities[i].visible;
var priority = parsedServerInfoJSON.info.priorities[i].priority;
var btn_type = "default";
if (active) btn_type = "warning";
if (visible) btn_type = "success";
data += '<button id="srcBtn'+i+'" type="button" class="btn btn-lg btn-'+btn_type+' btn_input_selection" style="margin:10px;min-width:200px" title="prio '+priority+'" onclick="requestSetSource('+priority+');">'+owner+'</button><br/>';
}
data += '<button id="srcBtn'+i+'" type="button" class="btn btn-lg btn-info btn_input_selection" style="margin:10px;min-width:200px" onclick="requestSetSource(\'auto\');">auto select</button><br/>';
$('#hyperion_inputs').html(data);
var max_width=200;
$('.btn_input_selection').each(function() {
elem_width = $(this).css('width')
elem_width = elem_width.substring(0, elem_width.length - 2);
if (elem_width > max_width)
max_width = elem_width;
});
$('.btn_input_selection').css("min-width",max_width+"px");
});
</script>

View File

@ -1,7 +1,7 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_colors_token">Colors</h1>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_colors_token">Colors</h1>
<div class="col-lg-1">
<span lang="en" data-lang-token="remote_colors_label_color">Set color: </span>
</div>
@ -25,12 +25,13 @@
$('#cp2').colorpicker({
format: 'rgb',
colorSelectors: {'default': '#777777',
colorSelectors: {
'default': '#777777',
'primary': '#337ab7',
'success': '#5cb85c',
'info': '#5bc0de',
'info' : '#5bc0de',
'warning': '#f0ad4e',
'danger': '#d9534f'
'danger' : '#d9534f'
},
customClass: 'colorpicker-2x',
sliders: {
@ -48,19 +49,10 @@
});
$('#cp2').colorpicker().on('changeColor', function(e) {
//console.log(e.color.toRGB());
webSocket.send('{"command":"color", "color":['+e.color.toRGB().r+','+e.color.toRGB().g+','+e.color.toRGB().b+'], "priority":1}');
color = e.color.toRGB();
requestSetColor(color.r, color.g, color.b);
});
});
$("#reset_color").on("click", function() {
webSocket.send('{"command":"clear", "priority":1}');
});
webSocket.onmessage = function(event){
console.log(event.data);
}
$("#reset_color").on("click", requestPriorityClear);
</script>

View File

@ -0,0 +1,26 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_component_token">Effects</h1>
<div class="introd">
<h4 lang="en" data-lang-token="remote_components_intro">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.</h4>
</div>
<hr>
<div class="col-lg-12" id="componentsbutton">
</div>
</div>
</div>
</div>
<script>
new Enum('SMOOTHING', 'BLACKBORDER', 'KODICHECKER', 'FORWARDER', 'UDPLISTENER', 'BOBLIGHT_SERVER','GRABBER');
function Enum() {
for (var i in arguments)
{
this[arguments[i]] = i;
$('#componentsbutton').append('<button type="button" class="btn btn-success" onclick="requestSetComponentState(\''+arguments[i]+'\',true)"><i class="fa fa-play"></i></button> '+arguments[i]+'<br />');
$('#componentsbutton').append('<button type="button" class="btn btn-danger" onclick="requestSetComponentState(\''+arguments[i]+'\',false)"><i class="fa fa-play"></i></button> '+arguments[i]+'<br />');
}
}
</script>

View File

@ -110,4 +110,4 @@
</a>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_update_token">Update</h1>
<div class="introd">
<h4 lang="en" data-lang-token="update_label_intro">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</h4>
</div>
<hr>
<h4 id="update_currver"></h4>
<hr>
<div class="col-lg-12" id="versionlist">
</div>
</div>
</div>
</div>
<script>
for (key in parsedUpdateJSON)
{
$('#versionlist').append('<div class="col-lg-6"><div class="panel panel-default"><div class="panel-heading"><i class="fa fa-television fa-fw"></i>Hyperion V'+parsedUpdateJSON[key].versionnr+'</div><div class="panel-body"><p><span style="font-weight:bold;" lang="en" data-lang-token="update_label_type">Type:</span> '+ parsedUpdateJSON[key].channel +'</p><p><span style="font-weight:bold;" lang="en" data-lang-token="update_label_description">Description:</span> '+parsedUpdateJSON[key].versiondesc+'</p><hr><a class="btn btn-primary" href="'+ parsedUpdateJSON[key].versionchangelog +'" target="_blank"><i class="fa fa-list fa-fw"></i><span style="font-weight:bold;" lang="en" data-lang-token="update_button_changelog">Full changelog</span></a><button type="button" class="btn btn-warning pull-right"><i class="fa fa-download fa-fw"></i><span lang="en" data-lang-token="update_button_install">Install</span></button></div></div></div>');
}
$('#update_currver').append(currentVersion);
</script>

View File

@ -1,39 +0,0 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_effects_token">Effects</h1>
<div class="introd">
<h4 lang="en" data-lang-token="remote_effects_intro">The Effects remote enables you to set an effect for testing or demonstration purposes. Don't forget to stop it afterwards.</h4>
</div>
<hr>
<div class="col-lg-12" id="buttondiv">
<button type="button" class="btn btn-danger" id="effect_stop"><i class="fa fa-stop"></i></button><span lang="en" data-lang-token="remote_effects_label_stopeffect">Stop Effect</span><br />
</div>
</div>
</div>
</div>
<script>
$(document).ready( function() {
for(i = 0; i < parsedServerInfoJSON.info.effects.length; i++) {
//console.log(parsedServerInfoJSON.info.effects[i].name);
var effectName = parsedServerInfoJSON.info.effects[i].name;
$('#buttondiv').append('<button type="button" class="btn btn-success" onclick="playEffect(\''+effectName+'\')"><i class="fa fa-play"></i></button> '+effectName+'<br />');
}
$("#effect_stop").on("click", function() {
webSocket.send('{"command":"clear", "priority":1}');
});
});
function playEffect(effectName) {
console.log(effectName);
webSocket.send('{"command":"effect","effect":{"name":"'+effectName+'"},"priority":1}');
}
</script>

View File

@ -1,105 +0,0 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_general_conf_token">General</h1>
<div class="introd">
<h4 lang="en" data-lang-token="remote_general_conf_intro">You can edit the general configuration here.</h4>
</div>
<hr>
<div class="col-lg-12">
<form id="generalConfForm"></form>
<div id='editor_holder'></div>
<button id='submit'>Submit (console.log)</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready( function() {
webSocket = new WebSocket('ws://'+document.location.hostname+':'+hyperionport);
var serverInfo;
webSocket.onerror = function(event) {
alert(event.data);
};
webSocket.onopen = function(event) {
webSocket.send('{"command":"config","subcommand":"getschema"}');
};
webSocket.onmessage = function(response){
parsedConfSchemaJSON = JSON.parse(response.data);
console.log(parsedConfSchemaJSON)
schema_smoothing = parsedConfSchemaJSON.result.properties.smoothing;
schema_kodiVideoChecker = parsedConfSchemaJSON.result.properties.kodiVideoChecker;
schema_initialEffect = parsedConfSchemaJSON.result.properties.initialEffect;
//schema_grabber-v4l2 = parsedConfSchemaJSON.result.properties.grabber-v4l2;
schema_framegrabber = parsedConfSchemaJSON.result.properties.framegrabber;
schema_forwarder = parsedConfSchemaJSON.result.properties.forwarder;
schema_effects = parsedConfSchemaJSON.result.properties.effects;
schema_device = parsedConfSchemaJSON.result.properties.device;
schema_color = parsedConfSchemaJSON.result.properties.color;
schema_blackborderdetector = parsedConfSchemaJSON.result.properties.blackborderdetector;
schema_logger = parsedConfSchemaJSON.result.properties.logger;
schema_jsonServer = parsedConfSchemaJSON.result.properties.jsonServer;
schema_protoServer = parsedConfSchemaJSON.result.properties.protoServer;
schema_boblightServer = parsedConfSchemaJSON.result.properties.boblightServer;
schema_udpListener = parsedConfSchemaJSON.result.properties.udpListener;
schema_webConfig = parsedConfSchemaJSON.result.properties.webConfig;
var element = document.getElementById('editor_holder');
//JSONEditor.defaults.options.theme = 'bootstrap3';
var general_conf_editor = new JSONEditor(element,{
theme: 'bootstrap3',
disable_collapse: 'true',
form_name_root: 'sa',
disable_edit_json: 'true',
disable_properties: 'true',
no_additional_properties: 'true',
schema: {
title:' ',
properties: {
schema_blackborderdetector,
schema_color,
schema_device,
schema_effects,
schema_forwarder,
schema_framegrabber,
//schema_grabber-v4l2,
schema_initialEffect,
schema_kodiVideoChecker,
schema_smoothing,
schema_logger,
schema_jsonServer,
schema_protoServer,
schema_boblightServer,
schema_udpListener,
schema_webConfig
}
}
});
document.getElementById('submit').addEventListener('click',function() {
// Get the value from the editor
console.log(general_conf_editor.getValue());
});
// $("[type='checkbox']").bootstrapSwitch();
}
});
</script>

View File

@ -3,31 +3,32 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<title>Hyperion - WebUI</title>
<title>Hyperion - WebUI</title>
<!-- jQuery -->
<script src="js/lib/jquery.min.js"></script>
<script src="js/lib/jquery.min.js"></script>
<script src="js/hyperion.js"></script>
<script src="js/hyperion.js"></script>
<script src="js/ui_utils.js"></script>
<!-- Colorpicker -->
<script src="js/lib/bootstrap-colorpicker.min.js"></script>
<link href="css/bootstrap-colorpicker.min.css" rel="stylesheet">
<script src="js/lib/bootstrap-colorpicker.min.js"></script>
<link href="css/bootstrap-colorpicker.min.css" rel="stylesheet">
<!-- Bootstrap Switch -->
<script src="js/lib/bootstrap-switch.min.js"></script>
<link href="css/bootstrap-switch.min.css" rel="stylesheet">
<script src="js/lib/bootstrap-switch.min.js"></script>
<link href="css/bootstrap-switch.min.css" rel="stylesheet">
<!-- JSONForm -->
<!--<script src="js/lib/underscore.js"></script>
<script src="js/lib/jsonform.js"></script>-->
<script src="js/lib/jsoneditor.min.js"></script>
<!--<script src="js/lib/underscore.js"></script>
<script src="js/lib/jsonform.js"></script>-->
<script src="js/lib/jsoneditor.min.js"></script>
<!--Language Support -->
<script src="js/lib/jquery-lang.js" charset="utf-8" type="text/javascript"></script>
@ -49,481 +50,279 @@
</script>
<!-- Bootstrap Core CSS -->
<link href="css/bootstrap.css" rel="stylesheet">
<!-- Bootstrap Core CSS -->
<link href="css/bootstrap.css" rel="stylesheet">
<!-- Flags -->
<link href="css/flag-icon.min.css" rel="stylesheet">
<!-- MetisMenu CSS -->
<link href="css/metisMenu.css" rel="stylesheet">
<!-- MetisMenu CSS -->
<link href="css/metisMenu.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="css/sb-admin-2.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="css/sb-admin-2.css" rel="stylesheet">
<link href="css/hyperion.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
<!-- Custom Fonts -->
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div id="wrapper">
<div id="wrapper">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html"><img src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!"></a>
<a class="navbar-brand" href="/"><img src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!"></a>
</div>
<!-- /.navbar-header -->
<ul class="nav navbar-top-links navbar-right">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-globe fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-language">
<li>
<a href="#lang-en" onclick="window.lang.change('en'); return false;"><span lang="en" class="flag-icon flag-icon-gb" data-lang-token="general_speech_en">&emsp; English</span></a>
</li>
<li class="divider"></li>
<li>
<a href="#lang-de" onclick="window.lang.change('de'); return false;"><span lang="en" class="flag-icon flag-icon-de" data-lang-token="general_speech_de">&emsp; German</span></a>
</li>
</ul>
<!-- /.dropdown-language -->
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-tasks fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-tasks">
<li>
<a href="#">
<div>
<p>
<strong>Task 1</strong>
<span class="pull-right text-muted">40% Complete</span>
</p>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 40%">
<span class="sr-only">40% Complete (success)</span>
</div>
</div>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<p>
<strong>Task 2</strong>
<span class="pull-right text-muted">20% Complete</span>
</p>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: 20%">
<span class="sr-only">20% Complete</span>
</div>
</div>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<p>
<strong>Task 3</strong>
<span class="pull-right text-muted">60% Complete</span>
</p>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%">
<span class="sr-only">60% Complete (warning)</span>
</div>
</div>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<p>
<strong>Task 4</strong>
<span class="pull-right text-muted">80% Complete</span>
</p>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 80%">
<span class="sr-only">80% Complete (danger)</span>
</div>
</div>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a class="text-center" href="#">
<strong>See All Tasks</strong>
<i class="fa fa-angle-right"></i>
</a>
</li>
</ul>
<!-- /.dropdown-tasks -->
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-bell fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-alerts">
<li>
<a href="#">
<div>
<i class="fa fa-comment fa-fw"></i> New Comment
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-twitter fa-fw"></i> 3 New Followers
<span class="pull-right text-muted small">12 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-envelope fa-fw"></i> Message Sent
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-tasks fa-fw"></i> New Task
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-upload fa-fw"></i> Server Rebooted
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a class="text-center" href="#">
<strong>See All Alerts</strong>
<i class="fa fa-angle-right"></i>
</a>
</li>
</ul>
<!-- /.dropdown-alerts -->
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#"><i class="fa fa-user fa-fw"></i>User Profile</a>
</li>
<li><a href="#"><i class="fa fa-gear fa-fw"></i>Settings</a>
</li>
<li class="divider"></li>
<li><a href="login.html"><i class="fa fa-sign-out fa-fw"></i>Logout</a>
</li>
</ul>
<!-- /.dropdown-user -->
</li>
<!-- /.dropdown -->
</ul>
<!-- /.navbar-top-links -->
<div class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav" id="side-menu">
<li class="sidebar-search">
<div class="input-group custom-search-form">
<input type="text" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button class="btn btn-default" type="button">
<i class="fa fa-search"></i>
</button>
</span>
</div>
<!-- /input-group -->
</li>
<li>
<a class="active" href="index.html"><i class="fa fa-dashboard fa-fw"></i><span lang="en" data-lang-token="main_menu_dashboard_token">Dashboard</span></a>
</li>
<li>
<a class="inactive" href="#"><i class="fa fa-cog fa-fw"></i><span lang="en" data-lang-token="main_menu_configuration_token">Configuration</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li>
<a class="inactive" href="#" id="load_confGeneral"><i class="fa fa-play-circle-o fa-fw"></i><span lang="en" data-lang-token="main_menu_general_conf_token">General</span></a>
</li>
<li>
<a class="inactive" href="#" id="load_confKodi"><i class="fa fa-play-circle-o fa-fw"></i><span lang="en" data-lang-token="main_menu_kodiwatch_token">Kodi Watch</span></a>
</li>
<li>
<a class="inactive" href="#" id="load_huebridge"><i class="fa fa-cog fa-fw"></i><span lang="en" data-lang-token="main_menu_huebridge_token">Hue Bridge</span></a>
</li>
</ul>
</li>
<li>
<a class="inactive" href="#"><i class="fa fa-lightbulb-o fa-fw"></i><span lang="en" data-lang-token="main_menu_remotecontrol_token">Remote Control</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li>
<a class="inactive" href="#" id="load_lighttest"><i class="fa fa-lightbulb-o fa-fw"></i><span lang="en" data-lang-token="main_menu_colors_token">Colors</span></a>
</li>
<li>
<a class="inactive" href="#" id="load_effects"><i class="fa fa-certificate fa-fw"></i><span lang="en" data-lang-token="main_menu_effects_token">Effects</span></a>
</li>
<li>
<a class="inactive" href="#" id="load_components"><i class="fa fa-list fa-fw"></i><span lang="en" data-lang-token="main_menu_component_token">Components</span></a>
</li>
</ul>
</li>
<li>
<a class="inactive" href="#" id="load_support"><i class="fa fa-info fa-fw"></i><span lang="en" data-lang-token="main_menu_support_token">Support</span></a>
</li>
<li>
<a class="inactive" href="#"><i class="fa fa-cog fa-fw"></i><span lang="en" data-lang-token="main_menu_system_token">System</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li>
<a class="inactive" href="#" id="load_update"><i class="fa fa-download fa-fw"></i><span lang="en" data-lang-token="main_menu_update_token">Update</span></a>
</li>
</ul>
</li>
</ul>
</div>
<!-- /.sidebar-collapse -->
</div>
<!-- /.navbar-static-side -->
</nav>
<!-- Page Content -->
<div id="page-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">Dashboard</h1>
<div class="introd">
<h4 lang="en" data-lang-token="dashboard_label_intro">The dashboard give you a quick overview about the status of Hyperion and show you the latest news of the Hyperion Blog.</h4>
</div>
<hr>
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#myModal">Change JSON port</button>
<hr>
<div class="col-lg-4">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-info-circle fa-fw"></i>
<span lang="en" data-lang-token="dashboard_label_infobox_title">Information</span>
</div>
<div class="panel-body">
<table class="table borderless">
<tbody>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_currenthyp">Your Hyperion version:</td>
<td id="currentversion">unknown</td>
</tr>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_latesthyp">Latest version:</td>
<td id="latestversion">unknown</td>
</tr>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_leddevice">LED type:</td>
<td id="dash_leddevice">unknown</td>
</tr>
<tr>
<td lang="en" data-lang-token="dashboard_label_infobox_device">Device:</td>
<td id="dash_systeminfo"></td>
</tr>
</tbody>
</table>
<hr>
<span id="versioninforesult"></span>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-eye fa-fw"></i>
<span lang="en" data-lang-token="dashboard_label_componentbox_title">Components status</span>
</div>
<div class="panel-body">
<table class="table">
<thead>
<tr>
<th>Component</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kodi Watch</td>
<td><i class="fa fa-circle component-on"></i></td>
</tr>
<tr>
<td>Blackborder</td>
<td><i class="fa fa-circle component-off"></i></td>
</tr>
<tr>
<td>Booteffect</td>
<td><i class="fa fa-circle component-off"></i></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-newspaper-o fa-fw"></i>
<span lang="en" data-lang-token="dashboard_label_newsbox_title">Latest blog posts</span>
</div>
<div class="panel-body">
</div>
</div>
</div>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</div>
<!-- /#page-wrapper -->
</div>
<!-- /#wrapper -->
<!-- Modal -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Modal Test and port changer</h4>
</div>
<div class="modal-body">
<p>Input the JSON port you want to connect and press reload</p>
<form class="form-inline">
<div class="form-group">
<label for="port">Your JSON Port:</label>
<input type="number" min="100" max="65535" value="19444" class="form-control" id="json_port">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" onclick="button_reloaddata()" class="btn btn-primary" data-dismiss="modal">Reload!</button>
</div>
</div>
</div>
</div>
<!-- /.navbar-header -->
<div id="con_error_modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content" id="con_error_modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Oh noes! Something went wrong!</h4>
</div>
<div class="modal-body">
<p>We are sorry, the json port was not found. Just try another one!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Okay!</button>
</div>
</div>
</div>
</div>
<ul class="nav navbar-top-links navbar-right">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-globe fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-language">
<li>
<a href="#lang-en" onclick="window.lang.change('en'); return false;"><span lang="en" class="flag-icon flag-icon-gb" data-lang-token="general_speech_en">&emsp; English</span></a>
</li>
<li class="divider"></li>
<li>
<a href="#lang-de" onclick="window.lang.change('de'); return false;"><span lang="en" class="flag-icon flag-icon-de" data-lang-token="general_speech_de">&emsp; German</span></a>
</li>
</ul>
<!-- /.dropdown-language -->
</li>
<!-- /.dropdown -->
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-bell fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-alerts">
<li>
<a href="#">
<div>
<i class="fa fa-comment fa-fw"></i> New Comment
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-twitter fa-fw"></i> 3 New Followers
<span class="pull-right text-muted small">12 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-envelope fa-fw"></i> Message Sent
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-tasks fa-fw"></i> New Task
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="#">
<div>
<i class="fa fa-upload fa-fw"></i> Server Rebooted
<span class="pull-right text-muted small">4 minutes ago</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a class="text-center" href="#">
<strong>See All Alerts</strong>
<i class="fa fa-angle-right"></i>
</a>
</li>
</ul>
<!-- /.dropdown-alerts -->
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#"><i class="fa fa-user fa-fw"></i>User Profile</a>
</li>
<li><a href="#"><i class="fa fa-gear fa-fw"></i>Settings</a>
</li>
<li class="divider"></li>
<li><a href="login.html"><i class="fa fa-sign-out fa-fw"></i>Logout</a>
</li>
</ul>
<!-- /.dropdown-user -->
</li>
<!-- /.dropdown -->
</ul>
<!-- /.navbar-top-left -->
<!-- Bootstrap Core JavaScript -->
<script src="js/lib/bootstrap.min.js"></script>
<div 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>
<li>
<a class="inactive"><i class="fa fa-cog fa-fw"></i><span lang="en" data-lang-token="main_menu_configuration_token">Configuration</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li> <a class="inactive" id="load_confGeneral"><i class="fa fa-play-circle-o fa-fw"></i><span lang="en" data-lang-token="main_menu_general_conf_token">General</span></a> </li>
<li> <a class="inactive" id="load_confKodi"><i class="fa fa-play-circle-o fa-fw"></i><span lang="en" data-lang-token="main_menu_kodiwatch_token">Kodi Watch</span></a> </li>
<li> <a class="inactive" id="load_huebridge"><i class="fa fa-cog fa-fw"></i><span lang="en" data-lang-token="main_menu_huebridge_token">Hue Bridge</span></a> </li>
</ul>
</li>
<li>
<a class="inactive" href="#"><i class="fa fa-lightbulb-o fa-fw"></i><span lang="en" data-lang-token="main_menu_remotecontrol_token">Remote Control</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li> <a class="inactive" id="load_lighttest"><i class="fa fa-lightbulb-o fa-fw"></i><span lang="en" data-lang-token="main_menu_colors_token">Colors</span></a> </li>
<li> <a class="inactive" id="load_effects"><i class="fa fa-certificate fa-fw"></i><span lang="en" data-lang-token="main_menu_effects_token">Effects</span></a> </li>
<li> <a class="inactive" id="load_components"><i class="fa fa-list fa-fw"></i><span lang="en" data-lang-token="main_menu_component_token">Components</span></a> </li>
<li> <a class="inactive" id="load_input_selection"><i class="fa fa-random fa-fw"></i><span lang="en" data-lang-token="main_menu_input_selection_token">Input Selection</span></a> </li>
</ul>
</li>
<li> <a class="inactive" id="load_support"><i class="fa fa-info fa-fw"></i><span lang="en" data-lang-token="main_menu_support_token">Support</span></a> </li>
<li>
<a class="inactive"><i class="fa fa-cog fa-fw"></i><span lang="en" data-lang-token="main_menu_system_token">System</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li> <a class="inactive" id="load_update"><i class="fa fa-download fa-fw"></i><span lang="en" data-lang-token="main_menu_update_token">Update</span></a> </li>
</ul>
</li>
</ul>
</div>
<!-- /.sidebar-collapse -->
</div>
<!-- /.navbar-static-side -->
</nav>
<!-- Metis Menu Plugin JavaScript -->
<script src="js/lib/metisMenu.min.js"></script>
<!-- Page Content -->
<div id="page-wrapper" />
<!-- Custom Theme JavaScript -->
<script src="js/lib/sb-admin-2.js"></script>
</div>
<!-- /#wrapper -->
<div id="error_dialog" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title" />
</div>
<div class="modal-body" />
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Okay!</button>
</div>
</div>
</div>
<!-- Bootstrap Core JavaScript -->
<script src="js/lib/bootstrap.min.js"></script>
<!-- Metis Menu Plugin JavaScript -->
<script src="js/lib/metisMenu.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/lib/sb-admin-2.js"></script>
<script>
$(document).ready( function() {
$("#load_lighttest").on("click", function() {
$("#page-wrapper").load("lighttest.html");
});
$("#load_effects").on("click", function() {
$("#page-wrapper").load("effects.html");
initWebSocket();
bindNavToContent("#load_dashboard","dashboard",true);
bindNavToContent("#load_lighttest","lighttest");
bindNavToContent("#load_effects","effects");
bindNavToContent("#load_components","remote_components");
bindNavToContent("#load_input_selection","input_selection");
bindNavToContent("#load_huebridge","huebridge");
bindNavToContent("#load_support","support");
bindNavToContent("#load_confKodi","kodiconf");
bindNavToContent("#load_update","update");
bindNavToContent("#load_confGeneral","generalconf");
//Change all Checkboxes to Switches
$("[type='checkbox']").bootstrapSwitch();
$(hyperion).on("open",function(event){
requestServerInfo();
});
$("#load_components").on("click", function() {
$("#page-wrapper").load("remote_components.html");
});
$("#load_huebridge").on("click", function() {
$("#page-wrapper").load("huebridge.html");
});
$("#load_support").on("click", function() {
$("#page-wrapper").load("support.html");
});
$("#load_confKodi").on("click", function() {
$("#page-wrapper").load("kodiconf.html");
});
$("#load_update").on("click", function() {
$("#page-wrapper").load("update.html");
});
$("#load_confGeneral").on("click", function() {
$("#page-wrapper").load("generalconf.html");
});
//Change all Checkboxes to Switches
$("[type='checkbox']").bootstrapSwitch();
loaddata();
$(hyperion).on("cmd-serverinfo",function(event){
parsedServerInfoJSON = event.response;
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);
});
$(function(){
var sidebar = $('#side-menu'); // cache sidebar to a variable for performance
sidebar.delegate('a.inactive','click',function(){
sidebar.find('.active').toggleClass('active inactive');
$(this).toggleClass('active inactive');
$.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('<div lang="en" data-lang-token="dashboard_message_infobox_updatewarning" style="margin:0px;" class="alert alert-warning">A newer version of Hyperion is available!</div>');
}
else
{
$('#versioninforesult').html('<div lang="en" data-lang-token="dashboard_message_infobox_updatesuccess" style="margin:0px;" class="alert alert-success">You run the latest version of Hyperion.</div>');
}
});
}); // end cmd-serverinfo
$(hyperion).on("error",function(event){
$('#error_dialog .modal-title').html("error");
$('#error_dialog .modal-body').html(event.reason);
$('#error_dialog').modal('show');
});
});
$(function(){
var sidebar = $('#side-menu'); // cache sidebar to a variable for performance
sidebar.delegate('a.inactive','click',function(){
sidebar.find('.active').toggleClass('active inactive');
$(this).toggleClass('active inactive');
});
});
</script>
</body>

View File

@ -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('<div lang="en" data-lang-token="dashboard_message_infobox_updatewarning" style="margin:0px;" class="alert alert-warning">A newer version of Hyperion is available!</div>');
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. <br /> 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('<div lang="en" data-lang-token="dashboard_message_infobox_updatesuccess" style="margin:0px;" class="alert alert-success">You run the latest version of Hyperion.</div>');
$(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+'}');
}
};
};

View File

@ -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");
}
}

View File

@ -1,40 +0,0 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_component_token">Effects</h1>
<div class="introd">
<h4 lang="en" data-lang-token="remote_components_intro">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.</h4>
</div>
<hr>
<div class="col-lg-12" id="componentsbutton">
</div>
</div>
</div>
</div>
<script>
new Enum('SMOOTHING', 'BLACKBORDER', 'KODICHECKER', 'FORWARDER', 'UDPLISTENER', 'BOBLIGHT_SERVER','GRABBER');
function Enum() {
for (var i in arguments) {
this[arguments[i]] = i;
$('#componentsbutton').append('<button type="button" class="btn btn-success" onclick="compenable(\''+arguments[i]+'\')"><i class="fa fa-play"></i></button> '+arguments[i]+'<br />');
$('#componentsbutton').append('<button type="button" class="btn btn-danger" onclick="compdisable(\''+arguments[i]+'\')"><i class="fa fa-play"></i></button> '+arguments[i]+'<br />');
}
}
function compenable(comp){
webSocket.send('{"command":"componentstate","componentstate":{"component":"'+comp+'","state":true}}');
console.log('enable: '+comp);
}
function compdisable(comp){
webSocket.send('{"command":"componentstate","componentstate":{"component":"'+comp+'","state":false}}');
console.log('disable: '+comp);
}
</script>

View File

@ -1,98 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hyperion web control</title>
<!-- bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> -->
<link href="starter-template.css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<!-- <span class="icon-bar"></span>
<span class="icon-bar"></span>-->
</button>
<a class="navbar-brand" href="#">Hyperion</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Source Selection</a></li>
<!-- <li><a href="#about">About</a></li> -->
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div class="starter-template">
<div id="inputs"></div>
</div>
</div><!-- /.container -->
<!-- ====================================================================== -->
<script>
var webSocket = new WebSocket('ws://'+document.location.hostname+':19444');
var serverInfo;
function setSource( prio )
{
if ( prio == "auto" )
webSocket.send('{"command":"sourceselect", "auto" : true}');
else
webSocket.send('{"command":"sourceselect", "priority" : '+prio+'}');
updateButtons();
}
function updateButtons()
{
webSocket.send('{"command":"serverinfo"}');
}
webSocket.onerror = function(event) {
alert(event.data);
};
webSocket.onopen = function(event) {
updateButtons();
setInterval(function() {updateButtons();}, 3000);
};
webSocket.onmessage = function(event) {
serverInfo = JSON.parse( event.data );
var data = "";
var i;
for(i = 0; i < serverInfo.info.priorities.length; i++) {
var owner = serverInfo.info.priorities[i].owner;
var active = serverInfo.info.priorities[i].active;
var visible = serverInfo.info.priorities[i].visible;
var priority = serverInfo.info.priorities[i].priority;
var btn_type = "default";
if (active) btn_type = "warning";
if (visible) btn_type = "success";
data += '<button id="srcBtn'+i+'" type="button" class="btn btn-lg btn-'+btn_type+'" style="margin:10px;min-width:200px" title="prio '+priority+'" onclick="setSource('+priority+');">'+owner+'</button><br/>';
}
data += '<button id="srcBtn'+i+'" type="button" class="btn btn-lg btn-info" style="margin:10px;min-width:200px" onclick="setSource(\'auto\');">auto select</button><br/>';
$('#inputs').html(data);
};
</script>
</body>
</html>

View File

@ -1,8 +0,0 @@
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}

View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
print ("hello world");

View File

@ -0,0 +1,5 @@
#!/bin/sh
echo "hello world"
pwd

View File

@ -1,26 +0,0 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header" lang="en" data-lang-token="main_menu_update_token">Update</h1>
<div class="introd">
<h4 lang="en" data-lang-token="update_label_intro">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</h4>
</div>
<hr>
<h4 id="update_currver"></h4>
<hr>
<div class="col-lg-12" id="versionlist">
</div>
</div>
</div>
</div>
<script type="text/javascript">
for (key in parsedUpdateJSON)
{
$('#versionlist').append('<div class="col-lg-6"><div class="panel panel-default"><div class="panel-heading"><i class="fa fa-television fa-fw"></i>Hyperion V'+parsedUpdateJSON[key].versionnr+'</div><div class="panel-body"><p><span style="font-weight:bold;" lang="en" data-lang-token="update_label_type">Type:</span> '+ parsedUpdateJSON[key].channel +'</p><p><span style="font-weight:bold;" lang="en" data-lang-token="update_label_description">Description:</span> '+parsedUpdateJSON[key].versiondesc+'</p><hr><a class="btn btn-primary" href="'+ parsedUpdateJSON[key].versionchangelog +'" target="_blank"><i class="fa fa-list fa-fw"></i><span style="font-weight:bold;" lang="en" data-lang-token="update_button_changelog">Full changelog</span></a><button type="button" class="btn btn-warning pull-right"><i class="fa fa-download fa-fw"></i><span lang="en" data-lang-token="update_button_install">Install</span></button></div></div></div>');
}
$('#update_currver').append(currentVersion);
</script>

View File

@ -0,0 +1,8 @@
#include <string>
namespace FileUtils {
std::string getBaseName( std::string sourceFile);
std::string command_exec(const char* cmd);
};

View File

@ -12,6 +12,7 @@
// hyperion util includes
#include <utils/jsonschema/JsonSchemaChecker.h>
#include <utils/FileUtils.h>
// effect engine includes
#include <effectengine/EffectEngine.h>
@ -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;

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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

View File

@ -0,0 +1,35 @@
#include <utils/FileUtils.h>
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <QFileInfo>
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<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

@ -1,4 +1,5 @@
#include "utils/Logger.h"
#include <utils/Logger.h>
#include <utils/FileUtils.h>
#include <iostream>
#include <algorithm>
@ -7,11 +8,6 @@
#include <QFileInfo>
#include <QString>
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

View File

@ -1,5 +1,6 @@
#include "HyperionConfig.h"
#include "utils/Profiler.h"
#include <utils/Profiler.h>
#include <utils/FileUtils.h>
#include <QFileInfo>
#include <QString>
@ -15,21 +16,11 @@ static unsigned int blockCounter = 0;
static std::map<std::string,StopWatchItem> 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
{

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 == "advanced")
; // nothing to do.
else
{

View File

@ -5,11 +5,13 @@
#include "CgiHandler.h"
#include "QtHttpHeader.h"
#include <utils/FileUtils.h>
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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -33,7 +33,7 @@ private:
QtHttpServer * _server;
QMimeDatabase * _mimeDb;
CgiHandler _cgi;
Logger * _log;
Logger * _log;
};