mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Merge pull request #578 from Paulchen-Panther/api_auth
Token Management, Database, ...
This commit is contained in:
commit
c62ea87ab7
76
.codedocs
Normal file
76
.codedocs
Normal file
@ -0,0 +1,76 @@
|
||||
# Hyperion.NG .codedocs Configuration File
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# CodeDocs Configuration
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Include the Doxygen configuration from another file.
|
||||
# The file must be a relative path with respect to the root of the repository.
|
||||
|
||||
DOXYFILE =
|
||||
|
||||
# Specify external repository to link documentation with.
|
||||
# This is similar to Doxygen's TAGFILES option, but will automatically link to
|
||||
# tags of other repositories already using CodeDocs. List each repository to
|
||||
# link with by giving its location in the form of owner/repository.
|
||||
# For example:
|
||||
# TAGLINKS = doxygen/doxygen CodeDocs/osg
|
||||
# Note: these repositories must already be built on CodeDocs.
|
||||
|
||||
TAGLINKS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Doxygen Configuration
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Doxygen configuration may also be placed in this file.
|
||||
# Currently, the following Doxygen configuration options are available. Refer
|
||||
# to http://doxygen.org/manual/config.html for detailed explanation of the
|
||||
# options. To request support for more options, contact support@codedocs.xyz.
|
||||
#
|
||||
# ABBREVIATE_BRIEF =
|
||||
# ALIASES =
|
||||
# ALPHABETICAL_INDEX =
|
||||
# ALWAYS_DETAILED_SEC =
|
||||
# CASE_SENSE_NAMES =
|
||||
# CLASS_DIAGRAMS =
|
||||
# DISABLE_INDEX =
|
||||
# DISTRIBUTE_GROUP_DOC =
|
||||
# EXAMPLE_PATH =
|
||||
EXCLUDE = .ci/ \
|
||||
assets/ \
|
||||
bin/
|
||||
config/ \
|
||||
effects/ \
|
||||
test/ \
|
||||
# EXCLUDE_PATTERNS =
|
||||
# EXCLUDE_SYMBOLS =
|
||||
# EXTENSION_MAPPING =
|
||||
# EXTRACT_LOCAL_CLASSES =
|
||||
# FILE_PATTERNS =
|
||||
# GENERATE_TAGFILE =
|
||||
# GENERATE_TREEVIEW =
|
||||
# HIDE_COMPOUND_REFERENCE =
|
||||
# HIDE_SCOPE_NAMES =
|
||||
# HIDE_UNDOC_CLASSES =
|
||||
# HIDE_UNDOC_MEMBERS =
|
||||
# HTML_TIMESTAMP =
|
||||
# INLINE_GROUPED_CLASSES =
|
||||
# INPUT_ENCODING =
|
||||
# INTERNAL_DOCS =
|
||||
# OPTIMIZE_OUTPUT_FOR_C =
|
||||
PROJECT_BRIEF = "The successor to Hyperion aka Hyperion Next Generation"
|
||||
PROJECT_NAME = "Hyperion.NG"
|
||||
# PROJECT_NUMBER =
|
||||
# SHORT_NAMES =
|
||||
# SHOW_FILES =
|
||||
# SHOW_INCLUDE_FILES =
|
||||
# SHOW_NAMESPACES =
|
||||
# SORT_BRIEF_DOCS =
|
||||
# SORT_BY_SCOPE_NAME =
|
||||
# SORT_MEMBER_DOCS =
|
||||
# STRICT_PROTO_MATCHING =
|
||||
# TYPEDEF_HIDES_STRUCT =
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
# VERBATIM_HEADERS =
|
||||
#
|
@ -311,7 +311,7 @@ IF ( CMAKE_CROSSCOMPILING )
|
||||
ENDIF()
|
||||
|
||||
SET(QT_MIN_VERSION "5.5.0")
|
||||
find_package(Qt5 COMPONENTS Core Gui Network SerialPort REQUIRED)
|
||||
find_package(Qt5 COMPONENTS Core Gui Network SerialPort Sql REQUIRED)
|
||||
message( STATUS "Found Qt Version: ${Qt5Core_VERSION}" )
|
||||
IF ( "${Qt5Core_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" )
|
||||
message( FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}" )
|
||||
@ -354,9 +354,6 @@ endif ()
|
||||
# Add resources directory
|
||||
add_subdirectory(resources)
|
||||
|
||||
# Add the doxygen generation directory
|
||||
add_subdirectory(doc)
|
||||
|
||||
# remove generated files on make cleaan too
|
||||
LIST( APPEND GENERATED_QRC
|
||||
${CMAKE_BINARY_DIR}/EffectEngine.qrc
|
||||
|
@ -1,7 +1,7 @@
|
||||
# With Docker
|
||||
If you are using [Docker](https://www.docker.com/), you can compile Hyperion inside a docker container. This keeps your system clean and with a simple script it's easy to use. Supported is also cross compiling for Raspberry Pi (Debian Stretch or higher). To compile Hyperion just execute one of the following commands.
|
||||
|
||||
The compiled binaries and packages will be available at the deploy folder next to the script
|
||||
The compiled binaries and packages will be available at the deploy folder next to the script.<br/>
|
||||
Note: call the script with `./docker-compile.sh -h` for more options
|
||||
|
||||
## Native compiling on Raspberry Pi
|
||||
@ -40,7 +40,7 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libqt5sql5-sqlite
|
||||
```
|
||||
|
||||
**on RPI you need the videocore IV headers**
|
||||
|
@ -4,6 +4,7 @@
|
||||
[![Azure-Pipeline](https://dev.azure.com/Hyperion-Project/Hyperion.NG/_apis/build/status/Hyperion.NG?branchName=master)](https://dev.azure.com/Hyperion-Project/Hyperion.NG/_build/latest?definitionId=7&branchName=master)
|
||||
[![Travis-CI](https://travis-ci.org/hyperion-project/hyperion.ng.svg?branch=master)](https://travis-ci.org/hyperion-project/hyperion.ng)
|
||||
[![LGTM](https://img.shields.io/lgtm/alerts/g/hyperion-project/hyperion.ng.svg)](https://lgtm.com/projects/g/hyperion-project/hyperion.ng/alerts/)
|
||||
[![Documentation](https://codedocs.xyz/hyperion-project/hyperion.ng.svg)](https://codedocs.xyz/hyperion-project/hyperion.ng/)
|
||||
|
||||
## About Hyperion
|
||||
|
||||
|
@ -4,6 +4,27 @@
|
||||
<h3 class="page-header"><i class="fa fa-wrench fa-fw"></i><span data-i18n="conf_general_label_title">General</span></h3>
|
||||
<div class="row" id="conf_cont"></div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6" id="inst_desc">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-plus fa-fw"></i><span data-i18n="conf_general_inst_title"></span></div>
|
||||
<div class="panel-body">
|
||||
<div id="inst_desc_cont"></div>
|
||||
<div id="itable"></div>
|
||||
<div style="margin: 30px 0; border-top:1px solid #d0d0d0"></div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<p data-i18n="conf_general_inst_name_title" style="font-weight:bold"></p>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<input class="form-control" id="inst_name" type="text"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary" id="btn_create_inst" data-i18n="conf_general_createInst_btn" disabled>Create New Token</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6" id="conf_imp">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-wrench fa-fw"></i><span data-i18n="conf_general_impexp_title"></span></div>
|
||||
|
@ -2,9 +2,36 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h3 class="page-header"><i class="fa fa-sitemap fa-fw"></i><span data-i18n="main_menu_network_conf_token">Network Services</span></h3>
|
||||
<div id="conf_cont"></div>
|
||||
<div id="conf_cont">
|
||||
<div class="row" id="conf_cont_tok">
|
||||
<div class="col-lg-6" id="tok_desc">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-key fa-fw"></i><span data-i18n="conf_network_tok_title"></span></div>
|
||||
<div class="panel-body">
|
||||
<div id="tok_desc_cont"></div>
|
||||
<div id="tktable"></div>
|
||||
<div style="margin: 30px 0; border-top:1px solid #d0d0d0"></div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<p data-i18n="conf_network_tok_comment_title" style="font-weight:bold"></p>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<input class="form-control" id="tok_comment" type="text"></input>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<span id="tok_chars_needed"><br /></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-footer" style="text-align: right;">
|
||||
<button class="btn btn-primary" id="btn_create_tok" data-i18n="conf_network_createToken_btn" disabled>Create New Token</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/content_network.js"></script>
|
||||
<script src="/js/content_network.js"></script>
|
@ -27,7 +27,7 @@ var connectionLost = false;
|
||||
var connectionTimer;
|
||||
var count = 1;
|
||||
var reconnectInterval = 4000;
|
||||
var connURL = location.protocol+"//"+location.hostname+":"+window.jsonPort+location.pathname+location.hash;
|
||||
var connURL = window.location.protocol+"//"+window.location.hostname+":"+window.jsonPort+window.location.pathname+window.location.hash;
|
||||
|
||||
function tryReconnect()
|
||||
{
|
||||
@ -41,7 +41,14 @@ function tryReconnect()
|
||||
|
||||
$.ajax({ url: connURL }).done(function(data) {
|
||||
window.clearInterval(connectionTimer);
|
||||
window.location.href = connURL;
|
||||
if(reconnectInterval <= 2000){
|
||||
let url = connURL;
|
||||
if (window.connURL.includes("#"))
|
||||
url = window.connURL.slice(0,window.connURL.indexOf("#"));
|
||||
window.location.replace(url);
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
.fail( function( jqXHR, textStatus ) {
|
||||
count++;
|
||||
@ -55,7 +62,7 @@ function connectionLostAction()
|
||||
{
|
||||
connectionLost = true;
|
||||
// if we changed the webui port we connect faster
|
||||
if(window.fastReconnect){
|
||||
if(window.fastReconnect) {
|
||||
window.fastReconnect = false;
|
||||
reconnectInterval = 2000;
|
||||
}
|
||||
|
@ -68,13 +68,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-6 col-xxl-5">
|
||||
<div class="panel panel-default" >
|
||||
<div class="panel-heading"><i class="fa fa-wifi fa-fw"></i><span data-i18n="remote_adjustment_label"></span></div>
|
||||
<div class="panel-body" id="adjust_content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-6 col-xxl-5">
|
||||
<div class="panel panel-default" >
|
||||
<div class="panel-heading"><i class="fa fa-wifi fa-fw"></i><span data-i18n="remote_videoMode_label"></span></div>
|
||||
@ -83,6 +76,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-6 col-xxl-5">
|
||||
<div class="panel panel-default" >
|
||||
<div class="panel-heading"><i class="fa fa-wifi fa-fw"></i><span data-i18n="remote_adjustment_label"></span></div>
|
||||
<div class="panel-body" id="adjust_content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/content_remote.js" ></script>
|
||||
|
@ -10,11 +10,11 @@ body{font-family:Roboto,"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15
|
||||
padding-bottom:50px;
|
||||
}
|
||||
body{
|
||||
overflow-y: scroll;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.btn{margin: 2px 0;}
|
||||
/*
|
||||
#page-wrapper a[target=_blank]::after {
|
||||
#page-wrapper a[target=_blank]::after {
|
||||
content:"\f08e";
|
||||
position:relative;
|
||||
font:normal normal normal 10px/1 FontAwesome;
|
||||
@ -109,7 +109,7 @@ table label{margin:0}
|
||||
.colorpicker-2x .colorpicker-saturation {width: 200px;height: 200px;}
|
||||
.colorpicker-2x .colorpicker-hue,.colorpicker-2x .colorpicker-alpha {width: 30px;height: 200px;}
|
||||
.colorpicker-2x .colorpicker-color,.colorpicker-2x .colorpicker-color div {height: 30px;}
|
||||
|
||||
|
||||
/*Hint*/
|
||||
.info-hint{
|
||||
background-color:rgb(236,236,236);
|
||||
@ -123,7 +123,7 @@ table label{margin:0}
|
||||
box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.25);
|
||||
font-size:97%;
|
||||
}
|
||||
|
||||
|
||||
/*Support page*/
|
||||
.unlink,.unlink:hover{color:#333;text-decoration:none;}
|
||||
.support-container ul{padding-left:0px;list-style-type: none;}
|
||||
@ -196,6 +196,10 @@ table label{margin:0}
|
||||
background-color:#d1322e;
|
||||
}
|
||||
|
||||
.modal-icon-edit{
|
||||
background-color:#3579b6;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background-image: url('/img/hyperion/hyperionwhitelogo.png');
|
||||
background-repeat: no-repeat;
|
||||
|
@ -16,7 +16,6 @@
|
||||
"general_comp_BLACKBORDER": "Detekce černýh pruh",
|
||||
"general_comp_KODICHECKER": "Kodi Snímač",
|
||||
"general_comp_FORWARDER": "Zasílat",
|
||||
"general_comp_UDPLISTENER": "UDP server",
|
||||
"general_comp_BOBLIGHTSERVER": "Boblight Server",
|
||||
"general_comp_GRABBER": "Platforma zachycení",
|
||||
"general_comp_V4L": "USB zachycení",
|
||||
@ -142,7 +141,6 @@
|
||||
"conf_network_json_intro": "The JSON-RPC-Port této instance Hyperion, který se používá pro dálkové ovládání.",
|
||||
"conf_network_proto_intro": "PROTO-Port této instance Hyperion, používaný pro obrazové proudy (Hyperion ScreenCap, Kodi Addon, ...)",
|
||||
"conf_network_bobl_intro": "Přijímač pro Boblight",
|
||||
"conf_network_udpl_intro": "Přijímač pro UDP",
|
||||
"conf_network_forw_intro": "Předat všechny vstupy na druhou instanci Hyperion, která by mohla být řízena jiným LED ovladačem",
|
||||
"conf_kodi_label_title": "Kodi Snímač",
|
||||
"conf_kodi_intro": "Kodi Snímač umožňuje zapnout a vypnout obrazovku v závislosti na stavu Kodi. Nastavení není omezeno na stejný stroj, můžete pozorovat i Kodi na libovolně jiném zařízení v síti.",
|
||||
@ -518,13 +516,6 @@
|
||||
"edt_conf_js_heading_title": "JSON Server",
|
||||
"edt_conf_ps_heading_title": "PROTO Server",
|
||||
"edt_conf_bobls_heading_title": "Boblight Server",
|
||||
"edt_conf_udpl_heading_title": "UDP Listener",
|
||||
"edt_conf_udpl_address_title": "Adresa",
|
||||
"edt_conf_udpl_address_expl": "Adresa, kde jsou přijaty balíčky UDP.",
|
||||
"edt_conf_udpl_timeout_title": "Čas vypršel",
|
||||
"edt_conf_udpl_timeout_expl": "Pokud pro danou dobu nejsou přijaty žádné pakety, bude součást (soft) zakázána.",
|
||||
"edt_conf_udpl_shared_title": "Sdíled",
|
||||
"edt_conf_udpl_shared_expl": "Sdíleny všemi instancemi Hyperion.",
|
||||
"edt_conf_webc_heading_title": "Webová konfigurace",
|
||||
"edt_conf_webc_docroot_title": "Document Root",
|
||||
"edt_conf_webc_docroot_expl": "Místní kořenová cesta webového rozhraní (pouze pro vývojáře webui)",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,6 @@
|
||||
"general_comp_SMOOTHING" : "Smoothing",
|
||||
"general_comp_BLACKBORDER" : "Blackbar Detection",
|
||||
"general_comp_FORWARDER" : "Forwarder",
|
||||
"general_comp_UDPLISTENER" : "UDP Listener",
|
||||
"general_comp_BOBLIGHTSERVER" : "Boblight Server",
|
||||
"general_comp_FLATBUFSERVER" : "Flatbuffers Server",
|
||||
"general_comp_PROTOSERVER" : "Protocol Buffers Server",
|
||||
@ -31,10 +30,13 @@
|
||||
"general_button_savesettings" : "Save settings",
|
||||
"general_btn_yes" : "Yes",
|
||||
"general_btn_ok" : "OK",
|
||||
"general_btn_start" : "Start",
|
||||
"general_btn_stop" : "Stop",
|
||||
"general_btn_cancel" : "Cancel",
|
||||
"general_btn_delete" : "Delete",
|
||||
"general_btn_rename" : "Rename",
|
||||
"general_btn_continue" : "Continue",
|
||||
"general_btn_save" : "Save",
|
||||
"general_btn_saverestart" : "Save and restart",
|
||||
"general_btn_saveandreload" : "Save and reload",
|
||||
"general_btn_restarthyperion" : "Restart Hyperion",
|
||||
"general_btn_off" : "Off",
|
||||
@ -66,10 +68,12 @@
|
||||
"dashboard_newsbox_readmore" : "Read more",
|
||||
"dashboard_alert_message_confedit_t" : "Configuration modified",
|
||||
"dashboard_alert_message_confedit" : "Your Hyperion configuration has been modified. To apply it, restart Hyperion.",
|
||||
"dashboard_alert_message_disabled_t" : "Hyperion disabled",
|
||||
"dashboard_alert_message_disabled" : "Hyperion is currently disabled! To use it again, enable it at the dashboard.",
|
||||
"dashboard_alert_message_disabled_t" : "LED hardware instance disabled",
|
||||
"dashboard_alert_message_disabled" : "This instance is currently disabled! To use it again, enable it at the dashboard.",
|
||||
"dashboard_alert_message_confsave_success_t" : "Configuration saved",
|
||||
"dashboard_alert_message_confsave_success" : "Your Hyperion configuration has been saved successfully. Your changes are now active.",
|
||||
"dashboard_message_global_setting_t": "Instance independent setting",
|
||||
"dashboard_message_global_setting": "The settings on this page are not depending on a specific instance. Changes will be stored globally for all instances.",
|
||||
"main_menu_dashboard_token" : "Dashboard",
|
||||
"main_menu_configuration_token" : "Configuration",
|
||||
"main_menu_general_conf_token" : "General",
|
||||
@ -99,6 +103,15 @@
|
||||
"conf_general_impexp_l2" : "Export a configuration by clicking on \"Export\". Your browser starts a download.",
|
||||
"conf_general_impexp_impbtn" : "Import",
|
||||
"conf_general_impexp_expbtn" : "Export",
|
||||
"conf_general_inst_title" : "LED Hardware Instance Management",
|
||||
"conf_general_inst_desc" : "Use different LED hardware at the same time. Each instance runs independent of each other which allows different LED layouts and calibration settings. Running instances are available at the top icon bar",
|
||||
"conf_general_inst_namehead" : "Instance name",
|
||||
"conf_general_inst_actionhead" : "Action",
|
||||
"conf_general_inst_name_title" : "New Instance name",
|
||||
"conf_general_createInst_btn" : "Create Instance",
|
||||
"conf_general_inst_renreq_t" : "Enter a new name for your instance in the field below.",
|
||||
"conf_general_inst_delreq_h" : "Delete LED Hardware instance",
|
||||
"conf_general_inst_delreq_t" : "Are you sure that you want to delete instance \"$1\"? All settings will be deleted too.",
|
||||
"conf_helptable_option" : "Option",
|
||||
"conf_helptable_expl" : "Explanation",
|
||||
"conf_effect_path_intro" : "Load effects from the defined paths. Additional you can disable single effects by name to hide them from all effect lists.",
|
||||
@ -167,12 +180,22 @@
|
||||
"conf_colors_color_intro" : "Create one or more calibration profiles, adjust each color, brightness, linearization and more.",
|
||||
"conf_colors_smoothing_intro" : "Smoothing flattens color/brightness changes to reduce annoying distraction.",
|
||||
"conf_colors_blackborder_intro" : "Skip black bars wherever they are. Each mode use another detection algorithm which is tuned for special situations. Higher the threshold if it doesn't work for you.",
|
||||
"conf_network_json_intro" : "The JSON-RPC-Port of this Hyperion instance, used for remote control.",
|
||||
"conf_network_net_intro" : "Network related settings which are applied to all network services.",
|
||||
"conf_network_json_intro" : "The JSON-RPC-Port of all Hyperion instances, used for remote control.",
|
||||
"conf_network_bobl_intro" : "Receiver for Boblight",
|
||||
"conf_network_udpl_intro" : "Receiver for UDP",
|
||||
"conf_network_fbs_intro" : "Google Flatbuffers Receiver. Used for fast image transmission.",
|
||||
"conf_network_proto_intro" : "The PROTO-Port of this Hyperion instance, used for picture streams (HyperionScreenCap, Kodi Addon, ...)",
|
||||
"conf_network_proto_intro" : "The PROTO-Port of all Hyperion instances, used for picture streams (HyperionScreenCap, Kodi Addon, Android Hyperion Grabber, ...)",
|
||||
"conf_network_forw_intro" : "Forward all input to a second Hyperion instance which could be driven with another led controller",
|
||||
"conf_network_tok_title" : "Token Management",
|
||||
"conf_network_tok_desc" : "Tokens grant other applications access to the Hyperion API, an application can request a token where you need to accept it or you create them on your own below. These tokens are just required when \"API Authorization\" is enabled in network settings.",
|
||||
"conf_network_tok_cidhead" : "Description",
|
||||
"conf_network_tok_lastuse" : "Last use",
|
||||
"conf_network_tok_comment_title" : "Token description",
|
||||
"conf_network_tok_chars_needed" : "more characters needed",
|
||||
"conf_network_createToken_btn" : "Create Token",
|
||||
"conf_network_tok_diaTitle" : "New Token created!",
|
||||
"conf_network_tok_diaMsg" : "Here is your new token which can be used to grant an application access to the Hyperion API. For security reasons you can't view it again so use/note it now.",
|
||||
"conf_network_tok_intro" : "Here you can create and delete tokens for API authentification. Created tokens will only be deisplayed once.",
|
||||
"conf_logging_label_intro" : "Area to check log messages, depending on loglevel setting you see more or less information.",
|
||||
"conf_logging_btn_pbupload" : "Upload report for support request",
|
||||
"conf_logging_btn_autoscroll" : "Auto scrolling",
|
||||
@ -575,6 +598,16 @@
|
||||
"edt_conf_fw_proto_title" : "List of proto clients",
|
||||
"edt_conf_fw_proto_expl" : "One proto target per line. Contains IP:PORT (Example: 127.0.0.1:19401)",
|
||||
"edt_conf_fw_proto_itemtitle" : "Proto target",
|
||||
"edt_conf_net_heading_title" : "Network",
|
||||
"edt_conf_net_internetAccessAPI_title":"Internet API Access",
|
||||
"edt_conf_net_internetAccessAPI_expl":"Allow access to the Hyperion API/Webinterface from the internet, disable for higher security.",
|
||||
"edt_conf_net_ipWhitelist_title":"Whitelisted IP's",
|
||||
"edt_conf_net_ipWhitelist_expl":"You can whitelist IP addresses instead allowing all connections from internet to connect to the Hyperion API/Webinterface.",
|
||||
"edt_conf_net_ip_itemtitle":"IP",
|
||||
"edt_conf_net_apiAuth_title":"API Authentication",
|
||||
"edt_conf_net_apiAuth_expl":"Enforce all applications that use the Hyperion API to authenticate themself against Hyperion (Exception see \"Local API Authentication\"). Higher security, as you control the access and revoke it at any time.",
|
||||
"edt_conf_net_localApiAuth_title" : "Local API Authentication",
|
||||
"edt_conf_net_localApiAuth_expl" : "When enabled, connections from your home network needs to authenticate themself against Hyperion too.",
|
||||
"edt_conf_js_heading_title" : "JSON Server",
|
||||
"edt_conf_fbs_heading_title" : "Flatbuffers Server",
|
||||
"edt_conf_fbs_timeout_title" : "Timeout",
|
||||
@ -583,13 +616,6 @@
|
||||
"edt_conf_pbs_timeout_title" : "Timeout",
|
||||
"edt_conf_pbs_timeout_expl" : "If no data are received for the given period, the component will be (soft) disabled.",
|
||||
"edt_conf_bobls_heading_title" : "Boblight Server",
|
||||
"edt_conf_udpl_heading_title" : "UDP Listener",
|
||||
"edt_conf_udpl_address_title" : "Address",
|
||||
"edt_conf_udpl_address_expl" : "The address where UDP packages are accepted.",
|
||||
"edt_conf_udpl_timeout_title" : "Timeout",
|
||||
"edt_conf_udpl_timeout_expl" : "If no packages are received for the given period, the component will be (soft) disabled.",
|
||||
"edt_conf_udpl_shared_title" : "Shared",
|
||||
"edt_conf_udpl_shared_expl" : "Shared across all Hyperion instances.",
|
||||
"edt_conf_webc_heading_title" : "Web Configuration",
|
||||
"edt_conf_webc_docroot_title" : "Document Root",
|
||||
"edt_conf_webc_docroot_expl" : "Local webinterface root path (just for webui developer)",
|
||||
|
@ -16,7 +16,6 @@
|
||||
"general_comp_BLACKBORDER": "Detección de bordes negros",
|
||||
"general_comp_KODICHECKER": "Observador Kodi",
|
||||
"general_comp_FORWARDER": "JSON/PROTO Progresivo",
|
||||
"general_comp_UDPLISTENER": "Oyente UDP",
|
||||
"general_comp_BOBLIGHTSERVER": "Servidor Boblight",
|
||||
"general_comp_GRABBER": "Captura de plataforma",
|
||||
"general_comp_V4L": "Captura USB",
|
||||
@ -142,7 +141,6 @@
|
||||
"conf_network_json_intro": "El puerto JSON-RPC de esta instancia de Hyperion, utilizado para el control remoto.",
|
||||
"conf_network_proto_intro": "El PROTO-puerto de esta instancia de Hyperion, utilizado para flujos de imágenes (HyperionScreenCap, Kodi Adddon, ...)",
|
||||
"conf_network_bobl_intro": "Receptor para Boblight",
|
||||
"conf_network_udpl_intro": "Receptor para UDP",
|
||||
"conf_network_forw_intro": "Reenviar toda la entrada a una segunda instancia de Hyperion que podría ser gestionada con otro controlador led",
|
||||
"conf_kodi_label_title": "Observador Kodi",
|
||||
"conf_kodi_intro": "El Observador de Kodi te permite habilitar y deshabilitar la captura de pantalla dependiendo del estado de Kodi. Esto no se limita a la misma máquina, se puede observar también un Kodi en cualquier otro dispositivo de tu red.",
|
||||
@ -518,13 +516,6 @@
|
||||
"edt_conf_js_heading_title": "Servidor JSON",
|
||||
"edt_conf_ps_heading_title": "Servidor PROTO",
|
||||
"edt_conf_bobls_heading_title": "Servidor Boblight",
|
||||
"edt_conf_udpl_heading_title": "Oyente UDP",
|
||||
"edt_conf_udpl_address_title": "Dirección",
|
||||
"edt_conf_udpl_address_expl": "La dirección donde se aceptan paquetes UDP.",
|
||||
"edt_conf_udpl_timeout_title": "Tiempo muerto",
|
||||
"edt_conf_udpl_timeout_expl": "Si no se reciben paquetes durante el período especificado, el componente se desactivará (suavemente).",
|
||||
"edt_conf_udpl_shared_title": "Compartido",
|
||||
"edt_conf_udpl_shared_expl": "Compartido entre todas las instancias de Hyperion.",
|
||||
"edt_conf_webc_heading_title": "Configuración web",
|
||||
"edt_conf_webc_docroot_title": "Documento raíz",
|
||||
"edt_conf_webc_docroot_expl": "Ruta raíz de la interfaz web local (sólo para desarrolladores webui)",
|
||||
|
@ -16,7 +16,6 @@
|
||||
"general_comp_BLACKBORDER": "Rilevamento barra nera",
|
||||
"general_comp_KODICHECKER": "Controllo Kodi",
|
||||
"general_comp_FORWARDER": "Forwarder",
|
||||
"general_comp_UDPLISTENER": "Listener UDP",
|
||||
"general_comp_BOBLIGHTSERVER": "Server Boblight",
|
||||
"general_comp_GRABBER": "Cattura di Sistema",
|
||||
"general_comp_V4L": "Cattura USB",
|
||||
@ -142,7 +141,6 @@
|
||||
"conf_network_json_intro": "La porta JSON-RPC di questa istanza di Hyperion, usata per il controllo remoto.",
|
||||
"conf_network_proto_intro": "La porta PROTO di questa istanza di Hyperion, usata per stream di immagini (HyperionScreenCap, Kodi Adddon, ...)",
|
||||
"conf_network_bobl_intro": "Ricevitore Boblight",
|
||||
"conf_network_udpl_intro": "Ricevitore UDP",
|
||||
"conf_network_forw_intro": "Inoltra tutti gli input a una seconda istanza di Hyperion che può essere usata con un altro controller led",
|
||||
"conf_kodi_label_title": "Controllo Kodi",
|
||||
"conf_kodi_intro": "Il controllore di Kodi ti permetti di abilitare e disabilitare lo screencapture a seconda dello stato di Kodi. Non è limitato alla stessa macchina, puoi osservare Kodi su qualsiasi altro dispositivo nella tua rete.",
|
||||
@ -518,13 +516,6 @@
|
||||
"edt_conf_js_heading_title": "Server JSON",
|
||||
"edt_conf_ps_heading_title": "Server PROTO",
|
||||
"edt_conf_bobls_heading_title": "Srtver Boblight",
|
||||
"edt_conf_udpl_heading_title": "Listener UDP",
|
||||
"edt_conf_udpl_address_title": "Indirizzo",
|
||||
"edt_conf_udpl_address_expl": "L'indirizzo dove vengono accettati i pacchetti UDP",
|
||||
"edt_conf_udpl_timeout_title": "Timeout",
|
||||
"edt_conf_udpl_timeout_expl": "Se non vengono ricevuti pacchetti nel dato periodo la componente sarà disabilitata (temporaneo).",
|
||||
"edt_conf_udpl_shared_title": "Condivisi",
|
||||
"edt_conf_udpl_shared_expl": "Condivisi tra tutte le istanze di Hyperion.",
|
||||
"edt_conf_webc_heading_title": "Configurazione Web",
|
||||
"edt_conf_webc_docroot_title": "Documento Root",
|
||||
"edt_conf_webc_docroot_expl": "Percorso root della webinterface locale (solo per sviluppatori webui)",
|
||||
|
@ -93,11 +93,20 @@
|
||||
<!-- /.navbar-header -->
|
||||
|
||||
<ul class="nav navbar-top-links navbar-right">
|
||||
<li class="dropdown" id="btn_hypinstanceswitch" style="display:none">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-exchange fa-fw"></i> <i class="fa fa-caret-down"></i>
|
||||
</a>
|
||||
<ul id="hyp_inst_listing" class="dropdown-menu dropdown-alerts">
|
||||
</ul>
|
||||
</li>
|
||||
<!--
|
||||
<li class="dropdown" id="btn_instanceswitch" style="display:none">
|
||||
<a>
|
||||
<i class="fa fa-exchange fa-fw"></i>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="dropdown" id="btn_open_ledsim">
|
||||
<a>
|
||||
<i class="fa fa-television fa-fw"></i>
|
||||
@ -192,19 +201,18 @@
|
||||
|
||||
<!-- Page Content -->
|
||||
<div id="page-wrapper" style="padding-top:10px; overflow: hidden;">
|
||||
<div id="hyperion_reload_notify" style="display:none;padding:0 10px;margin:0">
|
||||
<div class="bs-callout bs-callout-warning">
|
||||
<h4 data-i18n="dashboard_alert_message_confedit_t"></h4>
|
||||
<span data-i18n="dashboard_alert_message_confedit"></span>
|
||||
<p><button id="btn_hyperion_reload" class="btn btn-warning btn-sm" style="margin-top:10px" data-i18n="general_btn_restarthyperion"></button></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hyperion_config_write_success_notify" style="display:none;padding:0 10px;margin:0">
|
||||
<div class="bs-callout bs-callout-success">
|
||||
<h4 data-i18n="dashboard_alert_message_confsave_success_t"></h4>
|
||||
<span data-i18n="dashboard_alert_message_confsave_success"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hyperion_global_setting_notify" style="display:none;padding:0 10px;margin:0">
|
||||
<div class="bs-callout bs-callout-warning">
|
||||
<h4 data-i18n="dashboard_message_global_setting_t"></h4>
|
||||
<span data-i18n="dashboard_message_global_setting"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hyperion_disabled_notify" style="display:none;padding:0 10px;margin:0">
|
||||
<div class="bs-callout bs-callout-danger">
|
||||
<h4 data-i18n="dashboard_alert_message_disabled_t"></h4>
|
||||
@ -233,6 +241,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- renameDialog -->
|
||||
<div id="modal_dialog_rename" class="modal fade" role="dialog" style="z-index:9999">
|
||||
<div class="modal-dialog">
|
||||
<center>
|
||||
<div class="modal-content">
|
||||
<div id="id_body_rename" class="modal-body"></div>
|
||||
<div id="id_footer_rename" class="modal-footer" style="text-align:center"></div>
|
||||
</div>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- wizardDialog -->
|
||||
<div id="wizard_modal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
@ -76,7 +76,7 @@ $(document).ready( function() {
|
||||
// add more info
|
||||
$('#dash_leddevice').html(window.serverInfo.ledDevices.active);
|
||||
$('#dash_currv').html(window.currentChannel+' '+window.currentVersion);
|
||||
$('#dash_instance').html(window.serverConfig.general.name);
|
||||
$('#dash_instance').html(window.currentHyperionInstanceName);
|
||||
$('#dash_ports').html(window.serverConfig.flatbufServer.port+' | '+window.serverConfig.protoServer.port);
|
||||
$('#dash_watchedversionbranch').html(window.serverConfig.general.watchedVersionBranch);
|
||||
|
||||
|
@ -25,6 +25,79 @@ $(document).ready( function() {
|
||||
requestWriteConfig(conf_editor.getValue());
|
||||
});
|
||||
|
||||
// Instance handling
|
||||
function handleInstanceRename(e)
|
||||
{
|
||||
var inst = e.currentTarget.id.split("_")[1];
|
||||
showInfoDialog('renInst', $.i18n('conf_general_inst_renreq_t'), getInstanceNameByIndex(inst));
|
||||
|
||||
$("#id_btn_ok").off().on('click', function(){
|
||||
requestInstanceRename(inst, $('#renInst_name').val())
|
||||
});
|
||||
|
||||
$('#renInst_name').off().on('input',function(e) {
|
||||
(e.currentTarget.value.length >= 5 && e.currentTarget.value != getInstanceNameByIndex(inst)) ? $('#id_btn_ok').attr('disabled', false) : $('#id_btn_ok').attr('disabled', true);
|
||||
});
|
||||
}
|
||||
|
||||
function handleInstanceStartStop(e)
|
||||
{
|
||||
var inst = e.currentTarget.id.split("_")[1];
|
||||
var start = (e.currentTarget.className == "btn btn-danger")
|
||||
requestInstanceStartStop(inst, start)
|
||||
}
|
||||
|
||||
function handleInstanceDelete(e)
|
||||
{
|
||||
var inst = e.currentTarget.id.split("_")[1];
|
||||
showInfoDialog('delInst',$.i18n('conf_general_inst_delreq_h'),$.i18n('conf_general_inst_delreq_t',getInstanceNameByIndex(inst)));
|
||||
$("#id_btn_yes").off().on('click', function(){
|
||||
requestInstanceDelete(inst)
|
||||
});
|
||||
}
|
||||
|
||||
function buildInstanceList()
|
||||
{
|
||||
var inst = serverInfo.instance
|
||||
$('.itbody').html("");
|
||||
for(var key in inst)
|
||||
{
|
||||
var startBtnColor = inst[key].running ? "success" : "danger";
|
||||
var startBtnIcon = inst[key].running ? "stop" : "play";
|
||||
var startBtnText = inst[key].running ? $.i18n('general_btn_stop') : $.i18n('general_btn_start');
|
||||
var renameBtn = '<button id="instren_'+inst[key].instance+'" type="button" class="btn btn-primary"><i class="fa fa-pencil"> '+$.i18n('general_btn_rename')+'</i></button>';
|
||||
var startBtn = ""
|
||||
var delBtn = "";
|
||||
if(inst[key].instance > 0)
|
||||
{
|
||||
delBtn = '<button id="instdel_'+inst[key].instance+'" type="button" class="btn btn-warning"><i class="fa fa-remove"> '+$.i18n('general_btn_delete')+'</i></button>';
|
||||
startBtn = '<button id="inst_'+inst[key].instance+'" type="button" class="btn btn-'+startBtnColor+'"><i class="fa fa-'+startBtnIcon+'"> '+startBtnText+'</i></button>';
|
||||
}
|
||||
$('.itbody').append(createTableRow([inst[key].friendly_name, renameBtn, startBtn, delBtn], false, true));
|
||||
$('#instren_'+inst[key].instance).off().on('click', handleInstanceRename);
|
||||
$('#inst_'+inst[key].instance).off().on('click', handleInstanceStartStop);
|
||||
$('#instdel_'+inst[key].instance).off().on('click', handleInstanceDelete);
|
||||
}
|
||||
}
|
||||
|
||||
createTable('ithead', 'itbody', 'itable');
|
||||
$('.ithead').html(createTableRow([$.i18n('conf_general_inst_namehead'), "", $.i18n('conf_general_inst_actionhead'), ""], true, true));
|
||||
buildInstanceList();
|
||||
|
||||
$('#inst_name').off().on('input',function(e) {
|
||||
(e.currentTarget.value.length >= 5) ? $('#btn_create_inst').attr('disabled', false) : $('#btn_create_inst').attr('disabled', true);
|
||||
});
|
||||
|
||||
$('#btn_create_inst').off().on('click',function(e) {
|
||||
requestInstanceCreate($('#inst_name').val());
|
||||
$('#inst_name').val("");
|
||||
$('#btn_create_inst').attr('disabled', true)
|
||||
});
|
||||
|
||||
$(hyperion).off("instance-updated").on("instance-updated", function(event) {
|
||||
buildInstanceList()
|
||||
});
|
||||
|
||||
//import
|
||||
function dis_imp_btn(state)
|
||||
{
|
||||
@ -104,6 +177,8 @@ $(document).ready( function() {
|
||||
//create introduction
|
||||
if(window.showOptHelp)
|
||||
createHint("intro", $.i18n('conf_general_intro'), "editor_container");
|
||||
createHint("intro", $.i18n('conf_general_tok_desc'), "tok_desc_cont");
|
||||
createHint("intro", $.i18n('conf_general_inst_desc'), "inst_desc_cont");
|
||||
|
||||
removeOverlay();
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
var instNameInit = false
|
||||
|
||||
$(document).ready( function() {
|
||||
|
||||
loadContentTo("#container_connection_lost","connection_lost");
|
||||
@ -21,10 +23,19 @@ $(document).ready( function() {
|
||||
}
|
||||
});
|
||||
|
||||
if (window.serverInfo.hyperion.enabled)
|
||||
$("#hyperion_disabled_notify").fadeOut("fast");
|
||||
// determine button visibility
|
||||
var running = window.serverInfo.instance.filter(entry => entry.running);
|
||||
if(running.length > 1)
|
||||
$('#btn_hypinstanceswitch').toggle(true)
|
||||
else
|
||||
$("#hyperion_disabled_notify").fadeIn("fast");
|
||||
$('#btn_hypinstanceswitch').toggle(false)
|
||||
// update listing at button
|
||||
updateHyperionInstanceListing()
|
||||
if(!instNameInit)
|
||||
{
|
||||
window.currentHyperionInstanceName = getInstanceNameByIndex(0);
|
||||
instNameInit = true;
|
||||
}
|
||||
|
||||
updateSessions();
|
||||
}); // end cmd-serverinfo
|
||||
@ -34,6 +45,11 @@ $(document).ready( function() {
|
||||
updateSessions();
|
||||
});
|
||||
|
||||
$(window.hyperion).one("cmd-authorize-getTokenList", function(event) {
|
||||
tokenList = event.response.info;
|
||||
requestServerInfo();
|
||||
});
|
||||
|
||||
$(window.hyperion).on("cmd-sysinfo", function(event) {
|
||||
requestServerInfo();
|
||||
window.sysInfo = event.response.info;
|
||||
@ -45,6 +61,7 @@ $(document).ready( function() {
|
||||
$(window.hyperion).one("cmd-config-getschema", function(event) {
|
||||
window.serverSchema = event.response.info;
|
||||
requestServerConfig();
|
||||
requestTokenInfo();
|
||||
|
||||
window.schema = window.serverSchema.properties;
|
||||
});
|
||||
@ -62,12 +79,16 @@ $(document).ready( function() {
|
||||
}
|
||||
});
|
||||
|
||||
$(window.hyperion).one("cmd-authorize-login", function(event) {
|
||||
requestServerConfigSchema();
|
||||
});
|
||||
|
||||
$(window.hyperion).on("error",function(event){
|
||||
showInfoDialog("error","Error", event.reason);
|
||||
});
|
||||
|
||||
$(window.hyperion).on("open",function(event){
|
||||
requestServerConfigSchema();
|
||||
requestAuthorization();
|
||||
});
|
||||
|
||||
$(window.hyperion).one("ready", function(event) {
|
||||
@ -103,6 +124,50 @@ $(document).ready( function() {
|
||||
$(window.hyperion).trigger("components-updated");
|
||||
});
|
||||
|
||||
$(window.hyperion).on("cmd-instance-update", function(event) {
|
||||
window.serverInfo.instance = event.response.data
|
||||
var avail = event.response.data;
|
||||
// notify the update
|
||||
$(window.hyperion).trigger("instance-updated");
|
||||
|
||||
// if our current instance is no longer available we are at instance 0 again.
|
||||
var isInData = false;
|
||||
for(var key in avail)
|
||||
{
|
||||
if(avail[key].instance == currentHyperionInstance && avail[key].running)
|
||||
{
|
||||
isInData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isInData)
|
||||
{
|
||||
currentHyperionInstance = 0;
|
||||
currentHyperionInstanceName = getInstanceNameByIndex(0);
|
||||
requestServerConfig();
|
||||
setTimeout(requestServerInfo,100)
|
||||
setTimeout(requestTokenInfo,200)
|
||||
setTimeout(loadContent,300, undefined, true)
|
||||
}
|
||||
|
||||
// determine button visibility
|
||||
var running = serverInfo.instance.filter(entry => entry.running);
|
||||
if(running.length > 1)
|
||||
$('#btn_hypinstanceswitch').toggle(true)
|
||||
else
|
||||
$('#btn_hypinstanceswitch').toggle(false)
|
||||
|
||||
// update listing for button
|
||||
updateHyperionInstanceListing()
|
||||
});
|
||||
|
||||
$(window.hyperion).on("cmd-instance-switchTo", function(event){
|
||||
requestServerConfig();
|
||||
setTimeout(requestServerInfo,200)
|
||||
setTimeout(requestTokenInfo,400)
|
||||
setTimeout(loadContent,400, undefined, true)
|
||||
});
|
||||
|
||||
$(window.hyperion).on("cmd-effects-update", function(event){
|
||||
window.serverInfo.effects = event.response.data.effects
|
||||
});
|
||||
|
@ -1,15 +1,20 @@
|
||||
$(document).ready( function() {
|
||||
performTranslation();
|
||||
|
||||
var conf_editor_net = null;
|
||||
var conf_editor_json = null;
|
||||
var conf_editor_proto = null;
|
||||
var conf_editor_fbs = null;
|
||||
var conf_editor_bobl = null;
|
||||
var conf_editor_udpl = null;
|
||||
var conf_editor_forw = null;
|
||||
|
||||
if(window.showOptHelp)
|
||||
{
|
||||
//network
|
||||
$('#conf_cont').append(createRow('conf_cont_net'))
|
||||
$('#conf_cont_net').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_net_heading_title"), 'editor_container_net', 'btn_submit_net'));
|
||||
$('#conf_cont_net').append(createHelpTable(window.schema.network.properties, $.i18n("edt_conf_net_heading_title")));
|
||||
|
||||
//jsonserver
|
||||
$('#conf_cont').append(createRow('conf_cont_json'))
|
||||
$('#conf_cont_json').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_js_heading_title"), 'editor_container_jsonserver', 'btn_submit_jsonserver'));
|
||||
@ -30,11 +35,6 @@ $(document).ready( function() {
|
||||
$('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver'));
|
||||
$('#conf_cont_bobl').append(createHelpTable(window.schema.boblightServer.properties, $.i18n("edt_conf_bobls_heading_title")));
|
||||
|
||||
//udplistener
|
||||
$('#conf_cont').append(createRow('conf_cont_udpl'))
|
||||
$('#conf_cont_udpl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_udpl_heading_title"), 'editor_container_udplistener', 'btn_submit_udplistener'));
|
||||
$('#conf_cont_udpl').append(createHelpTable(window.schema.udpListener.properties, $.i18n("edt_conf_udpl_heading_title")));
|
||||
|
||||
//forwarder
|
||||
if(storedAccess != 'default')
|
||||
{
|
||||
@ -46,15 +46,29 @@ $(document).ready( function() {
|
||||
else
|
||||
{
|
||||
$('#conf_cont').addClass('row');
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_net_heading_title"), 'editor_container_net', 'btn_submit_net'));
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_js_heading_title"), 'editor_container_jsonserver', 'btn_submit_jsonserver'));
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fbs_heading_title"), 'editor_container_fbserver', 'btn_submit_fbserver'));
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_pbs_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver'));
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver'));
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_udpl_heading_title"), 'editor_container_udplistener', 'btn_submit_udplistener'));
|
||||
if(storedAccess != 'default')
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fw_heading_title"), 'editor_container_forwarder', 'btn_submit_forwarder'));
|
||||
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fw_heading_title"), 'editor_container_forwarder', 'btn_submit_forwarder'));
|
||||
|
||||
$("#conf_cont_tok").removeClass('row');
|
||||
}
|
||||
|
||||
|
||||
// net
|
||||
conf_editor_net = createJsonEditor('editor_container_net', {
|
||||
network : window.schema.network
|
||||
}, true, true);
|
||||
|
||||
conf_editor_net.on('change',function() {
|
||||
conf_editor_net.validate().length ? $('#btn_submit_net').attr('disabled', true) : $('#btn_submit_net').attr('disabled', false);
|
||||
});
|
||||
|
||||
$('#btn_submit_net').off().on('click',function() {
|
||||
requestWriteConfig(conf_editor_net.getValue());
|
||||
});
|
||||
|
||||
//json
|
||||
conf_editor_json = createJsonEditor('editor_container_jsonserver', {
|
||||
jsonServer : window.schema.jsonServer
|
||||
@ -107,19 +121,6 @@ $(document).ready( function() {
|
||||
requestWriteConfig(conf_editor_bobl.getValue());
|
||||
});
|
||||
|
||||
//udplistener
|
||||
conf_editor_udpl = createJsonEditor('editor_container_udplistener', {
|
||||
udpListener : window.schema.udpListener
|
||||
}, true, true);
|
||||
|
||||
conf_editor_udpl.on('change',function() {
|
||||
conf_editor_udpl.validate().length ? $('#btn_submit_udplistener').attr('disabled', true) : $('#btn_submit_udplistener').attr('disabled', false);
|
||||
});
|
||||
|
||||
$('#btn_submit_udplistener').off().on('click',function() {
|
||||
requestWriteConfig(conf_editor_udpl.getValue());
|
||||
});
|
||||
|
||||
if(storedAccess != 'default')
|
||||
{
|
||||
//forwarder
|
||||
@ -139,13 +140,79 @@ $(document).ready( function() {
|
||||
//create introduction
|
||||
if(window.showOptHelp)
|
||||
{
|
||||
createHint("intro", $.i18n('conf_network_net_intro'), "editor_container_net");
|
||||
createHint("intro", $.i18n('conf_network_json_intro'), "editor_container_jsonserver");
|
||||
createHint("intro", $.i18n('conf_network_fbs_intro'), "editor_container_fbserver");
|
||||
createHint("intro", $.i18n('conf_network_proto_intro'), "editor_container_protoserver");
|
||||
createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver");
|
||||
createHint("intro", $.i18n('conf_network_udpl_intro'), "editor_container_udplistener");
|
||||
createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder");
|
||||
createHint("intro", $.i18n('conf_network_tok_intro'), "tok_desc_cont");
|
||||
}
|
||||
|
||||
// Token handling
|
||||
function buildTokenList()
|
||||
{
|
||||
$('.tktbody').html("");
|
||||
for(var key in tokenList)
|
||||
{
|
||||
var lastUse = (tokenList[key].last_use) ? tokenList[key].last_use : "-";
|
||||
var btn = '<button id="tok'+tokenList[key].id+'" type="button" class="btn btn-danger">'+$.i18n('general_btn_delete')+'</button>';
|
||||
$('.tktbody').append(createTableRow([tokenList[key].comment, lastUse, btn], false, true));
|
||||
$('#tok'+tokenList[key].id).off().on('click', handleDeleteToken);
|
||||
}
|
||||
}
|
||||
|
||||
createTable('tkthead', 'tktbody', 'tktable');
|
||||
$('.tkthead').html(createTableRow([$.i18n('conf_network_tok_cidhead'), $.i18n('conf_network_tok_lastuse'), $.i18n('general_btn_delete')], true, true));
|
||||
buildTokenList();
|
||||
|
||||
function handleDeleteToken(e)
|
||||
{
|
||||
var key = e.currentTarget.id.replace("tok","");
|
||||
requestTokenDelete(key);
|
||||
$('#tok'+key).parent().parent().remove();
|
||||
// rm deleted token id
|
||||
tokenList = tokenList.filter(function( obj ) {
|
||||
return obj.id !== key;
|
||||
});
|
||||
}
|
||||
|
||||
$('#btn_create_tok').off().on('click',function() {
|
||||
requestToken($('#tok_comment').val())
|
||||
$('#tok_comment').val("")
|
||||
$('#btn_create_tok').attr('disabled', true)
|
||||
});
|
||||
$('#tok_comment').off().on('input',function(e) {
|
||||
(e.currentTarget.value.length >= 10) ? $('#btn_create_tok').attr('disabled', false) : $('#btn_create_tok').attr('disabled', true);
|
||||
if(10-e.currentTarget.value.length >= 1 && 10-e.currentTarget.value.length <= 9)
|
||||
$('#tok_chars_needed').html(10-e.currentTarget.value.length + " " + $.i18n('conf_network_tok_chars_needed'))
|
||||
else
|
||||
$('#tok_chars_needed').html("<br />")
|
||||
});
|
||||
$(window.hyperion).off("cmd-authorize-createToken").on("cmd-authorize-createToken", function(event) {
|
||||
var val = event.response.info;
|
||||
showInfoDialog("newToken",$.i18n('conf_network_tok_diaTitle'),$.i18n('conf_network_tok_diaMsg')+'<br><div style="font-weight:bold">'+val.token+'</div>')
|
||||
tokenList.push(val)
|
||||
buildTokenList()
|
||||
});
|
||||
|
||||
//Reorder hardcoded token div after the general token setting div
|
||||
$("#conf_cont_tok").insertAfter("#conf_cont_net");
|
||||
|
||||
function checkApiTokenState(state)
|
||||
{
|
||||
if(state == false)
|
||||
$("#conf_cont_tok").attr('style', 'display:none')
|
||||
else
|
||||
$("#conf_cont_tok").removeAttr('style')
|
||||
}
|
||||
|
||||
$('#root_network_apiAuth').change(function () {
|
||||
var state = $(this).is(":checked");
|
||||
checkApiTokenState(state);
|
||||
})
|
||||
|
||||
checkApiTokenState(window.serverConfig.network.apiAuth);
|
||||
|
||||
removeOverlay();
|
||||
});
|
||||
|
@ -90,10 +90,9 @@ $(document).ready(function() {
|
||||
{
|
||||
$('.sstbody').html("");
|
||||
var prios = window.serverInfo.priorities;
|
||||
var i;
|
||||
var clearAll = false;
|
||||
|
||||
for(i = 0; i < prios.length; i++)
|
||||
for(var i = 0; i < prios.length; i++)
|
||||
{
|
||||
var origin = prios[i].origin ? prios[i].origin : "System";
|
||||
origin = origin.split("@");
|
||||
@ -149,9 +148,6 @@ $(document).ready(function() {
|
||||
case "BOBLIGHTSERVER":
|
||||
owner = $.i18n('general_comp_BOBLIGHTSERVER');
|
||||
break;
|
||||
case "UDPLISTENER":
|
||||
owner = $.i18n('general_comp_UDPLISTENER');
|
||||
break;
|
||||
case "FLATBUFSERVER":
|
||||
owner = $.i18n('general_comp_FLATBUFSERVER');
|
||||
break;
|
||||
@ -224,7 +220,7 @@ $(document).ready(function() {
|
||||
var enable_icon = (components[idx].enabled? "fa-play" : "fa-stop");
|
||||
var comp_name = components[idx].name;
|
||||
var comp_btn_id = "comp_btn_"+comp_name;
|
||||
var comp_goff = hyperionEnabled? "enabled" : "disabled";
|
||||
var comp_goff = hyperionEnabled? "enabled" : "disabled";
|
||||
|
||||
// create btn if not there
|
||||
if ($("#"+comp_btn_id).length == 0)
|
||||
@ -298,8 +294,9 @@ $(document).ready(function() {
|
||||
|
||||
createCP('cp2', cpcolor, function(rgbT,hex){
|
||||
rgb = rgbT;
|
||||
sendColor()
|
||||
sendColor();
|
||||
setStorage('rmcpcolor', hex);
|
||||
updateInputSelect();
|
||||
});
|
||||
|
||||
$("#reset_color").off().on("click", function(){
|
||||
@ -341,22 +338,22 @@ $(document).ready(function() {
|
||||
$(window.hyperion).on("components-updated",updateComponents);
|
||||
|
||||
$(window.hyperion).on("cmd-priorities-update", function(event){
|
||||
window.serverInfo.priorities = event.response.data.priorities
|
||||
window.serverInfo.priorities_autoselect = event.response.data.priorities_autoselect
|
||||
updateInputSelect()
|
||||
window.serverInfo.priorities = event.response.data.priorities;
|
||||
window.serverInfo.priorities_autoselect = event.response.data.priorities_autoselect;
|
||||
updateInputSelect();
|
||||
});
|
||||
$(window.hyperion).on("cmd-imageToLedMapping-update", function(event){
|
||||
window.serverInfo.imageToLedMappingType = event.response.data.imageToLedMappingType
|
||||
updateLedMapping()
|
||||
window.serverInfo.imageToLedMappingType = event.response.data.imageToLedMappingType;
|
||||
updateLedMapping();
|
||||
});
|
||||
|
||||
$(window.hyperion).on("cmd-videomode-update", function(event){
|
||||
window.serverInfo.videomode = event.response.data.videomode
|
||||
updateVideoMode()
|
||||
window.serverInfo.videomode = event.response.data.videomode;
|
||||
updateVideoMode();
|
||||
});
|
||||
|
||||
$(window.hyperion).on("cmd-effects-update", function(event){
|
||||
window.serverInfo.effects = event.response.data.effects
|
||||
window.serverInfo.effects = event.response.data.effects;
|
||||
updateEffectlist();
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,10 @@ window.loggingHandlerInstalled = false;
|
||||
window.watchdog = 0;
|
||||
window.debugMessagesActive = true;
|
||||
window.wSess = [];
|
||||
window.currentHyperionInstance = 0;
|
||||
window.currentHyperionInstanceName = "?";
|
||||
window.comps = [];
|
||||
tokenList = {};
|
||||
|
||||
function initRestart()
|
||||
{
|
||||
@ -162,10 +165,62 @@ function sendToHyperion(command, subcommand, msg)
|
||||
// -----------------------------------------------------------
|
||||
// wrapped server commands
|
||||
|
||||
// also used for watchdog
|
||||
function requestAuthorization()
|
||||
{
|
||||
sendToHyperion("authorize","login",'"username": "Hyperion", "password": "hyperion"');
|
||||
}
|
||||
|
||||
function requestToken(comment)
|
||||
{
|
||||
sendToHyperion("authorize","createToken",'"comment": "'+comment+'"');
|
||||
}
|
||||
|
||||
function requestTokenInfo()
|
||||
{
|
||||
sendToHyperion("authorize","getTokenList","");
|
||||
}
|
||||
|
||||
function requestHandleTokenRequest(id, state)
|
||||
{
|
||||
sendToHyperion("authorize","answerRequest",'"id":"'+id+'", "accept":'+state);
|
||||
}
|
||||
|
||||
function requestTokenDelete(id)
|
||||
{
|
||||
sendToHyperion("authorize","deleteToken",'"id":"'+id+'"');
|
||||
}
|
||||
|
||||
function requestInstanceRename(inst, name)
|
||||
{
|
||||
sendToHyperion("instance", "saveName",'"instance": '+inst+', "name": "'+name+'"');
|
||||
}
|
||||
|
||||
function requestInstanceStartStop(inst, start)
|
||||
{
|
||||
if(start)
|
||||
sendToHyperion("instance","startInstance",'"instance": '+inst);
|
||||
else
|
||||
sendToHyperion("instance","stopInstance",'"instance": '+inst);
|
||||
}
|
||||
|
||||
function requestInstanceDelete(inst)
|
||||
{
|
||||
sendToHyperion("instance","deleteInstance",'"instance": '+inst);
|
||||
}
|
||||
|
||||
function requestInstanceCreate(name)
|
||||
{
|
||||
sendToHyperion("instance","createInstance",'"name": "'+name+'"');
|
||||
}
|
||||
|
||||
function requestInstanceSwitch(inst)
|
||||
{
|
||||
sendToHyperion("instance","switchTo",'"instance": '+inst);
|
||||
}
|
||||
|
||||
function requestServerInfo()
|
||||
{
|
||||
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update"]');
|
||||
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update", "instance-update"]');
|
||||
}
|
||||
|
||||
function requestSysInfo()
|
||||
|
@ -125,6 +125,7 @@ $(document).ready( function() {
|
||||
for (var i = 0; i<window.wSess.length; i++)
|
||||
{
|
||||
if(lsys != window.wSess[i].host+':'+window.wSess[i].port)
|
||||
{
|
||||
var hyperionAddress
|
||||
|
||||
if (window.wSess[i].address.indexOf(':') > -1 && window.wSess[i].address.length == 36)
|
||||
@ -133,6 +134,7 @@ $(document).ready( function() {
|
||||
hyperionAddress = 'http://'+window.wSess[i].address+':'+window.wSess[i].port
|
||||
|
||||
$('#id_select').append(createSelOpt(hyperionAddress, window.wSess[i].host))
|
||||
}
|
||||
}
|
||||
|
||||
$('#id_btn_saveset').off().on('click',function() {
|
||||
|
@ -114,10 +114,70 @@ function loadContent(event, forceRefresh)
|
||||
$("#page-content").html('<h3>'+$.i18n('info_404')+'</h3>');
|
||||
removeOverlay();
|
||||
}
|
||||
updateUiOnInstance(window.currentHyperionInstance);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getInstanceNameByIndex(index)
|
||||
{
|
||||
var instData = window.serverInfo.instance
|
||||
for(var key in instData)
|
||||
{
|
||||
if(instData[key].instance == index)
|
||||
return instData[key].friendly_name;
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
function updateHyperionInstanceListing()
|
||||
{
|
||||
var data = window.serverInfo.instance.filter(entry => entry.running);
|
||||
$('#hyp_inst_listing').html("");
|
||||
for(var key in data)
|
||||
{
|
||||
var currInstMarker = (data[key].instance == window.currentHyperionInstance) ? "component-on" : "";
|
||||
|
||||
var html = '<li id="hyperioninstance_'+data[key].instance+'"> \
|
||||
<a> \
|
||||
<div> \
|
||||
<i class="fa fa-circle fa-fw '+currInstMarker+'"></i> \
|
||||
<span>'+data[key].friendly_name+'</span> \
|
||||
</div> \
|
||||
</a> \
|
||||
</li> '
|
||||
|
||||
if(data.length-1 > key)
|
||||
html += '<li class="divider"></li>'
|
||||
|
||||
$('#hyp_inst_listing').append(html);
|
||||
|
||||
$('#hyperioninstance_'+data[key].instance).off().on("click",function(e){
|
||||
var inst = e.currentTarget.id.split("_")[1]
|
||||
requestInstanceSwitch(inst)
|
||||
window.currentHyperionInstance = inst;
|
||||
window.currentHyperionInstanceName = getInstanceNameByIndex(inst);
|
||||
updateHyperionInstanceListing()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateUiOnInstance(inst)
|
||||
{
|
||||
if(inst != 0)
|
||||
{
|
||||
var currentURL = $(location).attr("href");
|
||||
if(currentURL.indexOf('#conf_network') != -1 || currentURL.indexOf('#update') != -1 || currentURL.indexOf('#conf_webconfig') != -1 || currentURL.indexOf('#conf_grabber') != -1 || currentURL.indexOf('#conf_logging') != -1)
|
||||
$("#hyperion_global_setting_notify").fadeIn("fast");
|
||||
else
|
||||
$("#hyperion_global_setting_notify").attr("style", "display:none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#hyperion_global_setting_notify").fadeOut("fast");
|
||||
}
|
||||
}
|
||||
|
||||
function loadContentTo(containerId, fileName)
|
||||
{
|
||||
$(containerId).load("/content/"+fileName+".html");
|
||||
@ -154,62 +214,103 @@ function setClassByBool(obj,enable,class1,class2)
|
||||
|
||||
function showInfoDialog(type,header,message)
|
||||
{
|
||||
if (type=="success"){
|
||||
if (type=="success")
|
||||
{
|
||||
$('#id_body').html('<i style="margin-bottom:20px" class="fa fa-check modal-icon-check">');
|
||||
if(header == "")
|
||||
$('#id_body').append('<h4 style="font-weight:bold;text-transform:uppercase;">'+$.i18n('infoDialog_general_success_title')+'</h4>');
|
||||
$('#id_footer').html('<button type="button" class="btn btn-success" data-dismiss="modal">'+$.i18n('general_btn_ok')+'</button>');
|
||||
}
|
||||
else if (type=="warning"){
|
||||
else if (type=="warning")
|
||||
{
|
||||
$('#id_body').html('<i style="margin-bottom:20px" class="fa fa-warning modal-icon-warning">');
|
||||
if(header == "")
|
||||
$('#id_body').append('<h4 style="font-weight:bold;text-transform:uppercase;">'+$.i18n('infoDialog_general_warning_title')+'</h4>');
|
||||
$('#id_footer').html('<button type="button" class="btn btn-warning" data-dismiss="modal">'+$.i18n('general_btn_ok')+'</button>');
|
||||
}
|
||||
else if (type=="error"){
|
||||
else if (type=="error")
|
||||
{
|
||||
$('#id_body').html('<i style="margin-bottom:20px" class="fa fa-warning modal-icon-error">');
|
||||
if(header == "")
|
||||
$('#id_body').append('<h4 style="font-weight:bold;text-transform:uppercase;">'+$.i18n('infoDialog_general_error_title')+'</h4>');
|
||||
$('#id_footer').html('<button type="button" class="btn btn-danger" data-dismiss="modal">'+$.i18n('general_btn_ok')+'</button>');
|
||||
}
|
||||
else if (type == "select"){
|
||||
else if (type == "select")
|
||||
{
|
||||
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
$('#id_footer').html('<button type="button" id="id_btn_saveset" class="btn btn-primary" data-dismiss="modal"><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_saveandreload')+'</button>');
|
||||
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "iswitch"){
|
||||
else if (type == "iswitch")
|
||||
{
|
||||
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
$('#id_footer').html('<button type="button" id="id_btn_saveset" class="btn btn-primary" data-dismiss="modal"><i class="fa fa-fw fa-exchange"></i>'+$.i18n('general_btn_iswitch')+'</button>');
|
||||
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "uilock"){
|
||||
else if (type == "uilock")
|
||||
{
|
||||
$('#id_body').html('<img src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
$('#id_footer').html('<b>'+$.i18n('InfoDialog_nowrite_foottext')+'</b>');
|
||||
}
|
||||
else if (type == "import"){
|
||||
else if (type == "import")
|
||||
{
|
||||
$('#id_body').html('<i style="margin-bottom:20px" class="fa fa-warning modal-icon-warning">');
|
||||
$('#id_footer').html('<button type="button" id="id_btn_import" class="btn btn-warning" data-dismiss="modal"><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_saverestart')+'</button>');
|
||||
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "delInst")
|
||||
{
|
||||
$('#id_body').html('<i style="margin-bottom:20px" class="fa fa-remove modal-icon-warning">');
|
||||
$('#id_footer').html('<button type="button" id="id_btn_yes" class="btn btn-warning" data-dismiss="modal"><i class="fa fa-fw fa-trash"></i>'+$.i18n('general_btn_yes')+'</button>');
|
||||
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "renInst")
|
||||
{
|
||||
$('#id_body_rename').html('<i style="margin-bottom:20px" class="fa fa-pencil modal-icon-edit"><br>');
|
||||
$('#id_body_rename').append('<h4>'+header+'</h4>');
|
||||
$('#id_body_rename').append('<input class="form-control" id="renInst_name" type="text" value="'+message+'">');
|
||||
$('#id_footer_rename').html('<button type="button" id="id_btn_ok" class="btn btn-success" data-dismiss-modal="#modal_dialog_rename" disabled><i class="fa fa-fw fa-save"></i>'+$.i18n('general_btn_ok')+'</button>');
|
||||
$('#id_footer_rename').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>'+$.i18n('general_btn_cancel')+'</button>');
|
||||
}
|
||||
else if (type == "checklist")
|
||||
{
|
||||
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
$('#id_body').append('<h4 style="font-weight:bold;text-transform:uppercase;">'+$.i18n('infoDialog_checklist_title')+'</h4>');
|
||||
$('#id_body').append(message);
|
||||
$('#id_body').append(header);
|
||||
$('#id_footer').html('<button type="button" class="btn btn-primary" data-dismiss="modal">'+$.i18n('general_btn_ok')+'</button>');
|
||||
}
|
||||
else if (type == "newToken")
|
||||
{
|
||||
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
$('#id_footer').html('<button type="button" class="btn btn-primary" data-dismiss="modal">'+$.i18n('general_btn_ok')+'</button>');
|
||||
}
|
||||
else if (type == "grantToken")
|
||||
{
|
||||
$('#id_body').html('<img style="margin-bottom:20px" src="img/hyperion/hyperionlogo.png" alt="Redefine ambient light!">');
|
||||
$('#id_footer').html('<button type="button" class="btn btn-primary" data-dismiss="modal" id="tok_grant_acc">'+$.i18n('general_btn_grantAccess')+'</button>');
|
||||
$('#id_footer').append('<button type="button" class="btn btn-danger" data-dismiss="modal" id="tok_deny_acc">'+$.i18n('general_btn_denyAccess')+'</button>');
|
||||
}
|
||||
|
||||
$('#id_body').append('<h4 style="font-weight:bold;text-transform:uppercase;">'+header+'</h4>');
|
||||
$('#id_body').append(message);
|
||||
if(type != "renInst")
|
||||
{
|
||||
$('#id_body').append('<h4 style="font-weight:bold;text-transform:uppercase;">'+header+'</h4>');
|
||||
$('#id_body').append(message);
|
||||
}
|
||||
|
||||
if(type == "select" || type == "iswitch")
|
||||
$('#id_body').append('<select id="id_select" class="form-control" style="margin-top:10px;width:auto;"></select>');
|
||||
|
||||
$("#modal_dialog").modal({
|
||||
|
||||
$(type == "renInst" ? "#modal_dialog_rename" : "#modal_dialog").modal({
|
||||
backdrop : "static",
|
||||
keyboard: false,
|
||||
show: true
|
||||
});
|
||||
|
||||
$(document).on('click', '[data-dismiss-modal]', function () {
|
||||
var target = $(this).attr('data-dismiss-modal');
|
||||
$(target).modal('hide');
|
||||
});
|
||||
}
|
||||
|
||||
function createHintH(type, text, container)
|
||||
@ -593,8 +694,8 @@ function createHelpTable(list, phead){
|
||||
// break one iteration (in the loop), if the schema has the entry hidden=true
|
||||
if ("options" in list[key] && "hidden" in list[key].options && (list[key].options.hidden))
|
||||
continue;
|
||||
if ("access" in list[key] && ((list[key].access == "advanced" && storedAccess == "default") || (list[key].access == "expert" && storedAccess != "expert")))
|
||||
continue;
|
||||
if ("access" in list[key] && ((list[key].access == "advanced" && storedAccess == "default") || (list[key].access == "expert" && storedAccess != "expert")))
|
||||
continue;
|
||||
var text = list[key].title.replace('title', 'expl');
|
||||
tbody.appendChild(createTableRow([$.i18n(list[key].title), $.i18n(text)], false, false));
|
||||
|
||||
@ -606,8 +707,8 @@ function createHelpTable(list, phead){
|
||||
// break one iteration (in the loop), if the schema has the entry hidden=true
|
||||
if ("options" in ilist[ikey] && "hidden" in ilist[ikey].options && (ilist[ikey].options.hidden))
|
||||
continue;
|
||||
if ("access" in ilist[ikey] && ((ilist[ikey].access == "advanced" && storedAccess == "default") || (ilist[ikey].access == "expert" && storedAccess != "expert")))
|
||||
continue;
|
||||
if ("access" in ilist[ikey] && ((ilist[ikey].access == "advanced" && storedAccess == "default") || (ilist[ikey].access == "expert" && storedAccess != "expert")))
|
||||
continue;
|
||||
var itext = ilist[ikey].title.replace('title', 'expl');
|
||||
tbody.appendChild(createTableRow([$.i18n(ilist[ikey].title), $.i18n(itext)], false, false));
|
||||
}
|
||||
|
@ -7,5 +7,6 @@ STRING ( STRIP "${BUILD_ID}" BUILD_ID )
|
||||
STRING ( STRIP "${VERSION_ID}" VERSION_ID )
|
||||
STRING ( STRIP "${GIT_REMOTE_PATH}" GIT_REMOTE_PATH )
|
||||
SET ( HYPERION_BUILD_ID "${VERSION_ID} (${BUILD_ID}) Git Remote: ${GIT_REMOTE_PATH}" )
|
||||
SET ( HYPERION_VERSION "${HYPERION_VERSION_CHANNEL}.${HYPERION_VERSION_MAJOR}.${HYPERION_VERSION_MINOR}.${HYPERION_VERSION_PATCH}" )
|
||||
message ( STATUS "Current Version: ${HYPERION_VERSION} (${HYPERION_BUILD_ID})" )
|
||||
SET ( HYPERION_VERSION "${HYPERION_VERSION_CHANNEL} ${HYPERION_VERSION_MAJOR}.${HYPERION_VERSION_MINOR}.${HYPERION_VERSION_PATCH}" )
|
||||
message ( STATUS "Current Version: ${HYPERION_VERSION}" )
|
||||
message ( STATUS " - Build: ${HYPERION_BUILD_ID}" )
|
||||
|
@ -13,11 +13,11 @@ ENDIF()
|
||||
find_package(RpmBuilder)
|
||||
find_package(DebBuilder)
|
||||
IF(RPM_BUILDER_FOUND)
|
||||
message("CPACK: Found RPM builder")
|
||||
message(STATUS "CPACK: Found RPM builder")
|
||||
SET ( CPACK_GENERATOR ${CPACK_GENERATOR} "RPM")
|
||||
ENDIF()
|
||||
IF(DEB_BUILDER_FOUND)
|
||||
message("CPACK: Found DEB builder")
|
||||
message(STATUS "CPACK: Found DEB builder")
|
||||
SET ( CPACK_GENERATOR ${CPACK_GENERATOR} "DEB")
|
||||
ENDIF()
|
||||
|
||||
|
@ -263,23 +263,6 @@
|
||||
"priority" : 128
|
||||
},
|
||||
|
||||
/// The configuration of the udp listener
|
||||
/// * enable : Enable or disable the udp listener (true/false)
|
||||
/// * address : The listener address, pre configured is multicast which listen also to unicast ip addresses at the same time. If emtpy, multicast is disabled and it also accepts unicast from all IPs
|
||||
/// * port : Port at which the udp listener starts
|
||||
/// * priority : Priority of the udp listener server (Default=200)
|
||||
/// * timeout : The timeout sets the timelimit for a "soft" off of the udp listener, if no packages are received (for example to switch to a gabber or InitialEffect - background-effect)
|
||||
/// * shared : If true, the udp listener is shared across all hyperion instances (if using more than one (forwarder))
|
||||
"udpListener" :
|
||||
{
|
||||
"enable" : false,
|
||||
"address" : "239.255.28.01",
|
||||
"port" : 2801,
|
||||
"priority" : 200,
|
||||
"timeout" : 10000,
|
||||
"shared" : false
|
||||
},
|
||||
|
||||
/// Configuration of the Hyperion webserver
|
||||
/// * document_root : path to hyperion webapp files (webconfig developer only)
|
||||
/// * port : the port where hyperion webapp is accasible
|
||||
@ -307,13 +290,22 @@
|
||||
]
|
||||
},
|
||||
|
||||
"instCapture" : {
|
||||
"instCapture" :
|
||||
{
|
||||
"systemEnable" : true,
|
||||
"systemPriority" : 250,
|
||||
"v4lEnable" : false,
|
||||
"v4lPriority" : 240
|
||||
},
|
||||
|
||||
"network" :
|
||||
{
|
||||
"internetAccessAPI" : false,
|
||||
"ipWhitelist" : [],
|
||||
"apiAuth" : true,
|
||||
"localApiAuth" : false
|
||||
},
|
||||
|
||||
/// Recreate and save led layouts made with web config. These values are just helpers for ui, not for Hyperion.
|
||||
"ledConfig" :
|
||||
{
|
||||
|
@ -151,16 +151,6 @@
|
||||
"priority" : 128
|
||||
},
|
||||
|
||||
"udpListener" :
|
||||
{
|
||||
"enable" : false,
|
||||
"address" : "239.255.28.01",
|
||||
"port" : 2801,
|
||||
"priority" : 200,
|
||||
"timeout" : 10000,
|
||||
"shared" : false
|
||||
},
|
||||
|
||||
"webConfig" :
|
||||
{
|
||||
"document_root" : "",
|
||||
@ -173,13 +163,22 @@
|
||||
"disable": [""]
|
||||
},
|
||||
|
||||
"instCapture" : {
|
||||
"instCapture" :
|
||||
{
|
||||
"systemEnable" : true,
|
||||
"systemPriority" : 250,
|
||||
"v4lEnable" : false,
|
||||
"v4lPriority" : 240
|
||||
},
|
||||
|
||||
"network" :
|
||||
{
|
||||
"internetAccessAPI" : false,
|
||||
"ipWhitelist" : [],
|
||||
"apiAuth" : true,
|
||||
"localApiAuth" : false
|
||||
},
|
||||
|
||||
"ledConfig" :
|
||||
{
|
||||
"top" : 8,
|
||||
|
@ -80,10 +80,10 @@ typedef struct {
|
||||
#define STATIC_ASSERT(condition, message) \
|
||||
_Static_assert(condition, message)
|
||||
#else
|
||||
#define STATIC_ASSERT(condition, message) // FIXME
|
||||
#define STATIC_ASSERT(condition, message)
|
||||
#endif
|
||||
#else
|
||||
#define STATIC_ASSERT(condition, message) // FIXME
|
||||
#define STATIC_ASSERT(condition, message)
|
||||
#endif
|
||||
|
||||
STATIC_ASSERT(sizeof(PacketHeader) == 8, "PacketHeader has invalid size");
|
||||
|
2
dependencies/external/flatbuffers
vendored
2
dependencies/external/flatbuffers
vendored
@ -1 +1 @@
|
||||
Subproject commit 0eb7b3beb037748bf5b469e4df9db862c4833e35
|
||||
Subproject commit 2d5315ff0eebfa4b9c967e708c24be0b21d921b6
|
2
dependencies/external/rpi_ws281x
vendored
2
dependencies/external/rpi_ws281x
vendored
@ -1 +1 @@
|
||||
Subproject commit 6c5ade93d1af78cd19e60ee5ecc34adfd111b186
|
||||
Subproject commit 68c6da2de32249d126264a363cc5ab788c87cc8b
|
@ -1,34 +0,0 @@
|
||||
option(BUILD_HYPERION_DOC "Build hyperion documentation" OFF)
|
||||
|
||||
# Find doxygen and check if Doxygen is installed
|
||||
find_package(Doxygen QUIET)
|
||||
|
||||
if (BUILD_HYPERION_DOC)
|
||||
if (DOXYGEN_FOUND)
|
||||
|
||||
# set input and output files
|
||||
set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/hyperion.in.doxygen)
|
||||
set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/hyperion.doxygen)
|
||||
|
||||
# request to configure the file
|
||||
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
|
||||
message(STATUS "Doxygen build started")
|
||||
|
||||
# Define all static (i.e. not generated) documentation files
|
||||
set(StaticDocumentationFiles hyperion-footer.html)
|
||||
|
||||
# Loop over all static documentation files
|
||||
foreach(StaticDocumentationFile ${StaticDocumentationFiles})
|
||||
# Copy the file to the bindary documentation directory
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${StaticDocumentationFile} ${CMAKE_CURRENT_BINARY_DIR}/html/${StaticDocumentationFile} COPYONLY)
|
||||
endforeach()
|
||||
|
||||
add_custom_target( doc_doxygen ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
VERBATIM )
|
||||
else(DOXYGEN_FOUND)
|
||||
message(WARNING "Doxygen not found, unable to generate documenation!")
|
||||
endif(DOXYGEN_FOUND)
|
||||
endif()
|
@ -1,8 +0,0 @@
|
||||
<hr>
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td><address style="text-align: right;"><small>Generated at $datetime for $projectname by <a href="http://www.doxygen.org">doxygen</a> $doxygenversion.</small></address></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -2234,7 +2234,7 @@ INTERACTIVE_SVG = NO
|
||||
# found. If left blank, it is assumed the dot tool can be found in the path.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_PATH = ""
|
||||
DOT_PATH =
|
||||
|
||||
# The DOTFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dot files that are included in the documentation (see the \dotfile
|
||||
|
@ -11,7 +11,12 @@
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
|
||||
// HyperionInstanceManager
|
||||
#include <hyperion/HyperionIManager.h>
|
||||
|
||||
class JsonCB;
|
||||
class AuthManager;
|
||||
class HyperionIManager;
|
||||
|
||||
class JsonAPI : public QObject
|
||||
{
|
||||
@ -24,16 +29,17 @@ public:
|
||||
/// @param peerAddress provide the Address of the peer
|
||||
/// @param log The Logger class of the creator
|
||||
/// @param parent Parent QObject
|
||||
/// @param localConnection True when the sender has origin home network
|
||||
/// @param noListener if true, this instance won't listen for hyperion push events
|
||||
///
|
||||
JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener = false);
|
||||
JsonAPI(QString peerAddress, Logger* log, const bool& localConnection, QObject* parent, bool noListener = false);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON message
|
||||
///
|
||||
/// @param message the incoming message as string
|
||||
///
|
||||
void handleMessage(const QString & message);
|
||||
void handleMessage(const QString & message, const QString& httpAuthHeader = "");
|
||||
|
||||
public slots:
|
||||
///
|
||||
@ -48,6 +54,32 @@ public slots:
|
||||
/// process and push new log messages from logger (if enabled)
|
||||
void incommingLogMessage(const Logger::T_LOG_MESSAGE&);
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// @brief Handle emits from AuthManager of new request, just _userAuthorized sessions are allowed to handle them
|
||||
/// @param id The id of the request
|
||||
/// @param The comment which needs to be accepted
|
||||
///
|
||||
void handlePendingTokenRequest(const QString& id, const QString& comment);
|
||||
|
||||
///
|
||||
/// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance we are allowed to send response.
|
||||
/// @param success If true the request was accepted else false and no token was created
|
||||
/// @param caller The origin caller instance who requested this token
|
||||
/// @param token The new token that is now valid
|
||||
/// @param comment The comment that was part of the request
|
||||
/// @param id The id that was part of the request
|
||||
///
|
||||
void handleTokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
|
||||
|
||||
///
|
||||
/// @brief Handle whenever the state of a instance (HyperionIManager) changes according to enum instanceState
|
||||
/// @param instaneState A state from enum
|
||||
/// @param instance The index of instance
|
||||
/// @param name The name of the instance, just available with H_CREATED
|
||||
///
|
||||
void handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name = QString());
|
||||
|
||||
signals:
|
||||
///
|
||||
/// Signal emits with the reply message provided with handleMessage()
|
||||
@ -60,6 +92,16 @@ signals:
|
||||
void forwardJsonMessage(QJsonObject);
|
||||
|
||||
private:
|
||||
/// Auth management pointer
|
||||
AuthManager* _authManager;
|
||||
|
||||
/// Reflect auth status of this client
|
||||
bool _authorized;
|
||||
bool _userAuthorized;
|
||||
|
||||
/// Reflect auth required
|
||||
bool _apiAuthRequired;
|
||||
|
||||
// true if further callbacks are forbidden (http)
|
||||
bool _noListener;
|
||||
|
||||
@ -69,6 +111,9 @@ private:
|
||||
/// Log instance
|
||||
Logger* _log;
|
||||
|
||||
/// Hyperion instance manager
|
||||
HyperionIManager* _instanceManager;
|
||||
|
||||
/// Hyperion instance
|
||||
Hyperion* _hyperion;
|
||||
|
||||
@ -95,6 +140,14 @@ private:
|
||||
/// timeout for led color refresh
|
||||
volatile qint64 _led_stream_timeout;
|
||||
|
||||
///
|
||||
/// @brief Handle the switches of Hyperion instances
|
||||
/// @param instance the instance to switch
|
||||
/// @param forced indicate if it was a forced switch by system
|
||||
/// @return true on success. false if not found
|
||||
///
|
||||
bool handleInstanceSwitch(const quint8& instance = 0, const bool& forced = false);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON Color message
|
||||
///
|
||||
@ -151,6 +204,13 @@ private:
|
||||
///
|
||||
void handleClearCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON Clearall message
|
||||
///
|
||||
/// @param message the incoming message
|
||||
///
|
||||
void handleClearallCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON Adjustment message
|
||||
///
|
||||
@ -214,12 +274,26 @@ private:
|
||||
///
|
||||
void handleVideoModeCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON Clearall message
|
||||
/// Handle an incoming JSON plugin message
|
||||
///
|
||||
/// @param message the incoming message
|
||||
///
|
||||
void handleClearallCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
void handleAuthorizeCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
///
|
||||
/// Handle HTTP on-the-fly token authorization
|
||||
/// @param command The command
|
||||
/// @param tan The tan
|
||||
/// @param token The token to verify
|
||||
/// @return True on succcess else false (pushes failed client feedback)
|
||||
///
|
||||
bool handleHTTPAuth(const QString& command, const int& tan, const QString& token);
|
||||
|
||||
/// Handle an incoming JSON instance message
|
||||
///
|
||||
/// @param message the incoming message
|
||||
///
|
||||
void handleInstanceCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON message of unknown type
|
||||
|
@ -23,7 +23,7 @@ class JsonCB : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
JsonCB(QObject* parent);
|
||||
JsonCB(Hyperion* hyperion, QObject* parent);
|
||||
|
||||
///
|
||||
/// @brief Subscribe to future data updates given by cmd
|
||||
@ -94,6 +94,18 @@ private slots:
|
||||
///
|
||||
void handleSettingsChange(const settings::type& type, const QJsonDocument& data);
|
||||
|
||||
///
|
||||
/// @brief Handle led config specific updates (required for led color streaming with positional display)
|
||||
/// @param type The settings type from enum
|
||||
/// @param data The data as QJsonDocument
|
||||
///
|
||||
void handleLedsConfigChange(const settings::type& type, const QJsonDocument& data);
|
||||
|
||||
///
|
||||
/// @brief Handle Hyperion instance manager change
|
||||
///
|
||||
void handleInstanceChange();
|
||||
|
||||
private:
|
||||
/// pointer of Hyperion instance
|
||||
Hyperion* _hyperion;
|
||||
|
222
include/db/AuthTable.h
Normal file
222
include/db/AuthTable.h
Normal file
@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
// hyperion
|
||||
#include <db/DBManager.h>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
#include <QUuid>
|
||||
|
||||
///
|
||||
/// @brief Authentication table interface
|
||||
///
|
||||
class AuthTable : public DBManager
|
||||
{
|
||||
|
||||
public:
|
||||
/// construct wrapper with auth table
|
||||
AuthTable(QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
{
|
||||
// init Auth table
|
||||
setTable("auth");
|
||||
// create table columns
|
||||
createTable(QStringList()<<"user TEXT"<<"password BLOB"<<"token BLOB"<<"salt BLOB"<<"comment TEXT"<<"id TEXT"<<"created_at TEXT"<<"last_use TEXT");
|
||||
};
|
||||
~AuthTable(){};
|
||||
|
||||
///
|
||||
/// @brief Create a user record, if called on a existing user the auth is recreated
|
||||
/// @param[in] user The username
|
||||
/// @param[in] pw The password
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool createUser(const QString& user, const QString& pw)
|
||||
{
|
||||
// new salt
|
||||
QByteArray salt = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha512).toHex();
|
||||
QVariantMap map;
|
||||
map["user"] = user;
|
||||
map["salt"] = salt;
|
||||
map["password"] = hashPasswordWithSalt(pw,salt);
|
||||
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user",user));
|
||||
return createRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if user record exists
|
||||
/// @param[in] user The user id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool userExist(const QString& user)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user",user));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if a user is authorized for access with given pw.
|
||||
/// @param user The user name
|
||||
/// @param pw The password
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool isUserAuthorized(const QString& user, const QString& pw)
|
||||
{
|
||||
if(userExist(user) && (calcPasswordHashOfUser(user, pw) == getPasswordHashOfUser(user)))
|
||||
{
|
||||
updateUserUsed(user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update 'last_use' column entry for the corresponding user
|
||||
/// @param[in] user The user to search for
|
||||
///
|
||||
inline void updateUserUsed(const QString& user)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if token record exists, updates last_use on success
|
||||
/// @param[in] token The token id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool tokenExist(const QString& token)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("token", hashToken(token)));
|
||||
if(recordExists(cond))
|
||||
{
|
||||
// update it
|
||||
createRecord(cond,map);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Create a new token record with comment
|
||||
/// @param[in] token The token id as plaintext
|
||||
/// @param[in] comment The comment for the token (eg a human readable identifier)
|
||||
/// @param[in] id The id for the token
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool createToken(const QString& token, const QString& comment, const QString& id)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["comment"] = comment;
|
||||
map["id"] = idExist(id) ? QUuid::createUuid().toString().remove("{").remove("}").left(5) : id;
|
||||
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("token", hashToken(token)));
|
||||
return createRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Delete token record by id
|
||||
/// @param[in] id The token id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool deleteToken(const QString& id)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("id", id));
|
||||
return deleteRecord(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get all 'comment', 'last_use' and 'id' column entries
|
||||
/// @return A vector of all lists
|
||||
///
|
||||
inline const QVector<QVariantMap> getTokenList()
|
||||
{
|
||||
QVector<QVariantMap> results;
|
||||
getRecords(results, QStringList() << "comment" << "id" << "last_use");
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if id exists
|
||||
/// @param[in] id The id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool idExist(const QString& id)
|
||||
{
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("id", id));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get the passwort hash of a user from db
|
||||
/// @param user The user name
|
||||
/// @return password as hash
|
||||
///
|
||||
inline const QByteArray getPasswordHashOfUser(const QString& user)
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
getRecord(cond, results, QStringList()<<"password");
|
||||
|
||||
return results["password"].toByteArray();
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Calc the password hash of a user based on user name and password
|
||||
/// @param user The user name
|
||||
/// @param pw The password
|
||||
/// @return The calced password hash
|
||||
///
|
||||
inline const QByteArray calcPasswordHashOfUser(const QString& user, const QString& pw)
|
||||
{
|
||||
// get salt
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("user", user));
|
||||
getRecord(cond, results, QStringList()<<"salt");
|
||||
|
||||
// calc
|
||||
return hashPasswordWithSalt(pw,results["salt"].toByteArray());
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Create a password hash of plaintex password + salt
|
||||
/// @param pw The plaintext password
|
||||
/// @param salt The salt
|
||||
/// @return The password hash with salt
|
||||
///
|
||||
inline const QByteArray hashPasswordWithSalt(const QString& pw, const QByteArray& salt)
|
||||
{
|
||||
return QCryptographicHash::hash(pw.toUtf8().append(salt), QCryptographicHash::Sha512).toHex();
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Create a token hash
|
||||
/// @param token The plaintext token
|
||||
/// @return The token hash
|
||||
///
|
||||
inline const QByteArray hashToken(const QString& token)
|
||||
{
|
||||
return QCryptographicHash::hash(token.toUtf8(), QCryptographicHash::Sha512).toHex();
|
||||
}
|
||||
};
|
130
include/db/DBManager.h
Normal file
130
include/db/DBManager.h
Normal file
@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QPair>
|
||||
#include <QVector>
|
||||
|
||||
class QSqlDatabase;
|
||||
class QSqlQuery;
|
||||
|
||||
typedef QPair<QString,QVariant> CPair;
|
||||
typedef QVector<CPair> VectorPair;
|
||||
|
||||
///
|
||||
/// @brief Database interface for SQLite3.
|
||||
/// Inherit this class to create component specific methods based on this interface
|
||||
/// Usage: setTable(name) once before you use read/write actions
|
||||
/// To use another database use setDb(newDB) (defaults to "hyperion")
|
||||
///
|
||||
/// Incompatible functions with SQlite3:
|
||||
/// QSqlQuery::size() returns always -1
|
||||
///
|
||||
class DBManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DBManager(QObject* parent = nullptr);
|
||||
~DBManager();
|
||||
|
||||
/// set root path
|
||||
void setRootPath(const QString& rootPath);
|
||||
/// define the database to work with
|
||||
void setDatabaseName(const QString& dbn) { _dbn = dbn; };
|
||||
/// set a table to work with
|
||||
void setTable(const QString& table);
|
||||
|
||||
/// get current database object set with setDB()
|
||||
QSqlDatabase getDB() const;
|
||||
|
||||
///
|
||||
/// @brief Create a table (if required) with the given columns. Older tables will be updated accordingly with missing columns
|
||||
/// Does not delete or migrate old columns
|
||||
/// @param[in] columns The columns of the table. Requires at least one entry!
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool createTable(QStringList& columns) const;
|
||||
|
||||
///
|
||||
/// @brief Create a column if the column already exists returns false and logs error
|
||||
/// @param[in] column The column of the table
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool createColumn(const QString& column) const;
|
||||
|
||||
///
|
||||
/// @brief Check if at least one record exists in table with the conditions
|
||||
/// @param[in] conditions The search conditions (WHERE)
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool recordExists(const VectorPair& conditions) const;
|
||||
|
||||
///
|
||||
/// @brief Create a new record in table when the conditions find no existing entry. Add additional key:value pairs in columns
|
||||
/// DO NOT repeat column keys between 'conditions' and 'columns' as they will be merged on creation
|
||||
/// @param[in] conditions conditions to search for, as a record may exist and should be updated instead (WHERE)
|
||||
/// @param[in] columns columns to create or update (optional)
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool createRecord(const VectorPair& conditions, const QVariantMap& columns = QVariantMap()) const;
|
||||
|
||||
///
|
||||
/// @brief Update a record with conditions and additional key:value pairs in columns
|
||||
/// @param[in] conditions conditions which rows should be updated (WHERE)
|
||||
/// @param[in] columns columns to update
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool updateRecord(const VectorPair& conditions, const QVariantMap& columns) const;
|
||||
|
||||
///
|
||||
/// @brief Get data of a single record, multiple records are not supported
|
||||
/// @param[in] conditions condition to search for (WHERE)
|
||||
/// @param[out] results results of query
|
||||
/// @param[in] tColumns target columns to search in (optional) if not provided returns all columns
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool getRecord(const VectorPair& conditions, QVariantMap& results, const QStringList& tColumns = QStringList()) const;
|
||||
|
||||
///
|
||||
/// @brief Get data of multiple records, you need to specify the columns. This search is without conditions. Good to grab all data from db
|
||||
/// @param[in] conditions condition to search for (WHERE)
|
||||
/// @param[out] results results of query
|
||||
/// @param[in] tColumns target columns to search in (optional) if not provided returns all columns
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool getRecords(QVector<QVariantMap>& results, const QStringList& tColumns = QStringList()) const;
|
||||
|
||||
///
|
||||
/// @brief Delete a record determined by conditions
|
||||
/// @param[in] conditions conditions of the row to delete it (WHERE)
|
||||
/// @return True on success; on error or not found false
|
||||
///
|
||||
bool deleteRecord(const VectorPair& conditions) const;
|
||||
|
||||
///
|
||||
/// @brief Check if table exists in current database
|
||||
/// @param[in] table The name of the table
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool tableExists(const QString& table) const;
|
||||
|
||||
///
|
||||
/// @brief Delete a table, fails silent (return true) if table is not found
|
||||
/// @param[in] table The name of the table
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool deleteTable(const QString& table) const;
|
||||
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
/// databse connection & file name, defaults to hyperion
|
||||
QString _dbn = "hyperion";
|
||||
/// table in database
|
||||
QString _table;
|
||||
|
||||
/// addBindValue to query given by QVariantList
|
||||
void doAddBindValue(QSqlQuery& query, const QVariantList& variants) const;
|
||||
};
|
231
include/db/InstanceTable.h
Normal file
231
include/db/InstanceTable.h
Normal file
@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
|
||||
// db
|
||||
#include <db/DBManager.h>
|
||||
#include <db/SettingsTable.h>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
|
||||
///
|
||||
/// @brief Hyperion instance manager specific database interface. prepares also the Hyperion database for all follow up usage (Init QtSqlConnection) along with db name
|
||||
///
|
||||
class InstanceTable : public DBManager
|
||||
{
|
||||
|
||||
public:
|
||||
InstanceTable(const QString& rootPath, QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
{
|
||||
// Init Hyperion database usage
|
||||
setRootPath(rootPath);
|
||||
setDatabaseName("hyperion");
|
||||
|
||||
// Init instance table
|
||||
setTable("instances");
|
||||
createTable(QStringList()<<"instance INTEGER"<<"friendly_name TEXT"<<"enabled INTEGER DEFAULT 0"<<"last_use TEXT");
|
||||
|
||||
// start/create the first Hyperion instance index 0
|
||||
createInstance();
|
||||
|
||||
};
|
||||
~InstanceTable(){};
|
||||
|
||||
///
|
||||
/// @brief Create a new Hyperion instance entry, the name needs to be unique
|
||||
/// @param name The name of the instance
|
||||
/// @param[out] inst The id that has been assigned
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool createInstance(const QString& name, quint8& inst)
|
||||
{
|
||||
VectorPair fcond;
|
||||
fcond.append(CPair("friendly_name",name));
|
||||
|
||||
// check duplicate
|
||||
if(!recordExists(fcond))
|
||||
{
|
||||
inst = 0;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance",inst));
|
||||
|
||||
// increment to next avail index
|
||||
while(recordExists(cond))
|
||||
{
|
||||
inst++;
|
||||
cond.removeFirst();
|
||||
cond.append(CPair("instance",inst));
|
||||
}
|
||||
// create
|
||||
QVariantMap data;
|
||||
data["friendly_name"] = name;
|
||||
data["instance"] = inst;
|
||||
VectorPair lcond;
|
||||
return createRecord(lcond, data);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Delete a Hyperion instance
|
||||
/// @param inst The id that has been assigned
|
||||
/// @return True on success else false
|
||||
///
|
||||
inline bool deleteInstance(const quint8& inst)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance",inst));
|
||||
if(deleteRecord(cond))
|
||||
{
|
||||
// delete settings entries
|
||||
SettingsTable settingsTable(inst);
|
||||
settingsTable.deleteInstance();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Assign a new name for the given instance
|
||||
/// @param inst The instance index
|
||||
/// @param name The new name of the instance
|
||||
/// @return True on success else false (instance not found)
|
||||
///
|
||||
inline bool saveName(const quint8& inst, const QString& name)
|
||||
{
|
||||
VectorPair fcond;
|
||||
fcond.append(CPair("friendly_name",name));
|
||||
|
||||
// check duplicate
|
||||
if(!recordExists(fcond))
|
||||
{
|
||||
if(instanceExist(inst))
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance",inst));
|
||||
QVariantMap data;
|
||||
data["friendly_name"] = name;
|
||||
|
||||
return updateRecord(cond, data);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// @brief Get all instances with all columns
|
||||
/// @param justEnabled return just enabled instances if true
|
||||
/// @return The found instances
|
||||
///
|
||||
inline QVector<QVariantMap> getAllInstances(const bool& justEnabled = false)
|
||||
{
|
||||
QVector<QVariantMap> results;
|
||||
getRecords(results);
|
||||
if(justEnabled)
|
||||
{
|
||||
for (auto it = results.begin(); it != results.end();)
|
||||
{
|
||||
if( ! (*it)["enabled"].toBool())
|
||||
{
|
||||
it = results.erase(it);
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if instance record exists
|
||||
/// @param[in] user The user id
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool instanceExist(const quint8& inst)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance",inst));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get instance name by instance index
|
||||
/// @param index The index to search for
|
||||
/// @return The name of this index, may return NOT FOUND if not found
|
||||
///
|
||||
inline const QString getNamebyIndex(const quint8 index)
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance", index));
|
||||
getRecord(cond, results, QStringList("friendly_name"));
|
||||
|
||||
QString name = results["friendly_name"].toString();
|
||||
return name.isEmpty() ? "NOT FOUND" : name;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update 'last_use' timestamp
|
||||
/// @param inst The instance to update
|
||||
///
|
||||
inline void setLastUse(const quint8& inst)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance", inst));
|
||||
QVariantMap map;
|
||||
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update 'enabled' column by instance index
|
||||
/// @param inst The instance to update
|
||||
/// @param newState True when enabled else false
|
||||
///
|
||||
inline void setEnable(const quint8& inst, const bool& newState)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance", inst));
|
||||
QVariantMap map;
|
||||
map["enabled"] = newState;
|
||||
updateRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get state of 'enabled' column by instance index
|
||||
/// @param inst The instance to get
|
||||
/// @return True when enabled else false
|
||||
///
|
||||
inline bool isEnabled(const quint8& inst)
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance", inst));
|
||||
QVariantMap results;
|
||||
getRecord(cond, results);
|
||||
|
||||
return results["enabled"].toBool();
|
||||
}
|
||||
|
||||
private:
|
||||
///
|
||||
/// @brief Create first Hyperion instance entry, if index 0 is not found.
|
||||
///
|
||||
inline void createInstance()
|
||||
{
|
||||
if(instanceExist(0))
|
||||
setEnable(0, true);
|
||||
else
|
||||
{
|
||||
QVariantMap data;
|
||||
data["friendly_name"] = "First LED Hardware instance";
|
||||
VectorPair cond;
|
||||
cond.append(CPair("instance", 0));
|
||||
if(createRecord(cond, data))
|
||||
setEnable(0, true);
|
||||
else
|
||||
throw std::runtime_error("Failed to create Hyperion root instance in db! This should never be the case...");
|
||||
}
|
||||
}
|
||||
};
|
62
include/db/MetaTable.h
Normal file
62
include/db/MetaTable.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
// hyperion
|
||||
#include <db/DBManager.h>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
#include <QUuid>
|
||||
#include <QNetworkInterface>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
///
|
||||
/// @brief meta table specific database interface
|
||||
///
|
||||
class MetaTable : public DBManager
|
||||
{
|
||||
|
||||
public:
|
||||
/// construct wrapper with plugins table and columns
|
||||
MetaTable(QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
{
|
||||
setTable("meta");
|
||||
createTable(QStringList()<<"uuid TEXT"<<"created_at TEXT");
|
||||
};
|
||||
~MetaTable(){};
|
||||
|
||||
///
|
||||
/// @brief Get the uuid, if the uuid is not set it will be created
|
||||
/// @return The uuid
|
||||
///
|
||||
inline const QString getUUID()
|
||||
{
|
||||
QVector<QVariantMap> results;
|
||||
getRecords(results, QStringList() << "uuid");
|
||||
|
||||
for(const auto & entry : results)
|
||||
{
|
||||
if(!entry["uuid"].toString().isEmpty())
|
||||
return entry["uuid"].toString();
|
||||
}
|
||||
|
||||
// create new uuidv5 based on net adapter MAC, save to db and return
|
||||
QString hash;
|
||||
foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces())
|
||||
{
|
||||
if (!(interface.flags() & QNetworkInterface::IsLoopBack))
|
||||
{
|
||||
hash = QCryptographicHash::hash(interface.hardwareAddress().toLocal8Bit(),QCryptographicHash::Sha1).toHex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
const QString newUuid = QUuid::createUuidV5(QUuid(), hash).toString().mid(1, 36);
|
||||
VectorPair cond;
|
||||
cond.append(CPair("uuid",newUuid));
|
||||
QVariantMap map;
|
||||
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
createRecord(cond, map);
|
||||
|
||||
return newUuid;
|
||||
}
|
||||
};
|
124
include/db/SettingsTable.h
Normal file
124
include/db/SettingsTable.h
Normal file
@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
// hyperion
|
||||
#include <db/DBManager.h>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
#include <QJsonDocument>
|
||||
|
||||
///
|
||||
/// @brief settings table db interface
|
||||
///
|
||||
class SettingsTable : public DBManager
|
||||
{
|
||||
|
||||
public:
|
||||
/// construct wrapper with settings table
|
||||
SettingsTable(const quint8& instance, QObject* parent = nullptr)
|
||||
: DBManager(parent)
|
||||
, _hyperion_inst(instance)
|
||||
{
|
||||
setTable("settings");
|
||||
// create table columns
|
||||
createTable(QStringList()<<"type TEXT"<<"config TEXT"<<"hyperion_inst INTEGER"<<"updated_at TEXT");
|
||||
};
|
||||
~SettingsTable(){};
|
||||
|
||||
///
|
||||
/// @brief Create or update a settings record
|
||||
/// @param[in] type type of setting
|
||||
/// @param[in] config The configuration data
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool createSettingsRecord(const QString& type, const QString& config) const
|
||||
{
|
||||
QVariantMap map;
|
||||
map["config"] = config;
|
||||
map["updated_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
return createRecord(cond, map);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Test if record exist, type can be global setting or local (instance)
|
||||
/// @param[in] type type of setting
|
||||
/// @param[in] hyperion_inst The instance of hyperion assigned (might be empty)
|
||||
/// @return true on success else false
|
||||
///
|
||||
inline bool recordExist(const QString& type) const
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
return recordExists(cond);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get 'config' column of settings entry as QJsonDocument
|
||||
/// @param[in] type The settings type
|
||||
/// @return The QJsonDocument
|
||||
///
|
||||
inline QJsonDocument getSettingsRecord(const QString& type) const
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
getRecord(cond, results, QStringList("config"));
|
||||
return QJsonDocument::fromJson(results["config"].toByteArray());
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get 'config' column of settings entry as QString
|
||||
/// @param[in] type The settings type
|
||||
/// @return The QString
|
||||
///
|
||||
inline QString getSettingsRecordString(const QString& type) const
|
||||
{
|
||||
QVariantMap results;
|
||||
VectorPair cond;
|
||||
cond.append(CPair("type",type));
|
||||
// when a setting is not global we are searching also for the instance
|
||||
if(!isSettingGlobal(type))
|
||||
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
|
||||
getRecord(cond, results, QStringList("config"));
|
||||
return results["config"].toString();
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Delete all settings entries associated with this instance, called from InstanceTable of HyperionIManager
|
||||
///
|
||||
inline void deleteInstance() const
|
||||
{
|
||||
VectorPair cond;
|
||||
cond.append(CPair("hyperion_inst",_hyperion_inst));
|
||||
deleteRecord(cond);
|
||||
}
|
||||
|
||||
inline bool isSettingGlobal(const QString& type) const
|
||||
{
|
||||
// list of global settings
|
||||
QStringList list;
|
||||
// server port services
|
||||
list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network"
|
||||
// capture
|
||||
<< "framegrabber" << "grabberV4L2"
|
||||
// other
|
||||
<< "logger";
|
||||
|
||||
return list.contains(type);
|
||||
}
|
||||
|
||||
private:
|
||||
const quint8 _hyperion_inst;
|
||||
};
|
@ -46,13 +46,13 @@ public:
|
||||
/// @brief Set manual interuption to true,
|
||||
/// Note: DO NOT USE QThread::interuption!
|
||||
///
|
||||
void setInteruptionFlag() { _interupt = true; };
|
||||
void requestInterruption() { _interupt = true; };
|
||||
|
||||
///
|
||||
/// @brief Check if the interuption flag has been set
|
||||
/// @return The flag state
|
||||
///
|
||||
bool hasInteruptionFlag() { return _interupt; };
|
||||
bool isInterruptionRequested() { return _interupt; };
|
||||
|
||||
QString getScript() const { return _script; }
|
||||
QString getName() const { return _name; }
|
||||
|
@ -49,5 +49,4 @@ public:
|
||||
static PyObject* wrapImageCOffset (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageCShear (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageResetT (PyObject *self, PyObject *args);
|
||||
static Effect * getEffect();
|
||||
};
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
class QTcpServer;
|
||||
class FlatBufferClient;
|
||||
class NetOrigin;
|
||||
|
||||
|
||||
///
|
||||
/// @brief A TcpServer to receive images of different formats with Google Flatbuffer
|
||||
@ -56,6 +58,7 @@ private:
|
||||
|
||||
private:
|
||||
QTcpServer* _server;
|
||||
NetOrigin* _netOrigin;
|
||||
Logger* _log;
|
||||
int _timeout;
|
||||
quint16 _port;
|
||||
|
@ -26,6 +26,9 @@ public slots:
|
||||
void setSignalDetectionEnable(bool enable);
|
||||
void setDeviceVideoStandard(QString device, VideoStandard videoStandard);
|
||||
|
||||
signals:
|
||||
void componentStateChanged(const hyperion::Components component, bool enable);
|
||||
|
||||
private slots:
|
||||
void newFrame(const Image<ColorRgb> & image);
|
||||
void readError(const char* err);
|
||||
|
173
include/hyperion/AuthManager.h
Normal file
173
include/hyperion/AuthManager.h
Normal file
@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/settings.h>
|
||||
|
||||
//qt
|
||||
#include <QMap>
|
||||
|
||||
class AuthTable;
|
||||
class MetaTable;
|
||||
class QTimer;
|
||||
|
||||
///
|
||||
/// @brief Manage the authorization of user and tokens. This class is created once as part of the HyperionDaemon
|
||||
/// To work with the global instance use AuthManager::getInstance()
|
||||
///
|
||||
class AuthManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
friend class HyperionDaemon;
|
||||
/// constructor is private, can be called from HyperionDaemon
|
||||
AuthManager(QObject* parent = 0);
|
||||
|
||||
public:
|
||||
struct AuthDefinition{
|
||||
QString id;
|
||||
QString comment;
|
||||
QObject* caller;
|
||||
uint64_t timeoutTime;
|
||||
QString token;
|
||||
QString lastUse;
|
||||
};
|
||||
|
||||
///
|
||||
/// @brief Get the unique id (imported from removed class 'Stats')
|
||||
/// @return The unique id
|
||||
///
|
||||
const QString & getID() { return _uuid; };
|
||||
|
||||
///
|
||||
/// @brief Get all available token entries
|
||||
///
|
||||
const QVector<AuthDefinition> getTokenList();
|
||||
|
||||
///
|
||||
/// @brief Check authorization is required according to the user setting
|
||||
/// @return True if authorization required else false
|
||||
///
|
||||
bool & isAuthRequired();
|
||||
|
||||
///
|
||||
/// @brief Check if authorization is required for local network connections
|
||||
/// @return True if authorization required else false
|
||||
///
|
||||
bool & isLocalAuthRequired();
|
||||
|
||||
///
|
||||
/// @brief Create a new token and skip the usual chain
|
||||
/// @param comment The comment that should be used for
|
||||
/// @return The new Auth definition
|
||||
///
|
||||
const AuthDefinition createToken(const QString& comment);
|
||||
|
||||
///
|
||||
/// @brief Check if user is authorized
|
||||
/// @param user The username
|
||||
/// @param pw The password
|
||||
/// @return True if authorized else false
|
||||
///
|
||||
bool isUserAuthorized(const QString& user, const QString& pw);
|
||||
|
||||
///
|
||||
/// @brief Check if token is authorized
|
||||
/// @param token The token
|
||||
/// @return True if authorized else false
|
||||
///
|
||||
bool isTokenAuthorized(const QString& token);
|
||||
|
||||
///
|
||||
/// @brief Generate a new pending token request with the provided comment and id as identifier helper
|
||||
/// @param caller The QObject of the caller to deliver the reply
|
||||
/// @param comment The comment as ident helper
|
||||
/// @param id The id created by the caller
|
||||
///
|
||||
void setNewTokenRequest(QObject* caller, const QString& comment, const QString& id);
|
||||
|
||||
///
|
||||
/// @brief Accept a token request by id, generate token and inform token caller
|
||||
/// @param id The id of the request
|
||||
/// @return True on success, false if not found
|
||||
///
|
||||
bool acceptTokenRequest(const QString& id);
|
||||
|
||||
///
|
||||
/// @brief Deny a token request by id, inform the requester
|
||||
/// @param id The id of the request
|
||||
/// @return True on success, false if not found
|
||||
///
|
||||
bool denyTokenRequest(const QString& id);
|
||||
|
||||
///
|
||||
/// @brief Get pending requests
|
||||
/// @return All pending requests
|
||||
///
|
||||
const QMap<QString, AuthDefinition> getPendingRequests();
|
||||
|
||||
///
|
||||
/// @brief Delete a token by id
|
||||
/// @param id The token id
|
||||
/// @return True on success else false (or not found)
|
||||
///
|
||||
bool deleteToken(const QString& id);
|
||||
|
||||
/// Pointer of this instance
|
||||
static AuthManager* manager;
|
||||
/// Get Pointer of this instance
|
||||
static AuthManager* getInstance() { return manager; };
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// @brief Handle settings update from Hyperion Settingsmanager emit
|
||||
/// @param type settings type from enum
|
||||
/// @param config configuration object
|
||||
///
|
||||
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Emits whenever a new token Request has been created along with the id and comment
|
||||
/// @param id The id of the request
|
||||
/// @param comment The comment of the request
|
||||
///
|
||||
void newPendingTokenRequest(const QString& id, const QString& comment);
|
||||
|
||||
///
|
||||
/// @brief Emits when the user has accepted or denied a token
|
||||
/// @param success If true the request was accepted else false and no token will be available
|
||||
/// @param caller The origin caller instance who requested this token
|
||||
/// @param token The new token that is now valid
|
||||
/// @param comment The comment that was part of the request
|
||||
/// @param id The id that was part of the request
|
||||
///
|
||||
void tokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
|
||||
|
||||
private:
|
||||
/// Database interface for auth table
|
||||
AuthTable* _authTable;
|
||||
|
||||
/// Database interface for meta table
|
||||
MetaTable* _metaTable;
|
||||
|
||||
/// Unique ID (imported from removed class 'Stats')
|
||||
QString _uuid;
|
||||
|
||||
/// All pending requests
|
||||
QMap<QString,AuthDefinition> _pendingRequests;
|
||||
|
||||
/// Reflect state of global auth
|
||||
bool _authRequired;
|
||||
|
||||
/// Reflect state of local auth
|
||||
bool _localAuthRequired;
|
||||
|
||||
/// Timer for counting against pendingRequest timeouts
|
||||
QTimer* _timer;
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// @brief Check timeout of pending requests
|
||||
///
|
||||
void checkTimeout();
|
||||
};
|
@ -9,6 +9,7 @@
|
||||
#include <grabber/VideoStandard.h>
|
||||
#include <utils/ImageResampler.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
///
|
||||
/// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler)
|
||||
@ -96,6 +97,12 @@ public:
|
||||
///
|
||||
void setEnabled(bool enable);
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief PIPE component state changes from HyperionDaemon to V4L2Grabber
|
||||
///
|
||||
void componentStateChanged(const hyperion::Components component, bool enable);
|
||||
|
||||
protected:
|
||||
ImageResampler _imageResampler;
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QMutex>
|
||||
|
||||
// hyperion-utils includes
|
||||
@ -36,7 +35,6 @@
|
||||
#include <utils/settings.h>
|
||||
|
||||
// Forward class declaration
|
||||
class QTimer;
|
||||
class HyperionDaemon;
|
||||
class ImageProcessor;
|
||||
class MessageForwarder;
|
||||
@ -71,28 +69,13 @@ public:
|
||||
///
|
||||
/// Destructor; cleans up resources
|
||||
///
|
||||
~Hyperion();
|
||||
virtual ~Hyperion();
|
||||
|
||||
///
|
||||
/// free all alocated objects, should be called only from constructor or before restarting hyperion
|
||||
///
|
||||
void freeObjects(bool emitCloseSignal=false);
|
||||
|
||||
///
|
||||
/// @brief creates a new Hyperion instance, usually called from the Hyperion Daemon
|
||||
/// @param[in] daemon The Hyperion daemon parent
|
||||
/// @param[in] instance The instance id
|
||||
/// @param[in] rootPath Root path of all hyperion userdata
|
||||
/// @return Hyperion instance pointer
|
||||
///
|
||||
static Hyperion* initInstance(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath);
|
||||
|
||||
///
|
||||
/// @brief Get a pointer of this Hyperion instance
|
||||
/// @return Hyperion instance pointer
|
||||
///
|
||||
static Hyperion* getInstance();
|
||||
|
||||
///
|
||||
/// @brief Get a pointer to the effect engine
|
||||
/// @return EffectEngine instance pointer
|
||||
@ -122,6 +105,12 @@ public:
|
||||
///
|
||||
bool saveSettings(QJsonObject config, const bool& correct = false);
|
||||
|
||||
///
|
||||
/// @brief Get instance index of this instance
|
||||
/// @return The index of this instance
|
||||
///
|
||||
const quint8 & getInstanceIndex() { return _instIndex; };
|
||||
|
||||
///
|
||||
/// Returns the number of attached leds
|
||||
///
|
||||
@ -194,26 +183,6 @@ public:
|
||||
/// @return json config
|
||||
const QJsonObject& getQJsonConfig();
|
||||
|
||||
/// get path+filename of configfile
|
||||
/// @return the current config path+filename
|
||||
QString getConfigFilePath() { return _configFile; };
|
||||
|
||||
/// get filename of configfile
|
||||
/// @return the current config filename
|
||||
QString getConfigFileName() const;
|
||||
|
||||
///
|
||||
/// @brief Register a new input by priority, the priority is not active (timeout -100 isn't muxer recognized) until you start to update the data with setInput()
|
||||
/// A repeated call to update the base data of a known priority won't overwrite their current timeout
|
||||
/// @param[in] priority The priority of the channel
|
||||
/// @param[in] component The component of the channel
|
||||
/// @param[in] origin Who set the channel (CustomString@IP)
|
||||
/// @param[in] owner Specific owner string, might be empty
|
||||
/// @param[in] smooth_cfg The smooth id to use
|
||||
///
|
||||
void registerInput(const int priority, const hyperion::Components& component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0);
|
||||
|
||||
|
||||
/// enable/disable automatic/priorized source selection
|
||||
/// @param enabled the state
|
||||
void setSourceAutoSelectEnabled(bool enabled);
|
||||
@ -244,21 +213,9 @@ public:
|
||||
|
||||
ComponentRegister& getComponentRegister() { return _componentRegister; };
|
||||
|
||||
bool configModified() { return _configMod; };
|
||||
|
||||
bool configWriteable() { return _configWrite; };
|
||||
|
||||
/// gets the methode how image is maped to leds
|
||||
const int & getLedMappingType();
|
||||
|
||||
/// get the root path for all hyperion user data files
|
||||
const QString &getRootPath() { return _rootPath; };
|
||||
|
||||
/// get unique id per instance
|
||||
const QString &getId(){ return _id; };
|
||||
/// set unique id
|
||||
void setId(QString id){ _id = id; };
|
||||
|
||||
int getLatchTime() const;
|
||||
|
||||
/// forward smoothing config
|
||||
@ -273,6 +230,17 @@ public:
|
||||
const QString & getActiveDevice();
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// @brief Register a new input by priority, the priority is not active (timeout -100 isn't muxer recognized) until you start to update the data with setInput()
|
||||
/// A repeated call to update the base data of a known priority won't overwrite their current timeout
|
||||
/// @param[in] priority The priority of the channel
|
||||
/// @param[in] component The component of the channel
|
||||
/// @param[in] origin Who set the channel (CustomString@IP)
|
||||
/// @param[in] owner Specific owner string, might be empty
|
||||
/// @param[in] smooth_cfg The smooth id to use
|
||||
///
|
||||
void registerInput(const int priority, const hyperion::Components& component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0);
|
||||
|
||||
///
|
||||
/// @brief Update the current color of a priority (prev registered with registerInput())
|
||||
/// DO NOT use this together with setInputImage() at the same time!
|
||||
@ -293,14 +261,7 @@ public slots:
|
||||
/// @param clearEffect Should be true when NOT called from an effect
|
||||
/// @return True on success, false when priority is not found
|
||||
///
|
||||
bool setInputImage(const int priority, const Image<ColorRgb>& image, int64_t timeout_ms = -1, const bool& clearEffect = true);
|
||||
|
||||
///
|
||||
/// @brief Set the given priority to inactive
|
||||
/// @param priority The priority
|
||||
/// @return True on success false if not found
|
||||
///
|
||||
bool setInputInactive(const quint8& priority);
|
||||
bool setInputImage(const int priority, const Image<ColorRgb>& image, const int64_t timeout_ms = -1, const bool& clearEffect = true);
|
||||
|
||||
///
|
||||
/// Writes a single color to all the leds for the given time and priority
|
||||
@ -309,10 +270,18 @@ public slots:
|
||||
///
|
||||
/// @param[in] priority The priority of the written color
|
||||
/// @param[in] ledColor The color to write to the leds
|
||||
/// @param[in] origin The setter
|
||||
/// @param[in] timeout_ms The time the leds are set to the given color [ms]
|
||||
/// @param[in] origin The setter
|
||||
/// @param clearEffect Should be true when NOT called from an effect
|
||||
///
|
||||
void setColor(int priority, const ColorRgb &ledColor, const int timeout_ms = -1, const QString& origin = "System" ,bool clearEffects = true);
|
||||
void setColor(const int priority, const ColorRgb &ledColor, const int timeout_ms = -1, const QString& origin = "System" ,bool clearEffects = true);
|
||||
|
||||
///
|
||||
/// @brief Set the given priority to inactive
|
||||
/// @param priority The priority
|
||||
/// @return True on success false if not found
|
||||
///
|
||||
bool setInputInactive(const quint8& priority);
|
||||
|
||||
///
|
||||
/// Returns the list with unique adjustment identifiers
|
||||
@ -336,7 +305,7 @@ public slots:
|
||||
/// @param[in] priority The priority channel
|
||||
/// @return True on success else false (not found)
|
||||
///
|
||||
bool clear(int priority);
|
||||
bool clear(const int priority);
|
||||
|
||||
///
|
||||
/// @brief Clears all priority channels. This will switch the leds off until a new priority is written.
|
||||
@ -372,8 +341,15 @@ public slots:
|
||||
///
|
||||
void setVideoMode(const VideoMode& mode);
|
||||
|
||||
public:
|
||||
static Hyperion *_hyperion;
|
||||
///
|
||||
/// @brief Init after thread start
|
||||
///
|
||||
void start();
|
||||
|
||||
///
|
||||
/// @brief Stop the execution of this thread, helper to properly track eventing
|
||||
///
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
/// Signal which is emitted when a priority channel is actively cleared
|
||||
@ -453,15 +429,24 @@ signals:
|
||||
///
|
||||
void rawLedColors(const std::vector<ColorRgb>& ledValues);
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// @brief Emits before thread quit is requested
|
||||
///
|
||||
void finished();
|
||||
|
||||
///
|
||||
/// @brief Emits after thread has been started
|
||||
///
|
||||
void started();
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// Updates the priority muxer with the current time and (re)writes the led color with applied
|
||||
/// transforms.
|
||||
///
|
||||
void update();
|
||||
|
||||
/// check for configWriteable and modified changes, called by _fsWatcher or fallback _cTimer
|
||||
void checkConfigState(QString cfile = NULL);
|
||||
private slots:
|
||||
|
||||
///
|
||||
/// @brief Apply ComponentRegister emits for COMP_ALL. Enables/Disables core timers
|
||||
@ -477,17 +462,23 @@ private slots:
|
||||
///
|
||||
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
|
||||
|
||||
///
|
||||
/// @brief Apply new videoMode from Daemon to _currVideoMode
|
||||
///
|
||||
void handleNewVideoMode(const VideoMode& mode) { _currVideoMode = mode; };
|
||||
|
||||
private:
|
||||
friend class HyperionDaemon;
|
||||
friend class HyperionIManager;
|
||||
|
||||
///
|
||||
/// Constructs the Hyperion instance based on the given Json configuration
|
||||
/// @brief Constructs the Hyperion instance, just accessible for HyperionIManager
|
||||
/// @param instance The instance index
|
||||
///
|
||||
/// @param[in] qjsonConfig The Json configuration
|
||||
///
|
||||
Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath);
|
||||
Hyperion(const quint8& instance);
|
||||
|
||||
/// The parent Hyperion Daemon
|
||||
HyperionDaemon* _daemon;
|
||||
/// instance index
|
||||
const quint8 _instIndex;
|
||||
|
||||
/// Settings manager of this instance
|
||||
SettingsManager* _settingsManager;
|
||||
@ -524,40 +515,17 @@ private:
|
||||
// Message forwarder
|
||||
MessageForwarder * _messageForwarder;
|
||||
|
||||
/// the name of config file
|
||||
QString _configFile;
|
||||
|
||||
/// root path for all hyperion user data files
|
||||
QString _rootPath;
|
||||
|
||||
/// unique id per instance
|
||||
QString _id;
|
||||
|
||||
/// Logger instance
|
||||
Logger * _log;
|
||||
|
||||
/// count of hardware leds
|
||||
unsigned _hwLedCount;
|
||||
|
||||
QByteArray _configHash;
|
||||
|
||||
QSize _ledGridSize;
|
||||
|
||||
/// Store the previous compID for smarter update()
|
||||
hyperion::Components _prevCompId;
|
||||
|
||||
/// Observe filesystem changes (_configFile), if failed use Timer
|
||||
QFileSystemWatcher _fsWatcher;
|
||||
QTimer* _cTimer;
|
||||
|
||||
/// holds the prev states of configWriteable and modified
|
||||
bool _prevConfigMod = false;
|
||||
bool _prevConfigWrite = true;
|
||||
|
||||
/// holds the current states of configWriteable and modified
|
||||
bool _configMod = false;
|
||||
bool _configWrite = true;
|
||||
|
||||
/// Background effect instance, kept active to react on setting changes
|
||||
BGEffectHandler* _BGEffectHandler;
|
||||
/// Capture control for Daemon native capture
|
||||
@ -566,6 +534,8 @@ private:
|
||||
/// buffer for leds (with adjustment)
|
||||
std::vector<ColorRgb> _ledBuffer;
|
||||
|
||||
VideoMode _currVideoMode = VIDEO_2D;
|
||||
|
||||
/// Boblight instance
|
||||
BoblightServer* _boblightServer;
|
||||
|
||||
|
176
include/hyperion/HyperionIManager.h
Normal file
176
include/hyperion/HyperionIManager.h
Normal file
@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
// util
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/VideoMode.h>
|
||||
#include <utils/settings.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
// qt
|
||||
#include <QMap>
|
||||
|
||||
class Hyperion;
|
||||
class InstanceTable;
|
||||
|
||||
enum instanceState{
|
||||
H_STARTED,
|
||||
H_ON_STOP,
|
||||
H_STOPPED,
|
||||
H_CREATED,
|
||||
H_DELETED
|
||||
};
|
||||
|
||||
///
|
||||
/// @brief HyperionInstanceManager manages the instances of the the Hyperion class
|
||||
///
|
||||
class HyperionIManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// global instance pointer
|
||||
static HyperionIManager* getInstance() { return HIMinstance; };
|
||||
static HyperionIManager* HIMinstance;
|
||||
|
||||
///
|
||||
/// @brief Is given instance running?
|
||||
/// @param inst The instance to check
|
||||
/// @return True when running else false
|
||||
///
|
||||
bool IsInstanceRunning(const quint8& inst) { return _runningInstances.contains(inst); };
|
||||
|
||||
///
|
||||
/// @brief Get a Hyperion instance by index
|
||||
/// @param intance the index
|
||||
/// @return Hyperion instance, if index is not found returns instance 0
|
||||
///
|
||||
Hyperion* getHyperionInstance(const quint8& instance = 0);
|
||||
|
||||
///
|
||||
/// @brief Get instance data of all instaces in db + running state
|
||||
///
|
||||
const QVector<QVariantMap> getInstanceData();
|
||||
|
||||
///
|
||||
/// @brief Start a Hyperion instance
|
||||
/// @param instance Instance index
|
||||
/// @param block If true return when thread has been started
|
||||
/// @return Return true on success, false if not found in db
|
||||
///
|
||||
bool startInstance(const quint8& inst, const bool& block = false);
|
||||
|
||||
///
|
||||
/// @brief Stop a Hyperion instance
|
||||
/// @param instance Instance index
|
||||
/// @return Return true on success, false if not found in db
|
||||
///
|
||||
bool stopInstance(const quint8& inst);
|
||||
|
||||
///
|
||||
/// @brief Create a new Hyperion instance entry in db
|
||||
/// @param name The friendly name of the instance
|
||||
/// @param start If true it will be started after creation (async)
|
||||
/// @return Return true on success false if name is already in use or a db error occurred
|
||||
///
|
||||
bool createInstance(const QString& name, const bool& start = false);
|
||||
|
||||
///
|
||||
/// @brief Delete Hyperion instance entry in db. Cleanup also all associated table data for this instance
|
||||
/// @param inst The instance index
|
||||
/// @return Return true on success, false if not found or not allowed
|
||||
///
|
||||
bool deleteInstance(const quint8& inst);
|
||||
|
||||
///
|
||||
/// @brief Assign a new name to the given instance
|
||||
/// @param inst The instance index
|
||||
/// @param name The instance name index
|
||||
/// @return Return true on success, false if not found
|
||||
///
|
||||
bool saveName(const quint8& inst, const QString& name);
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Emits whenever the state of a instance changes according to enum instanceState
|
||||
/// @param instaneState A state from enum
|
||||
/// @param instance The index of instance
|
||||
/// @param name The name of the instance, just available with H_CREATED
|
||||
///
|
||||
void instanceStateChanged(const instanceState& state, const quint8& instance, const QString& name = QString());
|
||||
|
||||
///
|
||||
/// @brief Emits whenever something changes, the lazy version of instanceStateChanged (- H_ON_STOP) + saveName() emit
|
||||
///
|
||||
void change();
|
||||
|
||||
signals:
|
||||
///////////////////////////////////////
|
||||
/// FROM HYPERIONDAEMON TO HYPERION ///
|
||||
///////////////////////////////////////
|
||||
|
||||
///
|
||||
/// @brief PIPE videoMode back to Hyperion
|
||||
///
|
||||
void newVideoMode(const VideoMode& mode);
|
||||
|
||||
///////////////////////////////////////
|
||||
/// FROM HYPERION TO HYPERIONDAEMON ///
|
||||
///////////////////////////////////////
|
||||
|
||||
///
|
||||
/// @brief PIPE settings events from Hyperion
|
||||
///
|
||||
void settingsChanged(const settings::type& type, const QJsonDocument& data);
|
||||
|
||||
///
|
||||
/// @brief PIPE videoMode request changes from Hyperion to HyperionDaemon
|
||||
///
|
||||
void requestVideoMode(const VideoMode& mode);
|
||||
|
||||
///
|
||||
/// @brief PIPE component state changes from Hyperion to HyperionDaemon
|
||||
///
|
||||
void componentStateChanged(const hyperion::Components component, bool enable);
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// @brief handle started signal of Hyperion instances
|
||||
///
|
||||
void handleStarted();
|
||||
|
||||
///
|
||||
/// @brief handle finished signal of Hyperion instances
|
||||
///
|
||||
void handleFinished();
|
||||
|
||||
private:
|
||||
friend class HyperionDaemon;
|
||||
///
|
||||
/// @brief Construct the Manager
|
||||
/// @param The root path of all userdata
|
||||
///
|
||||
HyperionIManager(const QString& rootPath, QObject* parent = nullptr);
|
||||
|
||||
///
|
||||
/// @brief Start all instances that are marked as enabled in db. Non blocking
|
||||
///
|
||||
void startAll();
|
||||
|
||||
///
|
||||
/// @brief Stop all instances, used from hyperiond
|
||||
///
|
||||
void stopAll();
|
||||
|
||||
///
|
||||
/// @brief check if a instance is allowed for management. Instance 0 represents the root instance
|
||||
/// @apram inst The instance to check
|
||||
///
|
||||
bool isInstAllowed(const quint8& inst) { return (inst > 0); };
|
||||
|
||||
private:
|
||||
Logger* _log;
|
||||
InstanceTable* _instanceTable;
|
||||
const QString _rootPath;
|
||||
QMap<quint8, Hyperion*> _runningInstances;
|
||||
QList<quint8> _startQueue;
|
||||
};
|
@ -182,6 +182,11 @@ public:
|
||||
///
|
||||
void clearAll(bool forceClearAll=false);
|
||||
|
||||
///
|
||||
/// @brief Queue a manual push where muxer doesn't recognize them (e.g. continous single color pushes)
|
||||
///
|
||||
void queuePush(void){ emit timeRunner(); };
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Signal which emits when a effect or color with timeout > -1 is running, once per second
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <QJsonObject>
|
||||
|
||||
class Hyperion;
|
||||
class SettingsTable;
|
||||
|
||||
///
|
||||
/// @brief Manage the settings read write from/to config file, on settings changed will emit a signal to update components accordingly
|
||||
@ -17,43 +18,37 @@ class SettingsManager : public QObject
|
||||
public:
|
||||
///
|
||||
/// @brief Construct a settings manager and assign a hyperion instance
|
||||
/// @params hyperion The parent hyperion instance
|
||||
/// @params instance Instance number of Hyperion
|
||||
/// @params instance Instance index of HyperionInstanceManager
|
||||
/// @params parent The parent hyperion instance
|
||||
///
|
||||
SettingsManager(Hyperion* hyperion, const quint8& instance, const QString& configFile);
|
||||
|
||||
///
|
||||
/// @brief Construct a settings manager for HyperionDaemon
|
||||
///
|
||||
SettingsManager(const quint8& instance, const QString& configFile);
|
||||
~SettingsManager();
|
||||
SettingsManager(const quint8& instance, QObject* parent = nullptr);
|
||||
|
||||
///
|
||||
/// @brief Save a complete json config
|
||||
/// @param config The entire config object
|
||||
/// @param correct If true will correct json against schema before save
|
||||
/// @return True on success else false
|
||||
/// @return True on success else false
|
||||
///
|
||||
bool saveSettings(QJsonObject config, const bool& correct = false);
|
||||
|
||||
///
|
||||
/// @brief get a single setting json from config
|
||||
/// @param type The settings::type from enum
|
||||
/// @return The requested json data as QJsonDocument
|
||||
/// @param type The settings::type from enum
|
||||
/// @return The requested json data as QJsonDocument
|
||||
///
|
||||
const QJsonDocument getSetting(const settings::type& type);
|
||||
|
||||
///
|
||||
/// @brief get the full settings object of this instance (with global settings)
|
||||
/// @return The requested json
|
||||
/// @return The requested json
|
||||
///
|
||||
const QJsonObject & getSettings() { return _qconfig; };
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Emits whenever a config part changed.
|
||||
/// @param type The settings type from enum
|
||||
/// @param data The data as QJsonDocument
|
||||
/// @param type The settings type from enum
|
||||
/// @param data The data as QJsonDocument
|
||||
///
|
||||
void settingsChanged(const settings::type& type, const QJsonDocument& data);
|
||||
|
||||
@ -64,6 +59,9 @@ private:
|
||||
/// Logger instance
|
||||
Logger* _log;
|
||||
|
||||
/// instance of database table interface
|
||||
SettingsTable* _sTable;
|
||||
|
||||
/// the schema
|
||||
static QJsonObject schemaJson;
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
// qt includes
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
|
||||
// STL incldues
|
||||
// STL includes
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
// Utility includes
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <utils/ColorRgbw.h>
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
class QTcpServer;
|
||||
class ProtoClientConnection;
|
||||
class NetOrigin;
|
||||
|
||||
///
|
||||
/// @brief This class creates a TCP server which accepts connections wich can then send
|
||||
@ -58,6 +59,7 @@ private:
|
||||
|
||||
private:
|
||||
QTcpServer* _server;
|
||||
NetOrigin* _netOrigin;
|
||||
Logger* _log;
|
||||
int _timeout;
|
||||
quint16 _port;
|
||||
|
@ -1,109 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <cstdint>
|
||||
|
||||
// Qt includes
|
||||
#include <QSet>
|
||||
#include <QHostAddress>
|
||||
#include <QJsonDocument>
|
||||
|
||||
// Hyperion includes
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Components.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
|
||||
// settings
|
||||
#include <utils/settings.h>
|
||||
|
||||
class BonjourServiceRegister;
|
||||
class QUdpSocket;
|
||||
|
||||
///
|
||||
/// This class creates a UDP server which accepts connections from boblight clients.
|
||||
///
|
||||
class UDPListener : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
///
|
||||
/// UDPListener constructor
|
||||
/// @param hyperion Hyperion instance
|
||||
/// @param port port number on which to start listening for connections
|
||||
///
|
||||
UDPListener(const QJsonDocument& config);
|
||||
~UDPListener();
|
||||
|
||||
///
|
||||
/// @return the port number on which this UDP listens for incoming connections
|
||||
///
|
||||
uint16_t getPort() const;
|
||||
|
||||
///
|
||||
/// @return true if server is active (bind to a port)
|
||||
///
|
||||
bool active() { return _isActive; };
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// bind server to network
|
||||
///
|
||||
void start();
|
||||
|
||||
///
|
||||
/// close server
|
||||
///
|
||||
void stop();
|
||||
|
||||
void componentStateChanged(const hyperion::Components component, bool enable);
|
||||
|
||||
///
|
||||
/// @brief Handle settings update from Hyperion Settingsmanager emit or this constructor
|
||||
/// @param type settingyType from enum
|
||||
/// @param config configuration object
|
||||
///
|
||||
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief forward register data to HyperionDaemon
|
||||
///
|
||||
void registerGlobalInput(const int priority, const hyperion::Components& component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0);
|
||||
|
||||
///
|
||||
/// @brief forward led data to HyperionDaemon
|
||||
///
|
||||
const bool setGlobalInput(const int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms = -1, const bool& clearEffect = true);
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// Slot which is called when a client tries to create a new connection
|
||||
///
|
||||
void readPendingDatagrams();
|
||||
void processTheDatagram(const QByteArray * datagram, const QHostAddress * sender);
|
||||
|
||||
private:
|
||||
/// The UDP server object
|
||||
QUdpSocket * _server;
|
||||
|
||||
/// hyperion priority
|
||||
int _priority;
|
||||
|
||||
/// hyperion timeout
|
||||
int _timeout;
|
||||
|
||||
/// Logger instance
|
||||
Logger * _log;
|
||||
|
||||
/// Bonjour Service Register
|
||||
BonjourServiceRegister* _serviceRegister = nullptr;
|
||||
|
||||
/// state of connection
|
||||
bool _isActive;
|
||||
|
||||
/// address to bind
|
||||
QHostAddress _listenAddress;
|
||||
uint16_t _listenPort;
|
||||
QAbstractSocket::BindFlag _bondage;
|
||||
};
|
@ -14,7 +14,6 @@ enum Components
|
||||
COMP_SMOOTHING,
|
||||
COMP_BLACKBORDER,
|
||||
COMP_FORWARDER,
|
||||
COMP_UDPLISTENER,
|
||||
COMP_BOBLIGHTSERVER,
|
||||
COMP_GRABBER,
|
||||
COMP_V4L,
|
||||
@ -34,7 +33,6 @@ inline const char* componentToString(Components c)
|
||||
case COMP_SMOOTHING: return "Smoothing";
|
||||
case COMP_BLACKBORDER: return "Blackborder detector";
|
||||
case COMP_FORWARDER: return "Json/Proto forwarder";
|
||||
case COMP_UDPLISTENER: return "UDP listener";
|
||||
case COMP_BOBLIGHTSERVER:return "Boblight server";
|
||||
case COMP_GRABBER: return "Framegrabber";
|
||||
case COMP_V4L: return "V4L capture device";
|
||||
@ -56,7 +54,6 @@ inline const char* componentToIdString(Components c)
|
||||
case COMP_SMOOTHING: return "SMOOTHING";
|
||||
case COMP_BLACKBORDER: return "BLACKBORDER";
|
||||
case COMP_FORWARDER: return "FORWARDER";
|
||||
case COMP_UDPLISTENER: return "UDPLISTENER";
|
||||
case COMP_BOBLIGHTSERVER:return "BOBLIGHTSERVER";
|
||||
case COMP_GRABBER: return "GRABBER";
|
||||
case COMP_V4L: return "V4L";
|
||||
@ -77,7 +74,6 @@ inline Components stringToComponent(QString component)
|
||||
if (component == "SMOOTHING") return COMP_SMOOTHING;
|
||||
if (component == "BLACKBORDER") return COMP_BLACKBORDER;
|
||||
if (component == "FORWARDER") return COMP_FORWARDER;
|
||||
if (component == "UDPLISTENER") return COMP_UDPLISTENER;
|
||||
if (component == "BOBLIGHTSERVER")return COMP_BOBLIGHTSERVER;
|
||||
if (component == "GRABBER") return COMP_GRABBER;
|
||||
if (component == "V4L") return COMP_V4L;
|
||||
|
@ -55,12 +55,6 @@ QString getDirName( QString sourceFile);
|
||||
///
|
||||
bool removeFile(const QString& path, Logger* log, bool ignError=false);
|
||||
|
||||
///
|
||||
/// @brief Convert a path that may contain special placeholders
|
||||
/// @param[in] path The path to convert
|
||||
///
|
||||
QString convertPath(const QString path);
|
||||
|
||||
///
|
||||
/// @brief resolve the file error and print a message
|
||||
/// @param[in] file The file which caused the error
|
||||
|
@ -3,6 +3,7 @@
|
||||
// util
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
// qt
|
||||
#include <QObject>
|
||||
@ -27,6 +28,10 @@ public:
|
||||
void operator=(GlobalSignals const&) = delete;
|
||||
|
||||
signals:
|
||||
///////////////////////////////////////
|
||||
///////////// TO HYPERION /////////////
|
||||
///////////////////////////////////////
|
||||
|
||||
///
|
||||
/// @brief PIPE SystemCapture images from GrabberWrapper to Hyperion class
|
||||
/// @param name The name of the platform capture that is currently active
|
||||
@ -39,5 +44,56 @@ signals:
|
||||
/// @param name The name of the v4l capture (path) that is currently active
|
||||
/// @param image The prepared image
|
||||
///
|
||||
void setV4lImage(const QString& name, const Image<ColorRgb> & image);
|
||||
void setV4lImage(const QString& name, const Image<ColorRgb>& image);
|
||||
|
||||
///
|
||||
/// @brief PIPE the register command for a new global input over HyperionDaemon to Hyperion class
|
||||
/// @param[in] priority The priority of the channel
|
||||
/// @param[in] component The component of the channel
|
||||
/// @param[in] origin Who set the channel (CustomString@IP)
|
||||
/// @param[in] owner Specific owner string, might be empty
|
||||
/// @param[in] smooth_cfg The smooth id to use
|
||||
///
|
||||
void registerGlobalInput(const int priority, const hyperion::Components& component, const QString& origin = "External", const QString& owner = "", unsigned smooth_cfg = 0);
|
||||
|
||||
///
|
||||
/// @brief PIPE the clear command for the global priority channel over HyperionDaemon to Hyperion class
|
||||
/// @param[in] priority The priority channel
|
||||
///
|
||||
void clearGlobalInput(int priority);
|
||||
|
||||
///
|
||||
/// @brief PIPE the clearAll command over HyperionDaemon to Hyperion class
|
||||
///
|
||||
void clearAllGlobalInput(bool forceClearAll=false);
|
||||
|
||||
///
|
||||
/// @brief PIPE external images over HyperionDaemon to Hyperion class
|
||||
/// @param[in] priority The priority of the channel
|
||||
/// @param image The prepared image
|
||||
/// @param[in] timeout_ms The timeout in milliseconds
|
||||
/// @param clearEffect Should be true when NOT called from an effect
|
||||
///
|
||||
void setGlobalImage(const int priority, const Image<ColorRgb>& image, const int timeout_ms, const bool& clearEffect = true);
|
||||
|
||||
///
|
||||
/// @brief PIPE external color message over HyperionDaemon to Hyperion class
|
||||
/// @param[in] priority The priority of the channel
|
||||
/// @param image The prepared color
|
||||
/// @param[in] timeout_ms The timeout in milliseconds
|
||||
/// @param[in] origin The setter
|
||||
/// @param clearEffect Should be true when NOT called from an effect
|
||||
///
|
||||
void setGlobalColor(const int priority, const ColorRgb &ledColor, const int timeout_ms, const QString& origin = "External" ,bool clearEffects = true);
|
||||
|
||||
///////////////////////////////////////
|
||||
//////////// FROM HYPERION ////////////
|
||||
///////////////////////////////////////
|
||||
|
||||
///
|
||||
/// @brief PIPE a registration request from the Hyperion class to the priority channel
|
||||
/// @param[in] priority The priority channel
|
||||
///
|
||||
void globalRegRequired(int priority);
|
||||
|
||||
};
|
||||
|
54
include/utils/NetOrigin.h
Normal file
54
include/utils/NetOrigin.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
// qt
|
||||
#include <QHostAddress>
|
||||
#include <QJsonArray>
|
||||
|
||||
// utils
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/settings.h>
|
||||
|
||||
///
|
||||
/// @brief Checks the origin ip addresses for access allowed
|
||||
///
|
||||
class NetOrigin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
friend class HyperionDaemon;
|
||||
NetOrigin(QObject* parent = 0, Logger* log = Logger::getInstance("NETWORK"));
|
||||
|
||||
public:
|
||||
///
|
||||
/// @brief Check if address is allowed to connect. The local address is the network adapter ip this connection comes from
|
||||
/// @param address The peer address
|
||||
/// @param local The local address of the socket (Differs based on NetworkAdapter IP or localhost)
|
||||
/// @return True when allowed, else false
|
||||
///
|
||||
bool accessAllowed(const QHostAddress& address, const QHostAddress& local);
|
||||
|
||||
///
|
||||
/// @brief Check if address is in subnet of local
|
||||
/// @return True or false
|
||||
///
|
||||
bool isLocalAddress(const QHostAddress& address, const QHostAddress& local);
|
||||
|
||||
static NetOrigin* getInstance(){ return instance; };
|
||||
static NetOrigin* instance;
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// @brief Handle settings update from SettingsManager
|
||||
/// @param type settingyType from enum
|
||||
/// @param config configuration object
|
||||
///
|
||||
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
|
||||
|
||||
private:
|
||||
Logger* _log;
|
||||
/// True when internet access is allowed
|
||||
bool _internetAccessAllowed;
|
||||
/// Whitelisted ip addresses
|
||||
QList<QHostAddress> _ipWhitelist;
|
||||
|
||||
};
|
@ -24,7 +24,6 @@ enum type {
|
||||
LEDS,
|
||||
LOGGER,
|
||||
SMOOTHING,
|
||||
UDPLISTENER,
|
||||
WEBSERVER,
|
||||
INSTCAPTURE,
|
||||
NETWORK,
|
||||
@ -58,7 +57,6 @@ inline QString typeToString(const type& type)
|
||||
case LEDS: return "leds";
|
||||
case LOGGER: return "logger";
|
||||
case SMOOTHING: return "smoothing";
|
||||
case UDPLISTENER: return "udpListener";
|
||||
case WEBSERVER: return "webConfig";
|
||||
case INSTCAPTURE: return "instCapture";
|
||||
case NETWORK: return "network";
|
||||
@ -91,7 +89,6 @@ inline type stringToType(const QString& type)
|
||||
else if (type == "leds") return LEDS;
|
||||
else if (type == "logger") return LOGGER;
|
||||
else if (type == "smoothing") return SMOOTHING;
|
||||
else if (type == "udpListener") return UDPLISTENER;
|
||||
else if (type == "webConfig") return WEBSERVER;
|
||||
else if (type == "instCapture") return INSTCAPTURE;
|
||||
else if (type == "network") return NETWORK;
|
||||
|
@ -12,11 +12,11 @@ add_subdirectory(protoserver)
|
||||
add_subdirectory(bonjour)
|
||||
add_subdirectory(ssdp)
|
||||
add_subdirectory(boblightserver)
|
||||
add_subdirectory(udplistener)
|
||||
add_subdirectory(leddevice)
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory(effectengine)
|
||||
add_subdirectory(grabber)
|
||||
add_subdirectory(webserver)
|
||||
add_subdirectory(db)
|
||||
add_subdirectory(api)
|
||||
add_subdirectory(python)
|
||||
|
44
libsrc/api/JSONRPC_schema/schema-authorize.json
Normal file
44
libsrc/api/JSONRPC_schema/schema-authorize.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"command": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["authorize"]
|
||||
},
|
||||
"subcommand" : {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["requestToken","createToken","deleteToken","getTokenList","logout","login","required","answerRequest","getPendingRequests"]
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"minLength" : 3
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength" : 8
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"minLength" : 36
|
||||
},
|
||||
"comment" : {
|
||||
"type" : "string",
|
||||
"minLength" : 5
|
||||
},
|
||||
"id" : {
|
||||
"type" : "string",
|
||||
"minLength" : 5,
|
||||
"maxLength" : 5
|
||||
},
|
||||
"accept" : {
|
||||
"type" : "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
"component":
|
||||
{
|
||||
"type" : "string",
|
||||
"enum" : ["ALL", "SMOOTHING", "BLACKBORDER", "FORWARDER", "UDPLISTENER", "BOBLIGHTSERVER", "GRABBER", "V4L", "LEDDEVICE"],
|
||||
"enum" : ["ALL", "SMOOTHING", "BLACKBORDER", "FORWARDER", "BOBLIGHTSERVER", "GRABBER", "V4L", "LEDDEVICE"],
|
||||
"required": true
|
||||
},
|
||||
"state":
|
||||
|
29
libsrc/api/JSONRPC_schema/schema-instance.json
Normal file
29
libsrc/api/JSONRPC_schema/schema-instance.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"command": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["instance"]
|
||||
},
|
||||
"subcommand" : {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["createInstance","deleteInstance","startInstance","stopInstance","saveName","switchTo"]
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"instance" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"maximum" : 255
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength" : 5
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
"command": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "transform", "correction" , "temperature"]
|
||||
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "instance", "transform", "correction" , "temperature"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@
|
||||
<file alias="schema-logging">JSONRPC_schema/schema-logging.json</file>
|
||||
<file alias="schema-processing">JSONRPC_schema/schema-processing.json</file>
|
||||
<file alias="schema-videomode">JSONRPC_schema/schema-videomode.json</file>
|
||||
<file alias="schema-authorize">JSONRPC_schema/schema-authorize.json</file>
|
||||
<file alias="schema-instance">JSONRPC_schema/schema-instance.json</file>
|
||||
<!-- The following schemas are derecated but used to ensure backward compatibility with hyperion Classic remote control-->
|
||||
<file alias="schema-transform">JSONRPC_schema/schema-hyperion-classic.json</file>
|
||||
<file alias="schema-correction">JSONRPC_schema/schema-hyperion-classic.json</file>
|
||||
|
@ -35,29 +35,94 @@
|
||||
// api includes
|
||||
#include <api/JsonCB.h>
|
||||
|
||||
// auth manager
|
||||
#include <hyperion/AuthManager.h>
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener)
|
||||
JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection, QObject* parent, bool noListener)
|
||||
: QObject(parent)
|
||||
, _authManager(AuthManager::getInstance())
|
||||
, _authorized(false)
|
||||
, _userAuthorized(false)
|
||||
, _apiAuthRequired(_authManager->isAuthRequired())
|
||||
, _noListener(noListener)
|
||||
, _peerAddress(peerAddress)
|
||||
, _log(log)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _instanceManager(HyperionIManager::getInstance())
|
||||
, _hyperion(nullptr)
|
||||
, _jsonCB(nullptr)
|
||||
, _streaming_logging_activated(false)
|
||||
, _image_stream_timeout(0)
|
||||
, _led_stream_timeout(0)
|
||||
{
|
||||
Q_INIT_RESOURCE(JSONRPC_schemas);
|
||||
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
// if this is localConnection and network allows unauth locals, set authorized flag
|
||||
if(_apiAuthRequired && localConnection)
|
||||
_authorized = !_authManager->isLocalAuthRequired();
|
||||
|
||||
// setup auth interface
|
||||
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &JsonAPI::handlePendingTokenRequest);
|
||||
connect(_authManager, &AuthManager::tokenResponse, this, &JsonAPI::handleTokenResponse);
|
||||
|
||||
// listen for killed instances
|
||||
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange);
|
||||
|
||||
// init Hyperion pointer
|
||||
handleInstanceSwitch(0);
|
||||
|
||||
// notify hyperion about a jsonMessageForward
|
||||
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
||||
}
|
||||
|
||||
void JsonAPI::handleMessage(const QString& messageString)
|
||||
bool JsonAPI::handleInstanceSwitch(const quint8& inst, const bool& forced)
|
||||
{
|
||||
// check if we are already on the requested instance
|
||||
if(_hyperion != nullptr && _hyperion->getInstanceIndex() == inst)
|
||||
return true;
|
||||
|
||||
if(_instanceManager->IsInstanceRunning(inst))
|
||||
{
|
||||
Debug(_log,"Client '%s' switch to Hyperion instance %d", QSTRING_CSTR(_peerAddress), inst);
|
||||
// cut all connections between hyperion / plugins and this
|
||||
if(_hyperion != nullptr)
|
||||
disconnect(_hyperion, 0, this, 0);
|
||||
|
||||
// get new Hyperion pointer
|
||||
_hyperion = _instanceManager->getHyperionInstance(inst);
|
||||
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
|
||||
QStringList cbCmds;
|
||||
if(_jsonCB != nullptr)
|
||||
{
|
||||
cbCmds = _jsonCB->getSubscribedCommands();
|
||||
delete _jsonCB;
|
||||
}
|
||||
|
||||
_jsonCB = new JsonCB(_hyperion, this);
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
|
||||
// read subs
|
||||
for(const auto & entry : cbCmds)
|
||||
{
|
||||
_jsonCB->subscribeFor(entry);
|
||||
}
|
||||
|
||||
// // imageStream last state
|
||||
// if(_ledcolorsImageActive)
|
||||
// connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection);
|
||||
//
|
||||
// //ledColor stream last state
|
||||
// if(_ledcolorsLedsActive)
|
||||
// connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader)
|
||||
{
|
||||
const QString ident = "JsonRpc@"+_peerAddress;
|
||||
QJsonObject message;
|
||||
@ -84,6 +149,29 @@ void JsonAPI::handleMessage(const QString& messageString)
|
||||
}
|
||||
|
||||
int tan = message["tan"].toInt();
|
||||
|
||||
// client auth before everything else but not for http
|
||||
if (!_noListener && command == "authorize")
|
||||
{
|
||||
handleAuthorizeCommand(message, command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// on the fly auth available for http from http Auth header, on failure we return and auth handler sends a failure
|
||||
if(_noListener && _apiAuthRequired && !_authorized)
|
||||
{
|
||||
// extract token from http header
|
||||
QString cToken = httpAuthHeader.mid(5).trimmed();
|
||||
if(!handleHTTPAuth(command, tan, cToken))
|
||||
return;
|
||||
}
|
||||
|
||||
// on strong api auth you need a auth for all cmds
|
||||
if(_apiAuthRequired && !_authorized)
|
||||
{
|
||||
sendErrorReply("No Authorization", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// switch over all possible commands and handle them
|
||||
if (command == "color") handleColorCommand (message, command, tan);
|
||||
@ -102,15 +190,17 @@ void JsonAPI::handleMessage(const QString& messageString)
|
||||
else if (command == "logging") handleLoggingCommand (message, command, tan);
|
||||
else if (command == "processing") handleProcessingCommand (message, command, tan);
|
||||
else if (command == "videomode") handleVideoModeCommand (message, command, tan);
|
||||
else if (command == "instance") handleInstanceCommand (message, command, tan);
|
||||
|
||||
// BEGIN | The following commands are derecated but used to ensure backward compatibility with hyperion Classic remote control
|
||||
else if (command == "clearall") handleClearallCommand(message, command, tan);
|
||||
else if (command == "clearall")
|
||||
handleClearallCommand(message, command, tan);
|
||||
else if (command == "transform" || command == "correction" || command == "temperature")
|
||||
sendErrorReply("The command " + command + "is deprecated, please use the Hyperion Web Interface to configure");
|
||||
// END
|
||||
|
||||
// handle not implemented commands
|
||||
else handleNotImplemented ();
|
||||
else handleNotImplemented();
|
||||
}
|
||||
|
||||
void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
@ -120,35 +210,13 @@ void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& comm
|
||||
// extract parameters
|
||||
int priority = message["priority"].toInt();
|
||||
int duration = message["duration"].toInt(-1);
|
||||
QString origin = message["origin"].toString("Empty") + "@"+_peerAddress;
|
||||
const QString origin = message["origin"].toString("Empty") + "@"+_peerAddress;
|
||||
|
||||
std::vector<ColorRgb> colorData(_hyperion->getLedCount());
|
||||
const QJsonArray & jsonColor = message["color"].toArray();
|
||||
unsigned int i = 0;
|
||||
for (; i < unsigned(jsonColor.size()/3) && i < _hyperion->getLedCount(); ++i)
|
||||
{
|
||||
colorData[i].red = uint8_t(jsonColor.at(3u*i).toInt());
|
||||
colorData[i].green = uint8_t(jsonColor.at(3u*i+1u).toInt());
|
||||
colorData[i].blue = uint8_t(jsonColor.at(3u*i+2u).toInt());
|
||||
}
|
||||
const ColorRgb color = {uint8_t(jsonColor.at(0).toInt()),uint8_t(jsonColor.at(1).toInt()),uint8_t(jsonColor.at(2).toInt())};
|
||||
|
||||
// copy full blocks of led colors
|
||||
unsigned size = i;
|
||||
while (i + size < _hyperion->getLedCount())
|
||||
{
|
||||
memcpy(&(colorData[i]), colorData.data(), size * sizeof(ColorRgb));
|
||||
i += size;
|
||||
}
|
||||
|
||||
// copy remaining block of led colors
|
||||
if (i < _hyperion->getLedCount())
|
||||
{
|
||||
memcpy(&(colorData[i]), colorData.data(), (_hyperion->getLedCount()-i) * sizeof(ColorRgb));
|
||||
}
|
||||
|
||||
// register and set color
|
||||
_hyperion->registerInput(priority, hyperion::COMP_COLOR, origin);
|
||||
_hyperion->setInput(priority, colorData, duration);
|
||||
// set color
|
||||
_hyperion->setColor(priority, color, duration, origin);
|
||||
|
||||
// send reply
|
||||
sendSuccessReply(command, tan);
|
||||
@ -251,7 +319,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, c
|
||||
hyperion["channel" ] = QString(HYPERION_VERSION_CHANNEL);
|
||||
hyperion["build" ] = QString(HYPERION_BUILD_ID);
|
||||
hyperion["time" ] = QString(__DATE__ " " __TIME__);
|
||||
hyperion["id" ] = _hyperion->getId();
|
||||
hyperion["id" ] = _authManager->getID();
|
||||
info["hyperion"] = hyperion;
|
||||
|
||||
// send the result
|
||||
@ -446,14 +514,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
info["components"] = component;
|
||||
info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType());
|
||||
|
||||
// Add Hyperion
|
||||
QJsonObject hyperion;
|
||||
hyperion["config_modified" ] = _hyperion->configModified();
|
||||
hyperion["config_writeable"] = _hyperion->configWriteable();
|
||||
hyperion["enabled"] = _hyperion->getComponentRegister().isComponentEnabled(hyperion::COMP_ALL) ? true : false;
|
||||
|
||||
info["hyperion"] = hyperion;
|
||||
|
||||
// add sessions
|
||||
QJsonArray sessions;
|
||||
for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices())
|
||||
@ -470,6 +530,22 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
}
|
||||
info["sessions"] = sessions;
|
||||
|
||||
// add instance info
|
||||
QJsonArray instanceInfo;
|
||||
for(const auto & entry : _instanceManager->getInstanceData())
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert("friendly_name", entry["friendly_name"].toString());
|
||||
obj.insert("instance", entry["instance"].toInt());
|
||||
//obj.insert("last_use", entry["last_use"].toString());
|
||||
obj.insert("running", entry["running"].toBool());
|
||||
instanceInfo.append(obj);
|
||||
}
|
||||
info["instance"] = instanceInfo;
|
||||
|
||||
// add leds configs
|
||||
info["leds"] = _hyperion->getSetting(settings::LEDS).array();
|
||||
|
||||
// BEGIN | The following entries are derecated but used to ensure backward compatibility with hyperion Classic remote control
|
||||
// TODO Output the real transformation information instead of default
|
||||
|
||||
@ -588,7 +664,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
}
|
||||
for(const auto & entry : subsArr)
|
||||
{
|
||||
if(entry == "settings-update")
|
||||
// config callbacks just if auth is set
|
||||
if(entry == "settings-update" && !_authorized)
|
||||
continue;
|
||||
|
||||
if(!_jsonCB->subscribeFor(entry.toString()))
|
||||
@ -617,6 +694,17 @@ void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& comm
|
||||
sendSuccessReply(command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
{
|
||||
emit forwardJsonMessage(message);
|
||||
|
||||
// clear priority
|
||||
_hyperion->clearall();
|
||||
|
||||
// send reply
|
||||
sendSuccessReply(command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
{
|
||||
const QJsonObject & adjustment = message["adjustment"].toObject();
|
||||
@ -848,8 +936,6 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QStr
|
||||
{
|
||||
if(_hyperion->getComponentRegister().setHyperionEnable(compState))
|
||||
sendSuccessReply(command, tan);
|
||||
else
|
||||
sendErrorReply(QString("Hyperion is already %1").arg(compState ? "enabled" : "disabled"), command, tan );
|
||||
|
||||
return;
|
||||
}
|
||||
@ -885,6 +971,7 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString &
|
||||
_streaming_image_reply["command"] = command+"-imagestream-update";
|
||||
_streaming_image_reply["tan"] = tan;
|
||||
connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection);
|
||||
_hyperion->update();
|
||||
}
|
||||
else if (subcommand == "imagestream-stop")
|
||||
{
|
||||
@ -892,7 +979,6 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString &
|
||||
}
|
||||
else
|
||||
{
|
||||
sendErrorReply("unknown subcommand \""+subcommand+"\"",command,tan);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -928,7 +1014,6 @@ void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString &co
|
||||
}
|
||||
else
|
||||
{
|
||||
sendErrorReply("unknown subcommand",command,tan);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -949,15 +1034,282 @@ void JsonAPI::handleVideoModeCommand(const QJsonObject& message, const QString &
|
||||
sendSuccessReply(command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
void JsonAPI::handleAuthorizeCommand(const QJsonObject & message, const QString &command, const int tan)
|
||||
{
|
||||
emit forwardJsonMessage(message);
|
||||
const QString& subc = message["subcommand"].toString().trimmed();
|
||||
// catch test if auth is required
|
||||
if(subc == "required")
|
||||
{
|
||||
QJsonObject req;
|
||||
req["required"] = _apiAuthRequired;
|
||||
sendSuccessDataReply(QJsonDocument(req), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// clear priority
|
||||
_hyperion->clearall();
|
||||
// catch logout
|
||||
if(subc == "logout")
|
||||
{
|
||||
_authorized = false;
|
||||
_userAuthorized = false;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// send reply
|
||||
sendSuccessReply(command, tan);
|
||||
// token created from ui
|
||||
if(subc == "createToken")
|
||||
{
|
||||
const QString& c = message["comment"].toString().trimmed();
|
||||
// for user authorized sessions
|
||||
if(_userAuthorized)
|
||||
{
|
||||
AuthManager::AuthDefinition def = _authManager->createToken(c);
|
||||
QJsonObject newTok;
|
||||
newTok["comment"] = def.comment;
|
||||
newTok["id"] = def.id;
|
||||
newTok["token"] = def.token;
|
||||
|
||||
sendSuccessDataReply(QJsonDocument(newTok), command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// delete token
|
||||
if(subc == "deleteToken")
|
||||
{
|
||||
const QString& did = message["id"].toString().trimmed();
|
||||
// for user authorized sessions
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_authManager->deleteToken(did);
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// catch token request
|
||||
if(subc == "requestToken")
|
||||
{
|
||||
const QString& comment = message["comment"].toString().trimmed();
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
_authManager->setNewTokenRequest(this, comment, id);
|
||||
// client should wait for answer
|
||||
return;
|
||||
}
|
||||
|
||||
// get pending token requests
|
||||
if(subc == "getPendingRequests")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
{
|
||||
QMap<QString, AuthManager::AuthDefinition> map = _authManager->getPendingRequests();
|
||||
QJsonArray arr;
|
||||
for(const auto& entry : map)
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["comment"] = entry.comment;
|
||||
obj["id"] = entry.id;
|
||||
obj["timeout"] = int(entry.timeoutTime - QDateTime::currentMSecsSinceEpoch());
|
||||
arr.append(obj);
|
||||
}
|
||||
sendSuccessDataReply(QJsonDocument(arr),command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// accept/deny token request
|
||||
if(subc == "answerRequest")
|
||||
{
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
const bool& accept = message["accept"].toBool(false);
|
||||
if(_userAuthorized)
|
||||
{
|
||||
if(accept)
|
||||
_authManager->acceptTokenRequest(id);
|
||||
else
|
||||
_authManager->denyTokenRequest(id);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
|
||||
return;
|
||||
}
|
||||
// deny token request
|
||||
if(subc == "acceptRequest")
|
||||
{
|
||||
const QString& id = message["id"].toString().trimmed();
|
||||
if(_userAuthorized)
|
||||
{
|
||||
_authManager->acceptTokenRequest(id);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// cath get token list
|
||||
if(subc == "getTokenList")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
{
|
||||
QVector<AuthManager::AuthDefinition> defVect = _authManager->getTokenList();
|
||||
QJsonArray tArr;
|
||||
for(const auto& entry : defVect)
|
||||
{
|
||||
QJsonObject subO;
|
||||
subO["comment"] = entry.comment;
|
||||
subO["id"] = entry.id;
|
||||
subO["last_use"] = entry.lastUse;
|
||||
|
||||
tArr.append(subO);
|
||||
}
|
||||
|
||||
sendSuccessDataReply(QJsonDocument(tArr),command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// login
|
||||
if(subc == "login")
|
||||
{
|
||||
// catch token auth
|
||||
const QString& token = message["token"].toString().trimmed();
|
||||
|
||||
if(!token.isEmpty())
|
||||
{
|
||||
if(token.count() >= 36)
|
||||
{
|
||||
if(_authManager->isTokenAuthorized(token))
|
||||
{
|
||||
_authorized = true;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("Token is too short", command+"-"+subc, tan);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// user & password
|
||||
const QString& user = message["username"].toString().trimmed();
|
||||
const QString& password = message["password"].toString().trimmed();
|
||||
|
||||
if(user.count() >= 3 && password.count() >= 8)
|
||||
{
|
||||
if(_authManager->isUserAuthorized(user, password))
|
||||
{
|
||||
_authorized = true;
|
||||
_userAuthorized = true;
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization", command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("User or password string too short", command+"-"+subc, tan);
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonAPI::handleHTTPAuth(const QString& command, const int& tan, const QString& token)
|
||||
{
|
||||
if(_authManager->isTokenAuthorized(token))
|
||||
{
|
||||
_authorized = true;
|
||||
return true;
|
||||
}
|
||||
sendErrorReply("No Authorization", command, tan);
|
||||
return false;
|
||||
}
|
||||
|
||||
void JsonAPI::handleInstanceCommand(const QJsonObject & message, const QString &command, const int tan)
|
||||
{
|
||||
const QString & subc = message["subcommand"].toString();
|
||||
const quint8 & inst = message["instance"].toInt();
|
||||
const QString & name = message["name"].toString();
|
||||
|
||||
if(subc == "switchTo")
|
||||
{
|
||||
if(handleInstanceSwitch(inst))
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
else
|
||||
sendErrorReply("Selected Hyperion instance isn't running",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
if(subc == "startInstance")
|
||||
{
|
||||
// silent fail
|
||||
_instanceManager->startInstance(inst);
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
if(subc == "stopInstance")
|
||||
{
|
||||
// silent fail
|
||||
_instanceManager->stopInstance(inst);
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
if(subc == "deleteInstance")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
{
|
||||
if(_instanceManager->deleteInstance(inst))
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
else
|
||||
sendErrorReply(QString("Failed to delete instance '%1'").arg(inst), command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// create and save name requires name
|
||||
if(name.isEmpty())
|
||||
sendErrorReply("Name string required for this command",command+"-"+subc, tan);
|
||||
|
||||
if(subc == "createInstance")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
{
|
||||
if(_instanceManager->createInstance(name))
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
else
|
||||
sendErrorReply(QString("The instance name '%1' is already in use").arg(name), command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
if(subc == "saveName")
|
||||
{
|
||||
if(_userAuthorized)
|
||||
{
|
||||
// silent fail
|
||||
if(_instanceManager->saveName(inst,name))
|
||||
sendSuccessReply(command+"-"+subc, tan);
|
||||
else
|
||||
sendErrorReply(QString("The instance name '%1' is already in use").arg(name), command+"-"+subc, tan);
|
||||
}
|
||||
else
|
||||
sendErrorReply("No Authorization",command+"-"+subc, tan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonAPI::handleNotImplemented()
|
||||
@ -1093,3 +1445,49 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg)
|
||||
// send the result
|
||||
emit callbackMessage(_streaming_logging_reply);
|
||||
}
|
||||
|
||||
void JsonAPI::handlePendingTokenRequest(const QString& id, const QString& comment)
|
||||
{
|
||||
// just user sessions are allowed to react on this, to prevent that token authorized instances authorize new tokens on their own
|
||||
if(_userAuthorized)
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["command"] = "authorize-event";
|
||||
obj["comment"] = comment;
|
||||
obj["id"] = id;
|
||||
|
||||
emit callbackMessage(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonAPI::handleTokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id)
|
||||
{
|
||||
// if this is the requester, we send the reply
|
||||
if(this == caller)
|
||||
{
|
||||
const QString cmd = "authorize-requestToken";
|
||||
QJsonObject result;
|
||||
result["token"] = token;
|
||||
result["comment"] = comment;
|
||||
result["id"] = id;
|
||||
|
||||
if(success)
|
||||
sendSuccessDataReply(QJsonDocument(result), cmd);
|
||||
else
|
||||
sendErrorReply("Token request timeout or denied", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonAPI::handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name)
|
||||
{
|
||||
switch(state){
|
||||
case H_ON_STOP:
|
||||
if(_hyperion->getInstanceIndex() == instance)
|
||||
{
|
||||
handleInstanceSwitch();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,23 @@
|
||||
|
||||
// hyperion
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
// HyperionIManager
|
||||
#include <hyperion/HyperionIManager.h>
|
||||
// components
|
||||
|
||||
#include <hyperion/ComponentRegister.h>
|
||||
// bonjour wrapper
|
||||
|
||||
#include <bonjour/bonjourbrowserwrapper.h>
|
||||
// priorityMuxer
|
||||
|
||||
#include <hyperion/PriorityMuxer.h>
|
||||
|
||||
// utils
|
||||
#include <utils/ColorSys.h>
|
||||
|
||||
// qt
|
||||
#include <QDateTime>
|
||||
|
||||
// Image to led map helper
|
||||
@ -17,15 +27,15 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
JsonCB::JsonCB(QObject* parent)
|
||||
JsonCB::JsonCB(Hyperion* hyperion, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _hyperion(hyperion)
|
||||
, _componentRegister(& _hyperion->getComponentRegister())
|
||||
, _bonjour(BonjourBrowserWrapper::getInstance())
|
||||
, _prioMuxer(_hyperion->getMuxerInstance())
|
||||
{
|
||||
_availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update"
|
||||
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update";
|
||||
<< "adjustment-update" << "videomode-update" << "effects-update" << "settings-update" << "leds-update" << "instance-update";
|
||||
}
|
||||
|
||||
bool JsonCB::subscribeFor(const QString& type)
|
||||
@ -82,6 +92,19 @@ bool JsonCB::subscribeFor(const QString& type)
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
if(type == "leds-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
|
||||
if(type == "instance-update")
|
||||
{
|
||||
_subscribedCommands << type;
|
||||
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -301,3 +324,29 @@ void JsonCB::handleSettingsChange(const settings::type& type, const QJsonDocumen
|
||||
|
||||
doCallback("settings-update", QVariant(dat));
|
||||
}
|
||||
|
||||
void JsonCB::handleLedsConfigChange(const settings::type& type, const QJsonDocument& data)
|
||||
{
|
||||
if(type == settings::LEDS)
|
||||
{
|
||||
QJsonObject dat;
|
||||
dat[typeToString(type)] = data.array();
|
||||
doCallback("leds-update", QVariant(dat));
|
||||
}
|
||||
}
|
||||
|
||||
void JsonCB::handleInstanceChange()
|
||||
{
|
||||
QJsonArray arr;
|
||||
|
||||
for(const auto & entry : HyperionIManager::getInstance()->getInstanceData())
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert("friendly_name", entry["friendly_name"].toString());
|
||||
obj.insert("instance", entry["instance"].toInt());
|
||||
//obj.insert("last_use", entry["last_use"].toString());
|
||||
obj.insert("running", entry["running"].toBool());
|
||||
arr.append(obj);
|
||||
}
|
||||
doCallback("instance-update", QVariant(arr));
|
||||
}
|
||||
|
@ -44,12 +44,9 @@ BoblightClientConnection::BoblightClientConnection(Hyperion* hyperion, QTcpSocke
|
||||
|
||||
BoblightClientConnection::~BoblightClientConnection()
|
||||
{
|
||||
if (_priority < 255)
|
||||
{
|
||||
// clear the current channel
|
||||
// clear the current channel
|
||||
if (_priority != 0 && _priority >= 128 && _priority < 254)
|
||||
_hyperion->clear(_priority);
|
||||
_priority = 255;
|
||||
}
|
||||
|
||||
delete _socket;
|
||||
}
|
||||
@ -83,12 +80,9 @@ void BoblightClientConnection::readData()
|
||||
|
||||
void BoblightClientConnection::socketClosed()
|
||||
{
|
||||
if (_priority < 255)
|
||||
{
|
||||
// clear the current channel
|
||||
// clear the current channel
|
||||
if (_priority != 0 && _priority >= 128 && _priority < 254)
|
||||
_hyperion->clear(_priority);
|
||||
_priority = 255;
|
||||
}
|
||||
|
||||
emit connectionClosed(this);
|
||||
}
|
||||
@ -163,8 +157,11 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
||||
rgb.green = green;
|
||||
rgb.blue = blue;
|
||||
|
||||
if (_priority == 0 || _priority < 128 || _priority >= 254)
|
||||
return;
|
||||
|
||||
// send current color values to hyperion if this is the last led assuming leds values are send in order of id
|
||||
if ((ledIndex == _ledColors.size() -1) && _priority < 255)
|
||||
if (ledIndex == _ledColors.size() -1)
|
||||
{
|
||||
_hyperion->setInput(_priority, _ledColors);
|
||||
}
|
||||
@ -188,27 +185,38 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
||||
int prio = messageParts[2].toInt(&rc);
|
||||
if (rc && prio != _priority)
|
||||
{
|
||||
if (_priority < 255)
|
||||
{
|
||||
// clear the current channel
|
||||
if (_priority != 0 && _hyperion->getPriorityInfo(_priority).componentId == hyperion::COMP_BOBLIGHTSERVER)
|
||||
_hyperion->clear(_priority);
|
||||
|
||||
if (prio < 128 || prio >= 254)
|
||||
{
|
||||
_priority = 128;
|
||||
while (_hyperion->getActivePriorities().contains(_priority))
|
||||
{
|
||||
_priority += 1;
|
||||
}
|
||||
|
||||
// warn against invalid priority
|
||||
Warning(_log, "The priority %i is not in the priority range between 128 and 253. Priority %i is used instead.", prio, _priority);
|
||||
// register new priority (previously modified)
|
||||
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// register new priority
|
||||
_hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString()));
|
||||
_priority = prio;
|
||||
}
|
||||
|
||||
_priority = prio;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (messageParts[0] == "sync")
|
||||
{
|
||||
// send current color values to hyperion
|
||||
if (_priority < 255)
|
||||
{
|
||||
_hyperion->setInput(_priority, _ledColors);
|
||||
}
|
||||
if (_priority != 0 && _priority >= 128 && _priority < 254)
|
||||
_hyperion->setInput(_priority, _ledColors); // send current color values to hyperion
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -216,12 +224,6 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
||||
Debug(_log, "unknown boblight message: %s", QSTRING_CSTR(message));
|
||||
}
|
||||
|
||||
void BoblightClientConnection::sendMessage(const QByteArray & message)
|
||||
{
|
||||
//std::cout << "send boblight message: " << message;
|
||||
_socket->write(message);
|
||||
}
|
||||
|
||||
void BoblightClientConnection::sendLightMessage()
|
||||
{
|
||||
char buffer[256];
|
||||
|
@ -63,7 +63,7 @@ private:
|
||||
///
|
||||
/// @param message The boblight message to send
|
||||
///
|
||||
void sendMessage(const QByteArray &message);
|
||||
void sendMessage(const QByteArray &message) { _socket->write(message); };
|
||||
|
||||
///
|
||||
/// Send a lights message the to connected client
|
||||
|
@ -7,9 +7,13 @@
|
||||
|
||||
// hyperion includes
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
// qt incl
|
||||
#include <QTcpServer>
|
||||
|
||||
// netUtil
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
BoblightServer::BoblightServer(Hyperion* hyperion,const QJsonDocument& config)
|
||||
@ -42,11 +46,9 @@ void BoblightServer::start()
|
||||
if ( _server->isListening() )
|
||||
return;
|
||||
|
||||
if (!_server->listen(QHostAddress::Any, _port))
|
||||
{
|
||||
Error(_log, "Could not bind to port '%d', please use an available port", _port);
|
||||
return;
|
||||
}
|
||||
if (NetUtils::portAvailable(_port, _log))
|
||||
_server->listen(QHostAddress::Any, _port);
|
||||
|
||||
Info(_log, "Started on port %d", _port);
|
||||
|
||||
_hyperion->getComponentRegister().componentStateChanged(COMP_BOBLIGHTSERVER, _server->isListening());
|
||||
|
@ -34,7 +34,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <HyperionConfig.h>
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <hyperion/AuthManager.h>
|
||||
|
||||
BonjourServiceRegister::BonjourServiceRegister(QObject *parent)
|
||||
: QObject(parent), dnssref(0), bonjourSocket(0)
|
||||
@ -79,7 +79,7 @@ void BonjourServiceRegister::registerService(const BonjourRecord &record, quint1
|
||||
}
|
||||
#endif
|
||||
// base txtRec
|
||||
std::vector<std::pair<std::string, std::string> > txtBase = {{"id",Hyperion::getInstance()->getId().toStdString()},{"version",HYPERION_VERSION}};
|
||||
std::vector<std::pair<std::string, std::string> > txtBase = {{"id",AuthManager::getInstance()->getID().toStdString()},{"version",HYPERION_VERSION}};
|
||||
// create txt record
|
||||
TXTRecordRef txtRec;
|
||||
TXTRecordCreate(&txtRec,0,NULL);
|
||||
|
18
libsrc/db/CMakeLists.txt
Normal file
18
libsrc/db/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
find_package(Qt5 COMPONENTS Sql REQUIRED)
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/db)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/db)
|
||||
|
||||
FILE ( GLOB DB_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(database
|
||||
${DB_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(database
|
||||
hyperion
|
||||
hyperion-utils
|
||||
Qt5::Core
|
||||
Qt5::Sql
|
||||
)
|
396
libsrc/db/DBManager.cpp
Normal file
396
libsrc/db/DBManager.cpp
Normal file
@ -0,0 +1,396 @@
|
||||
#include <db/DBManager.h>
|
||||
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlRecord>
|
||||
#include <QThreadStorage>
|
||||
#include <QUuid>
|
||||
#include <QDir>
|
||||
|
||||
// not in header because of linking
|
||||
static QString _rootPath;
|
||||
static QThreadStorage<QSqlDatabase> _databasePool;
|
||||
|
||||
DBManager::DBManager(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("DB"))
|
||||
{
|
||||
}
|
||||
|
||||
DBManager::~DBManager()
|
||||
{
|
||||
}
|
||||
|
||||
void DBManager::setRootPath(const QString& rootPath)
|
||||
{
|
||||
_rootPath = rootPath;
|
||||
// create directory
|
||||
QDir().mkpath(_rootPath+"/db");
|
||||
}
|
||||
|
||||
void DBManager::setTable(const QString& table)
|
||||
{
|
||||
_table = table;
|
||||
}
|
||||
|
||||
QSqlDatabase DBManager::getDB() const
|
||||
{
|
||||
if(_databasePool.hasLocalData())
|
||||
return _databasePool.localData();
|
||||
else
|
||||
{
|
||||
auto db = QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString());
|
||||
_databasePool.setLocalData(db);
|
||||
db.setDatabaseName(_rootPath+"/db/"+_dbn+".db");
|
||||
if(!db.open())
|
||||
{
|
||||
Error(_log, QSTRING_CSTR(db.lastError().text()));
|
||||
throw std::runtime_error("Failed to open database connection!");
|
||||
}
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
bool DBManager::createRecord(const VectorPair& conditions, const QVariantMap& columns) const
|
||||
{
|
||||
if(recordExists(conditions))
|
||||
{
|
||||
// if there is no column data, return
|
||||
if(columns.isEmpty())
|
||||
return true;
|
||||
|
||||
if(!updateRecord(conditions, columns))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
query.setForwardOnly(true);
|
||||
|
||||
QVariantList cValues;
|
||||
QStringList prep;
|
||||
QStringList placeh;
|
||||
// prep merge columns & condition
|
||||
QVariantMap::const_iterator i = columns.constBegin();
|
||||
while (i != columns.constEnd()) {
|
||||
prep.append(i.key());
|
||||
cValues += i.value();
|
||||
placeh.append("?");
|
||||
|
||||
++i;
|
||||
}
|
||||
for(const auto& pair : conditions)
|
||||
{
|
||||
// remove the condition statements
|
||||
QString tmp = pair.first;
|
||||
prep << tmp.remove("AND");
|
||||
cValues << pair.second;
|
||||
placeh.append("?");
|
||||
}
|
||||
query.prepare(QString("INSERT INTO %1 ( %2 ) VALUES ( %3 )").arg(_table,prep.join(", ")).arg(placeh.join(", ")));
|
||||
// add column & condition values
|
||||
doAddBindValue(query, cValues);
|
||||
if(!query.exec())
|
||||
{
|
||||
Error(_log, "Failed to create record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prep.join(", ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBManager::recordExists(const VectorPair& conditions) const
|
||||
{
|
||||
if(conditions.isEmpty())
|
||||
return false;
|
||||
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
query.setForwardOnly(true);
|
||||
|
||||
QStringList prepCond;
|
||||
QVariantList bindVal;
|
||||
prepCond << "WHERE";
|
||||
|
||||
for(const auto& pair : conditions)
|
||||
{
|
||||
prepCond << pair.first+"=?";
|
||||
bindVal << pair.second;
|
||||
}
|
||||
query.prepare(QString("SELECT * FROM %1 %2").arg(_table,prepCond.join(" ")));
|
||||
doAddBindValue(query, bindVal);
|
||||
if(!query.exec())
|
||||
{
|
||||
Error(_log, "Failed recordExists(): '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
|
||||
int entry = 0;
|
||||
while (query.next()) {
|
||||
entry++;
|
||||
}
|
||||
|
||||
if(entry)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBManager::updateRecord(const VectorPair& conditions, const QVariantMap& columns) const
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
query.setForwardOnly(true);
|
||||
|
||||
QVariantList values;
|
||||
QStringList prep;
|
||||
|
||||
// prepare columns valus
|
||||
QVariantMap::const_iterator i = columns.constBegin();
|
||||
while (i != columns.constEnd()) {
|
||||
prep += i.key()+"=?";
|
||||
values += i.value();
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
// prepare condition values
|
||||
QStringList prepCond;
|
||||
QVariantList prepBindVal;
|
||||
if(!conditions.isEmpty())
|
||||
prepCond << "WHERE";
|
||||
|
||||
for(const auto& pair : conditions)
|
||||
{
|
||||
prepCond << pair.first+"=?";
|
||||
prepBindVal << pair.second;
|
||||
}
|
||||
|
||||
query.prepare(QString("UPDATE %1 SET %2 %3").arg(_table,prep.join(", ")).arg(prepCond.join(" ")));
|
||||
// add column values
|
||||
doAddBindValue(query, values);
|
||||
// add condition values
|
||||
doAddBindValue(query, prepBindVal);
|
||||
if(!query.exec())
|
||||
{
|
||||
Error(_log, "Failed to update record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBManager::getRecord(const VectorPair& conditions, QVariantMap& results, const QStringList& tColumns) const
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
query.setForwardOnly(true);
|
||||
|
||||
QString sColumns("*");
|
||||
if(!tColumns.isEmpty())
|
||||
sColumns = tColumns.join(", ");
|
||||
|
||||
// prep conditions
|
||||
QStringList prepCond;
|
||||
QVariantList bindVal;
|
||||
if(!conditions.isEmpty())
|
||||
prepCond << "WHERE";
|
||||
|
||||
for(const auto& pair : conditions)
|
||||
{
|
||||
prepCond << pair.first+"=?";
|
||||
bindVal << pair.second;
|
||||
}
|
||||
query.prepare(QString("SELECT %1 FROM %2 %3").arg(sColumns,_table).arg(prepCond.join(" ")));
|
||||
doAddBindValue(query, bindVal);
|
||||
|
||||
if(!query.exec())
|
||||
{
|
||||
Error(_log, "Failed to get record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// go to first row
|
||||
query.next();
|
||||
|
||||
QSqlRecord rec = query.record();
|
||||
for(int i = 0; i<rec.count(); i++)
|
||||
{
|
||||
results[rec.fieldName(i)] = rec.value(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBManager::getRecords(QVector<QVariantMap>& results, const QStringList& tColumns) const
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
query.setForwardOnly(true);
|
||||
|
||||
QString sColumns("*");
|
||||
if(!tColumns.isEmpty())
|
||||
sColumns = tColumns.join(", ");
|
||||
|
||||
query.prepare(QString("SELECT %1 FROM %2").arg(sColumns,_table));
|
||||
|
||||
if(!query.exec())
|
||||
{
|
||||
Error(_log, "Failed to get records: '%s' in table: '%s' Error: %s", QSTRING_CSTR(sColumns), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate through all found records
|
||||
while(query.next())
|
||||
{
|
||||
QVariantMap entry;
|
||||
QSqlRecord rec = query.record();
|
||||
for(int i = 0; i<rec.count(); i++)
|
||||
{
|
||||
entry[rec.fieldName(i)] = rec.value(i);
|
||||
}
|
||||
results.append(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DBManager::deleteRecord(const VectorPair& conditions) const
|
||||
{
|
||||
if(conditions.isEmpty())
|
||||
{
|
||||
Error(_log, "Oops, a deleteRecord() call wants to delete the entire table (%s)! Denied it", QSTRING_CSTR(_table));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(recordExists(conditions))
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
|
||||
// prep conditions
|
||||
QStringList prepCond("WHERE");
|
||||
QVariantList bindValues;
|
||||
|
||||
for(const auto& pair : conditions)
|
||||
{
|
||||
prepCond << pair.first+"=?";
|
||||
bindValues << pair.second;
|
||||
}
|
||||
|
||||
query.prepare(QString("DELETE FROM %1 %2").arg(_table,prepCond.join(" ")));
|
||||
doAddBindValue(query, bindValues);
|
||||
if(!query.exec())
|
||||
{
|
||||
Error(_log, "Failed to delete record: '%s' in table: '%s' Error: %s", QSTRING_CSTR(prepCond.join(" ")), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBManager::createTable(QStringList& columns) const
|
||||
{
|
||||
if(columns.isEmpty())
|
||||
{
|
||||
Error(_log,"Empty tables aren't supported!");
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlDatabase idb = getDB();
|
||||
// create table if required
|
||||
QSqlQuery query(idb);
|
||||
if(!tableExists(_table))
|
||||
{
|
||||
// empty tables aren't supported by sqlite, add one column
|
||||
QString tcolumn = columns.takeFirst();
|
||||
// default CURRENT_TIMESTAMP is not supported by ALTER TABLE
|
||||
if(!query.exec(QString("CREATE TABLE %1 ( %2 )").arg(_table,tcolumn)))
|
||||
{
|
||||
Error(_log, "Failed to create table: '%s' Error: %s", QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// create columns if required
|
||||
QSqlRecord rec = idb.record(_table);
|
||||
int err = 0;
|
||||
for(const auto& column : columns)
|
||||
{
|
||||
QStringList id = column.split(' ');
|
||||
if(rec.indexOf(id.at(0)) == -1)
|
||||
{
|
||||
if(!createColumn(column))
|
||||
{
|
||||
err++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(err)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBManager::createColumn(const QString& column) const
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
if(!query.exec(QString("ALTER TABLE %1 ADD COLUMN %2").arg(_table,column)))
|
||||
{
|
||||
Error(_log, "Failed to create column: '%s' in table: '%s' Error: %s", QSTRING_CSTR(column), QSTRING_CSTR(_table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBManager::tableExists(const QString& table) const
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QStringList tables = idb.tables();
|
||||
if(tables.contains(table))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBManager::deleteTable(const QString& table) const
|
||||
{
|
||||
if(tableExists(table))
|
||||
{
|
||||
QSqlDatabase idb = getDB();
|
||||
QSqlQuery query(idb);
|
||||
if(!query.exec(QString("DROP TABLE %1").arg(table)))
|
||||
{
|
||||
Error(_log, "Failed to delete table: '%s' Error: %s", QSTRING_CSTR(table), QSTRING_CSTR(idb.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DBManager::doAddBindValue(QSqlQuery& query, const QVariantList& variants) const
|
||||
{
|
||||
for(const auto& variant : variants)
|
||||
{
|
||||
QVariant::Type t = variant.type();
|
||||
switch(t)
|
||||
{
|
||||
case QVariant::UInt:
|
||||
case QVariant::Int:
|
||||
case QVariant::Bool:
|
||||
query.addBindValue(variant.toInt());
|
||||
break;
|
||||
case QVariant::Double:
|
||||
query.addBindValue(variant.toFloat());
|
||||
break;
|
||||
case QVariant::ByteArray:
|
||||
query.addBindValue(variant.toByteArray());
|
||||
break;
|
||||
default:
|
||||
query.addBindValue(variant.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ void Effect::run()
|
||||
PyObject * module = PyImport_ImportModule("hyperion");
|
||||
|
||||
// add a capsule containing 'this' to the module to be able to retrieve the effect from the callback function
|
||||
PyObject_SetAttrString(module, "__effectObj", PyCapsule_New(this, nullptr, nullptr));
|
||||
PyModule_AddObject(module, "__effectObj", PyCapsule_New((void*)this, "hyperion.__effectObj", nullptr));
|
||||
|
||||
// add ledCount variable to the interpreter
|
||||
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _hyperion->getLedCount()));
|
||||
|
@ -168,10 +168,11 @@ int EffectEngine::runEffectScript(const QString &script, const QString &name, co
|
||||
channelCleared(priority);
|
||||
|
||||
// create the effect
|
||||
Effect *effect = new Effect(_hyperion, priority, timeout, script, name, args, imageData);
|
||||
Effect *effect = new Effect(_hyperion, priority, timeout, script, name, args, imageData);
|
||||
connect(effect, &Effect::setInput, _hyperion, &Hyperion::setInput, Qt::QueuedConnection);
|
||||
connect(effect, &Effect::setInputImage, _hyperion, &Hyperion::setInputImage, Qt::QueuedConnection);
|
||||
connect(effect, &QThread::finished, this, &EffectEngine::effectFinished);
|
||||
connect(_hyperion, &Hyperion::finished, effect, &Effect::requestInterruption, Qt::DirectConnection);
|
||||
_activeEffects.push_back(effect);
|
||||
|
||||
// start the effect
|
||||
@ -185,9 +186,9 @@ void EffectEngine::channelCleared(int priority)
|
||||
{
|
||||
for (Effect * effect : _activeEffects)
|
||||
{
|
||||
if (effect->getPriority() == priority)
|
||||
if (effect->getPriority() == priority && !effect->isInterruptionRequested())
|
||||
{
|
||||
effect->setInteruptionFlag();
|
||||
effect->requestInterruption();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,9 +197,9 @@ void EffectEngine::allChannelsCleared()
|
||||
{
|
||||
for (Effect * effect : _activeEffects)
|
||||
{
|
||||
if (effect->getPriority() != 254)
|
||||
if (effect->getPriority() != 254 && !effect->isInterruptionRequested())
|
||||
{
|
||||
effect->setInteruptionFlag();
|
||||
effect->requestInterruption();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,7 +207,7 @@ void EffectEngine::allChannelsCleared()
|
||||
void EffectEngine::effectFinished()
|
||||
{
|
||||
Effect* effect = qobject_cast<Effect*>(sender());
|
||||
if (!effect->hasInteruptionFlag())
|
||||
if (!effect->isInterruptionRequested())
|
||||
{
|
||||
// effect stopped by itself. Clear the channel
|
||||
_hyperion->clear(effect->getPriority());
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include <QImageReader>
|
||||
#include <QBuffer>
|
||||
|
||||
// Get the effect from the capsule
|
||||
#define getEffect() static_cast<Effect*>((Effect*)PyCapsule_Import("hyperion.__effectObj", 0))
|
||||
|
||||
// create the hyperion module
|
||||
struct PyModuleDef EffectModule::moduleDef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
@ -41,9 +44,9 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
|
||||
switch (jsonData.type())
|
||||
{
|
||||
case QJsonValue::Null:
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
case QJsonValue::Undefined:
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
case QJsonValue::Double:
|
||||
{
|
||||
if (std::round(jsonData.toDouble()) != jsonData.toDouble())
|
||||
@ -84,7 +87,7 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return nullptr;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// Python method table
|
||||
@ -118,26 +121,17 @@ PyMethodDef EffectModule::effectMethods[] = {
|
||||
|
||||
PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
|
||||
{
|
||||
// get the effect
|
||||
Effect * effect = getEffect();
|
||||
|
||||
// check if we have aborted already
|
||||
if (effect->hasInteruptionFlag())
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
// determine the timeout
|
||||
int timeout = effect->_timeout;
|
||||
int timeout = getEffect()->_timeout;
|
||||
if (timeout > 0)
|
||||
{
|
||||
timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||
timeout = getEffect()->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// we are done if the time has passed
|
||||
if (timeout <= 0)
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
if (timeout <= 0) Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// check the number of arguments
|
||||
@ -148,9 +142,9 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
|
||||
ColorRgb color;
|
||||
if (PyArg_ParseTuple(args, "bbb", &color.red, &color.green, &color.blue))
|
||||
{
|
||||
effect->_colors.fill(color);
|
||||
effect->setInput(effect->_priority, effect->_colors.toStdVector(), timeout, false);
|
||||
return Py_BuildValue("");
|
||||
getEffect()->_colors.fill(color);
|
||||
getEffect()->setInput(getEffect()->_priority, getEffect()->_colors.toStdVector(), timeout, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -163,12 +157,12 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
|
||||
if (PyByteArray_Check(bytearray))
|
||||
{
|
||||
size_t length = PyByteArray_Size(bytearray);
|
||||
if (length == 3 * effect->_hyperion->getLedCount())
|
||||
if (length == 3 * getEffect()->_hyperion->getLedCount())
|
||||
{
|
||||
char * data = PyByteArray_AS_STRING(bytearray);
|
||||
memcpy(effect->_colors.data(), data, length);
|
||||
effect->setInput(effect->_priority, effect->_colors.toStdVector(), timeout, false);
|
||||
return Py_BuildValue("");
|
||||
memcpy(getEffect()->_colors.data(), data, length);
|
||||
getEffect()->setInput(getEffect()->_priority, getEffect()->_colors.toStdVector(), timeout, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -196,26 +190,17 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
|
||||
{
|
||||
// get the effect
|
||||
Effect * effect = getEffect();
|
||||
|
||||
// check if we have aborted already
|
||||
if (effect->hasInteruptionFlag())
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
// determine the timeout
|
||||
int timeout = effect->_timeout;
|
||||
int timeout = getEffect()->_timeout;
|
||||
if (timeout > 0)
|
||||
{
|
||||
timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||
timeout = getEffect()->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// we are done if the time has passed
|
||||
if (timeout <= 0)
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
if (timeout <= 0) Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// bytearray of values
|
||||
@ -231,8 +216,8 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
|
||||
Image<ColorRgb> image(width, height);
|
||||
char * data = PyByteArray_AS_STRING(bytearray);
|
||||
memcpy(image.memptr(), data, length);
|
||||
effect->setInputImage(effect->_priority, image, timeout, false);
|
||||
return Py_BuildValue("");
|
||||
getEffect()->setInputImage(getEffect()->_priority, image, timeout, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -258,13 +243,14 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect *effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
QString file;
|
||||
QBuffer buffer;
|
||||
QImageReader reader;
|
||||
|
||||
if (effect->_imageData.isEmpty())
|
||||
if (getEffect()->_imageData.isEmpty())
|
||||
{
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
|
||||
@ -272,7 +258,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
if(!PyArg_ParseTuple(args, "s", &source))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "String required");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file = QString::fromUtf8(source);
|
||||
@ -285,7 +271,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.setData(QByteArray::fromBase64(effect->_imageData.toUtf8()));
|
||||
buffer.setData(QByteArray::fromBase64(getEffect()->_imageData.toUtf8()));
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
reader.setDecideFormatFromContent(true);
|
||||
reader.setDevice(&buffer);
|
||||
@ -321,7 +307,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, reader.errorString().toUtf8().constData());
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -329,39 +315,29 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, reader.errorString().toUtf8().constData());
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapAbort(PyObject *self, PyObject *)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
|
||||
// Test if the effect has reached it end time
|
||||
if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime)
|
||||
{
|
||||
effect->setInteruptionFlag();
|
||||
}
|
||||
|
||||
return Py_BuildValue("i", effect->hasInteruptionFlag() ? 1 : 0);
|
||||
return Py_BuildValue("i", getEffect()->isInterruptionRequested() ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
PyObject* EffectModule::wrapImageShow(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
// determine the timeout
|
||||
int timeout = effect->_timeout;
|
||||
int timeout = getEffect()->_timeout;
|
||||
if (timeout > 0)
|
||||
{
|
||||
timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||
timeout = getEffect()->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// we are done if the time has passed
|
||||
if (timeout <= 0)
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
if (timeout <= 0) Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
@ -372,13 +348,13 @@ PyObject* EffectModule::wrapImageShow(PyObject *self, PyObject *args)
|
||||
argsOk = true;
|
||||
}
|
||||
|
||||
if ( ! argsOk || (imgId>-1 && imgId >= effect->_imageStack.size()))
|
||||
if ( ! argsOk || (imgId>-1 && imgId >= getEffect()->_imageStack.size()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
QImage * qimage = (imgId<0) ? &(effect->_image) : &(effect->_imageStack[imgId]);
|
||||
QImage * qimage = (imgId<0) ? &(getEffect()->_image) : &(getEffect()->_imageStack[imgId]);
|
||||
int width = qimage->width();
|
||||
int height = qimage->height();
|
||||
|
||||
@ -397,14 +373,15 @@ PyObject* EffectModule::wrapImageShow(PyObject *self, PyObject *args)
|
||||
}
|
||||
|
||||
memcpy(image.memptr(), binaryImage.data(), binaryImage.size());
|
||||
effect->setInputImage(effect->_priority, image, timeout, false);
|
||||
getEffect()->setInputImage(getEffect()->_priority, image, timeout, false);
|
||||
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
PyObject * bytearray = nullptr;
|
||||
@ -412,8 +389,8 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
|
||||
int startRY = 0;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int endX, width = effect->_imageSize.width();
|
||||
int endY, height = effect->_imageSize.height();
|
||||
int endX, width = getEffect()->_imageSize.width();
|
||||
int endY, height = getEffect()->_imageSize.height();
|
||||
int spread = 0;
|
||||
|
||||
bool argsOK = false;
|
||||
@ -452,9 +429,9 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
|
||||
}
|
||||
|
||||
gradient.setSpread(static_cast<QGradient::Spread>(spread));
|
||||
effect->_painter->fillRect(myQRect, gradient);
|
||||
getEffect()->_painter->fillRect(myQRect, gradient);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -473,15 +450,16 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
PyObject * bytearray = nullptr;
|
||||
int centerX, centerY, angle;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int width = effect->_imageSize.width();
|
||||
int height = effect->_imageSize.height();
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int height = getEffect()->_imageSize.height();
|
||||
|
||||
bool argsOK = false;
|
||||
|
||||
@ -519,9 +497,9 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
|
||||
));
|
||||
}
|
||||
|
||||
effect->_painter->fillRect(myQRect, gradient);
|
||||
getEffect()->_painter->fillRect(myQRect, gradient);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -541,15 +519,16 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
PyObject * bytearray = nullptr;
|
||||
int centerX, centerY, radius, focalX, focalY, focalRadius, spread;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int width = effect->_imageSize.width();
|
||||
int height = effect->_imageSize.height();
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int height = getEffect()->_imageSize.height();
|
||||
|
||||
bool argsOK = false;
|
||||
|
||||
@ -600,9 +579,9 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
|
||||
}
|
||||
|
||||
gradient.setSpread(static_cast<QGradient::Spread>(spread));
|
||||
effect->_painter->fillRect(myQRect, gradient);
|
||||
getEffect()->_painter->fillRect(myQRect, gradient);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -621,7 +600,9 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
PyObject * bytearray = nullptr;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
@ -654,14 +635,14 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
|
||||
points.append(QPoint((int)(data[idx]),(int)(data[idx+1])));
|
||||
}
|
||||
|
||||
QPainter * painter = effect->_painter;
|
||||
QPainter * painter = getEffect()->_painter;
|
||||
QPen oldPen = painter->pen();
|
||||
QPen newPen(QColor(r,g,b,a));
|
||||
painter->setPen(newPen);
|
||||
painter->setBrush(QBrush(QColor(r,g,b,a), Qt::SolidPattern));
|
||||
painter->drawPolygon(points);
|
||||
painter->setPen(oldPen);
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -680,7 +661,9 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
PyObject * bytearray = nullptr;
|
||||
|
||||
QString brush;
|
||||
@ -714,7 +697,7 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
|
||||
if (argsOK)
|
||||
{
|
||||
QPainter * painter = effect->_painter;
|
||||
QPainter * painter = getEffect()->_painter;
|
||||
startAngle = qMax(qMin(startAngle,360),0);
|
||||
spanAngle = qMax(qMin(spanAngle,360),-360);
|
||||
|
||||
@ -745,7 +728,7 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
}
|
||||
painter->setBrush(gradient);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -768,22 +751,23 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
painter->setPen(newPen);
|
||||
painter->drawPie(centerX - radius, centerY - radius, centerX + radius, centerY + radius, startAngle * 16, spanAngle * 16);
|
||||
painter->setPen(oldPen);
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
int a = 255;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int width = effect->_imageSize.width();
|
||||
int height = effect->_imageSize.height();
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int height = getEffect()->_imageSize.height();
|
||||
|
||||
bool argsOK = false;
|
||||
|
||||
@ -807,8 +791,8 @@ PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
|
||||
if (argsOK)
|
||||
{
|
||||
QRect myQRect(startX,startY,width,height);
|
||||
effect->_painter->fillRect(myQRect, QColor(r,g,b,a));
|
||||
return Py_BuildValue("");
|
||||
getEffect()->_painter->fillRect(myQRect, QColor(r,g,b,a));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -816,7 +800,8 @@ PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
@ -824,8 +809,8 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int thick = 1;
|
||||
int endX = effect->_imageSize.width();
|
||||
int endY = effect->_imageSize.height();
|
||||
int endX = getEffect()->_imageSize.width();
|
||||
int endY = getEffect()->_imageSize.height();
|
||||
|
||||
bool argsOK = false;
|
||||
|
||||
@ -840,7 +825,7 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
|
||||
|
||||
if (argsOK)
|
||||
{
|
||||
QPainter * painter = effect->_painter;
|
||||
QPainter * painter = getEffect()->_painter;
|
||||
QRect myQRect(startX, startY, endX, endY);
|
||||
QPen oldPen = painter->pen();
|
||||
QPen newPen(QColor(r,g,b,a));
|
||||
@ -849,14 +834,15 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
|
||||
painter->drawLine(startX, startY, endX, endY);
|
||||
painter->setPen(oldPen);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b, x, y;
|
||||
@ -876,7 +862,7 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
|
||||
|
||||
if (argsOK)
|
||||
{
|
||||
QPainter * painter = effect->_painter;
|
||||
QPainter * painter = getEffect()->_painter;
|
||||
QPen oldPen = painter->pen();
|
||||
QPen newPen(QColor(r,g,b,a));
|
||||
newPen.setWidth(thick);
|
||||
@ -884,14 +870,15 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
|
||||
painter->drawPoint(x, y);
|
||||
painter->setPen(oldPen);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
@ -899,8 +886,8 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int thick = 1;
|
||||
int width = effect->_imageSize.width();
|
||||
int height = effect->_imageSize.height();
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int height = getEffect()->_imageSize.height();
|
||||
|
||||
bool argsOK = false;
|
||||
|
||||
@ -915,7 +902,7 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
|
||||
if (argsOK)
|
||||
{
|
||||
QPainter * painter = effect->_painter;
|
||||
QPainter * painter = getEffect()->_painter;
|
||||
QRect myQRect(startX,startY,width,height);
|
||||
QPen oldPen = painter->pen();
|
||||
QPen newPen(QColor(r,g,b,a));
|
||||
@ -924,7 +911,7 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
painter->drawRect(startX, startY, width, height);
|
||||
painter->setPen(oldPen);
|
||||
|
||||
return Py_BuildValue("");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -932,15 +919,16 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b, x, y;
|
||||
|
||||
if ( argCount == 5 && PyArg_ParseTuple(args, "iiiii", &x, &y, &r, &g, &b ) )
|
||||
{
|
||||
effect->_image.setPixel(x,y,qRgb(r,g,b));
|
||||
return Py_BuildValue("");
|
||||
getEffect()->_image.setPixel(x,y,qRgb(r,g,b));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -949,14 +937,15 @@ PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageGetPixel(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int x, y;
|
||||
|
||||
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &x, &y) )
|
||||
{
|
||||
QRgb rgb = effect->_image.pixel(x,y);
|
||||
QRgb rgb = getEffect()->_image.pixel(x,y);
|
||||
return Py_BuildValue("iii",qRed(rgb),qGreen(rgb),qBlue(rgb));
|
||||
}
|
||||
return nullptr;
|
||||
@ -964,52 +953,60 @@ PyObject* EffectModule::wrapImageGetPixel(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageSave(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
QImage img(effect->_image.copy());
|
||||
effect->_imageStack.append(img);
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
return Py_BuildValue("i", effect->_imageStack.size()-1);
|
||||
QImage img(getEffect()->_image.copy());
|
||||
getEffect()->_imageStack.append(img);
|
||||
|
||||
return Py_BuildValue("i", getEffect()->_imageStack.size()-1);
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int w, h;
|
||||
int width = effect->_imageSize.width();
|
||||
int height = effect->_imageSize.height();
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int height = getEffect()->_imageSize.height();
|
||||
|
||||
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &w, &h) )
|
||||
{
|
||||
if (width<w || height<h)
|
||||
{
|
||||
delete effect->_painter;
|
||||
delete getEffect()->_painter;
|
||||
|
||||
effect->_image = effect->_image.scaled(qMax(width,w),qMax(height,h), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
effect->_imageSize = effect->_image.size();
|
||||
effect->_painter = new QPainter(&(effect->_image));
|
||||
getEffect()->_image = getEffect()->_image.scaled(qMax(width,w),qMax(height,h), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
getEffect()->_imageSize = getEffect()->_image.size();
|
||||
getEffect()->_painter = new QPainter(&(getEffect()->_image));
|
||||
}
|
||||
return Py_BuildValue("ii", effect->_image.width(), effect->_image.height());
|
||||
return Py_BuildValue("ii", getEffect()->_image.width(), getEffect()->_image.height());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageWidth(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
return Py_BuildValue("i", effect->_imageSize.width());
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
return Py_BuildValue("i", getEffect()->_imageSize.width());
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageHeight(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
return Py_BuildValue("i", effect->_imageSize.height());
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
return Py_BuildValue("i", getEffect()->_imageSize.height());
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageCRotate(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int angle;
|
||||
@ -1017,15 +1014,16 @@ PyObject* EffectModule::wrapImageCRotate(PyObject *self, PyObject *args)
|
||||
if ( argCount == 1 && PyArg_ParseTuple(args, "i", &angle ) )
|
||||
{
|
||||
angle = qMax(qMin(angle,360),0);
|
||||
effect->_painter->rotate(angle);
|
||||
return Py_BuildValue("");
|
||||
getEffect()->_painter->rotate(angle);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageCOffset(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int offsetX = 0;
|
||||
int offsetY = 0;
|
||||
@ -1036,60 +1034,31 @@ PyObject* EffectModule::wrapImageCOffset(PyObject *self, PyObject *args)
|
||||
PyArg_ParseTuple(args, "ii", &offsetX, &offsetY );
|
||||
}
|
||||
|
||||
effect->_painter->translate(QPoint(offsetX,offsetY));
|
||||
return Py_BuildValue("");
|
||||
getEffect()->_painter->translate(QPoint(offsetX,offsetY));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageCShear(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
int sh,sv;
|
||||
int argCount = PyTuple_Size(args);
|
||||
|
||||
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &sh, &sv ))
|
||||
{
|
||||
effect->_painter->shear(sh,sv);
|
||||
return Py_BuildValue("");
|
||||
getEffect()->_painter->shear(sh,sv);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* EffectModule::wrapImageResetT(PyObject *self, PyObject *args)
|
||||
{
|
||||
Effect * effect = getEffect();
|
||||
// check if we have aborted already
|
||||
if (getEffect()->isInterruptionRequested()) Py_RETURN_NONE;
|
||||
|
||||
effect->_painter->resetTransform();
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
Effect * EffectModule::getEffect()
|
||||
{
|
||||
// extract the module from the runtime
|
||||
PyObject * module = PyObject_GetAttrString(PyImport_AddModule("__main__"), "hyperion");
|
||||
|
||||
if (!PyModule_Check(module))
|
||||
{
|
||||
// something is wrong
|
||||
Py_XDECREF(module);
|
||||
Error(Logger::getInstance("EFFECTENGINE"), "Unable to retrieve the effect object from the Python runtime");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// retrieve the capsule with the effect
|
||||
PyObject * effectCapsule = PyObject_GetAttrString(module, "__effectObj");
|
||||
Py_XDECREF(module);
|
||||
|
||||
if (!PyCapsule_CheckExact(effectCapsule))
|
||||
{
|
||||
// something is wrong
|
||||
Py_XDECREF(effectCapsule);
|
||||
Error(Logger::getInstance("EFFECTENGINE"), "Unable to retrieve the effect object from the Python runtime");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the effect from the capsule
|
||||
Effect * effect = reinterpret_cast<Effect *>(PyCapsule_GetPointer(effectCapsule, nullptr));
|
||||
Py_XDECREF(effectCapsule);
|
||||
return effect;
|
||||
getEffect()->_painter->resetTransform();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <QTimer>
|
||||
#include <QRgb>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("FLATBUFSERVER"))
|
||||
@ -15,7 +14,6 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObje
|
||||
, _timeoutTimer(new QTimer(this))
|
||||
, _timeout(timeout * 1000)
|
||||
, _priority()
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
{
|
||||
// timer setup
|
||||
_timeoutTimer->setSingleShot(true);
|
||||
@ -70,8 +68,10 @@ void FlatBufferClient::forceClose()
|
||||
void FlatBufferClient::disconnected()
|
||||
{
|
||||
Debug(_log, "Socket Closed");
|
||||
_socket->deleteLater();
|
||||
_hyperion->clear(_priority);
|
||||
_socket->deleteLater();
|
||||
if (_priority != 0 && _priority >= 100 && _priority < 200)
|
||||
emit clearGlobalInput(_priority);
|
||||
|
||||
emit clientDisconnected();
|
||||
}
|
||||
|
||||
@ -101,16 +101,35 @@ void FlatBufferClient::handleColorCommand(const hyperionnet::Color *colorReq)
|
||||
color.blue = qBlue(rgbData);
|
||||
|
||||
// set output
|
||||
_hyperion->setColor(_priority, color, colorReq->duration());
|
||||
emit setGlobalInputColor(_priority, color, colorReq->duration());
|
||||
|
||||
// send reply
|
||||
sendSuccessReply();
|
||||
}
|
||||
|
||||
void FlatBufferClient::registationRequired(const int priority)
|
||||
{
|
||||
if (_priority == priority)
|
||||
{
|
||||
auto reply = hyperionnet::CreateReplyDirect(_builder, nullptr, -1, -1);
|
||||
_builder.Finish(reply);
|
||||
|
||||
// send reply
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void FlatBufferClient::handleRegisterCommand(const hyperionnet::Register *regReq)
|
||||
{
|
||||
if (regReq->priority() < 100 || regReq->priority() >= 200)
|
||||
{
|
||||
// Warning(_log, "Register request from client %s contains invalid priority %d. Valid rage is between 100 and 199.", QSTRING_CSTR(_clientAddress), regReq->priority());
|
||||
sendErrorReply("The priority " + std::to_string(regReq->priority()) + " is not in the priority range between 100 and 199.");
|
||||
return;
|
||||
}
|
||||
|
||||
_priority = regReq->priority();
|
||||
_hyperion->registerInput(_priority, hyperion::COMP_FLATBUFSERVER, regReq->origin()->c_str()+_clientAddress);
|
||||
emit registerGlobalInput(_priority, hyperion::COMP_FLATBUFSERVER, regReq->origin()->c_str()+_clientAddress);
|
||||
|
||||
auto reply = hyperionnet::CreateReplyDirect(_builder, nullptr, -1, (_priority ? _priority : -1));
|
||||
_builder.Finish(reply);
|
||||
@ -140,7 +159,7 @@ void FlatBufferClient::handleImageCommand(const hyperionnet::Image *image)
|
||||
|
||||
Image<ColorRgb> imageDest(width, height);
|
||||
memmove(imageDest.memptr(), imageData->data(), imageData->size());
|
||||
_hyperion->setInputImage(_priority, imageDest, duration);
|
||||
emit setGlobalInputImage(_priority, imageDest, duration);
|
||||
}
|
||||
|
||||
// send reply
|
||||
@ -154,7 +173,7 @@ void FlatBufferClient::handleClearCommand(const hyperionnet::Clear *clear)
|
||||
const int priority = clear->priority();
|
||||
|
||||
if (priority == -1) {
|
||||
_hyperion->clearall();
|
||||
emit clearAllGlobalInput();
|
||||
}
|
||||
else {
|
||||
// Check if we are clearing ourselves.
|
||||
@ -162,7 +181,7 @@ void FlatBufferClient::handleClearCommand(const hyperionnet::Clear *clear)
|
||||
_priority = -1;
|
||||
}
|
||||
|
||||
_hyperion->clear(priority);
|
||||
emit clearGlobalInput(priority);
|
||||
}
|
||||
|
||||
sendSuccessReply();
|
||||
|
@ -12,10 +12,9 @@
|
||||
|
||||
class QTcpSocket;
|
||||
class QTimer;
|
||||
class Hyperion;
|
||||
|
||||
namespace flatbuf {
|
||||
class HyperionRequest;
|
||||
class HyperionRequest;
|
||||
}
|
||||
|
||||
///
|
||||
@ -37,12 +36,27 @@ signals:
|
||||
///
|
||||
/// @brief forward register data to HyperionDaemon
|
||||
///
|
||||
void registerGlobalInput(const int priority, const hyperion::Components& component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0);
|
||||
void registerGlobalInput(const int priority, const hyperion::Components& component, const QString& origin = "FlatBuffer", const QString& owner = "", unsigned smooth_cfg = 0);
|
||||
|
||||
///
|
||||
/// @brief Forward clear command to HyperionDaemon
|
||||
///
|
||||
void clearGlobalInput(const int priority);
|
||||
|
||||
///
|
||||
/// @brief Forward clearAll command to HyperionDaemon
|
||||
///
|
||||
void clearAllGlobalInput(bool forceClearAll=false);
|
||||
|
||||
///
|
||||
/// @brief forward prepared image to HyperionDaemon
|
||||
///
|
||||
const bool setGlobalInputImage(const int priority, const Image<ColorRgb>& image, const int timeout_ms = -1);
|
||||
const bool setGlobalInputImage(const int priority, const Image<ColorRgb>& image, const int timeout_ms, const bool& clearEffect = false);
|
||||
|
||||
///
|
||||
/// @brief Forward requested color
|
||||
///
|
||||
void setGlobalInputColor(const int priority, const ColorRgb &ledColor, const int timeout_ms, const QString& origin = "FlatBuffer" ,bool clearEffects = true);
|
||||
|
||||
///
|
||||
/// @brief Emits whenever the client disconnected
|
||||
@ -50,6 +64,11 @@ signals:
|
||||
void clientDisconnected();
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// @brief Requests a registration from the client
|
||||
///
|
||||
void registationRequired(const int priority);
|
||||
|
||||
///
|
||||
/// @brief close the socket and call disconnected()
|
||||
///
|
||||
@ -125,7 +144,6 @@ private:
|
||||
QTimer *_timeoutTimer;
|
||||
int _timeout;
|
||||
int _priority;
|
||||
Hyperion* _hyperion;
|
||||
|
||||
QByteArray _receiveBuffer;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Qt includes
|
||||
#include <QRgb>
|
||||
|
||||
// protoserver includes
|
||||
// flatbuffer includes
|
||||
#include <flatbufserver/FlatBufferConnection.h>
|
||||
|
||||
FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString & address, const int& priority, const bool& skipReply)
|
||||
@ -210,12 +210,15 @@ bool FlatBufferConnection::parseReply(const hyperionnet::Reply *reply)
|
||||
}
|
||||
|
||||
// We got a registered reply.
|
||||
if (registered != -1 && registered != _priority)
|
||||
if (registered == -1 || registered != _priority)
|
||||
_registered = false;
|
||||
else
|
||||
_registered = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error(reply->error()->str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
#include <flatbufserver/FlatBufferServer.h>
|
||||
#include "FlatBufferClient.h"
|
||||
|
||||
// util
|
||||
#include <utils/NetOrigin.h>
|
||||
#include <utils/GlobalSignals.h>
|
||||
|
||||
// qt
|
||||
#include <QJsonObject>
|
||||
#include <QTcpServer>
|
||||
@ -24,6 +28,7 @@ FlatBufferServer::~FlatBufferServer()
|
||||
|
||||
void FlatBufferServer::initServer()
|
||||
{
|
||||
_netOrigin = NetOrigin::getInstance();
|
||||
connect(_server, &QTcpServer::newConnection, this, &FlatBufferServer::newConnection);
|
||||
|
||||
// apply config
|
||||
@ -58,11 +63,22 @@ void FlatBufferServer::newConnection()
|
||||
{
|
||||
if(QTcpSocket* socket = _server->nextPendingConnection())
|
||||
{
|
||||
Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString()));
|
||||
FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this);
|
||||
// internal
|
||||
connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected);
|
||||
_openConnections.append(client);
|
||||
if(_netOrigin->accessAllowed(socket->peerAddress(), socket->localAddress()))
|
||||
{
|
||||
Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString()));
|
||||
FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this);
|
||||
// internal
|
||||
connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected);
|
||||
connect(client, &FlatBufferClient::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput);
|
||||
connect(client, &FlatBufferClient::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput);
|
||||
connect(client, &FlatBufferClient::clearAllGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearAllGlobalInput);
|
||||
connect(client, &FlatBufferClient::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage);
|
||||
connect(client, &FlatBufferClient::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor);
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::globalRegRequired, client, &FlatBufferClient::registationRequired);
|
||||
_openConnections.append(client);
|
||||
}
|
||||
else
|
||||
socket->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ void QtGrabber::geometryChanged(const QRect &geo)
|
||||
|
||||
int QtGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
{
|
||||
if (!_enabled) return 0;
|
||||
if(_screen == nullptr)
|
||||
{
|
||||
// reinit, this will disable capture on failure
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <hyperion/HyperionIManager.h>
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
@ -57,9 +58,9 @@ V4L2Grabber::V4L2Grabber(const QString & device
|
||||
setPixelDecimation(pixelDecimation);
|
||||
getV4Ldevices();
|
||||
|
||||
// listen for component change for build-in grabber only
|
||||
if (Hyperion::_hyperion)
|
||||
connect(Hyperion::getInstance(), &Hyperion::componentStateChanged, this, &V4L2Grabber::componentStateChanged);
|
||||
// connect componentStateChange only for build-in grabber
|
||||
if (HyperionIManager::HIMinstance)
|
||||
connect(this, &Grabber::componentStateChanged, this, &V4L2Grabber::componentStateChanged);
|
||||
|
||||
// init
|
||||
setDeviceVideoStandard(device, videoStandard);
|
||||
|
@ -21,8 +21,10 @@ V4L2Wrapper::V4L2Wrapper(const QString &device,
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
|
||||
// Handle the image in the captured thread using a direct connection
|
||||
QObject::connect(&_grabber, SIGNAL(newFrame(Image<ColorRgb>)), this, SLOT(newFrame(Image<ColorRgb>)), Qt::DirectConnection);
|
||||
QObject::connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
connect(&_grabber, SIGNAL(newFrame(Image<ColorRgb>)), this, SLOT(newFrame(Image<ColorRgb>)), Qt::DirectConnection);
|
||||
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
|
||||
connect(this, &V4L2Wrapper::componentStateChanged, _ggrabber, &Grabber::componentStateChanged);
|
||||
}
|
||||
|
||||
bool V4L2Wrapper::start()
|
||||
|
169
libsrc/hyperion/AuthManager.cpp
Normal file
169
libsrc/hyperion/AuthManager.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include <hyperion/AuthManager.h>
|
||||
|
||||
// db
|
||||
#include <db/AuthTable.h>
|
||||
#include <db/MetaTable.h>
|
||||
|
||||
// qt
|
||||
#include <QJsonObject>
|
||||
#include <QTimer>
|
||||
|
||||
AuthManager* AuthManager::manager = nullptr;
|
||||
|
||||
AuthManager::AuthManager(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _authTable(new AuthTable(this))
|
||||
, _metaTable(new MetaTable(this))
|
||||
, _pendingRequests()
|
||||
, _authRequired(true)
|
||||
, _timer(new QTimer(this))
|
||||
{
|
||||
AuthManager::manager = this;
|
||||
|
||||
// get uuid
|
||||
_uuid = _metaTable->getUUID();
|
||||
|
||||
// setup timer
|
||||
_timer->setInterval(1000);
|
||||
connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout);
|
||||
|
||||
// init with default user and password
|
||||
if(!_authTable->userExist("Hyperion"))
|
||||
{
|
||||
_authTable->createUser("Hyperion","hyperion");
|
||||
}
|
||||
}
|
||||
|
||||
bool & AuthManager::isAuthRequired()
|
||||
{
|
||||
return _authRequired;
|
||||
}
|
||||
|
||||
bool & AuthManager::isLocalAuthRequired()
|
||||
{
|
||||
return _localAuthRequired;
|
||||
}
|
||||
|
||||
const AuthManager::AuthDefinition AuthManager::createToken(const QString& comment)
|
||||
{
|
||||
const QString token = QUuid::createUuid().toString().mid(1, 36);
|
||||
const QString id = QUuid::createUuid().toString().mid(1, 36).left(5);
|
||||
|
||||
_authTable->createToken(token, comment, id);
|
||||
|
||||
AuthDefinition def;
|
||||
def.comment = comment;
|
||||
def.token = token;
|
||||
def.id = id;
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
const QVector<AuthManager::AuthDefinition> AuthManager::getTokenList()
|
||||
{
|
||||
QVector<QVariantMap> vector = _authTable->getTokenList();
|
||||
QVector<AuthManager::AuthDefinition> finalVec;
|
||||
for(const auto& entry : vector)
|
||||
{
|
||||
AuthDefinition def;
|
||||
def.comment = entry["comment"].toString();
|
||||
def.id = entry["id"].toString();
|
||||
def.lastUse = entry["last_use"].toString();
|
||||
|
||||
// don't add empty ids
|
||||
if(!entry["id"].toString().isEmpty())
|
||||
finalVec.append(def);
|
||||
}
|
||||
return finalVec;
|
||||
}
|
||||
|
||||
bool AuthManager::isUserAuthorized(const QString& user, const QString& pw)
|
||||
{
|
||||
return _authTable->isUserAuthorized(user, pw);
|
||||
}
|
||||
|
||||
bool AuthManager::isTokenAuthorized(const QString& token)
|
||||
{
|
||||
return _authTable->tokenExist(token);
|
||||
}
|
||||
|
||||
void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id)
|
||||
{
|
||||
if(!_pendingRequests.contains(id))
|
||||
{
|
||||
AuthDefinition newDef {id, comment, caller, uint64_t(QDateTime::currentMSecsSinceEpoch()+60000)};
|
||||
_pendingRequests[id] = newDef;
|
||||
_timer->start();
|
||||
emit newPendingTokenRequest(id, comment);
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthManager::acceptTokenRequest(const QString& id)
|
||||
{
|
||||
if(_pendingRequests.contains(id))
|
||||
{
|
||||
const QString token = QUuid::createUuid().toString().remove("{").remove("}");
|
||||
AuthDefinition def = _pendingRequests.take(id);
|
||||
_authTable->createToken(token, def.comment, id);
|
||||
emit tokenResponse(true, def.caller, token, def.comment, id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthManager::denyTokenRequest(const QString& id)
|
||||
{
|
||||
if(_pendingRequests.contains(id))
|
||||
{
|
||||
AuthDefinition def = _pendingRequests.take(id);
|
||||
emit tokenResponse(false, def.caller, QString(), def.comment, id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const QMap<QString, AuthManager::AuthDefinition> AuthManager::getPendingRequests()
|
||||
{
|
||||
return _pendingRequests;
|
||||
}
|
||||
|
||||
bool AuthManager::deleteToken(const QString& id)
|
||||
{
|
||||
if(_authTable->deleteToken(id))
|
||||
{
|
||||
//emit tokenDeleted(token);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AuthManager::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::NETWORK)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
_authRequired = obj["apiAuth"].toBool(true);
|
||||
_localAuthRequired = obj["localApiAuth"].toBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthManager::checkTimeout()
|
||||
{
|
||||
const uint64_t now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
QMapIterator<QString, AuthDefinition> i(_pendingRequests);
|
||||
while (i.hasNext())
|
||||
{
|
||||
i.next();
|
||||
|
||||
const AuthDefinition& def = i.value();
|
||||
if(def.timeoutTime <= now)
|
||||
{
|
||||
emit tokenResponse(false, def.caller, QString(), def.comment, def.id);
|
||||
_pendingRequests.remove(i.key());
|
||||
}
|
||||
}
|
||||
// abort if empty
|
||||
if(_pendingRequests.isEmpty())
|
||||
_timer->stop();
|
||||
}
|
@ -25,5 +25,6 @@ target_link_libraries(hyperion
|
||||
bonjour
|
||||
boblightserver
|
||||
effectengine
|
||||
database
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
@ -81,6 +81,8 @@ void CaptureCont::setSystemCaptureEnable(const bool& enable)
|
||||
{
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, 0, 0);
|
||||
_hyperion->clear(_systemCaptPrio);
|
||||
_systemInactiveTimer->stop();
|
||||
_systemCaptName = "";
|
||||
}
|
||||
_systemCaptEnabled = enable;
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_GRABBER, enable);
|
||||
@ -103,6 +105,7 @@ void CaptureCont::setV4LCaptureEnable(const bool& enable)
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, 0, 0);
|
||||
_hyperion->clear(_v4lCaptPrio);
|
||||
_v4lInactiveTimer->stop();
|
||||
_v4lCaptName = "";
|
||||
}
|
||||
_v4lCaptEnabled = enable;
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_V4L, enable);
|
||||
|
@ -11,7 +11,7 @@ ComponentRegister::ComponentRegister(Hyperion* hyperion)
|
||||
{
|
||||
// init all comps to false
|
||||
QVector<hyperion::Components> vect;
|
||||
vect << COMP_ALL << COMP_SMOOTHING << COMP_BLACKBORDER << COMP_FORWARDER << COMP_UDPLISTENER << COMP_BOBLIGHTSERVER << COMP_GRABBER << COMP_V4L << COMP_LEDDEVICE;
|
||||
vect << COMP_ALL << COMP_SMOOTHING << COMP_BLACKBORDER << COMP_FORWARDER << COMP_BOBLIGHTSERVER << COMP_GRABBER << COMP_V4L << COMP_LEDDEVICE;
|
||||
for(auto e : vect)
|
||||
{
|
||||
_componentStates.emplace(e, ((e == COMP_ALL) ? true : false));
|
||||
|
@ -25,7 +25,7 @@ GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned
|
||||
connect(_timer, &QTimer::timeout, this, &GrabberWrapper::action);
|
||||
|
||||
// connect the image forwarding
|
||||
_grabberName.startsWith("V4L")
|
||||
(_grabberName.startsWith("V4L"))
|
||||
? connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setV4lImage)
|
||||
: connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setSystemImage);
|
||||
}
|
||||
|
@ -5,17 +5,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
// QT includes
|
||||
#include <QDateTime>
|
||||
#include <QThread>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QCryptographicHash>
|
||||
#include <QTimer>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QHostInfo>
|
||||
#include <QCryptographicHash>
|
||||
#include <QThread>
|
||||
|
||||
// hyperion include
|
||||
#include <hyperion/Hyperion.h>
|
||||
@ -25,6 +17,7 @@
|
||||
|
||||
// utils
|
||||
#include <utils/hyperion.h>
|
||||
#include <utils/GlobalSignals.h>
|
||||
|
||||
// Leddevice includes
|
||||
#include <leddevice/LedDeviceWrapper.h>
|
||||
@ -35,9 +28,6 @@
|
||||
// effect engine includes
|
||||
#include <effectengine/EffectEngine.h>
|
||||
|
||||
// Hyperion Daemon
|
||||
#include <../src/hyperiond/hyperiond.h>
|
||||
|
||||
// settingsManagaer
|
||||
#include <hyperion/SettingsManager.h>
|
||||
|
||||
@ -50,28 +40,10 @@
|
||||
// Boblight
|
||||
#include <boblightserver/BoblightServer.h>
|
||||
|
||||
Hyperion* Hyperion::_hyperion = nullptr;
|
||||
|
||||
Hyperion* Hyperion::initInstance( HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath)
|
||||
{
|
||||
if ( Hyperion::_hyperion != nullptr )
|
||||
throw std::runtime_error("Hyperion::initInstance can be called only one time");
|
||||
Hyperion::_hyperion = new Hyperion(daemon, instance, configFile, rootPath);
|
||||
|
||||
return Hyperion::_hyperion;
|
||||
}
|
||||
|
||||
Hyperion* Hyperion::getInstance()
|
||||
{
|
||||
if ( Hyperion::_hyperion == nullptr )
|
||||
throw std::runtime_error("Hyperion::getInstance used without call of Hyperion::initInstance before");
|
||||
|
||||
return Hyperion::_hyperion;
|
||||
}
|
||||
|
||||
Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath)
|
||||
: _daemon(daemon)
|
||||
, _settingsManager(new SettingsManager(this, instance, configFile))
|
||||
Hyperion::Hyperion(const quint8& instance)
|
||||
: QObject()
|
||||
, _instIndex(instance)
|
||||
, _settingsManager(new SettingsManager(instance, this))
|
||||
, _componentRegister(this)
|
||||
, _ledString(hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
|
||||
, _ledStringClone(hyperion::createLedStringClone(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
|
||||
@ -79,18 +51,33 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
, _muxer(_ledString.leds().size())
|
||||
, _raw2ledAdjustment(hyperion::createLedColorsAdjustment(_ledString.leds().size(), getSetting(settings::COLOR).object()))
|
||||
, _effectEngine(nullptr)
|
||||
, _messageForwarder(new MessageForwarder(this))
|
||||
, _configFile(configFile)
|
||||
, _rootPath(rootPath)
|
||||
, _messageForwarder(nullptr)
|
||||
, _log(Logger::getInstance("HYPERION"))
|
||||
, _hwLedCount()
|
||||
, _configHash()
|
||||
, _ledGridSize(hyperion::getLedLayoutGridSize(getSetting(settings::LEDS).array()))
|
||||
, _prevCompId(hyperion::COMP_INVALID)
|
||||
, _ledBuffer(_ledString.leds().size(), ColorRgb::BLACK)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Hyperion::~Hyperion()
|
||||
{
|
||||
freeObjects(false);
|
||||
}
|
||||
|
||||
void Hyperion::start()
|
||||
{
|
||||
// forward settings changed to Hyperion
|
||||
connect(_settingsManager, &SettingsManager::settingsChanged, this, &Hyperion::settingsChanged);
|
||||
|
||||
// get newVideoMode from HyperionIManager
|
||||
connect(this, &Hyperion::newVideoMode, this, &Hyperion::handleNewVideoMode);
|
||||
|
||||
if (!_raw2ledAdjustment->verifyAdjustments())
|
||||
{
|
||||
Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!");
|
||||
}
|
||||
|
||||
// handle hwLedCount
|
||||
_hwLedCount = qMax(unsigned(getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount())), getLedCount());
|
||||
@ -100,12 +87,14 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
{
|
||||
_ledStringColorOrder.push_back(led.colorOrder);
|
||||
}
|
||||
|
||||
for (Led& led : _ledStringClone.leds())
|
||||
{
|
||||
_ledStringColorOrder.insert(_ledStringColorOrder.begin() + led.index, led.colorOrder);
|
||||
}
|
||||
|
||||
// connect Hyperion::update with Muxer visible priority changes as muxer updates independent
|
||||
connect(&_muxer, &PriorityMuxer::visiblePriorityChanged, this, &Hyperion::update);
|
||||
|
||||
// listens for ComponentRegister changes of COMP_ALL to perform core enable/disable actions
|
||||
connect(&_componentRegister, &ComponentRegister::updatedComponentState, this, &Hyperion::updatedComponentState);
|
||||
|
||||
@ -128,22 +117,14 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
_deviceSmooth = new LinearColorSmoothing(getSetting(settings::SMOOTHING), this);
|
||||
connect(this, &Hyperion::settingsChanged, _deviceSmooth, &LinearColorSmoothing::handleSettingsUpdate);
|
||||
|
||||
// create the message forwarder only on main instance
|
||||
if (_instIndex == 0)
|
||||
_messageForwarder = new MessageForwarder(this);
|
||||
|
||||
// create the effect engine; needs to be initialized after smoothing!
|
||||
_effectEngine = new EffectEngine(this);
|
||||
connect(_effectEngine, &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated);
|
||||
|
||||
// setup config state checks and initial shot
|
||||
checkConfigState();
|
||||
if(_fsWatcher.addPath(_configFile))
|
||||
QObject::connect(&_fsWatcher, &QFileSystemWatcher::fileChanged, this, &Hyperion::checkConfigState);
|
||||
else
|
||||
{
|
||||
_cTimer = new QTimer(this);
|
||||
Warning(_log,"Filesystem Observer failed for file: %s, use fallback timer", _configFile.toStdString().c_str());
|
||||
connect(_cTimer, SIGNAL(timeout()), this, SLOT(checkConfigState()));
|
||||
_cTimer->start(2000);
|
||||
}
|
||||
|
||||
// initial startup effect
|
||||
hyperion::handleInitialEffect(this, getSetting(settings::FGEFFECT).object());
|
||||
|
||||
@ -153,6 +134,13 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
// create the Daemon capture interface
|
||||
_captureCont = new CaptureCont(this);
|
||||
|
||||
// forwards global signals to the corresponding slots
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput, this, &Hyperion::registerInput);
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput, this, &Hyperion::clear);
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::clearAllGlobalInput, this, &Hyperion::clearall);
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor, this, &Hyperion::setColor);
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &Hyperion::setInputImage);
|
||||
|
||||
// if there is no startup / background eff and no sending capture interface we probably want to push once BLACK (as PrioMuxer won't emit a prioritiy change)
|
||||
update();
|
||||
|
||||
@ -160,13 +148,15 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
_boblightServer = new BoblightServer(this, getSetting(settings::BOBLSERVER));
|
||||
connect(this, &Hyperion::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate);
|
||||
|
||||
// set unique id
|
||||
_id = QString(QCryptographicHash::hash(getConfigFileName().toLocal8Bit(),QCryptographicHash::Sha1).toHex());
|
||||
// instance inited
|
||||
emit started();
|
||||
// enter thread event loop
|
||||
}
|
||||
|
||||
Hyperion::~Hyperion()
|
||||
void Hyperion::stop()
|
||||
{
|
||||
freeObjects(false);
|
||||
emit finished();
|
||||
thread()->wait();
|
||||
}
|
||||
|
||||
void Hyperion::freeObjects(bool emitCloseSignal)
|
||||
@ -209,9 +199,6 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
|
||||
|
||||
const QJsonArray leds = config.array();
|
||||
|
||||
// // lock update()
|
||||
// _lockUpdate = true;
|
||||
|
||||
// stop and cache all running effects, as effects depend heavily on ledlayout
|
||||
_effectEngine->cacheRunningEffects();
|
||||
|
||||
@ -238,23 +225,16 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
|
||||
// handle hwLedCount update
|
||||
_hwLedCount = qMax(unsigned(getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount())), getLedCount());
|
||||
|
||||
// update led count in device
|
||||
//_ledDeviceWrapper->setLedCount(_hwLedCount);
|
||||
|
||||
// change in leds are also reflected in adjustment
|
||||
delete _raw2ledAdjustment;
|
||||
_raw2ledAdjustment = hyperion::createLedColorsAdjustment(_ledString.leds().size(), getSetting(settings::COLOR).object());
|
||||
|
||||
// start cached effects
|
||||
_effectEngine->startCachedEffects();
|
||||
|
||||
// // unlock
|
||||
// _lockUpdate = false;
|
||||
}
|
||||
else if(type == settings::DEVICE)
|
||||
{
|
||||
QMutexLocker lock(&_changes);
|
||||
// _lockUpdate = true;
|
||||
QJsonObject dev = config.object();
|
||||
|
||||
// handle hwLedCount update
|
||||
@ -268,18 +248,11 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum
|
||||
_imageProcessor->setLedString(_ledString);
|
||||
}
|
||||
|
||||
/* // reinit led device type on change
|
||||
if(_device->getActiveDevice() != dev["type"].toString("file").toLower())
|
||||
{
|
||||
}
|
||||
// update led count
|
||||
_device->setLedCount(_hwLedCount);
|
||||
*/
|
||||
// do always reinit until the led devices can handle dynamic changes
|
||||
dev["currentLedCount"] = int(_hwLedCount); // Inject led count info
|
||||
_ledDeviceWrapper->createLedDevice(dev);
|
||||
// _lockUpdate = false;
|
||||
}
|
||||
|
||||
// update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer color
|
||||
update();
|
||||
}
|
||||
@ -294,12 +267,6 @@ bool Hyperion::saveSettings(QJsonObject config, const bool& correct)
|
||||
return _settingsManager->saveSettings(config, correct);
|
||||
}
|
||||
|
||||
QString Hyperion::getConfigFileName() const
|
||||
{
|
||||
QFileInfo cF(_configFile);
|
||||
return cF.fileName();
|
||||
}
|
||||
|
||||
int Hyperion::getLatchTime() const
|
||||
{
|
||||
return _ledDeviceWrapper->getLatchTime();
|
||||
@ -315,40 +282,6 @@ unsigned Hyperion::getLedCount() const
|
||||
return _ledString.leds().size();
|
||||
}
|
||||
|
||||
void Hyperion::checkConfigState(QString cfile)
|
||||
{
|
||||
// Check config modifications
|
||||
QFile f(_configFile);
|
||||
if (f.open(QFile::ReadOnly))
|
||||
{
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
if (hash.addData(&f))
|
||||
{
|
||||
if (_configHash.size() == 0)
|
||||
{
|
||||
_configHash = hash.result();
|
||||
}
|
||||
_configMod = _configHash != hash.result() ? true : false;
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
|
||||
if(_prevConfigMod != _configMod)
|
||||
{
|
||||
_prevConfigMod = _configMod;
|
||||
}
|
||||
|
||||
// Check config writeable
|
||||
QFile file(_configFile);
|
||||
QFileInfo fileInfo(file);
|
||||
_configWrite = fileInfo.isWritable() && fileInfo.isReadable() ? true : false;
|
||||
|
||||
if(_prevConfigWrite != _configWrite)
|
||||
{
|
||||
_prevConfigWrite = _configWrite;
|
||||
}
|
||||
}
|
||||
|
||||
void Hyperion::setSourceAutoSelectEnabled(bool enabled)
|
||||
{
|
||||
if(_muxer.setSourceAutoSelectEnabled(enabled))
|
||||
@ -372,6 +305,7 @@ void Hyperion::setNewComponentState(const hyperion::Components& component, const
|
||||
|
||||
void Hyperion::setComponentState(const hyperion::Components component, const bool state)
|
||||
{
|
||||
// TODO REMOVE THIS STEP
|
||||
emit componentStateChanged(component, state);
|
||||
}
|
||||
|
||||
@ -399,6 +333,12 @@ bool Hyperion::setInput(const int priority, const std::vector<ColorRgb>& ledColo
|
||||
|
||||
bool Hyperion::setInputImage(const int priority, const Image<ColorRgb>& image, int64_t timeout_ms, const bool& clearEffect)
|
||||
{
|
||||
if (!_muxer.hasPriority(priority))
|
||||
{
|
||||
emit GlobalSignals::getInstance()->globalRegRequired(priority);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_muxer.setInputImage(priority, image, timeout_ms))
|
||||
{
|
||||
// clear effect if this call does not come from an effect
|
||||
@ -419,7 +359,7 @@ bool Hyperion::setInputInactive(const quint8& priority)
|
||||
return _muxer.setInputInactive(priority);
|
||||
}
|
||||
|
||||
void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms, const QString& origin, bool clearEffects)
|
||||
void Hyperion::setColor(const int priority, const ColorRgb &color, const int timeout_ms, const QString& origin, bool clearEffects)
|
||||
{
|
||||
// clear effect if this call does not come from an effect
|
||||
if(clearEffects)
|
||||
@ -431,8 +371,10 @@ void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_m
|
||||
// register color
|
||||
registerInput(priority, hyperion::COMP_COLOR, origin);
|
||||
|
||||
// write color to muxer
|
||||
// write color to muxer & queuePush
|
||||
setInput(priority, ledColors, timeout_ms);
|
||||
if(timeout_ms <= 0)
|
||||
_muxer.queuePush();
|
||||
}
|
||||
|
||||
const QStringList & Hyperion::getAdjustmentIds() const
|
||||
@ -451,7 +393,7 @@ void Hyperion::adjustmentsUpdated()
|
||||
update();
|
||||
}
|
||||
|
||||
bool Hyperion::clear(int priority)
|
||||
bool Hyperion::clear(const int priority)
|
||||
{
|
||||
// send clear signal to the effect engine
|
||||
// (outside the check so the effect gets cleared even when the effect is not sending colors)
|
||||
@ -552,7 +494,7 @@ void Hyperion::setVideoMode(const VideoMode& mode)
|
||||
|
||||
const VideoMode & Hyperion::getCurrentVideoMode()
|
||||
{
|
||||
return _daemon->getVideoMode();
|
||||
return _currVideoMode;
|
||||
}
|
||||
|
||||
const QString & Hyperion::getActiveDevice()
|
||||
@ -572,9 +514,6 @@ void Hyperion::updatedComponentState(const hyperion::Components comp, const bool
|
||||
_prevCompId = comp;
|
||||
_raw2ledAdjustment->setBacklightEnabled((_prevCompId != hyperion::COMP_COLOR && _prevCompId != hyperion::COMP_EFFECT));
|
||||
}
|
||||
|
||||
if(comp == hyperion::COMP_ALL)
|
||||
_muxer.setEnable(state); // first muxer to update all inputs
|
||||
}
|
||||
|
||||
void Hyperion::update()
|
||||
|
203
libsrc/hyperion/HyperionIManager.cpp
Normal file
203
libsrc/hyperion/HyperionIManager.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include <hyperion/HyperionIManager.h>
|
||||
|
||||
// hyperion
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <db/InstanceTable.h>
|
||||
|
||||
// qt
|
||||
#include <QThread>
|
||||
|
||||
HyperionIManager* HyperionIManager::HIMinstance;
|
||||
|
||||
HyperionIManager::HyperionIManager(const QString& rootPath, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("HYPERION"))
|
||||
, _instanceTable( new InstanceTable(rootPath, this) )
|
||||
, _rootPath( rootPath )
|
||||
{
|
||||
HIMinstance = this;
|
||||
qRegisterMetaType<instanceState>("instanceState");
|
||||
}
|
||||
|
||||
Hyperion* HyperionIManager::getHyperionInstance(const quint8& instance)
|
||||
{
|
||||
if(_runningInstances.contains(instance))
|
||||
return _runningInstances.value(instance);
|
||||
|
||||
Warning(_log,"The requested instance index '%d' with name '%s' isn't running, return main instance", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
|
||||
return _runningInstances.value(0);
|
||||
}
|
||||
|
||||
const QVector<QVariantMap> HyperionIManager::getInstanceData()
|
||||
{
|
||||
QVector<QVariantMap> instances = _instanceTable->getAllInstances();
|
||||
for( auto & entry : instances)
|
||||
{
|
||||
// add running state
|
||||
entry["running"] = _runningInstances.contains(entry["instance"].toInt());
|
||||
}
|
||||
return instances;
|
||||
}
|
||||
|
||||
void HyperionIManager::startAll()
|
||||
{
|
||||
for(const auto entry : _instanceTable->getAllInstances(true))
|
||||
{
|
||||
startInstance(entry["instance"].toInt());
|
||||
}
|
||||
}
|
||||
|
||||
void HyperionIManager::stopAll()
|
||||
{
|
||||
// copy the instances due to loop corruption, even with .erase() return next iter
|
||||
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
||||
for(const auto instance : instCopy)
|
||||
{
|
||||
instance->stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool HyperionIManager::startInstance(const quint8& inst, const bool& block)
|
||||
{
|
||||
if(_instanceTable->instanceExist(inst))
|
||||
{
|
||||
if(!_runningInstances.contains(inst) && !_startQueue.contains(inst))
|
||||
{
|
||||
QThread* hyperionThread = new QThread();
|
||||
Hyperion* hyperion = new Hyperion(inst);
|
||||
hyperion->moveToThread(hyperionThread);
|
||||
// setup thread management
|
||||
connect(hyperionThread, &QThread::started, hyperion, &Hyperion::start);
|
||||
connect(hyperion, &Hyperion::started, this, &HyperionIManager::handleStarted);
|
||||
connect(hyperion, &Hyperion::finished, this, &HyperionIManager::handleFinished);
|
||||
connect(hyperion, &Hyperion::finished, hyperionThread, &QThread::quit, Qt::DirectConnection);
|
||||
|
||||
// setup further connections
|
||||
// from Hyperion
|
||||
connect(hyperion, &Hyperion::settingsChanged, this, &HyperionIManager::settingsChanged);
|
||||
connect(hyperion, &Hyperion::videoMode, this, &HyperionIManager::requestVideoMode);
|
||||
connect(hyperion, &Hyperion::componentStateChanged, this, &HyperionIManager::componentStateChanged);
|
||||
// to Hyperion
|
||||
connect(this, &HyperionIManager::newVideoMode, hyperion, &Hyperion::newVideoMode);
|
||||
|
||||
// add to queue and start
|
||||
_startQueue << inst;
|
||||
hyperionThread->start();
|
||||
|
||||
// update db
|
||||
_instanceTable->setLastUse(inst);
|
||||
_instanceTable->setEnable(inst, true);
|
||||
|
||||
if(block)
|
||||
{
|
||||
while(!hyperionThread->isRunning()){};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Debug(_log,"Can't start Hyperion instance index '%d' with name '%s' it's already running or queued for start", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst)));
|
||||
return false;
|
||||
}
|
||||
Debug(_log,"Can't start Hyperion instance index '%d' it doesn't exist in DB", inst);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HyperionIManager::stopInstance(const quint8& inst)
|
||||
{
|
||||
// inst 0 can't be stopped
|
||||
if(!isInstAllowed(inst))
|
||||
return false;
|
||||
|
||||
if(_instanceTable->instanceExist(inst))
|
||||
{
|
||||
if(_runningInstances.contains(inst))
|
||||
{
|
||||
// notify a ON_STOP rather sooner than later, queued signal listener should have some time to drop the pointer before it's deleted
|
||||
emit instanceStateChanged(H_ON_STOP, inst);
|
||||
Hyperion* hyperion = _runningInstances.value(inst);
|
||||
hyperion->stop();
|
||||
|
||||
// update db
|
||||
_instanceTable->setEnable(inst, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
Debug(_log,"Can't stop Hyperion instance index '%d' with name '%s' it's not running'", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst)));
|
||||
return false;
|
||||
}
|
||||
Debug(_log,"Can't stop Hyperion instance index '%d' it doesn't exist in DB", inst);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HyperionIManager::createInstance(const QString& name, const bool& start)
|
||||
{
|
||||
quint8 inst;
|
||||
if(_instanceTable->createInstance(name, inst))
|
||||
{
|
||||
Info(_log,"New Hyperion instance created with name '%s'",QSTRING_CSTR(name));
|
||||
emit instanceStateChanged(H_CREATED, inst, name);
|
||||
emit change();
|
||||
|
||||
if(start)
|
||||
startInstance(inst);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HyperionIManager::deleteInstance(const quint8& inst)
|
||||
{
|
||||
// inst 0 can't be deleted
|
||||
if(!isInstAllowed(inst))
|
||||
return false;
|
||||
|
||||
// stop it if required as blocking and wait
|
||||
stopInstance(inst);
|
||||
|
||||
if(_instanceTable->deleteInstance(inst))
|
||||
{
|
||||
Info(_log,"Hyperion instance with index '%d' has been deleted", inst);
|
||||
emit instanceStateChanged(H_DELETED, inst);
|
||||
emit change();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HyperionIManager::saveName(const quint8& inst, const QString& name)
|
||||
{
|
||||
if(_instanceTable->saveName(inst, name))
|
||||
{
|
||||
emit change();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HyperionIManager::handleFinished()
|
||||
{
|
||||
Hyperion* hyperion = qobject_cast<Hyperion*>(sender());
|
||||
const quint8 & instance = hyperion->getInstanceIndex();
|
||||
|
||||
Info(_log,"Hyperion instance '%s' has been stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
|
||||
|
||||
_runningInstances.remove(instance);
|
||||
hyperion->deleteLater();
|
||||
emit instanceStateChanged(H_STOPPED, instance);
|
||||
emit change();
|
||||
|
||||
}
|
||||
|
||||
void HyperionIManager::handleStarted()
|
||||
{
|
||||
Hyperion* hyperion = qobject_cast<Hyperion*>(sender());
|
||||
const quint8 & instance = hyperion->getInstanceIndex();
|
||||
|
||||
Info(_log,"Hyperion instance '%s' has been started", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
|
||||
|
||||
_startQueue.removeAll(instance);
|
||||
_runningInstances.insert(instance, hyperion);
|
||||
emit instanceStateChanged(H_STARTED, instance);
|
||||
emit change();
|
||||
}
|
@ -180,8 +180,6 @@ void LinearColorSmoothing::componentStateChange(const hyperion::Components compo
|
||||
|
||||
void LinearColorSmoothing::setEnable(bool enable)
|
||||
{
|
||||
LedDevice::setEnable(enable);
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
_timer->stop();
|
||||
|
@ -63,7 +63,7 @@ public:
|
||||
///
|
||||
/// @brief select a smoothing cfg given by cfg index from addConfig()
|
||||
/// @param cfg The index to use
|
||||
/// @param force Overwrite in any case the current values (used for cfg 0 settings udpate)
|
||||
/// @param force Overwrite in any case the current values (used for cfg 0 settings update)
|
||||
///
|
||||
/// @return On success return else false (and falls back to cfg 0)
|
||||
///
|
||||
|
@ -22,9 +22,9 @@ PriorityMuxer::PriorityMuxer(int ledCount)
|
||||
, _activeInputs()
|
||||
, _lowestPriorityInfo()
|
||||
, _sourceAutoSelectEnabled(true)
|
||||
, _updateTimer(new QTimer(this))
|
||||
, _timer(new QTimer(this))
|
||||
, _blockTimer(new QTimer(this))
|
||||
, _updateTimer(new QTimer())
|
||||
, _timer(new QTimer())
|
||||
, _blockTimer(new QTimer())
|
||||
{
|
||||
// init lowest priority info
|
||||
_lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY;
|
||||
@ -43,12 +43,12 @@ PriorityMuxer::PriorityMuxer(int ledCount)
|
||||
// forward timeRunner signal to prioritiesChanged signal & threading workaround
|
||||
connect(this, &PriorityMuxer::timeRunner, this, &PriorityMuxer::prioritiesChanged);
|
||||
connect(this, &PriorityMuxer::signalTimeTrigger, this, &PriorityMuxer::timeTrigger);
|
||||
connect(this, &PriorityMuxer::activeStateChanged, this, &PriorityMuxer::prioritiesChanged);
|
||||
|
||||
// start muxer timer
|
||||
connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::setCurrentTime);
|
||||
_updateTimer->setInterval(250);
|
||||
_updateTimer->start();
|
||||
InputInfo ninfo;
|
||||
}
|
||||
|
||||
PriorityMuxer::~PriorityMuxer()
|
||||
@ -279,7 +279,8 @@ void PriorityMuxer::clearAll(bool forceClearAll)
|
||||
void PriorityMuxer::setCurrentTime(void)
|
||||
{
|
||||
const int64_t now = QDateTime::currentMSecsSinceEpoch();
|
||||
int newPriority = PriorityMuxer::LOWEST_PRIORITY;
|
||||
int newPriority;
|
||||
_activeInputs.contains(0) ? newPriority = 0 : newPriority = PriorityMuxer::LOWEST_PRIORITY;
|
||||
|
||||
for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();)
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
// util
|
||||
#include <utils/JsonUtils.h>
|
||||
#include <db/SettingsTable.h>
|
||||
|
||||
// json schema process
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
@ -11,16 +12,13 @@
|
||||
// write config to filesystem
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
// hyperion
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
QJsonObject SettingsManager::schemaJson;
|
||||
|
||||
SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, const QString& configFile)
|
||||
: _hyperion(hyperion)
|
||||
SettingsManager::SettingsManager(const quint8& instance, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("SettingsManager"))
|
||||
, _sTable(new SettingsTable(instance, this))
|
||||
{
|
||||
connect(this, &SettingsManager::settingsChanged, _hyperion, &Hyperion::settingsChanged);
|
||||
// get schema
|
||||
if(schemaJson.isEmpty())
|
||||
{
|
||||
@ -34,112 +32,78 @@ SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, con
|
||||
throw std::runtime_error(error.what());
|
||||
}
|
||||
}
|
||||
|
||||
// get default config
|
||||
QJsonObject defaultConfig;
|
||||
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
|
||||
throw std::runtime_error("Failed to read default config");
|
||||
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
QJsonSchemaChecker schemaCheckerT;
|
||||
schemaCheckerT.setSchema(schemaJson);
|
||||
|
||||
if(!JsonUtils::readFile(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("Failed to load config!");
|
||||
|
||||
// validate config with schema and correct it if required
|
||||
QPair<bool, bool> validate = schemaCheckerT.validate(_qconfig);
|
||||
|
||||
// errors in schema syntax, abort
|
||||
if (!validate.second)
|
||||
// transform json to string lists
|
||||
QStringList keyList = defaultConfig.keys();
|
||||
QStringList defValueList;
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Hyperion schema has syntax errors!");
|
||||
}
|
||||
// errors in configuration, correct it!
|
||||
if (!validate.first)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
|
||||
_qconfig = schemaCheckerT.getAutoCorrectedConfig(_qconfig);
|
||||
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
if (!JsonUtils::write(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
}
|
||||
|
||||
Debug(_log,"Settings database initialized")
|
||||
}
|
||||
|
||||
SettingsManager::SettingsManager(const quint8& instance, const QString& configFile)
|
||||
: _hyperion(nullptr)
|
||||
, _log(Logger::getInstance("SettingsManager"))
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
// get schema
|
||||
if(schemaJson.isEmpty())
|
||||
{
|
||||
try
|
||||
if(defaultConfig[key].isObject())
|
||||
{
|
||||
schemaJson = QJsonFactory::readSchema(":/hyperion-schema");
|
||||
defValueList << QString(QJsonDocument(defaultConfig[key].toObject()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
catch(const std::runtime_error& error)
|
||||
else if(defaultConfig[key].isArray())
|
||||
{
|
||||
throw std::runtime_error(error.what());
|
||||
defValueList << QString(QJsonDocument(defaultConfig[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
}
|
||||
// get default config
|
||||
QJsonObject defaultConfig;
|
||||
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
|
||||
throw std::runtime_error("Failed to read default config");
|
||||
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
QJsonSchemaChecker schemaCheckerT;
|
||||
schemaCheckerT.setSchema(schemaJson);
|
||||
|
||||
if(!JsonUtils::readFile(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("Failed to load config!");
|
||||
|
||||
// validate config with schema and correct it if required
|
||||
QPair<bool, bool> validate = schemaCheckerT.validate(_qconfig);
|
||||
|
||||
// errors in schema syntax, abort
|
||||
if (!validate.second)
|
||||
// fill database with default data if required
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Hyperion schema has syntax errors!");
|
||||
QString val = defValueList.takeFirst();
|
||||
// prevent overwrite
|
||||
if(!_sTable->recordExist(key))
|
||||
_sTable->createSettingsRecord(key,val);
|
||||
}
|
||||
// errors in configuration, correct it!
|
||||
if (!validate.first)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
|
||||
_qconfig = schemaCheckerT.getAutoCorrectedConfig(_qconfig);
|
||||
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
// need to validate all data in database constuct the entire data object
|
||||
// TODO refactor schemaChecker to accept QJsonArray in validate(); QJsonDocument container? To validate them per entry...
|
||||
QJsonObject dbConfig;
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
QJsonDocument doc = _sTable->getSettingsRecord(key);
|
||||
if(doc.isArray())
|
||||
dbConfig[key] = doc.array();
|
||||
else
|
||||
dbConfig[key] = doc.object();
|
||||
}
|
||||
|
||||
// validate full dbconfig against schema, on error we need to rewrite entire table
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaJson);
|
||||
QPair<bool,bool> valid = schemaChecker.validate(dbConfig);
|
||||
// check if our main schema syntax is IO
|
||||
if (!valid.second)
|
||||
{
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
throw std::runtime_error("The config schema has invalid syntax. This should never happen! Go fix it!");
|
||||
}
|
||||
if (!valid.first)
|
||||
{
|
||||
Info(_log,"Table upgrade required...");
|
||||
dbConfig = schemaChecker.getAutoCorrectedConfig(dbConfig);
|
||||
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
if (!JsonUtils::write(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
saveSettings(dbConfig);
|
||||
}
|
||||
else
|
||||
_qconfig = dbConfig;
|
||||
|
||||
Debug(_log,"Settings database initialized")
|
||||
}
|
||||
|
||||
SettingsManager::~SettingsManager()
|
||||
{
|
||||
}
|
||||
|
||||
const QJsonDocument SettingsManager::getSetting(const settings::type& type)
|
||||
{
|
||||
QString key = settings::typeToString(type);
|
||||
if(_qconfig[key].isObject())
|
||||
return QJsonDocument(_qconfig[key].toObject());
|
||||
else
|
||||
return QJsonDocument(_qconfig[key].toArray());
|
||||
return _sTable->getSettingsRecord(settings::typeToString(type));
|
||||
}
|
||||
|
||||
bool SettingsManager::saveSettings(QJsonObject config, const bool& correct)
|
||||
@ -161,32 +125,34 @@ bool SettingsManager::saveSettings(QJsonObject config, const bool& correct)
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
}
|
||||
|
||||
// save data to file
|
||||
if(_hyperion != nullptr)
|
||||
{
|
||||
if(!JsonUtils::write(_hyperion->getConfigFilePath(), config, _log))
|
||||
return false;
|
||||
}
|
||||
|
||||
// compare old data with new data to emit/save changes accordingly
|
||||
for(const auto key : config.keys())
|
||||
{
|
||||
QString newData, oldData;
|
||||
|
||||
_qconfig[key].isObject()
|
||||
? oldData = QString(QJsonDocument(_qconfig[key].toObject()).toJson(QJsonDocument::Compact))
|
||||
: oldData = QString(QJsonDocument(_qconfig[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
|
||||
config[key].isObject()
|
||||
? newData = QString(QJsonDocument(config[key].toObject()).toJson(QJsonDocument::Compact))
|
||||
: newData = QString(QJsonDocument(config[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
|
||||
if(oldData != newData)
|
||||
emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(newData.toLocal8Bit()));
|
||||
}
|
||||
|
||||
// store the current state
|
||||
// store the new config
|
||||
_qconfig = config;
|
||||
|
||||
// extract keys and data
|
||||
QStringList keyList = config.keys();
|
||||
QStringList newValueList;
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
if(config[key].isObject())
|
||||
{
|
||||
newValueList << QString(QJsonDocument(config[key].toObject()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
else if(config[key].isArray())
|
||||
{
|
||||
newValueList << QString(QJsonDocument(config[key].toArray()).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
}
|
||||
|
||||
// compare database data with new data to emit/save changes accordingly
|
||||
for(const auto key : keyList)
|
||||
{
|
||||
QString data = newValueList.takeFirst();
|
||||
if(_sTable->getSettingsRecordString(key) != data)
|
||||
{
|
||||
_sTable->createSettingsRecord(key, data);
|
||||
|
||||
emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(data.toLocal8Bit()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -63,10 +63,6 @@
|
||||
{
|
||||
"$ref": "schema-boblightServer.json"
|
||||
},
|
||||
"udpListener" :
|
||||
{
|
||||
"$ref": "schema-udpListener.json"
|
||||
},
|
||||
"webConfig" :
|
||||
{
|
||||
"$ref": "schema-webConfig.json"
|
||||
@ -79,6 +75,10 @@
|
||||
{
|
||||
"$ref": "schema-instCapture.json"
|
||||
},
|
||||
"network":
|
||||
{
|
||||
"$ref": "schema-network.json"
|
||||
},
|
||||
"ledConfig":
|
||||
{
|
||||
"$ref": "schema-ledConfig.json"
|
||||
|
@ -17,11 +17,11 @@
|
||||
<file alias="schema-flatbufServer.json">schema/schema-flatbufServer.json</file>
|
||||
<file alias="schema-protoServer.json">schema/schema-protoServer.json</file>
|
||||
<file alias="schema-boblightServer.json">schema/schema-boblightServer.json</file>
|
||||
<file alias="schema-udpListener.json">schema/schema-udpListener.json</file>
|
||||
<file alias="schema-webConfig.json">schema/schema-webConfig.json</file>
|
||||
<file alias="schema-effects.json">schema/schema-effects.json</file>
|
||||
<file alias="schema-ledConfig.json">schema/schema-ledConfig.json</file>
|
||||
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
||||
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
||||
<file alias="schema-network.json">schema/schema-network.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
59
libsrc/hyperion/schema/schema-network.json
Normal file
59
libsrc/hyperion/schema/schema-network.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"title" : "edt_conf_net_heading_title",
|
||||
"required" : true,
|
||||
"properties" :
|
||||
{
|
||||
"apiAuth" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_apiAuth_title",
|
||||
"required" : true,
|
||||
"default" : true,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"internetAccessAPI" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_internetAccessAPI_title",
|
||||
"required" : true,
|
||||
"default" : false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"apiAuth": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"ipWhitelist" :
|
||||
{
|
||||
"type" : "array",
|
||||
"title" : "edt_conf_net_ipWhitelist_title",
|
||||
"required" : true,
|
||||
"items" : {
|
||||
"type": "string",
|
||||
"title" : "edt_conf_net_ip_itemtitle"
|
||||
},
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"internetAccessAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"localApiAuth" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_net_localApiAuth_title",
|
||||
"required" : true,
|
||||
"default" : false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"apiAuth": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 4
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"title" : "edt_conf_udpl_heading_title",
|
||||
"properties" :
|
||||
{
|
||||
"enable" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_general_enable_title",
|
||||
"default" : false,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"address" :
|
||||
{
|
||||
"type" : "string",
|
||||
"title" : "edt_conf_udpl_address_title",
|
||||
"default" : "239.255.28.01",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"port" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_general_port_title",
|
||||
"minimum" : 0,
|
||||
"maximum" : 65535,
|
||||
"default" : 2801,
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"priority" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_general_priority_title",
|
||||
"minimum" : 100,
|
||||
"maximum" : 254,
|
||||
"default" : 200,
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"timeout" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_udpl_timeout_title",
|
||||
"minimum" : 1000,
|
||||
"default" : 10000,
|
||||
"append" : "edt_append_ms",
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"shared" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_udpl_shared_title",
|
||||
"default" : false,
|
||||
"propertyOrder" : 6
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
@ -6,20 +6,16 @@
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
|
||||
// websocket includes
|
||||
#include "webserver/WebSocketClient.h"
|
||||
|
||||
JsonClientConnection::JsonClientConnection(QTcpSocket *socket)
|
||||
JsonClientConnection::JsonClientConnection(QTcpSocket *socket, const bool& localConnection)
|
||||
: QObject()
|
||||
, _socket(socket)
|
||||
, _websocketClient(nullptr)
|
||||
, _receiveBuffer()
|
||||
, _log(Logger::getInstance("JSONCLIENTCONNECTION"))
|
||||
{
|
||||
connect(_socket, &QTcpSocket::disconnected, this, &JsonClientConnection::disconnected);
|
||||
connect(_socket, &QTcpSocket::readyRead, this, &JsonClientConnection::readRequest);
|
||||
// create a new instance of JsonAPI
|
||||
_jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, this);
|
||||
_jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, localConnection, this);
|
||||
// get the callback messages from JsonAPI and send it to the client
|
||||
connect(_jsonAPI,SIGNAL(callbackMessage(QJsonObject)),this,SLOT(sendMessage(QJsonObject)));
|
||||
}
|
||||
@ -27,37 +23,21 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket)
|
||||
void JsonClientConnection::readRequest()
|
||||
{
|
||||
_receiveBuffer += _socket->readAll();
|
||||
|
||||
// might be an old hyperion classic handshake request or raw socket data
|
||||
if(_receiveBuffer.contains("Upgrade: websocket"))
|
||||
// raw socket data, handling as usual
|
||||
int bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
while(bytes > 0)
|
||||
{
|
||||
if(_websocketClient == Q_NULLPTR)
|
||||
{
|
||||
// disconnect this slot from socket for further requests
|
||||
disconnect(_socket, &QTcpSocket::readyRead, this, &JsonClientConnection::readRequest);
|
||||
int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19;
|
||||
QByteArray header(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data());
|
||||
_websocketClient = new WebSocketClient(header, _socket, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// raw socket data, handling as usual
|
||||
int bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
while(bytes > 0)
|
||||
{
|
||||
// create message string
|
||||
QString message(QByteArray(_receiveBuffer.data(), bytes));
|
||||
// create message string
|
||||
QString message(QByteArray(_receiveBuffer.data(), bytes));
|
||||
|
||||
// remove message data from buffer
|
||||
_receiveBuffer = _receiveBuffer.mid(bytes);
|
||||
// remove message data from buffer
|
||||
_receiveBuffer = _receiveBuffer.mid(bytes);
|
||||
|
||||
// handle message
|
||||
_jsonAPI->handleMessage(message);
|
||||
// handle message
|
||||
_jsonAPI->handleMessage(message);
|
||||
|
||||
// try too look up '\n' again
|
||||
bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
}
|
||||
// try too look up '\n' again
|
||||
bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
class JsonAPI;
|
||||
class QTcpSocket;
|
||||
class WebSocketClient;
|
||||
|
||||
///
|
||||
/// The Connection object created by \a JsonServer when a new connection is established
|
||||
@ -24,7 +23,7 @@ public:
|
||||
/// Constructor
|
||||
/// @param socket The Socket object for this connection
|
||||
///
|
||||
JsonClientConnection(QTcpSocket * socket);
|
||||
JsonClientConnection(QTcpSocket * socket, const bool& localConnection);
|
||||
|
||||
signals:
|
||||
void connectionClosed();
|
||||
@ -42,7 +41,6 @@ private slots:
|
||||
|
||||
private:
|
||||
QTcpSocket* _socket;
|
||||
WebSocketClient* _websocketClient;
|
||||
/// new instance of JsonAPI
|
||||
JsonAPI * _jsonAPI;
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
// bonjour include
|
||||
#include <bonjour/bonjourserviceregister.h>
|
||||
#include <utils/NetOrigin.h>
|
||||
|
||||
// qt includes
|
||||
#include <QTcpServer>
|
||||
@ -19,6 +20,7 @@ JsonServer::JsonServer(const QJsonDocument& config)
|
||||
, _server(new QTcpServer(this))
|
||||
, _openConnections()
|
||||
, _log(Logger::getInstance("JSONSERVER"))
|
||||
, _netOrigin(NetOrigin::getInstance())
|
||||
{
|
||||
Debug(_log, "Created instance");
|
||||
|
||||
@ -95,12 +97,17 @@ void JsonServer::newConnection()
|
||||
{
|
||||
if (QTcpSocket * socket = _server->nextPendingConnection())
|
||||
{
|
||||
Debug(_log, "New connection from: %s ",socket->localAddress().toString().toStdString().c_str());
|
||||
JsonClientConnection * connection = new JsonClientConnection(socket);
|
||||
_openConnections.insert(connection);
|
||||
if(_netOrigin->accessAllowed(socket->peerAddress(), socket->localAddress()))
|
||||
{
|
||||
Debug(_log, "New connection from: %s ",socket->localAddress().toString().toStdString().c_str());
|
||||
JsonClientConnection * connection = new JsonClientConnection(socket, _netOrigin->isLocalAddress(socket->peerAddress(), socket->localAddress()));
|
||||
_openConnections.insert(connection);
|
||||
|
||||
// register slot for cleaning up after the connection closed
|
||||
connect(connection, &JsonClientConnection::connectionClosed, this, &JsonServer::closedConnection);
|
||||
// register slot for cleaning up after the connection closed
|
||||
connect(connection, &JsonClientConnection::connectionClosed, this, &JsonServer::closedConnection);
|
||||
}
|
||||
else
|
||||
socket->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user