Refactor Hyperion JSON-API (#1727)

This commit is contained in:
LordGrey 2024-05-08 22:06:32 +02:00 committed by GitHub
parent 94850d890a
commit cf287f5adb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 4203 additions and 2962 deletions

View File

@ -8,14 +8,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Breaking
**JSON-API**
- Align JSON subscription update elements. `ledcolors-imagestream-update, ledcolors-ledstream-update, logmsg-update` now return data via `data` and not `result
### Added
- Support gaps on Matrix Layout (#1696)
**JSON-API**
- New subscription support for event updates, i.e. `Suspend, Resume, Idle, idleResume, Restart, Quit`.
- Support direct or multiple instance addressing via single requests (#809)
- Support of `serverinfo` subcommands: `getInfo, subscribe, unsubscribe, getSubscriptions, getSubscriptionCommands`
- [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates
### Changed
- Fixed: Cross Site Scripting Vulnerability (CVE-2024-4174, CVE-2024-4175)
- Fixed: hyperion-v4l2 taking screenshot failed (#1722)
- Nanoleaf: Support new devices and do not restore ExtControl state
- Workaround to address Web UI keeps forcing browser to download the html instead (#1692)
- Fixed: Kodi Color Calibration, Refactor Wizards (#1674)
- Fixed: Token Dialog not closing
**JSON-API**
- Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization.
- Provide additional error details with API responses, esp. on JSON parsing, validation or token errors.
- Generate random TANs for every API request from the Hyperion UI
- Fixed: Handling of IP4 addresses wrapped in IPv6 for external network connections-
### Removed
**JSON-API**
- Removed ability to enable/disable local admin authorization. All admin commands require authorization, i.e. `authorize-adminRequired` will always be `true`.
- Removed `session-updates` subscription
- `serverinfo/subscribe` element will be deprecated and replaced by corresponding subcommand
## [2.0.16](https://github.com/hyperion-project/hyperion.ng/releases/tag/2.0.16) - 2024-01
### Added

View File

@ -186,6 +186,7 @@
"conf_network_json_intro": "The JSON-RPC-Port of all Hyperion instances, used for remote control.",
"conf_network_net_intro": "Network related settings which are applied to all network services.",
"conf_network_proto_intro": "The PROTO-Port of all Hyperion instances, used for picture streams (HyperionScreenCap, Kodi Addon, Android Hyperion Grabber, ...)",
"conf_network_tok_idhead": "ID",
"conf_network_tok_cidhead": "Description",
"conf_network_tok_comment_title": "Token description",
"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.",
@ -500,19 +501,16 @@
"edt_conf_log_level_expl": "Depending on loglevel you see less or more messages in your log.",
"edt_conf_log_level_title": "Log-Level",
"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_apiAuth_title": "API Authentication",
"edt_conf_net_heading_title": "Network",
"edt_conf_net_internetAccessAPI_expl": "Allow access to the Hyperion API/Webinterface from the internet. Disable for higher security.",
"edt_conf_net_internetAccessAPI_expl": "Allow access to the Hyperion API/Web Interface from the Internet. Disable for increased security.",
"edt_conf_net_internetAccessAPI_title": "Internet API Access",
"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_ipWhitelist_title": "Whitelisted IP's",
"edt_conf_net_ipWhitelist_expl": "Define whitelisted IP addresses from which API requests from the Internet are allowed. All other external connections will be denied.",
"edt_conf_net_ipWhitelist_title": "Whitelisted IP addresses",
"edt_conf_net_ip_itemtitle": "IP",
"edt_conf_net_localAdminAuth_expl": "When enabled, administration access from your local network needs a password.",
"edt_conf_net_localAdminAuth_title": "Local Admin API Authentication",
"edt_conf_net_localApiAuth_expl": "When enabled, connections from your home network needs to authenticate themselves against Hyperion with a token.",
"edt_conf_net_localApiAuth_expl": "When disabled, API authorisation via password or token is not required for local connections. The exception is administrative commands.",
"edt_conf_net_localApiAuth_title": "Local API Authentication",
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict the access to the API through the internet to certain IP's.",
"edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP's",
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict API requests over the Internet to only those IP addresses on the whitelist.",
"edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP addresses",
"edt_conf_os_events_lockEnable_title": "Listen to lock events",
"edt_conf_os_events_lockEnable_expl": "Listen to screen lock/unlock events",
"edt_conf_os_events_suspendEnable_title": "Listen to suspend events",

View File

@ -73,26 +73,30 @@ $(document).ready(function () {
//End language selection
$(window.hyperion).on("cmd-authorize-tokenRequest cmd-authorize-getPendingTokenRequests", function (event) {
var val = event.response.info;
if (Array.isArray(event.response.info)) {
if (event.response.info.length == 0) {
return
}
val = event.response.info[0]
if (val.comment == '')
$('#modal_dialog').modal('hide');
}
showInfoDialog("grantToken", $.i18n('conf_network_tok_grantT'), $.i18n('conf_network_tok_grantMsg') + '<br><span style="font-weight:bold">App: ' + val.comment + '</span><br><span style="font-weight:bold">Code: ' + val.id + '</span>')
$("#tok_grant_acc").off().on('click', function () {
tokenList.push(val)
// forward event, in case we need to rebuild the list now
$(window.hyperion).trigger({ type: "build-token-list" });
requestHandleTokenRequest(val.id, true)
});
$("#tok_deny_acc").off().on('click', function () {
requestHandleTokenRequest(val.id, false)
});
if (event.response && event.response.info !== undefined) {
var val = event.response.info;
if (Array.isArray(event.response.info)) {
if (event.response.info.length == 0) {
return
}
val = event.response.info[0]
if (val.comment == '')
$('#modal_dialog').modal('hide');
}
showInfoDialog("grantToken", $.i18n('conf_network_tok_grantT'), $.i18n('conf_network_tok_grantMsg') + '<br><span style="font-weight:bold">App: ' + val.comment + '</span><br><span style="font-weight:bold">Code: ' + val.id + '</span>')
$("#tok_grant_acc").off().on('click', function () {
tokenList.push(val)
// forward event, in case we need to rebuild the list now
$(window.hyperion).trigger({ type: "build-token-list" });
requestHandleTokenRequest(val.id, true)
});
$("#tok_deny_acc").off().on('click', function () {
requestHandleTokenRequest(val.id, false)
});
}
});
$(window.hyperion).one("cmd-authorize-getTokenList", function (event) {
@ -186,21 +190,12 @@ $(document).ready(function () {
}
});
$(window.hyperion).on("cmd-authorize-adminRequired", function (event) {
//Check if a admin login is required.
//If yes: check if default pw is set. If no: go ahead to get server config and render page
if (event.response.info.adminRequired === true)
requestRequiresDefaultPasswortChange();
else
requestServerConfigSchema();
});
$(window.hyperion).on("error", function (event) {
//If we are getting an error "No Authorization" back with a set loginToken we will forward to new Login (Token is expired.
//e.g.: hyperiond was started new in the meantime)
if (event.reason == "No Authorization" && getStorage("loginToken")) {
removeStorage("loginToken");
requestRequiresAdminAuth();
requestRequiresDefaultPasswortChange();
}
else if (event.reason == "Selected Hyperion instance isn't running") {
//Switch to default instance
@ -211,7 +206,7 @@ $(document).ready(function () {
});
$(window.hyperion).on("open", function (event) {
requestRequiresAdminAuth();
requestRequiresDefaultPasswortChange();
});
$(window.hyperion).on("ready", function (event) {

View File

@ -3,10 +3,13 @@ var createdCont = false;
var isScroll = true;
performTranslation();
requestLoggingStop();
$(document).ready(function () {
window.addEventListener('hashchange', function(event) {
requestLoggingStop();
});
requestLoggingStart();
$('#conf_cont').append(createOptPanel('fa-reorder', $.i18n("edt_conf_log_heading_title"), 'editor_container', 'btn_submit'));
@ -178,9 +181,9 @@ $(document).ready(function () {
if (!window.loggingHandlerInstalled) {
window.loggingHandlerInstalled = true;
$(window.hyperion).on("cmd-logging-update", function (event) {
$(window.hyperion).on("cmd-logmsg-update", function (event) {
var messages = (event.response.result.messages);
var messages = (event.response.data.messages);
if (messages.length != 0) {
if (!createdCont) {

View File

@ -213,13 +213,13 @@ $(document).ready(function () {
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));
$('.tktbody').append(createTableRow([tokenList[key].id, 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));
$('.tkthead').html(createTableRow([$.i18n('conf_network_tok_idhead'), $.i18n('conf_network_tok_cidhead'), $.i18n('conf_network_tok_lastuse'), $.i18n('general_btn_delete')], true, true));
buildTokenList();
function handleDeleteToken(e) {

View File

@ -177,6 +177,7 @@ function sendToHyperion(command, subcommand, msg)
else
msg = "";
window.wsTan = Math.floor(Math.random() * 1000)
window.websocket.send('{"command":"'+command+'", "tan":'+window.wsTan+subcommand+msg+'}');
}
@ -187,7 +188,7 @@ function sendToHyperion(command, subcommand, msg)
// data: The json data as Object
// tan: The optional tan, default 1. If the tan is -1, we skip global response error handling
// Returns data of response or false if timeout
async function sendAsyncToHyperion (command, subcommand, data, tan = 1) {
async function sendAsyncToHyperion (command, subcommand, data, tan = Math.floor(Math.random() * 1000) ) {
let obj = { command, tan }
if (subcommand) {Object.assign(obj, {subcommand})}
if (data) { Object.assign(obj, data) }
@ -486,38 +487,38 @@ async function requestLedDeviceDiscovery(type, params)
{
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "discover", data, Math.floor(Math.random() * 1000) );
return sendAsyncToHyperion("leddevice", "discover", data);
}
async function requestLedDeviceProperties(type, params)
{
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "getProperties", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "getProperties", data);
}
function requestLedDeviceIdentification(type, params)
{
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "identify", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "identify", data);
}
async function requestLedDeviceAddAuthorization(type, params) {
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "addAuthorization", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "addAuthorization", data);
}
async function requestInputSourcesDiscovery(type, params) {
let data = { sourceType: type, params: params };
return sendAsyncToHyperion("inputsource", "discover", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("inputsource", "discover", data);
}
async function requestServiceDiscovery(type, params) {
let data = { serviceType: type, params: params };
return sendAsyncToHyperion("service", "discover", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("service", "discover", data);
}

View File

@ -261,7 +261,7 @@ $(document).ready(function () {
$("body").get(0).style.setProperty("--background-var", "none");
}
else {
printLedsToCanvas(event.response.result.leds)
printLedsToCanvas(event.response.data.leds)
$("body").get(0).style.setProperty("--background-var", "url(" + ($('#leds_preview_canv')[0]).toDataURL("image/jpg") + ") no-repeat top left");
}
});
@ -275,7 +275,7 @@ $(document).ready(function () {
}
}
else {
var imageData = (event.response.result.image);
var imageData = (event.response.data.image);
var image = new Image();
image.onload = function () {

View File

@ -319,9 +319,9 @@ function showInfoDialog(type, header, message) {
});
$(document).on('click', '[data-dismiss-modal]', function () {
var target = $(this).attr('data-dismiss-modal');
$.find(target).modal('hide');
});
var target = $(this).data('dismiss-modal');
$($.find(target)).modal('hide');
});
}
function createHintH(type, text, container) {

View File

@ -220,9 +220,7 @@
"internetAccessAPI": false,
"restirctedInternetAccessAPI": false,
"ipWhitelist": [],
"apiAuth": true,
"localApiAuth": false,
"localAdminAuth": true
"localApiAuth": false
},
"ledConfig": {

View File

@ -0,0 +1,122 @@
# JSON-API Commands Overview
## Commands & Sub-Commands
List of commands and related sub-commands which can be used via JSON-API requests.
_Authorization (via password or bearer token)_
**No** - No authorization required<br>
**Yes** - Authorization required, but can be disabled for local network calls<br>
**Admin**: Authorization is always required
_Instance specific_
**Yes** - A specific instance can be addressed<br>
**Multi** - Multiple instances can be addressed via one request<br>
**No** - The command is not instance related
_http/s Support_
**Yes** - Command can be used by individual http/s requests<br>
**No** - Applies only to WebSocket or http/s sessions
| Command | Sub-Command | Authorization | Instance specific | http/s Support |
|:---------------|:------------------------|:--------------|:------------------|:---------------|
| adjustment | - | Yes | Multi | Yes |
| authorize | adminRequired | No | No | Yes |
| authorize | answerRequest | Admin | No | No |
| authorize | createToken | Admin | No | No |
| authorize | deleteToken | Admin | No | Yes |
| authorize | getPendingTokenRequests | Admin | No | No |
| authorize | getTokenList | Admin | No | Yes |
| authorize | login | No | No | No |
| authorize | logout | No | No | No |
| authorize | newPassword | Admin | No | Yes |
| authorize | newPasswordRequired | No | No | Yes |
| authorize | renameToken | Admin | No | Yes |
| authorize | requestToken | No | No | Yes |
| authorize | tokenRequired | No | No | Yes |
| clear | - | Yes | Multi | Yes |
| clearall | - | Yes | Multi | Yes |
| color | - | Yes | Multi | Yes |
| componentstate | - | Yes | Multi | Yes |
| config | getconfig | Admin | Yes | Yes |
| config | getschema | Admin | Yes | Yes |
| config | reload | Admin | Yes | Yes |
| config | restoreconfig | Admin | Yes | Yes |
| config | setconfig | Admin | Yes | Yes |
| correction | - | Yes | Yes | Yes |
| create-effect | - | Yes | Yes | Yes |
| delete-effect | - | Yes | Yes | Yes |
| effect | - | Yes | Multi | Yes |
| image | - | Yes | Multi | Yes |
| inputsource | discover | Yes | No | Yes |
| inputsource | getProperties | Yes | No | Yes |
| instance | createInstance | Admin | No | Yes |
| instance | deleteInstance | Admin | No | Yes |
| instance | saveName | Admin | No | Yes |
| instance | startInstance | Yes | No | Yes |
| instance | stopInstance | Yes | No | Yes |
| instance | switchTo | Yes | No | Yes |
| ledcolors | imagestream-start | Yes | Yes | Yes |
| ledcolors | imagestream-stop | Yes | Yes | Yes |
| ledcolors | ledstream-start | Yes | Yes | Yes |
| ledcolors | ledstream-stop | Yes | Yes | Yes |
| leddevice | addAuthorization | Yes | Yes | Yes |
| leddevice | discover | Yes | Yes | Yes |
| leddevice | getProperties | Yes | Yes | Yes |
| leddevice | identify | Yes | Yes | Yes |
| logging | start | Yes | No | Yes |
| logging | stop | Yes | No | Yes |
| processing | - | Yes | Multi | Yes |
| serverinfo | - | Yes | Yes | Yes |
| serverinfo | getInfo | Yes | Yes | Yes |
| serverinfo | subscribe | Yes | Yes | No |
| serverinfo | unsubscribe | Yes | Yes | No |
| serverinfo | getSubscriptions | Yes | Yes | No |
| serverinfo | getSubscriptionCommands | No | No | No |
| service | discover | Yes | No | Yes |
| sourceselect | - | Yes | Multi | Yes |
| sysinfo | - | Yes | No | Yes |
| system | restart | Yes | No | Yes |
| system | resume | Yes | No | Yes |
| system | suspend | Yes | No | Yes |
| system | toggleSuspend | Yes | No | Yes |
| system | idle | Yes | No | Yes |
| system | toggleIdle | Yes | No | Yes |
| temperature | - | Yes | Yes | Yes |
| transform | - | Yes | Yes | Yes |
| videomode | - | Yes | No | Yes |
## Subscription updates
List of updates which can be subscribed to via the `serverinfo/subscribe`request.
_Instance specific_
**Yes** - A specific instance can be addressed<br>
**No** - The command is not instance related
_in "all"_
**Yes** - Updates are subscribed using "all" as the command<br>
**No** - Subscription is only triggered via JSON-API request
| Subscription Command | Instance specific | in "all" |
|:-----------------------------|:------------------|:---------|
| adjustment-update | Yes | Yes |
| components-update | Yes | Yes |
| effects-update | Yes | Yes |
| event-update | No | Yes |
| imageToLedMapping-update | Yes | Yes |
| instance-update | Yes | Yes |
| ledcolors-imagestream-update | Yes | No |
| ledcolors-ledstream-update | Yes | No |
| leds-update | Yes | Yes |
| logmsg-update | No | No |
| priorities-update | Yes | Yes |
| settings-update | Yes | Yes |
| token-update | No | Yes |
| videomode-update | No | Yes |

View File

@ -14,11 +14,15 @@
// qt includes
#include <QString>
class QTimer;
class JsonCB;
class JsonCallbacks;
class HyperionIManager;
const QString NO_AUTH = "No Authorization";
// Constants
namespace {
const char NO_AUTHORIZATION[] = "No Authorization";;
}
///
/// @brief API for Hyperion to be inherted from a child class with specific protocol implementations
@ -31,205 +35,214 @@ const QString NO_AUTH = "No Authorization";
class API : public QObject
{
Q_OBJECT
Q_OBJECT
public:
#include <api/apiStructs.h>
// workaround Q_ARG std::map template issues
typedef std::map<int, registerData> MapRegister;
typedef QMap<QString, AuthManager::AuthDefinition> MapAuthDefs;
///
/// Constructor
///
///@ param The parent Logger
/// @param localConnection Is this a local network connection? Use utils/NetOrigin to check that
/// @param parent Parent QObject
///
API(Logger *log, bool localConnection, QObject *parent);
protected:
///
/// @brief Initialize the API
/// This call is REQUIRED!
///
void init();
///
/// @brief Set a single color
/// @param[in] priority The priority of the written color
/// @param[in] ledColor The color to write to the leds
/// @param[in] timeout_ms The time the leds are set to the given color [ms]
/// @param[in] origin The setter
///
void setColor(int priority, const std::vector<uint8_t> &ledColors, int timeout_ms = -1, const QString &origin = "API", hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set a image
/// @param[in] data The command data
/// @param[in] comp The component that should be used
/// @param[out] replyMsg The replyMsg on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @return True on success
///
bool setImage(ImageCmdData &data, hyperion::Components comp, QString &replyMsg, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Clear a priority in the Muxer, if -1 all priorities are cleared
/// @param priority The priority to clear
/// @param replyMsg the message on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @return True on success
///
bool clearPriority(int priority, QString &replyMsg, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set a new component state
/// @param comp The component name
/// @param compState The new state of the comp
/// @param replyMsg The reply on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @ return True on success
///
bool setComponentState(const QString &comp, bool &compState, QString &replyMsg, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set a ledToImageMapping type
/// @param type mapping type string
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setLedMappingType(int type, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set the 2D/3D modes type
/// @param mode The VideoMode
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setVideoMode(VideoMode mode, hyperion::Components callerComp = hyperion::COMP_INVALID);
#if defined(ENABLE_EFFECTENGINE)
///
/// @brief Set an effect
/// @param dat The effect data
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// REQUIRED dat fields: effectName, priority, duration, origin
/// @return True on success else false
///
bool setEffect(const EffectCmdData &dat, hyperion::Components callerComp = hyperion::COMP_INVALID);
#endif
///
/// @brief Set source auto select enabled or disabled
/// @param sate The new state
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setSourceAutoSelect(bool state, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set the visible priority to given priority
/// @param priority The priority to set
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setVisiblePriority(int priority, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Register a input or update the meta data of a previous register call
/// ATTENTION: Check unregisterInput() method description !!!
/// @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] callerComp The component that call this (e.g. PROTO/FLAT)
///
void registerInput(int priority, hyperion::Components component, const QString &origin, const QString &owner, hyperion::Components callerComp);
///
/// @brief Revoke a registerInput() call by priority. We maintain all registered priorities in this scope
/// ATTENTION: This is MANDATORY if you change (priority change) or stop(clear/timeout) DURING lifetime. If this class destructs it's not needed
/// @param priority The priority to unregister
///
void unregisterInput(int priority);
///
/// @brief Handle the instance switching
/// @param inst The requested instance
/// @return True on success else false
///
bool setHyperionInstance(quint8 inst);
// workaround Q_ARG std::map template issues
typedef std::map<int, registerData> MapRegister;
typedef QMap<QString, AuthManager::AuthDefinition> MapAuthDefs;
///
/// @brief Check if Hyperion ist enabled
/// @return True when enabled else false
///
bool isHyperionEnabled();
/// Constructor
///
///@ param The parent Logger
/// @param localConnection Is this a local network connection? Use utils/NetOrigin to check that
/// @param parent Parent QObject
///
API(Logger *log, bool localConnection, QObject *parent);
///
/// @brief Get all instances data
/// @return The instance data
///
QVector<QVariantMap> getAllInstanceData();
protected:
///
/// @brief Initialize the API
/// This call is REQUIRED!
///
void init();
///
/// @brief Start instance
/// @param index The instance index
/// @param tan The tan
/// @return True on success else false
///
bool startInstance(quint8 index, int tan = 0);
virtual void stopDataConnections() = 0;
///
/// @brief Stop instance
/// @param index The instance index
///
void stopInstance(quint8 index);
///
/// @brief Set a single color
/// @param[in] priority The priority of the written color
/// @param[in] ledColor The color to write to the leds
/// @param[in] timeout_ms The time the leds are set to the given color [ms]
/// @param[in] origin The setter
///
void setColor(int priority, const std::vector<uint8_t> &ledColors, int timeout_ms = -1, const QString &origin = "API", hyperion::Components callerComp = hyperion::COMP_INVALID);
//////////////////////////////////
/// AUTH / ADMINISTRATION METHODS
//////////////////////////////////
///
/// @brief Set a image
/// @param[in] data The command data
/// @param[in] comp The component that should be used
/// @param[out] replyMsg The replyMsg on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @return True on success
///
bool setImage(ImageCmdData &data, hyperion::Components comp, QString &replyMsg, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Delete instance. Requires ADMIN ACCESS
/// @param index The instance index
/// @param replyMsg The reply Msg
/// @return False with reply
///
bool deleteInstance(quint8 index, QString &replyMsg);
///
/// @brief Clear a priority in the Muxer, if -1 all priorities are cleared
/// @param priority The priority to clear
/// @param replyMsg the message on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @return True on success
///
bool clearPriority(int priority, QString &replyMsg, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Create instance. Requires ADMIN ACCESS
/// @param name With given name
/// @return False with reply
///
QString createInstance(const QString &name);
///
/// @brief Set a new component state
/// @param comp The component name
/// @param compState The new state of the comp
/// @param replyMsg The reply on failure
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// @ return True on success
///
bool setComponentState(const QString &comp, bool &compState, QString &replyMsg, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Rename an instance. Requires ADMIN ACCESS
/// @param index The instance index
/// @param name With given name
/// @return False with reply
///
QString setInstanceName(quint8 index, const QString &name);
///
/// @brief Set a ledToImageMapping type
/// @param type mapping type string
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setLedMappingType(int type, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set the 2D/3D modes type
/// @param mode The VideoMode
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setVideoMode(VideoMode mode, hyperion::Components callerComp = hyperion::COMP_INVALID);
#if defined(ENABLE_EFFECTENGINE)
///
/// @brief Delete an effect. Requires ADMIN ACCESS
/// @param name The effect name
/// @return True on success else false
///
QString deleteEffect(const QString &name);
///
/// @brief Delete an effect. Requires ADMIN ACCESS
/// @param name The effect name
/// @return True on success else false
///
QString saveEffect(const QJsonObject &data);
///
/// @brief Set an effect
/// @param dat The effect data
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
/// REQUIRED dat fields: effectName, priority, duration, origin
/// @return True on success else false
///
bool setEffect(const EffectCmdData &dat, hyperion::Components callerComp = hyperion::COMP_INVALID);
#endif
///
/// @brief Save settings object. Requires ADMIN ACCESS
/// @param data The data object
///
///
/// @brief Set source auto select enabled or disabled
/// @param sate The new state
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setSourceAutoSelect(bool state, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Set the visible priority to given priority
/// @param priority The priority to set
/// @param callerComp The HYPERION COMPONENT that calls this function! e.g. PROT/FLATBUF
///
void setVisiblePriority(int priority, hyperion::Components callerComp = hyperion::COMP_INVALID);
///
/// @brief Register a input or update the meta data of a previous register call
/// ATTENTION: Check unregisterInput() method description !!!
/// @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] callerComp The component that call this (e.g. PROTO/FLAT)
///
void registerInput(int priority, hyperion::Components component, const QString &origin, const QString &owner, hyperion::Components callerComp);
///
/// @brief Revoke a registerInput() call by priority. We maintain all registered priorities in this scope
/// ATTENTION: This is MANDATORY if you change (priority change) or stop(clear/timeout) DURING lifetime. If this class destructs it's not needed
/// @param priority The priority to unregister
///
void unregisterInput(int priority);
///
/// @brief Handle the instance switching
/// @param inst The requested instance
/// @return True on success else false
///
bool setHyperionInstance(quint8 inst);
///
/// @brief Check if Hyperion ist enabled
/// @return True when enabled else false
///
bool isHyperionEnabled();
///
/// @brief Get all instances data
/// @return The instance data
///
QVector<QVariantMap> getAllInstanceData() const;
///
/// @brief Get the current instances index
/// @return The instance index set
///
quint8 getCurrentInstanceIndex() const { return _currInstanceIndex; }
///
/// @brief Start instance
/// @param index The instance index
/// @param tan The tan
/// @return True on success else false
///
bool startInstance(quint8 index, int tan = 0);
///
/// @brief Stop instance
/// @param index The instance index
///
void stopInstance(quint8 index);
//////////////////////////////////
/// AUTH / ADMINISTRATION METHODS
//////////////////////////////////
///
/// @brief Delete instance. Requires ADMIN ACCESS
/// @param index The instance index
/// @param replyMsg The reply Msg
/// @return False with reply
///
bool deleteInstance(quint8 index, QString &replyMsg);
///
/// @brief Create instance. Requires ADMIN ACCESS
/// @param name With given name
/// @return False with reply
///
QString createInstance(const QString &name);
///
/// @brief Rename an instance. Requires ADMIN ACCESS
/// @param index The instance index
/// @param name With given name
/// @return False with reply
///
QString setInstanceName(quint8 index, const QString &name);
#if defined(ENABLE_EFFECTENGINE)
///
/// @brief Delete an effect. Requires ADMIN ACCESS
/// @param name The effect name
/// @return True on success else false
///
QString deleteEffect(const QString &name);
///
/// @brief Delete an effect. Requires ADMIN ACCESS
/// @param name The effect name
/// @return True on success else false
///
QString saveEffect(const QJsonObject &data);
#endif
///
/// @brief Save settings object. Requires ADMIN ACCESS
/// @param data The data object
///
bool saveSettings(const QJsonObject &data);
///
@ -238,171 +251,189 @@ protected:
///
bool restoreSettings(const QJsonObject &data);
///
/// @brief Test if we are authorized to use the interface
/// @return The result
///
bool isAuthorized() { return _authorized; };
///
/// @brief Set the authorizationn state
/// @param authorized True, if authorized
///
void setAuthorization(bool authorized) { _authorized = authorized; }
///
/// @brief Test if we are authorized to use the admin interface
/// @return The result
///
bool isAdminAuthorized() { return _adminAuthorized; };
///
/// @brief Test if we are authorized to use the interface
/// @return The result
///
bool isAuthorized() const { return _authorized; }
///
/// @brief Update the Password of Hyperion. Requires ADMIN ACCESS
/// @param password Old password
/// @param newPassword New password
/// @return True on success else false
///
bool updateHyperionPassword(const QString &password, const QString &newPassword);
///
/// @brief Set the authorizationn state for admin activities
/// @param authorized True, if authorized
///
void setAdminAuthorization(bool adminAuthorized) { _adminAuthorized = adminAuthorized; }
///
/// @brief Get a new token from AuthManager. Requires ADMIN ACCESS
/// @param comment The comment of the request
/// @param def The final definition
/// @return Empty string on success else error message
///
QString createToken(const QString &comment, AuthManager::AuthDefinition &def);
///
/// @brief Test if we are authorized to use admin activites
/// @return The result
///
bool isAdminAuthorized() const { return _adminAuthorized; }
///
/// @brief Rename a token by given id. Requires ADMIN ACCESS
/// @param id The id of the token
/// @param comment The new comment
/// @return Empty string on success else error message
///
QString renameToken(const QString &id, const QString &comment);
///
/// @brief Return, if connection is from local network segment
/// @return The result
///
bool islocalConnection() const { return _localConnection; }
///
/// @brief Delete a token by given id. Requires ADMIN ACCESS
/// @param id The id of the token
/// @return Empty string on success else error message
///
QString deleteToken(const QString &id);
///
/// @brief Update the Password of Hyperion. Requires ADMIN ACCESS
/// @param password Old password
/// @param newPassword New password
/// @return True on success else false
///
bool updateHyperionPassword(const QString &password, const QString &newPassword);
///
/// @brief Set a new token request
/// @param comment The comment
/// @param id The id
///
/// @brief Get a new token from AuthManager. Requires ADMIN ACCESS
/// @param comment The comment of the request
/// @param def The final definition
/// @return Empty string on success else error message
///
QString createToken(const QString &comment, AuthManager::AuthDefinition &def);
///
/// @brief Rename a token by given id. Requires ADMIN ACCESS
/// @param tokenId The id of the token
/// @param comment The new comment
/// @return Empty string on success else error message
///
QString renameToken(const QString &tokenId, const QString &comment);
///
/// @brief Delete a token by given id. Requires ADMIN ACCESS
/// @param tokenId The id of the token
/// @return Empty string on success else error message
///
QString deleteToken(const QString &tokenId);
///
/// @brief Set a new token request
/// @param comment The comment
/// @param tokenId The id of the token
/// @param tan The tan
///
void setNewTokenRequest(const QString &comment, const QString &id, const int &tan);
///
void setNewTokenRequest(const QString &comment, const QString &tokenId, const int &tan);
///
/// @brief Cancel new token request
/// @param comment The comment
/// @param id The id
///
void cancelNewTokenRequest(const QString &comment, const QString &id);
///
/// @brief Cancel new token request
/// @param comment The comment
/// @param tokenId The id of the token
///
void cancelNewTokenRequest(const QString &comment, const QString &tokenId);
///
/// @brief Handle a pending token request. Requires ADMIN ACCESS
/// @param id The id fo the request
/// @param accept True when it should be accepted, else false
/// @return True on success
bool handlePendingTokenRequest(const QString &id, bool accept);
///
/// @brief Get the current List of Tokens. Requires ADMIN ACCESS
/// @param def returns the defintions
/// @return True on success
///
bool getTokenList(QVector<AuthManager::AuthDefinition> &def);
///
/// @brief Get all current pending token requests. Requires ADMIN ACCESS
/// @return True on success
///
bool getPendingTokenRequests(QVector<AuthManager::AuthDefinition> &map);
///
/// @brief Is User Token Authorized. On success this will grant acces to API and ADMIN API
/// @param userToken The user Token
/// @return True on succes
///
bool isUserTokenAuthorized(const QString &userToken);
///
/// @brief Get the current User Token (session token). Requires ADMIN ACCESS
/// @param userToken The user Token
/// @return True on success
///
bool getUserToken(QString &userToken);
///
/// @brief Is a token authorized. On success this will grant acces to the API (NOT ADMIN API)
/// @param token The user Token
///
/// @brief Handle a pending token request. Requires ADMIN ACCESS
/// @param tokenId The id fo the request
/// @param accept True when it should be accepted, else false
/// @return True on success
///
bool isTokenAuthorized(const QString &token);
bool handlePendingTokenRequest(const QString &tokenId, bool accept);
///
/// @brief Is User authorized. On success this will grant acces to the API and ADMIN API
/// @param password The password of the User
/// @return True if authorized
///
bool isUserAuthorized(const QString &password);
///
/// @brief Get the current List of Tokens. Requires ADMIN ACCESS
/// @param def returns the defintions
/// @return True on success
///
bool getTokenList(QVector<AuthManager::AuthDefinition> &def);
///
/// @brief Test if Hyperion has the default PW
/// @return The result
///
bool hasHyperionDefaultPw();
///
/// @brief Get all current pending token requests. Requires ADMIN ACCESS
/// @return True on success
///
bool getPendingTokenRequests(QVector<AuthManager::AuthDefinition> &map);
///
/// @brief Logout revokes all authorizations
///
void logout();
///
/// @brief Is User Token Authorized. On success this will grant acces to API and ADMIN API
/// @param userToken The user Token
/// @return True on succes
///
bool isUserTokenAuthorized(const QString &userToken);
/// Reflect auth status of this client
bool _authorized;
bool _adminAuthorized;
///
/// @brief Get the current User Token (session token). Requires ADMIN ACCESS
/// @param userToken The user Token
/// @return True on success
///
bool getUserToken(QString &userToken);
/// Is this a local connection
bool _localConnection;
///
/// @brief Is a token authorized. On success this will grant acces to the API (NOT ADMIN API)
/// @param token The user Token
/// @return True on success
///
bool isTokenAuthorized(const QString &token);
AuthManager *_authManager;
HyperionIManager *_instanceManager;
///
/// @brief Is User authorized. On success this will grant acces to the API and ADMIN API
/// @param password The password of the User
/// @return True if authorized
///
bool isUserAuthorized(const QString &password);
Logger *_log;
Hyperion *_hyperion;
///
/// @brief Test if Hyperion has the default PW
/// @return The result
///
bool hasHyperionDefaultPw();
///
/// @brief Logout revokes all authorizations
///
void logout();
AuthManager *_authManager;
HyperionIManager *_instanceManager;
Logger *_log;
Hyperion *_hyperion;
signals:
///
/// @brief The API might decide to block connections for security reasons, this emitter should close the socket
///
void forceClose();
///
/// @brief The API might decide to block connections for security reasons, this emitter should close the socket
///
void forceClose();
///
/// @brief Emits whenever a new Token request is pending. This signal is just active when ADMIN ACCESS has been granted
/// @param id The id of the request
/// @param comment The comment of the request; If the commen is EMPTY the request has been revoked by the caller. So remove it from the pending list
///
void onPendingTokenRequest(const QString &id, const QString &comment);
///
/// @brief Emits whenever a new Token request is pending. This signal is just active when ADMIN ACCESS has been granted
/// @param tokenId The id of the request
/// @param comment The comment of the request; If the commen is EMPTY the request has been revoked by the caller. So remove it from the pending list
///
void onPendingTokenRequest(const QString &tokenId, const QString &comment);
///
/// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance it will emit.
/// @param success If true the request was accepted else false and no token was created
/// @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
/// @param tan The tan that was part of the request
///
void onTokenResponse(bool success, const QString &token, const QString &comment, const QString &id, const int &tan);
///
/// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance it will emit.
/// @param success If true the request was accepted else false and no token was created
/// @param token The new token that is now valid
/// @param comment The comment that was part of the request
/// @param tokenId The id that was part of the request
/// @param tan The tan that was part of the request
///
void onTokenResponse(bool success, const QString &token, const QString &comment, const QString &tokenId, const int &tan);
///
/// @brief Handle emits from HyperionIManager of startInstance request, just if QObject matches with this instance it will emit.
/// @param tan The tan that was part of the request
///
void onStartInstanceResponse(const int &tan);
///
/// @brief Handle emits from HyperionIManager of startInstance request, just if QObject matches with this instance it will emit.
/// @param tan The tan that was part of the request
///
void onStartInstanceResponse(const int &tan);
private:
void stopDataConnectionss();
// Contains all active register call data
std::map<int, registerData> _activeRegisters;
/// Reflect authorization status of this client
bool _authorized;
bool _adminAuthorized;
// current instance index
quint8 _currInstanceIndex;
/// Is this a local connection
bool _localConnection;
// Contains all active register call data
std::map<int, registerData> _activeRegisters;
// current instance index
quint8 _currInstanceIndex;
};

View File

@ -2,19 +2,24 @@
// parent class
#include <api/API.h>
#include <api/JsonApiCommand.h>
#include <events/EventEnum.h>
// hyperion includes
#include <utils/Components.h>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
#include <utils/RgbChannelAdjustment.h>
// qt includes
#include <QJsonObject>
#include <QString>
#include <QSharedPointer>
#include <QScopedPointer>
class QTimer;
class JsonCB;
class JsonCallbacks;
class AuthManager;
class JsonAPI : public API
@ -46,40 +51,24 @@ public:
void initialize();
public slots:
///
/// @brief Is called whenever the current Hyperion instance pushes new led raw values (if enabled)
/// @param ledColors The current led colors
///
void streamLedcolorsUpdate(const std::vector<ColorRgb> &ledColors);
///
/// @brief Push images whenever hyperion emits (if enabled)
/// @param image The current image
///
void setImage(const Image<ColorRgb> &image);
///
/// @brief Process and push new log messages from logger (if enabled)
///
void incommingLogMessage(const Logger::T_LOG_MESSAGE &);
private slots:
///
/// @brief Handle emits from API of a new Token request.
/// @param id The id of the request
/// @param identifier The identifier of the request
/// @param comment The comment which needs to be accepted
///
void newPendingTokenRequest(const QString &id, const QString &comment);
void issueNewPendingTokenRequest(const QString &identifier, 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 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
/// @param identifier The identifier that was part of the request
/// @param tan The tan that was part of the request
///
void handleTokenResponse(bool success, const QString &token, const QString &comment, const QString &id, const int &tan);
void handleTokenResponse(bool success, const QString &token, const QString &comment, const QString &identifier, const int &tan);
///
/// @brief Handle whenever the state of a instance (HyperionIManager) changes according to enum instanceState
@ -89,11 +78,6 @@ private slots:
///
void handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name = QString());
///
/// @brief Stream a new LED Colors update
///
void streamLedColorsUpdate();
signals:
///
/// Signal emits with the reply message provided with handleMessage()
@ -111,31 +95,9 @@ signals:
void signalEvent(Event event);
private:
// true if further callbacks are forbidden (http)
bool _noListener;
/// The peer address of the client
QString _peerAddress;
// The JsonCB instance which handles data subscription/notifications
JsonCB *_jsonCB;
// streaming buffers
QJsonObject _streaming_leds_reply;
QJsonObject _streaming_image_reply;
QJsonObject _streaming_logging_reply;
/// flag to determine state of log streaming
bool _streaming_logging_activated;
/// timer for led color refresh
QTimer *_ledStreamTimer;
/// led stream connection handle
QMetaObject::Connection _ledStreamConnection;
/// the current streaming led values
std::vector<ColorRgb> _currentLedValues;
void handleCommand(const JsonApiCommand& cmd, const QJsonObject &message);
void handleInstanceCommand(const JsonApiCommand& cmd, const QJsonObject &message);
///
/// @brief Handle the switches of Hyperion instances
@ -150,14 +112,14 @@ private:
///
/// @param message the incoming message
///
void handleColorCommand(const QJsonObject &message, const QString &command, int tan);
void handleColorCommand(const QJsonObject& message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Image message
///
/// @param message the incoming message
///
void handleImageCommand(const QJsonObject &message, const QString &command, int tan);
void handleImageCommand(const QJsonObject &message, const JsonApiCommand& cmd);
#if defined(ENABLE_EFFECTENGINE)
///
@ -165,21 +127,21 @@ private:
///
/// @param message the incoming message
///
void handleEffectCommand(const QJsonObject &message, const QString &command, int tan);
void handleEffectCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Effect message (Write JSON Effect)
///
/// @param message the incoming message
///
void handleCreateEffectCommand(const QJsonObject &message, const QString &command, int tan);
void handleCreateEffectCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Effect message (Delete JSON Effect)
///
/// @param message the incoming message
///
void handleDeleteEffectCommand(const QJsonObject &message, const QString &command, int tan);
void handleDeleteEffectCommand(const QJsonObject &message, const JsonApiCommand& cmd);
#endif
///
@ -187,158 +149,250 @@ private:
///
/// @param message the incoming message
///
void handleSysInfoCommand(const QJsonObject &message, const QString &command, int tan);
void handleSysInfoCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Server info message
///
/// @param message the incoming message
///
void handleServerInfoCommand(const QJsonObject &message, const QString &command, int tan);
void handleServerInfoCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Clear message
///
/// @param message the incoming message
///
void handleClearCommand(const QJsonObject &message, const QString &command, int tan);
void handleClearCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Clearall message
///
/// @param message the incoming message
///
void handleClearallCommand(const QJsonObject &message, const QString &command, int tan);
void handleClearallCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Adjustment message
///
/// @param message the incoming message
///
void handleAdjustmentCommand(const QJsonObject &message, const QString &command, int tan);
void handleAdjustmentCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON SourceSelect message
///
/// @param message the incoming message
///
void handleSourceSelectCommand(const QJsonObject &message, const QString &command, int tan);
void handleSourceSelectCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON GetConfig message and check subcommand
///
/// @param message the incoming message
///
void handleConfigCommand(const QJsonObject &message, const QString &command, int tan);
void handleConfigCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON GetSchema message from handleConfigCommand()
///
/// @param message the incoming message
///
void handleSchemaGetCommand(const QJsonObject &message, const QString &command, int tan);
void handleSchemaGetCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON SetConfig message from handleConfigCommand()
///
/// @param message the incoming message
///
void handleConfigSetCommand(const QJsonObject &message, const QString &command, int tan);
void handleConfigSetCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON RestoreConfig message from handleConfigCommand()
///
/// @param message the incoming message
///
void handleConfigRestoreCommand(const QJsonObject &message, const QString &command, int tan);
void handleConfigRestoreCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON Component State message
///
/// @param message the incoming message
///
void handleComponentStateCommand(const QJsonObject &message, const QString &command, int tan);
void handleComponentStateCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON Led Colors message
///
/// @param message the incoming message
///
void handleLedColorsCommand(const QJsonObject &message, const QString &command, int tan);
void handleLedColorsCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON Logging message
///
/// @param message the incoming message
///
void handleLoggingCommand(const QJsonObject &message, const QString &command, int tan);
void handleLoggingCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON Processing message
///
/// @param message the incoming message
///
void handleProcessingCommand(const QJsonObject &message, const QString &command, int tan);
void handleProcessingCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON VideoMode message
///
/// @param message the incoming message
///
void handleVideoModeCommand(const QJsonObject &message, const QString &command, int tan);
void handleVideoModeCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON plugin message
///
/// @param message the incoming message
///
void handleAuthorizeCommand(const QJsonObject &message, const QString &command, int tan);
void handleAuthorizeCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON instance message
///
/// @param message the incoming message
///
void handleInstanceCommand(const QJsonObject &message, const QString &command, int tan);
void handleInstanceCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON Led Device message
///
/// @param message the incoming message
///
void handleLedDeviceCommand(const QJsonObject &message, const QString &command, int tan);
void handleLedDeviceCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON message regarding Input Sources (Grabbers)
///
/// @param message the incoming message
///
void handleInputSourceCommand(const QJsonObject& message, const QString& command, int tan);
void handleInputSourceCommand(const QJsonObject& message, const JsonApiCommand& cmd);
/// Handle an incoming JSON message to request remote hyperion servers providing a given hyperion service
///
/// @param message the incoming message
///
void handleServiceCommand(const QJsonObject &message, const QString &command, int tan);
void handleServiceCommand(const QJsonObject &message, const JsonApiCommand& cmd);
/// Handle an incoming JSON message for actions related to the overall Hyperion system
///
/// @param message the incoming message
///
void handleSystemCommand(const QJsonObject &message, const QString &command, int tan);
void handleSystemCommand(const QJsonObject &message, const JsonApiCommand& cmd);
///
/// Handle an incoming JSON message of unknown type
///
void handleNotImplemented(const QString &command, int tan);
void applyColorAdjustments(const QJsonObject &adjustment, ColorAdjustment *colorAdjustment);
void applyColorAdjustment(const QString &colorName, const QJsonObject &adjustment, RgbChannelAdjustment &rgbAdjustment);
void applyGammaTransform(const QString &transformName, const QJsonObject &adjustment, RgbTransform &rgbTransform, char channel);
void applyTransforms(const QJsonObject &adjustment, ColorAdjustment *colorAdjustment);
template<typename T>
void applyTransform(const QString &transformName, const QJsonObject &adjustment, T &transform, void (T::*setFunction)(bool));
template<typename T>
void applyTransform(const QString &transformName, const QJsonObject &adjustment, T &transform, void (T::*setFunction)(double));
template<typename T>
void applyTransform(const QString &transformName, const QJsonObject &adjustment, T &transform, void (T::*setFunction)(uint8_t));
void handleTokenRequired(const JsonApiCommand& cmd);
void handleAdminRequired(const JsonApiCommand& cmd);
void handleNewPasswordRequired(const JsonApiCommand& cmd);
void handleLogout(const JsonApiCommand& cmd);
void handleNewPassword(const QJsonObject &message, const JsonApiCommand& cmd);
void handleCreateToken(const QJsonObject &message, const JsonApiCommand& cmd);
void handleRenameToken(const QJsonObject &message, const JsonApiCommand& cmd);
void handleDeleteToken(const QJsonObject &message, const JsonApiCommand& cmd);
void handleRequestToken(const QJsonObject &message, const JsonApiCommand& cmd);
void handleGetPendingTokenRequests(const JsonApiCommand& cmd);
void handleAnswerRequest(const QJsonObject &message, const JsonApiCommand& cmd);
void handleGetTokenList(const JsonApiCommand& cmd);
void handleLogin(const QJsonObject &message, const JsonApiCommand& cmd);
void handleLedDeviceDiscover(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd);
void handleLedDeviceGetProperties(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd);
void handleLedDeviceIdentify(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd);
void handleLedDeviceAddAuthorization(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd);
QJsonObject getBasicCommandReply(bool success, const QString &command, int tan, InstanceCmd::Type isInstanceCmd) const;
///
/// Send a standard reply indicating success
///
void sendSuccessReply(const QString &command = "", int tan = 0);
void sendSuccessReply(const JsonApiCommand& cmd);
///
/// Send a standard reply indicating success
///
void sendSuccessReply(const QString &command = "", int tan = 0, InstanceCmd::Type isInstanceCmd = InstanceCmd::No);
///
/// Send a standard reply indicating success with data
///
void sendSuccessDataReply(const QJsonDocument &doc, const QString &command = "", int tan = 0);
void sendSuccessDataReply(const QJsonValue &infoData, const JsonApiCommand& cmd);
///
/// Send a standard reply indicating success with data
///
void sendSuccessDataReply(const QJsonValue &infoData, const QString &command = "", int tan = 0, InstanceCmd::Type isInstanceCmd = InstanceCmd::No);
///
/// Send a standard reply indicating success with data and error details
///
void sendSuccessDataReplyWithError(const QJsonValue &infoData, const JsonApiCommand& cmd, const QStringList& errorDetails = {});
///
/// Send a standard reply indicating success with data and error details
///
void sendSuccessDataReplyWithError(const QJsonValue &infoData, const QString &command = "", int tan = 0, const QStringList& errorDetails = {}, InstanceCmd::Type isInstanceCmd = InstanceCmd::No);
///
/// Send a message with data.
/// Note: To be used as a new message and not as a response to a previous request.
///
void sendNewRequest(const QJsonValue &infoData, const JsonApiCommand& cmd);
///
/// Send a message with data
/// Note: To be used as a new message and not as a response to a previous request.
///
void sendNewRequest(const QJsonValue &infoData, const QString &command, InstanceCmd::Type isInstanceCmd = InstanceCmd::No);
///
/// Send an error message back to the client
///
/// @param error String describing the error
///
void sendErrorReply(const QString &error, const QString &command = "", int tan = 0);
void sendErrorReply(const QString &error, const JsonApiCommand& cmd);
///
/// Send an error message back to the client
///
/// @param error String describing the error
/// @param errorDetails additional information detailing the error scenario
///
void sendErrorReply(const QString &error, const QStringList& errorDetails, const JsonApiCommand& cmd);
///
/// Send an error message back to the client
///
/// @param error String describing the error
/// @param errorDetails additional information detailing the error scenario
///
void sendErrorReply(const QString &error, const QStringList& errorDetails = {}, const QString &command = "", int tan = 0, InstanceCmd::Type isInstanceCmd = InstanceCmd::No);
void sendNoAuthorization(const JsonApiCommand& cmd);
///
/// @brief Kill all signal/slot connections to stop possible data emitter
///
void stopDataConnections();
void stopDataConnections() override;
static QString findCommand (const QString& jsonS);
static int findTan (const QString& jsonString);
// true if further callbacks are forbidden (http)
bool _noListener;
/// The peer address of the client
QString _peerAddress;
// The JsonCallbacks instance which handles data subscription/notifications
QSharedPointer<JsonCallbacks> _jsonCB;
};

View File

@ -0,0 +1,332 @@
#ifndef JSONAPICOMMAND_H
#define JSONAPICOMMAND_H
#include <QMap>
#include <QPair>
#include <QString>
class Command {
public:
enum Type {
Unknown,
Adjustment,
Authorize,
Clear,
ClearAll,
Color,
ComponentState,
Config,
Correction,
CreateEffect,
DeleteEffect,
Effect,
Image,
InputSource,
Instance,
LedColors,
LedDevice,
Logging,
Processing,
ServerInfo,
Service,
SourceSelect,
SysInfo,
System,
Temperature,
Transform,
VideoMode
};
static QString toString(Type type) {
switch (type) {
case Adjustment: return "adjustment";
case Authorize: return "authorize";
case Clear: return "clear";
case ClearAll: return "clearall";
case Color: return "color";
case ComponentState: return "componentstate";
case Config: return "config";
case Correction: return "correction";
case CreateEffect: return "create-effect";
case DeleteEffect: return "delete-effect";
case Effect: return "effect";
case Image: return "image";
case InputSource: return "inputsource";
case Instance: return "instance";
case LedColors: return "ledcolors";
case LedDevice: return "leddevice";
case Logging: return "logging";
case Processing: return "processing";
case ServerInfo: return "serverinfo";
case SourceSelect: return "sourceselect";
case SysInfo: return "sysinfo";
case System: return "system";
case Temperature: return "temperature";
case Transform: return "transform";
case VideoMode: return "videomode";
case Service: return "service";
default: return "unknown";
}
}
};
class SubCommand {
public:
enum Type {
Unknown,
Empty,
AdminRequired,
AddAuthorization,
AnswerRequest,
CreateInstance,
CreateToken,
DeleteInstance,
DeleteToken,
Discover,
GetConfig,
GetInfo,
GetPendingTokenRequests,
GetProperties,
GetSchema,
GetSubscriptionCommands,
GetSubscriptions,
GetTokenList,
Identify,
Idle,
ImageStreamStart,
ImageStreamStop,
LedStreamStart,
LedStreamStop,
Login,
Logout,
NewPassword,
NewPasswordRequired,
Reload,
RenameToken,
RequestToken,
Restart,
RestoreConfig,
Resume,
SaveName,
SetConfig,
Start,
StartInstance,
Stop,
StopInstance,
Subscribe,
Suspend,
SwitchTo,
ToggleIdle,
ToggleSuspend,
TokenRequired,
Unsubscribe
};
static QString toString(Type type) {
switch (type) {
case Empty: return "";
case AdminRequired: return "adminRequired";
case AddAuthorization: return "addAuthorization";
case AnswerRequest: return "answerRequest";
case CreateInstance: return "createInstance";
case CreateToken: return "createToken";
case DeleteInstance: return "deleteInstance";
case DeleteToken: return "deleteToken";
case Discover: return "discover";
case GetConfig: return "getconfig";
case GetInfo: return "getInfo";
case GetPendingTokenRequests: return "getPendingTokenRequests";
case GetProperties: return "getProperties";
case GetSchema: return "getschema";
case GetSubscriptionCommands: return "getSubscriptionCommands";
case GetSubscriptions: return "getSubscriptions";
case GetTokenList: return "getTokenList";
case Identify: return "identify";
case Idle: return "idle";
case ImageStreamStart: return "imagestream-start";
case ImageStreamStop: return "imagestream-stop";
case LedStreamStart: return "ledstream-start";
case LedStreamStop: return "ledstream-stop";
case Login: return "login";
case Logout: return "logout";
case NewPassword: return "newPassword";
case NewPasswordRequired: return "newPasswordRequired";
case Reload: return "reload";
case RenameToken: return "renameToken";
case RequestToken: return "requestToken";
case Restart: return "restart";
case RestoreConfig: return "restoreconfig";
case Resume: return "resume";
case SaveName: return "saveName";
case SetConfig: return "setconfig";
case Start: return "start";
case StartInstance: return "startInstance";
case Stop: return "stop";
case StopInstance: return "stopInstance";
case Subscribe: return "subscribe";
case Suspend: return "suspend";
case SwitchTo: return "switchTo";
case ToggleIdle: return "toggleIdle";
case ToggleSuspend: return "toggleSuspend";
case TokenRequired: return "tokenRequired";
case Unsubscribe: return "unsubscribe";
default: return "unknown";
}
}
};
class Authorization {
public:
enum Type {
Admin,
Yes,
No
};
};
class NoListenerCmd {
public:
enum Type {
No,
Yes
};
};
class InstanceCmd {
public:
enum Type {
No,
Yes,
Multi
};
};
class JsonApiCommand {
public:
JsonApiCommand()
: command(Command::Unknown),
subCommand(SubCommand::Unknown),
tan(0),
authorization(Authorization::Admin),
isInstanceCmd(InstanceCmd::No),
isNolistenerCmd(NoListenerCmd::Yes)
{}
JsonApiCommand(Command::Type command, SubCommand::Type subCommand,
Authorization::Type authorization,
InstanceCmd::Type isInstanceCmd,
NoListenerCmd::Type isNolistenerCmd,
int tan = 0)
: command(command),
subCommand(subCommand),
tan(tan),
authorization(authorization),
isInstanceCmd(isInstanceCmd),
isNolistenerCmd(isNolistenerCmd)
{}
Command::Type getCommand() const { return command; }
SubCommand::Type getSubCommand() const { return subCommand; }
InstanceCmd::Type instanceCmd() const { return isInstanceCmd; }
int getTan() const { return tan; }
QString toString() const {
QString cmd = Command::toString(command);
if (subCommand > SubCommand::Empty) {
cmd += QString("-%2").arg(SubCommand::toString(subCommand));
}
return cmd;
}
Command::Type command;
SubCommand::Type subCommand;
int tan;
Authorization::Type authorization;
InstanceCmd::Type isInstanceCmd;
NoListenerCmd::Type isNolistenerCmd;
};
typedef QMap<QPair<QString, QString>, JsonApiCommand> CommandLookupMap;
class ApiCommandRegister {
public:
static const CommandLookupMap& getCommandLookup() {
static const CommandLookupMap commandLookup {
{ {"adjustment", ""}, { Command::Adjustment, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"authorize", "adminRequired"}, { Command::Authorize, SubCommand::AdminRequired, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "answerRequest"}, { Command::Authorize, SubCommand::AnswerRequest, Authorization::Admin, InstanceCmd::No, NoListenerCmd::No} },
{ {"authorize", "createToken"}, { Command::Authorize, SubCommand::CreateToken, Authorization::Admin, InstanceCmd::No, NoListenerCmd::No} },
{ {"authorize", "deleteToken"}, { Command::Authorize, SubCommand::DeleteToken, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "getPendingTokenRequests"}, { Command::Authorize, SubCommand::GetPendingTokenRequests, Authorization::Admin, InstanceCmd::No, NoListenerCmd::No} },
{ {"authorize", "getTokenList"}, { Command::Authorize, SubCommand::GetTokenList, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "login"}, { Command::Authorize, SubCommand::Login, Authorization::No, InstanceCmd::No, NoListenerCmd::No} },
{ {"authorize", "logout"}, { Command::Authorize, SubCommand::Logout, Authorization::No, InstanceCmd::No, NoListenerCmd::No} },
{ {"authorize", "newPassword"}, { Command::Authorize, SubCommand::NewPassword, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "newPasswordRequired"}, { Command::Authorize, SubCommand::NewPasswordRequired, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "renameToken"}, { Command::Authorize, SubCommand::RenameToken, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "requestToken"}, { Command::Authorize, SubCommand::RequestToken, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"authorize", "tokenRequired"}, { Command::Authorize, SubCommand::TokenRequired, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"clear", ""}, { Command::Clear, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"clearall", ""}, { Command::ClearAll, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"color", ""}, { Command::Color, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"componentstate", ""}, { Command::ComponentState, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"config", "getconfig"}, { Command::Config, SubCommand::GetConfig, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"config", "getschema"}, { Command::Config, SubCommand::GetSchema, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"config", "reload"}, { Command::Config, SubCommand::Reload, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"config", "restoreconfig"}, { Command::Config, SubCommand::RestoreConfig, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"config", "setconfig"}, { Command::Config, SubCommand::SetConfig, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"correction", ""}, { Command::Correction, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"create-effect", ""}, { Command::CreateEffect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"delete-effect", ""}, { Command::DeleteEffect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"effect", ""}, { Command::Effect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"image", ""}, { Command::Image, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"inputsource", "discover"}, { Command::InputSource, SubCommand::Discover, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"inputsource", "getProperties"}, { Command::InputSource, SubCommand::GetProperties, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"instance", "createInstance"}, { Command::Instance, SubCommand::CreateInstance, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"instance", "deleteInstance"}, { Command::Instance, SubCommand::DeleteInstance, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"instance", "saveName"}, { Command::Instance, SubCommand::SaveName, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"instance", "startInstance"}, { Command::Instance, SubCommand::StartInstance, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"instance", "stopInstance"}, { Command::Instance, SubCommand::StopInstance, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"instance", "switchTo"}, { Command::Instance, SubCommand::SwitchTo, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"ledcolors", "imagestream-start"}, { Command::LedColors, SubCommand::ImageStreamStart, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"ledcolors", "imagestream-stop"}, { Command::LedColors, SubCommand::ImageStreamStop, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"ledcolors", "ledstream-start"}, { Command::LedColors, SubCommand::LedStreamStart, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"ledcolors", "ledstream-stop"}, { Command::LedColors, SubCommand::LedStreamStop, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"leddevice", "addAuthorization"}, { Command::LedDevice, SubCommand::AddAuthorization, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"leddevice", "discover"}, { Command::LedDevice, SubCommand::Discover, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"leddevice", "getProperties"}, { Command::LedDevice, SubCommand::GetProperties, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"leddevice", "identify"}, { Command::LedDevice, SubCommand::Identify, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"logging", "start"}, { Command::Logging, SubCommand::Start, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"logging", "stop"}, { Command::Logging, SubCommand::Stop, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"processing", ""}, { Command::Processing, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"serverinfo", ""}, { Command::ServerInfo, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"serverinfo", "getInfo"}, { Command::ServerInfo, SubCommand::GetInfo, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"serverinfo", "subscribe"}, { Command::ServerInfo, SubCommand::Subscribe, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::No} },
{ {"serverinfo", "unsubscribe"}, { Command::ServerInfo, SubCommand::Unsubscribe, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::No} },
{ {"serverinfo", "getSubscriptions"}, { Command::ServerInfo, SubCommand::GetSubscriptions, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::No} },
{ {"serverinfo", "getSubscriptionCommands"}, { Command::ServerInfo, SubCommand::GetSubscriptionCommands, Authorization::No, InstanceCmd::No, NoListenerCmd::No} },
{ {"service", "discover"}, { Command::Service, SubCommand::Discover, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"sourceselect", ""}, { Command::SourceSelect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} },
{ {"sysinfo", ""}, { Command::SysInfo, SubCommand::Empty, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"system", "restart"}, { Command::System, SubCommand::Restart, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"system", "resume"}, { Command::System, SubCommand::Resume, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"system", "suspend"}, { Command::System, SubCommand::Suspend, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"system", "toggleSuspend"}, { Command::System, SubCommand::ToggleSuspend, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"system", "idle"}, { Command::System, SubCommand::Idle, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"system", "toggleIdle"}, { Command::System, SubCommand::ToggleIdle, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} },
{ {"temperature", ""}, { Command::Temperature, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"transform", ""}, { Command::Transform, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} },
{ {"videomode", ""}, { Command::VideoMode, SubCommand::Empty, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }
};
return commandLookup;
}
static JsonApiCommand getCommandInfo(const QString& command, const QString& subCommand) {
return getCommandLookup().value({command, subCommand});
}
};
#endif // JSONAPICOMMAND_H

View File

@ -0,0 +1,135 @@
#ifndef JSONAPISUBSCRIPTION_H
#define JSONAPISUBSCRIPTION_H
#include <HyperionConfig.h> // Required to determine the cmake options
#include <QMap>
#include <QString>
class Subscription {
public:
enum Type {
Unknown,
AdjustmentUpdate,
ComponentsUpdate,
#if defined(ENABLE_EFFECTENGINE)
EffectsUpdate,
#endif
EventUpdate,
ImageToLedMappingUpdate,
ImageUpdate,
InstanceUpdate,
LedColorsUpdate,
LedsUpdate,
LogMsgUpdate,
PrioritiesUpdate,
SettingsUpdate,
TokenUpdate,
VideomodeUpdate
};
static QString toString(Type type) {
switch (type) {
case AdjustmentUpdate: return "adjustment-update";
case ComponentsUpdate: return "components-update";
#if defined(ENABLE_EFFECTENGINE)
case EffectsUpdate: return "effects-update";
#endif
case EventUpdate: return "event-update";
case ImageToLedMappingUpdate: return "imageToLedMapping-update";
case ImageUpdate: return "ledcolors-imagestream-update";
case InstanceUpdate: return "instance-update";
case LedColorsUpdate: return "ledcolors-ledstream-update";
case LedsUpdate: return "leds-update";
case LogMsgUpdate: return "logmsg-update";
case PrioritiesUpdate: return "priorities-update";
case SettingsUpdate: return "settings-update";
case TokenUpdate: return "token-update";
case VideomodeUpdate: return "videomode-update";
default: return "unknown";
}
}
static bool isInstanceSpecific(Type type) {
switch (type) {
case AdjustmentUpdate:
case ComponentsUpdate:
#if defined(ENABLE_EFFECTENGINE)
case EffectsUpdate:
#endif
case ImageToLedMappingUpdate:
case ImageUpdate:
case LedColorsUpdate:
case LedsUpdate:
case PrioritiesUpdate:
case SettingsUpdate:
return true;
case EventUpdate:
case InstanceUpdate:
case LogMsgUpdate:
case TokenUpdate:
case VideomodeUpdate:
default:
return false;
}
}
};
class JsonApiSubscription {
public:
JsonApiSubscription()
: cmd(Subscription::Unknown),
isAll(false)
{}
JsonApiSubscription(Subscription::Type cmd, bool isAll)
: cmd(cmd),
isAll(isAll)
{}
Subscription::Type getSubscription() const { return cmd; }
bool isPartOfAll() const { return isAll; }
QString toString() const {
return Subscription::toString(cmd);
}
Subscription::Type cmd;
bool isAll;
};
typedef QMap<QString, JsonApiSubscription> SubscriptionLookupMap;
class ApiSubscriptionRegister {
public:
static const SubscriptionLookupMap& getSubscriptionLookup() {
static const SubscriptionLookupMap subscriptionLookup {
{ {"adjustment-update"}, { Subscription::AdjustmentUpdate, true} },
{ {"components-update"}, { Subscription::ComponentsUpdate, true} },
#if defined(ENABLE_EFFECTENGINE)
{ {"effects-update"}, { Subscription::EffectsUpdate, true} },
#endif
{ {"event-update"}, { Subscription::EventUpdate, true} },
{ {"imageToLedMapping-update"}, { Subscription::ImageToLedMappingUpdate, true} },
{ {"ledcolors-imagestream-update"}, { Subscription::ImageUpdate, false} },
{ {"ledcolors-ledstream-update"}, { Subscription::LedColorsUpdate, false} },
{ {"instance-update"}, { Subscription::InstanceUpdate, true} },
{ {"leds-update"}, { Subscription::LedsUpdate, true} },
{ {"logmsg-update"}, { Subscription::LogMsgUpdate, false} },
{ {"priorities-update"}, { Subscription::PrioritiesUpdate, true} },
{ {"settings-update"}, { Subscription::SettingsUpdate, true} },
{ {"token-update"}, { Subscription::TokenUpdate, true} },
{ {"videomode-update"}, { Subscription::VideomodeUpdate, true} }
};
return subscriptionLookup;
}
static JsonApiSubscription getSubscriptionInfo(const QString& subscription) {
return getSubscriptionLookup().value({subscription});
}
};
#endif // JSONAPISUBSCRIPTION_H

View File

@ -1,51 +1,82 @@
#pragma once
#include "api/JsonApiSubscription.h"
#include <api/API.h>
#include <events/EventEnum.h>
// qt incl
#include <QObject>
#include <QJsonObject>
#include <QSet>
// components def
#include <utils/Components.h>
// videModes
#include <utils/VideoMode.h>
// settings
#include <utils/settings.h>
// AuthManager
#include <hyperion/AuthManager.h>
#include <hyperion/PriorityMuxer.h>
class Hyperion;
class ComponentRegister;
class PriorityMuxer;
class JsonCB : public QObject
class JsonCallbacks : public QObject
{
Q_OBJECT
public:
JsonCB(QObject* parent);
JsonCallbacks(Logger* log, const QString& peerAddress, QObject* parent);
///
/// @brief Subscribe to future data updates given by cmd
/// @param cmd The cmd which will be subscribed for
/// @param unsubscribe Revert subscription
/// @param cmd The cmd which will be subscribed for
/// @return True on success, false if not found
///
bool subscribeFor(const QString& cmd, bool unsubscribe = false);
bool subscribe(const QString& cmd);
///
/// @brief Subscribe to future data updates given by subscription list
/// @param type Array of subscriptionsm
///
QStringList subscribe(const QJsonArray& subscriptions);
///
/// @brief Subscribe to future data updates given by cmd
/// @param cmd The cmd which will be subscribed to
/// @return True on success, false if not found
///
bool subscribe(Subscription::Type subscription);
///
/// @brief Unsubscribe to future data updates given by cmd
/// @param cmd The cmd which will be unsubscribed
/// @return True on success, false if not found
///
bool unsubscribe(const QString& cmd);
///
/// @brief Unsubscribe to future data updates given by subscription list
/// @param type Array of subscriptions
///
QStringList unsubscribe(const QJsonArray& subscriptions);
///
/// @brief Unsubscribe to future data updates given by cmd
/// @param cmd The cmd which will be subscribed to
/// @return True on success, false if not found
///
bool unsubscribe(Subscription::Type cmd);
///
/// @brief Get all possible commands to subscribe for
/// @param fullList Return all possible commands or those not triggered by API requests (subscriptions="ALL")
/// @return The list of commands
///
QStringList getCommands() { return _availableCommands; };
QStringList getCommands(bool fullList = true) const;
///
/// @brief Get all subscribed commands
/// @return The list of commands
///
QStringList getSubscribedCommands() { return _subscribedCommands; };
QStringList getSubscribedCommands() const;
///
/// @brief Reset subscriptions, disconnect all signals
@ -124,18 +155,49 @@ private slots:
///
void handleTokenChange(const QVector<AuthManager::AuthDefinition> &def);
///
/// @brief Is called whenever the current Hyperion instance pushes new led raw values (if enabled)
/// @param ledColors The current led colors
///
void handleLedColorUpdate(const std::vector<ColorRgb> &ledColors);
///
/// @brief Is called whenever the current Hyperion instance pushes new image update (if enabled)
/// @param image The current image
///
void handleImageUpdate(const Image<ColorRgb> &image);
///
/// @brief Process and push new log messages from logger (if enabled)
///
void handleLogMessageUpdate(const Logger::T_LOG_MESSAGE &);
///
/// @brief Is called whenever an event is triggert
/// @param image The current event
///
void handleEventUpdate(const Event &event);
private:
/// pointer of Hyperion instance
/// construct callback msg
void doCallback(Subscription::Type cmd, const QVariant& data);
Logger *_log;
Hyperion* _hyperion;
/// The peer address of the client
QString _peerAddress;
/// pointer of comp register
ComponentRegister* _componentRegister;
/// priority muxer instance
PriorityMuxer* _prioMuxer;
/// contains all available commands
QStringList _availableCommands;
/// contains active subscriptions
QStringList _subscribedCommands;
/// construct callback msg
void doCallback(const QString& cmd, const QVariant& data);
QSet<Subscription::Type> _subscribedCommands;
/// flag to determine state of log streaming
bool _islogMsgStreamingActive;
};

43
include/api/JsonInfo.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef JSONINFO_H
#define JSONINFO_H
#include <utils/Logger.h>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
#include <QJsonObject>
#include <QJsonArray>
class JsonInfo
{
public:
static QJsonArray getAdjustmentInfo(const Hyperion* hyperion, Logger* log);
static QJsonArray getPrioritiestInfo(const Hyperion* hyperion);
static QJsonArray getPrioritiestInfo(int currentPriority, const PriorityMuxer::InputsMap& activeInputs);
static QJsonArray getEffects(const Hyperion* hyperion);
static QJsonArray getAvailableScreenGrabbers();
static QJsonArray getAvailableVideoGrabbers();
static QJsonArray getAvailableAudioGrabbers();
static QJsonObject getGrabbers(const Hyperion* hyperion);
static QJsonObject getAvailableLedDevices();
static QJsonObject getCecInfo();
static QJsonArray getServices();
static QJsonArray getComponents(const Hyperion* hyperion);
static QJsonArray getInstanceInfo();
static QJsonArray getActiveEffects(const Hyperion* hyperion);
static QJsonArray getActiveColors(const Hyperion* hyperion);
static QJsonArray getTransformationInfo(const Hyperion* hyperion);
static QJsonObject getSystemInfo(const Hyperion* hyperion);
QJsonObject discoverSources (const QString& sourceType, const QJsonObject& params);
private:
template<typename GrabberType>
void discoverGrabber(QJsonArray& inputs, const QJsonObject& params) const;
QJsonArray discoverScreenInputs(const QJsonObject& params) const;
QJsonArray discoverVideoInputs(const QJsonObject& params) const;
QJsonArray discoverAudioInputs(const QJsonObject& params) const;
};
#endif // JSONINFO_H

View File

@ -2,6 +2,9 @@
#include <QString>
#include <QByteArray>
#include <QJsonObject>
#include <utils/Components.h>
struct ImageCmdData
{

View File

@ -8,6 +8,11 @@
#include <QDateTime>
#include <QUuid>
namespace hyperion {
const char DEFAULT_USER[] = "Hyperion";
const char DEFAULT_PASSWORD[] = "hyperion";
}
///
/// @brief Authentication table interface
///
@ -149,10 +154,10 @@ public:
inline bool resetHyperionUser()
{
QVariantMap map;
map["password"] = calcPasswordHashOfUser("Hyperion", "hyperion");
map["password"] = calcPasswordHashOfUser(hyperion::DEFAULT_USER, hyperion::DEFAULT_PASSWORD);
VectorPair cond;
cond.append(CPair("user", "Hyperion"));
cond.append(CPair("user", hyperion::DEFAULT_USER));
return updateRecord(cond, map);
}

View File

@ -13,7 +13,8 @@ enum class Event
ResumeIdle,
ToggleIdle,
Reload,
Restart
Restart,
Quit
};
inline const char* eventToString(Event event)
@ -24,6 +25,7 @@ inline const char* eventToString(Event event)
case Event::Resume: return "Resume";
case Event::ToggleSuspend: return "ToggleSuspend";
case Event::Idle: return "Idle";
case Event::Quit: return "Quit";
case Event::ResumeIdle: return "ResumeIdle";
case Event::ToggleIdle: return "ToggleIdle";
case Event::Reload: return "Reload";
@ -39,6 +41,7 @@ inline Event stringToEvent(const QString& event)
if (event.compare("Resume")==0) return Event::Resume;
if (event.compare("ToggleSuspend")==0) return Event::ToggleSuspend;
if (event.compare("Idle")==0) return Event::Idle;
if (event.compare("Quit")==0) return Event::Quit;
if (event.compare("ResumeIdle")==0) return Event::ResumeIdle;
if (event.compare("ToggleIdle")==0) return Event::ToggleIdle;
if (event.compare("Reload")==0) return Event::Reload;

View File

@ -29,6 +29,7 @@ public:
public slots:
void suspend(bool sleep);
void lock(bool isLocked);
void quit();
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
@ -101,6 +102,7 @@ public:
void handleSignal(int signum);
private:
static OsEventHandlerLinux* getInstance();

View File

@ -0,0 +1,58 @@
#ifndef GRABBERCONFIG_H
#define GRABBERCONFIG_H
#if defined(ENABLE_MF)
#include <grabber/video/mediafoundation/MFGrabber.h>
#elif defined(ENABLE_V4L2)
#include <grabber/video/v4l2/V4L2Grabber.h>
#endif
#if defined(ENABLE_AUDIO)
#include <grabber/audio/AudioGrabber.h>
#ifdef WIN32
#include <grabber/audio/AudioGrabberWindows.h>
#endif
#ifdef __linux__
#include <grabber/audio/AudioGrabberLinux.h>
#endif
#endif
#ifdef ENABLE_QT
#include <grabber/qt/QtGrabber.h>
#endif
#ifdef ENABLE_DX
#include <grabber/directx/directXGrabber.h>
#endif
#if defined(ENABLE_X11)
#include <grabber/x11/X11Grabber.h>
#endif
#if defined(ENABLE_XCB)
#include <grabber/xcb/XcbGrabber.h>
#endif
#if defined(ENABLE_DX)
#include <grabber/directx/DirectXGrabber.h>
#endif
#if defined(ENABLE_FB)
#include <grabber/framebuffer/FramebufferFrameGrabber.h>
#endif
#if defined(ENABLE_DISPMANX)
#include <grabber/dispmanx/DispmanxFrameGrabber.h>
#endif
#if defined(ENABLE_AMLOGIC)
#include <grabber/amlogic/AmlogicGrabber.h>
#endif
#if defined(ENABLE_OSX)
#include <grabber/osx/OsxFrameGrabber.h>
#endif
#endif // GRABBERCONFIG_H

View File

@ -3,6 +3,8 @@
#include <utils/Logger.h>
#include <utils/settings.h>
#include <db/AuthTable.h>
//qt
#include <QMap>
#include <QVector>
@ -41,24 +43,12 @@ public:
///
QString getID() const { return _uuid; }
///
/// @brief Check authorization is required according to the user setting
/// @return True if authorization required else false
///
bool isAuthRequired() const { return _authRequired; }
///
/// @brief Check if authorization is required for local network connections
/// @return True if authorization required else false
///
bool isLocalAuthRequired() const { return _localAuthRequired; }
///
/// @brief Check if authorization is required for local network connections for admin access
/// @return True if authorization required else false
///
bool isLocalAdminAuthRequired() const { return _localAdminAuthRequired; }
///
/// @brief Reset Hyperion user
/// @return True on success else false
@ -172,7 +162,7 @@ public slots:
/// @param usr the defined user
/// @return The token
///
QString getUserToken(const QString &usr = "Hyperion") const;
QString getUserToken(const QString &usr = hyperion::DEFAULT_USER) const;
///
/// @brief Get all available token entries
@ -230,15 +220,9 @@ private:
/// All pending requests
QMap<QString, AuthDefinition> _pendingRequests;
/// Reflect state of global auth
bool _authRequired;
/// Reflect state of local auth
bool _localAuthRequired;
/// Reflect state of local admin auth
bool _localAdminAuthRequired;
/// Timer for counting against pendingRequest timeouts
QTimer *_timer;

View File

@ -67,6 +67,7 @@ class Hyperion : public QObject
Q_OBJECT
public:
/// Type definition of the info structure used by the priority muxer
using InputsMap = PriorityMuxer::InputsMap;
using InputInfo = PriorityMuxer::InputInfo;
///
@ -107,7 +108,7 @@ public:
///
QString getActiveDeviceType() const;
bool getReadOnlyMode() {return _readOnlyMode; }
bool getReadOnlyMode() const {return _readOnlyMode; }
public slots:
@ -235,13 +236,13 @@ public slots:
/// @param priority The priority channel of the effect
/// @param timeout The timeout of the effect (after the timout, the effect will be cleared)
int setEffect(const QString &effectName
, const QJsonObject &args
, int priority
, int timeout = PriorityMuxer::ENDLESS
, const QString &pythonScript = ""
, const QString &origin="System"
, const QString &imageData = ""
);
, const QJsonObject &args
, int priority
, int timeout = PriorityMuxer::ENDLESS
, const QString &pythonScript = ""
, const QString &origin="System"
, const QString &imageData = ""
);
/// Get the list of available effects
/// @return The list of available effects
@ -303,7 +304,14 @@ public slots:
QList<int> getActivePriorities() const;
///
/// Returns the information of a specific priorrity channel
/// Returns the information of all priority channels.
///
/// @return The information fo all priority channels
///
PriorityMuxer::InputsMap getPriorityInfo() const;
///
/// Returns the information of a specific priority channel
///
/// @param[in] priority The priority channel
///
@ -346,7 +354,7 @@ public slots:
/// @brief Get the component Register
/// return Component register pointer
///
ComponentRegister* getComponentRegister() { return _componentRegister; }
ComponentRegister* getComponentRegister() const { return _componentRegister; }
///
/// @brief Called from components to update their current state. DO NOT CALL FROM USERS

View File

@ -55,10 +55,16 @@ public slots:
Hyperion* getHyperionInstance(quint8 instance = 0);
///
/// @brief Get instance data of all instaces in db + running state
/// @brief Get instance data of all instances in db + running state
///
QVector<QVariantMap> getInstanceData() const;
///
/// @brief Get all instance indicies of running instances
///
QList<quint8> getRunningInstanceIdx() const;
///
/// @brief Start a Hyperion instance
/// @param instance Instance index

View File

@ -141,6 +141,13 @@ public:
///
QList<int> getPriorities() const;
///
/// Returns the information of all priority channels.
///
/// @return The information fo all priority channels
///
InputsMap getInputInfo() const;
///
/// Returns the information of a specified priority channel.
/// If a priority is no longer available the _lowestPriorityInfo (255) is returned

View File

@ -3,6 +3,8 @@
#include <utils/FileUtils.h>
#include <QJsonObject>
#include <QPair>
#include <QStringList>
#include <utils/Logger.h>
namespace JsonUtils {
@ -14,7 +16,7 @@ namespace JsonUtils {
/// @param[in] ignError Ignore errors during file read (no log output)
/// @return true on success else false
///
bool readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError=false);
QPair<bool, QStringList> readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError=false);
///
/// @brief read a schema file and resolve $refs
@ -33,7 +35,7 @@ namespace JsonUtils {
/// @param[in] log The logger of the caller to print errors
/// @return true on success else false
///
bool parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log);
QPair<bool, QStringList> parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log);
///
/// @brief parse a json QString and get a QJsonArray. Overloaded function
@ -42,8 +44,8 @@ namespace JsonUtils {
/// @param[out] arr Retuns the parsed QJsonArray
/// @param[in] log The logger of the caller to print errors
/// @return true on success else false
///
bool parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log);
//
QPair<bool, QStringList> parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log);
///
/// @brief parse a json QString and get a QJsonDocument
@ -53,7 +55,7 @@ namespace JsonUtils {
/// @param[in] log The logger of the caller to print errors
/// @return true on success else false
///
bool parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log);
QPair<bool, QStringList> parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log);
///
/// @brief Validate json data against a schema
@ -63,7 +65,7 @@ namespace JsonUtils {
/// @param[in] log The logger of the caller to print errors
/// @return true on success else false
///
bool validate(const QString& file, const QJsonObject& json, const QString& schemaPath, Logger* log);
QPair<bool, QStringList> validate(const QString& file, const QJsonObject& json, const QString& schemaPath, Logger* log);
///
/// @brief Validate json data against a schema
@ -73,7 +75,7 @@ namespace JsonUtils {
/// @param[in] log The logger of the caller to print errors
/// @return true on success else false
///
bool validate(const QString& file, const QJsonObject& json, const QJsonObject& schema, Logger* log);
QPair<bool, QStringList> validate(const QString& file, const QJsonObject& json, const QJsonObject& schema, Logger* log);
///
/// @brief Write json data to file

View File

@ -47,7 +47,9 @@ private slots:
private:
Logger* _log;
/// True when internet access is allowed
bool _internetAccessAllowed;
bool _isInternetAccessAllowed;
/// True when internet access is restricted by a white list
bool _isInternetAccessRestricted;
/// Whitelisted ip addresses
QList<QHostAddress> _ipWhitelist;

View File

@ -31,7 +31,9 @@ public:
if (!schemaChecker.validate(configTree).first)
{
for (int i = 0; i < messages.size(); ++i)
{
std::cout << messages[i].toStdString() << std::endl;
}
std::cerr << "Validation failed for configuration file: " << config.toStdString() << std::endl;
return -3;
@ -61,9 +63,10 @@ public:
if (error.error != QJsonParseError::NoError)
{
// report to the user the failure and their locations in the document.
int errorLine(0), errorColumn(0);
int errorLine(0);
int errorColumn(0);
for( int i=0, count=qMin( error.offset,config.size()); i<count; ++i )
for(long i=0, count=qMin( error.offset,config.size()); i<count; ++i )
{
++errorColumn;
if(config.at(i) == '\n' )

View File

@ -2,8 +2,6 @@
#include <api/API.h>
// stl includes
#include <iostream>
#include <iterator>
// Qt includes
#include <QResource>
@ -27,90 +25,91 @@
// ledmapping int <> string transform methods
#include <hyperion/ImageProcessor.h>
// api includes
#include <api/JsonCB.h>
using namespace hyperion;
// Constants
namespace {
const int IMAGE_HEIGHT_MAX = 2000;
const int IMAGE_WIDTH_MAX = 2000;
const int IMAGE_SCALE = 2000;
}
API::API(Logger *log, bool localConnection, QObject *parent)
: QObject(parent)
: QObject(parent)
{
qRegisterMetaType<int64_t>("int64_t");
qRegisterMetaType<VideoMode>("VideoMode");
qRegisterMetaType<std::map<int, registerData>>("std::map<int,registerData>");
// Init
_log = log;
_authManager = AuthManager::getInstance();
// Init
_log = log;
_authManager = AuthManager::getInstance();
_instanceManager = HyperionIManager::getInstance();
_localConnection = localConnection;
_localConnection = localConnection;
_authorized = false;
_adminAuthorized = false;
_authorized = false;
_adminAuthorized = false;
_currInstanceIndex = 0;
_currInstanceIndex = 0;
// connect to possible token responses that has been requested
connect(_authManager, &AuthManager::tokenResponse, [=] (bool success, QObject *caller, const QString &token, const QString &comment, const QString &id, const int &tan)
{
if (this == caller)
emit onTokenResponse(success, token, comment, id, tan);
});
// connect to possible token responses that has been requested
connect(_authManager, &AuthManager::tokenResponse, this, [=] (bool success, const QObject *caller, const QString &token, const QString &comment, const QString &tokenId, const int &tan)
{
if (this == caller)
{
emit onTokenResponse(success, token, comment, tokenId, tan);
}
});
// connect to possible startInstance responses that has been requested
connect(_instanceManager, &HyperionIManager::startInstanceResponse, [=] (QObject *caller, const int &tan)
{
if (this == caller)
emit onStartInstanceResponse(tan);
});
connect(_instanceManager, &HyperionIManager::startInstanceResponse, this, [=] (const QObject *caller, const int &tan)
{
if (this == caller)
{
emit onStartInstanceResponse(tan);
}
});
}
void API::init()
{
_hyperion = _instanceManager->getHyperionInstance(0);
_authorized = false;
bool apiAuthRequired = _authManager->isAuthRequired();
// For security we block external connections if default PW is set
if (!_localConnection && API::hasHyperionDefaultPw())
{
emit forceClose();
}
// if this is localConnection and network allows unauth locals, set authorized flag
if (apiAuthRequired && _localConnection)
// For security we block external connections, if default PW is set
if (!_localConnection && API::hasHyperionDefaultPw())
{
_authorized = !_authManager->isLocalAuthRequired();
Warning(_log, "Non local network connect attempt identified, but default Hyperion passwort set! - Reject connection.");
emit forceClose();
}
// admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access
if (_localConnection)
{
_adminAuthorized = !_authManager->isLocalAdminAuthRequired();
// just in positive direction
if (_adminAuthorized)
// if this is localConnection and network allows unauth locals
if ( _localConnection && !_authManager->isLocalAuthRequired())
{
_authorized = true;
}
// // admin access is only allowed after login via user & password or via authorization via token.
_adminAuthorized = false;
}
void API::setColor(int priority, const std::vector<uint8_t> &ledColors, int timeout_ms, const QString &origin, hyperion::Components /*callerComp*/)
{
if (ledColors.size() % 3 == 0)
{
std::vector<ColorRgb> fledColors;
for (unsigned i = 0; i < ledColors.size(); i += 3)
{
_authorized = true;
fledColors.emplace_back(ColorRgb{ledColors[i], ledColors[i + 1], ledColors[i + 2]});
}
}
QMetaObject::invokeMethod(_hyperion, "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector<ColorRgb>, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin));
}
}
void API::setColor(int priority, const std::vector<uint8_t> &ledColors, int timeout_ms, const QString &origin, hyperion::Components callerComp)
bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &replyMsg, hyperion::Components /*callerComp*/)
{
std::vector<ColorRgb> fledColors;
if (ledColors.size() % 3 == 0)
{
for (unsigned i = 0; i < ledColors.size(); i += 3)
{
fledColors.emplace_back(ColorRgb{ledColors[i], ledColors[i + 1], ledColors[i + 2]});
}
QMetaObject::invokeMethod(_hyperion, "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector<ColorRgb>, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin));
}
}
bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &replyMsg, hyperion::Components callerComp)
{
// truncate name length
data.imgName.truncate(16);
// truncate name length
data.imgName.truncate(16);
if (!data.format.isEmpty())
{
@ -128,424 +127,475 @@ bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &reply
}
QImage img = QImage::fromData(data.data, QSTRING_CSTR(data.format));
if (img.isNull())
{
if (img.isNull())
{
replyMsg = "Failed to parse picture, the file might be corrupted or content does not match the given format [" + data.format + "]";
return false;
}
return false;
}
// check for requested scale
if (data.scale > 24)
{
if (img.height() > data.scale)
{
img = img.scaledToHeight(data.scale);
}
if (img.width() > data.scale)
{
img = img.scaledToWidth(data.scale);
}
}
// check for requested scale
if (data.scale > 24)
{
if (img.height() > data.scale)
{
img = img.scaledToHeight(data.scale);
}
if (img.width() > data.scale)
{
img = img.scaledToWidth(data.scale);
}
}
// check if we need to force a scale
if (img.width() > 2000 || img.height() > 2000)
{
data.scale = 2000;
if (img.height() > data.scale)
{
img = img.scaledToHeight(data.scale);
}
if (img.width() > data.scale)
{
img = img.scaledToWidth(data.scale);
}
}
// check if we need to force a scale
if (img.width() > IMAGE_WIDTH_MAX || img.height() > IMAGE_HEIGHT_MAX)
{
data.scale = IMAGE_SCALE;
if (img.height() > data.scale)
{
img = img.scaledToHeight(data.scale);
}
if (img.width() > data.scale)
{
img = img.scaledToWidth(data.scale);
}
}
data.width = img.width();
data.height = img.height();
data.width = img.width();
data.height = img.height();
// extract image
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
data.data.clear();
data.data.reserve(img.width() * img.height() * 3);
for (int i = 0; i < img.height(); ++i)
{
const QRgb *scanline = reinterpret_cast<const QRgb *>(img.scanLine(i));
for (int j = 0; j < img.width(); ++j)
{
data.data.append((char)qRed(scanline[j]));
data.data.append((char)qGreen(scanline[j]));
data.data.append((char)qBlue(scanline[j]));
}
}
}
else
{
// check consistency of the size of the received data
if (data.data.size() != data.width * data.height * 3)
{
replyMsg = "Size of image data does not match with the width and height";
return false;
}
}
// extract image
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
data.data.clear();
data.data.reserve(static_cast<int>(img.width() * img.height() * 3));
for (int i = 0; i < img.height(); ++i)
{
const QRgb *scanline = reinterpret_cast<const QRgb *>(img.scanLine(i));
for (int j = 0; j < img.width(); ++j)
{
data.data.append(static_cast<char>(qRed(scanline[j])));
data.data.append(static_cast<char>(qGreen(scanline[j])));
data.data.append(static_cast<char>(qBlue(scanline[j])));
}
}
}
else
{
// check consistency of the size of the received data
if (static_cast<size_t>(data.data.size()) != static_cast<size_t>(data.width) * static_cast<size_t>(data.height) * 3)
{
replyMsg = "Size of image data does not match with the width and height";
return false;
}
}
// copy image
Image<ColorRgb> image(data.width, data.height);
memcpy(image.memptr(), data.data.data(), data.data.size());
// copy image
Image<ColorRgb> image(data.width, data.height);
memcpy(image.memptr(), data.data.data(), static_cast<size_t>(data.data.size()));
QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName));
QMetaObject::invokeMethod(_hyperion, "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image<ColorRgb>, image), Q_ARG(int64_t, data.duration));
QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName));
QMetaObject::invokeMethod(_hyperion, "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image<ColorRgb>, image), Q_ARG(int64_t, data.duration));
return true;
return true;
}
bool API::clearPriority(int priority, QString &replyMsg, hyperion::Components callerComp)
bool API::clearPriority(int priority, QString &replyMsg, hyperion::Components /*callerComp*/)
{
if (priority < 0 || (priority > 0 && priority < 254))
{
QMetaObject::invokeMethod(_hyperion, "clear", Qt::QueuedConnection, Q_ARG(int, priority));
}
else
{
replyMsg = QString("Priority %1 is not allowed to be cleared").arg(priority);
return false;
}
return true;
if (priority < 0 || (priority > 0 && priority < PriorityMuxer::BG_PRIORITY))
{
QMetaObject::invokeMethod(_hyperion, "clear", Qt::QueuedConnection, Q_ARG(int, priority));
}
else
{
replyMsg = QString("Priority %1 is not allowed to be cleared").arg(priority);
return false;
}
return true;
}
bool API::setComponentState(const QString &comp, bool &compState, QString &replyMsg, hyperion::Components callerComp)
bool API::setComponentState(const QString &comp, bool &compState, QString &replyMsg, hyperion::Components /*callerComp*/)
{
Components component = stringToComponent(comp);
Components component = stringToComponent(comp);
if (component != COMP_INVALID)
{
QMetaObject::invokeMethod(_hyperion, "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState));
return true;
}
replyMsg = QString("Unknown component name: %1").arg(comp);
return false;
if (component != COMP_INVALID)
{
QMetaObject::invokeMethod(_hyperion, "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState));
return true;
}
replyMsg = QString("Unknown component name: %1").arg(comp);
return false;
}
void API::setLedMappingType(int type, hyperion::Components callerComp)
void API::setLedMappingType(int type, hyperion::Components /*callerComp*/)
{
QMetaObject::invokeMethod(_hyperion, "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type));
QMetaObject::invokeMethod(_hyperion, "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type));
}
void API::setVideoMode(VideoMode mode, hyperion::Components callerComp)
void API::setVideoMode(VideoMode mode, hyperion::Components /*callerComp*/)
{
QMetaObject::invokeMethod(_hyperion, "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode));
QMetaObject::invokeMethod(_hyperion, "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode));
}
#if defined(ENABLE_EFFECTENGINE)
bool API::setEffect(const EffectCmdData &dat, hyperion::Components callerComp)
bool API::setEffect(const EffectCmdData &dat, hyperion::Components /*callerComp*/)
{
int res;
if (!dat.args.isEmpty())
{
QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, res), Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data));
}
else
{
QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, res), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin));
}
int isStarted;
if (!dat.args.isEmpty())
{
QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data));
}
else
{
QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin));
}
return res >= 0;
return isStarted >= 0;
}
#endif
void API::setSourceAutoSelect(bool state, hyperion::Components callerComp)
void API::setSourceAutoSelect(bool state, hyperion::Components /*callerComp*/)
{
QMetaObject::invokeMethod(_hyperion, "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state));
QMetaObject::invokeMethod(_hyperion, "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state));
}
void API::setVisiblePriority(int priority, hyperion::Components callerComp)
void API::setVisiblePriority(int priority, hyperion::Components /*callerComp*/)
{
QMetaObject::invokeMethod(_hyperion, "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority));
QMetaObject::invokeMethod(_hyperion, "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority));
}
void API::registerInput(int priority, hyperion::Components component, const QString &origin, const QString &owner, hyperion::Components callerComp)
{
if (_activeRegisters.count(priority))
_activeRegisters.erase(priority);
if (_activeRegisters.count(priority) != 0)
{
_activeRegisters.erase(priority);
}
_activeRegisters.insert({priority, registerData{component, origin, owner, callerComp}});
_activeRegisters.insert({priority, registerData{component, origin, owner, callerComp}});
QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner));
QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner));
}
void API::unregisterInput(int priority)
{
if (_activeRegisters.count(priority))
_activeRegisters.erase(priority);
if (_activeRegisters.count(priority) != 0)
{
_activeRegisters.erase(priority);
}
}
bool API::setHyperionInstance(quint8 inst)
{
if (_currInstanceIndex == inst)
return true;
bool isRunning;
QMetaObject::invokeMethod(_instanceManager, "IsInstanceRunning", Qt::DirectConnection, Q_RETURN_ARG(bool, isRunning), Q_ARG(quint8, inst));
if (!isRunning)
return false;
if (_currInstanceIndex == inst)
{
return true;
}
disconnect(_hyperion, 0, this, 0);
QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::DirectConnection, Q_RETURN_ARG(Hyperion *, _hyperion), Q_ARG(quint8, inst));
_currInstanceIndex = inst;
return true;
bool isRunning;
QMetaObject::invokeMethod(_instanceManager, "IsInstanceRunning", Qt::DirectConnection, Q_RETURN_ARG(bool, isRunning), Q_ARG(quint8, inst));
if (!isRunning)
{
return false;
}
disconnect(_hyperion, nullptr, this, nullptr);
QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::DirectConnection, Q_RETURN_ARG(Hyperion *, _hyperion), Q_ARG(quint8, inst));
_currInstanceIndex = inst;
return true;
}
bool API::isHyperionEnabled()
{
int res;
QMetaObject::invokeMethod(_hyperion, "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, res), Q_ARG(hyperion::Components, hyperion::COMP_ALL));
return res > 0;
int isEnabled;
QMetaObject::invokeMethod(_hyperion, "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isEnabled), Q_ARG(hyperion::Components, hyperion::COMP_ALL));
return isEnabled > 0;
}
QVector<QVariantMap> API::getAllInstanceData()
QVector<QVariantMap> API::getAllInstanceData() const
{
QVector<QVariantMap> vec;
QMetaObject::invokeMethod(_instanceManager, "getInstanceData", Qt::DirectConnection, Q_RETURN_ARG(QVector<QVariantMap>, vec));
return vec;
QVector<QVariantMap> vec;
QMetaObject::invokeMethod(_instanceManager, "getInstanceData", Qt::DirectConnection, Q_RETURN_ARG(QVector<QVariantMap>, vec));
return vec;
}
bool API::startInstance(quint8 index, int tan)
{
bool res;
(_instanceManager->thread() != this->thread())
? QMetaObject::invokeMethod(_instanceManager, "startInstance", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(quint8, index), Q_ARG(bool, false), Q_ARG(QObject*, this), Q_ARG(int, tan))
: res = _instanceManager->startInstance(index, false, this, tan);
bool isStarted;
(_instanceManager->thread() != this->thread())
? QMetaObject::invokeMethod(_instanceManager, "startInstance", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isStarted), Q_ARG(quint8, index), Q_ARG(bool, false), Q_ARG(QObject*, this), Q_ARG(int, tan))
: isStarted = _instanceManager->startInstance(index, false, this, tan);
return res;
return isStarted;
}
void API::stopInstance(quint8 index)
{
QMetaObject::invokeMethod(_instanceManager, "stopInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
QMetaObject::invokeMethod(_instanceManager, "stopInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
}
bool API::deleteInstance(quint8 index, QString &replyMsg)
{
if (_adminAuthorized)
{
QMetaObject::invokeMethod(_instanceManager, "deleteInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
return true;
}
replyMsg = NO_AUTH;
return false;
if (_adminAuthorized)
{
QMetaObject::invokeMethod(_instanceManager, "deleteInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
return true;
}
replyMsg = NO_AUTHORIZATION;
return false;
}
QString API::createInstance(const QString &name)
{
if (_adminAuthorized)
{
bool success;
QMetaObject::invokeMethod(_instanceManager, "createInstance", Qt::DirectConnection, Q_RETURN_ARG(bool, success), Q_ARG(QString, name));
if (!success)
return QString("Instance name '%1' is already in use").arg(name);
return "";
}
return NO_AUTH;
if (_adminAuthorized)
{
bool success;
QMetaObject::invokeMethod(_instanceManager, "createInstance", Qt::DirectConnection, Q_RETURN_ARG(bool, success), Q_ARG(QString, name));
if (!success)
{
return QString("Instance name '%1' is already in use").arg(name);
}
return "";
}
return NO_AUTHORIZATION;
}
QString API::setInstanceName(quint8 index, const QString &name)
{
if (_adminAuthorized)
{
QMetaObject::invokeMethod(_instanceManager, "saveName", Qt::QueuedConnection, Q_ARG(quint8, index), Q_ARG(QString, name));
return "";
}
return NO_AUTH;
if (_adminAuthorized)
{
QMetaObject::invokeMethod(_instanceManager, "saveName", Qt::QueuedConnection, Q_ARG(quint8, index), Q_ARG(QString, name));
return "";
}
return NO_AUTHORIZATION;
}
#if defined(ENABLE_EFFECTENGINE)
QString API::deleteEffect(const QString &name)
{
if (_adminAuthorized)
{
QString res;
QMetaObject::invokeMethod(_hyperion, "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name));
return res;
}
return NO_AUTH;
if (_adminAuthorized)
{
QString res;
QMetaObject::invokeMethod(_hyperion, "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name));
return res;
}
return NO_AUTHORIZATION;
}
QString API::saveEffect(const QJsonObject &data)
{
if (_adminAuthorized)
{
QString res;
QMetaObject::invokeMethod(_hyperion, "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data));
return res;
}
return NO_AUTH;
if (_adminAuthorized)
{
QString res;
QMetaObject::invokeMethod(_hyperion, "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data));
return res;
}
return NO_AUTHORIZATION;
}
#endif
bool API::saveSettings(const QJsonObject &data)
{
bool rc = true;
if (!_adminAuthorized)
bool isSaved {true};
if (!_adminAuthorized)
{
rc = false;
isSaved = false;
}
else
{
QMetaObject::invokeMethod(_hyperion, "saveSettings", Qt::DirectConnection, Q_RETURN_ARG(bool, rc), Q_ARG(QJsonObject, data), Q_ARG(bool, true));
QMetaObject::invokeMethod(_hyperion, "saveSettings", Qt::DirectConnection, Q_RETURN_ARG(bool, isSaved), Q_ARG(QJsonObject, data), Q_ARG(bool, true));
}
return rc;
return isSaved;
}
bool API::restoreSettings(const QJsonObject &data)
{
bool rc = true;
bool isRestored {true};
if (!_adminAuthorized)
{
rc = false;
isRestored = false;
}
else
{
QMetaObject::invokeMethod(_hyperion, "restoreSettings", Qt::DirectConnection, Q_RETURN_ARG(bool, rc), Q_ARG(QJsonObject, data), Q_ARG(bool, true));
QMetaObject::invokeMethod(_hyperion, "restoreSettings", Qt::DirectConnection, Q_RETURN_ARG(bool, isRestored), Q_ARG(QJsonObject, data), Q_ARG(bool, true));
}
return rc;
return isRestored;
}
bool API::updateHyperionPassword(const QString &password, const QString &newPassword)
{
if (!_adminAuthorized)
return false;
bool res;
QMetaObject::invokeMethod(_authManager, "updateUserPassword", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, password), Q_ARG(QString, newPassword));
return res;
bool isPwUpdated {true};
if (!_adminAuthorized)
{
isPwUpdated = false;
}
else
{
QMetaObject::invokeMethod(_authManager, "updateUserPassword", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isPwUpdated), Q_ARG(QString, DEFAULT_USER), Q_ARG(QString, password), Q_ARG(QString, newPassword));
}
return isPwUpdated;
}
QString API::createToken(const QString &comment, AuthManager::AuthDefinition &def)
{
if (!_adminAuthorized)
return NO_AUTH;
if (comment.isEmpty())
return "comment is empty";
QMetaObject::invokeMethod(_authManager, "createToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AuthManager::AuthDefinition, def), Q_ARG(QString, comment));
return "";
if (!_adminAuthorized)
{
return NO_AUTHORIZATION;
}
if (comment.isEmpty())
{
return "Missing token comment";
}
QMetaObject::invokeMethod(_authManager, "createToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AuthManager::AuthDefinition, def), Q_ARG(QString, comment));
return "";
}
QString API::renameToken(const QString &id, const QString &comment)
QString API::renameToken(const QString &tokenId, const QString &comment)
{
if (!_adminAuthorized)
return NO_AUTH;
if (comment.isEmpty() || id.isEmpty())
return "Empty comment or id";
if (!_adminAuthorized)
{
return NO_AUTHORIZATION;
}
QMetaObject::invokeMethod(_authManager, "renameToken", Qt::QueuedConnection, Q_ARG(QString, id), Q_ARG(QString, comment));
return "";
if (comment.isEmpty())
{
return "Missing token comment";
}
if (tokenId.isEmpty()) {
return "Missing token id";
}
bool isTokenRenamed {false};
QMetaObject::invokeMethod(_authManager, "renameToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isTokenRenamed), Q_ARG(QString, tokenId), Q_ARG(QString, comment));
return (!isTokenRenamed) ? "Token does not exist" : "";
}
QString API::deleteToken(const QString &id)
QString API::deleteToken(const QString &tokenId)
{
if (!_adminAuthorized)
return NO_AUTH;
if (id.isEmpty())
return "Empty id";
if (!_adminAuthorized)
{
return NO_AUTHORIZATION;
}
QMetaObject::invokeMethod(_authManager, "deleteToken", Qt::QueuedConnection, Q_ARG(QString, id));
return "";
if (tokenId.isEmpty())
{
return "Missing token id";
}
bool isTokenDeleted {false};
QMetaObject::invokeMethod(_authManager, "deleteToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isTokenDeleted), Q_ARG(QString, tokenId));
return (!isTokenDeleted) ? "Token does not exist" : "";
}
void API::setNewTokenRequest(const QString &comment, const QString &id, const int &tan)
void API::setNewTokenRequest(const QString &comment, const QString &tokenId, const int &tan)
{
QMetaObject::invokeMethod(_authManager, "setNewTokenRequest", Qt::QueuedConnection, Q_ARG(QObject *, this), Q_ARG(QString, comment), Q_ARG(QString, id), Q_ARG(int, tan));
QMetaObject::invokeMethod(_authManager, "setNewTokenRequest", Qt::QueuedConnection, Q_ARG(QObject *, this), Q_ARG(QString, comment), Q_ARG(QString, tokenId), Q_ARG(int, tan));
}
void API::cancelNewTokenRequest(const QString &comment, const QString &id)
void API::cancelNewTokenRequest(const QString &comment, const QString &tokenId)
{
QMetaObject::invokeMethod(_authManager, "cancelNewTokenRequest", Qt::QueuedConnection, Q_ARG(QObject *, this), Q_ARG(QString, comment), Q_ARG(QString, id));
QMetaObject::invokeMethod(_authManager, "cancelNewTokenRequest", Qt::QueuedConnection, Q_ARG(QObject *, this), Q_ARG(QString, comment), Q_ARG(QString, tokenId));
}
bool API::handlePendingTokenRequest(const QString &id, bool accept)
bool API::handlePendingTokenRequest(const QString &tokenId, bool accept)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "handlePendingTokenRequest", Qt::QueuedConnection, Q_ARG(QString, id), Q_ARG(bool, accept));
return true;
if (!_adminAuthorized)
{
return false;
}
QMetaObject::invokeMethod(_authManager, "handlePendingTokenRequest", Qt::QueuedConnection, Q_ARG(QString, tokenId), Q_ARG(bool, accept));
return true;
}
bool API::getTokenList(QVector<AuthManager::AuthDefinition> &def)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "getTokenList", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<AuthManager::AuthDefinition>, def));
return true;
if (!_adminAuthorized)
{
return false;
}
QMetaObject::invokeMethod(_authManager, "getTokenList", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<AuthManager::AuthDefinition>, def));
return true;
}
bool API::getPendingTokenRequests(QVector<AuthManager::AuthDefinition> &map)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "getPendingRequests", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<AuthManager::AuthDefinition>, map));
return true;
if (!_adminAuthorized)
{
return false;
}
QMetaObject::invokeMethod(_authManager, "getPendingRequests", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector<AuthManager::AuthDefinition>, map));
return true;
}
bool API::isUserTokenAuthorized(const QString &userToken)
{
bool res;
QMetaObject::invokeMethod(_authManager, "isUserTokenAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, userToken));
if (res)
{
_authorized = true;
_adminAuthorized = true;
// Listen for ADMIN ACCESS protected signals
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest, Qt::UniqueConnection);
}
return res;
QMetaObject::invokeMethod(_authManager, "isUserTokenAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, _authorized), Q_ARG(QString, DEFAULT_USER), Q_ARG(QString, userToken));
_adminAuthorized = _authorized;
if (_authorized)
{
// Listen for ADMIN ACCESS protected signals
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
}
else
{
disconnect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
}
return _authorized;
}
bool API::getUserToken(QString &userToken)
{
if (!_adminAuthorized)
return false;
QMetaObject::invokeMethod(_authManager, "getUserToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, userToken));
return true;
if (!_adminAuthorized)
{
return false;
}
QMetaObject::invokeMethod(_authManager, "getUserToken", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, userToken));
return true;
}
bool API::isTokenAuthorized(const QString &token)
{
(_authManager->thread() != this->thread())
? QMetaObject::invokeMethod(_authManager, "isTokenAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, _authorized), Q_ARG(QString, token))
: _authorized = _authManager->isTokenAuthorized(token);
? QMetaObject::invokeMethod(_authManager, "isTokenAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, _authorized), Q_ARG(QString, token))
: _authorized = _authManager->isTokenAuthorized(token);
_adminAuthorized = _authorized;
return _authorized;
return _authorized;
}
bool API::isUserAuthorized(const QString &password)
{
bool res;
QMetaObject::invokeMethod(_authManager, "isUserAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, password));
if (res)
{
_authorized = true;
_adminAuthorized = true;
// Listen for ADMIN ACCESS protected signals
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest, Qt::UniqueConnection);
}
return res;
bool isUserAuthorized;
QMetaObject::invokeMethod(_authManager, "isUserAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isUserAuthorized), Q_ARG(QString, DEFAULT_USER), Q_ARG(QString, password));
if (isUserAuthorized)
{
_authorized = true;
_adminAuthorized = true;
// Listen for ADMIN ACCESS protected signals
connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
}
else
{
disconnect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
}
return isUserAuthorized;
}
bool API::hasHyperionDefaultPw()
{
bool res;
QMetaObject::invokeMethod(_authManager, "isUserAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, "Hyperion"), Q_ARG(QString, "hyperion"));
return res;
bool isDefaultPassort;
QMetaObject::invokeMethod(_authManager, "isUserAuthorized", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isDefaultPassort), Q_ARG(QString, DEFAULT_USER), Q_ARG(QString, DEFAULT_PASSWORD));
return isDefaultPassort;
}
void API::logout()
{
_authorized = false;
_adminAuthorized = false;
// Stop listenig for ADMIN ACCESS protected signals
disconnect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
stopDataConnectionss();
}
void API::stopDataConnectionss()
{
_authorized = false;
_adminAuthorized = false;
// Stop listenig for ADMIN ACCESS protected signals
disconnect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest);
stopDataConnections();
}

View File

@ -2,10 +2,14 @@ add_library(hyperion-api
${CMAKE_SOURCE_DIR}/include/api/apiStructs.h
${CMAKE_SOURCE_DIR}/include/api/API.h
${CMAKE_SOURCE_DIR}/include/api/JsonAPI.h
${CMAKE_SOURCE_DIR}/include/api/JsonCB.h
${CMAKE_SOURCE_DIR}/include/api/JsonCallbacks.h
${CMAKE_SOURCE_DIR}/include/api/JsonApiCommand.h
${CMAKE_SOURCE_DIR}/include/api/JsonApiSubscription.h
${CMAKE_SOURCE_DIR}/include/api/JsonInfo.h
${CMAKE_SOURCE_DIR}/libsrc/api/JsonAPI.cpp
${CMAKE_SOURCE_DIR}/libsrc/api/API.cpp
${CMAKE_SOURCE_DIR}/libsrc/api/JsonCB.cpp
${CMAKE_SOURCE_DIR}/libsrc/api/JsonCallbacks.cpp
${CMAKE_SOURCE_DIR}/libsrc/api/JsonInfo.cpp
${CMAKE_SOURCE_DIR}/libsrc/api/JSONRPC_schemas.qrc
)

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["adjustment"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["clear"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["clearall"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
}

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["color"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -9,6 +9,12 @@
"required" : true,
"enum" : ["componentstate"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -12,6 +12,11 @@
"required" : true,
"enum" : ["getconfig","getschema","setconfig","restoreconfig","reload"]
},
"instance" : {
"type" : "integer",
"minimum": 0,
"maximum": 255
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,11 @@
"required" : true,
"enum" : ["create-effect"]
},
"instance" : {
"type" : "integer",
"minimum": 0,
"maximum": 255
},
"tan" : {
"type" : "integer"
},

View File

@ -8,6 +8,11 @@
"required" : true,
"enum" : ["delete-effect"]
},
"instance" : {
"type" : "integer",
"minimum": 0,
"maximum": 255
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["effect"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["image"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -7,21 +7,18 @@
"required" : true,
"enum" : ["ledcolors"]
},
"instance" : {
"type" : "integer",
"minimum": 0,
"maximum": 255
},
"tan" : {
"type" : "integer"
},
"subcommand": {
"type" : "string",
"required" : true,
"enum" : ["ledstream-stop","ledstream-start","testled","imagestream-start","imagestream-stop"]
},
"oneshot": {
"type" : "bool"
},
"interval": {
"type" : "integer",
"required" : false,
"minimum": 50
"enum" : ["ledstream-stop","ledstream-start","imagestream-start","imagestream-stop"]
}
},

View File

@ -7,6 +7,9 @@
"required" : true,
"enum" : ["leddevice"]
},
"instance" : {
"type" : "integer"
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["processing"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

View File

@ -7,6 +7,25 @@
"required" : true,
"enum" : ["serverinfo"]
},
"subcommand": {
"type": "string",
"enum": ["getInfo", "subscribe", "unsubscribe", "getSubscriptions", "getSubscriptionCommands"]
},
"instance" : {
"type" : "integer",
"minimum": 0,
"maximum": 255
},
"data": {
"type": ["null", "array"],
"properties": {
"subscriptions": {
"type": "array",
"items": {}
}
},
"additionalProperties": false
},
"subscribe" : {
"type" : "array"
},

View File

@ -7,6 +7,12 @@
"required" : true,
"enum" : ["sourceselect"]
},
"instance" : {
"type": "array",
"required": false,
"items" : {},
"minItems": 1
},
"tan" : {
"type" : "integer"
},

File diff suppressed because it is too large Load Diff

View File

@ -1,416 +0,0 @@
// proj incl
#include <api/JsonCB.h>
// hyperion
#include <hyperion/Hyperion.h>
// HyperionIManager
#include <hyperion/HyperionIManager.h>
// components
#include <hyperion/ComponentRegister.h>
// priorityMuxer
#include <hyperion/PriorityMuxer.h>
// utils
#include <utils/ColorSys.h>
// qt
#include <QDateTime>
#include <QVariant>
// Image to led map helper
#include <hyperion/ImageProcessor.h>
using namespace hyperion;
JsonCB::JsonCB(QObject* parent)
: QObject(parent)
, _hyperion(nullptr)
, _componentRegister(nullptr)
, _prioMuxer(nullptr)
{
_availableCommands << "components-update" << "priorities-update" << "imageToLedMapping-update"
<< "adjustment-update" << "videomode-update" << "settings-update" << "leds-update" << "instance-update" << "token-update";
#if defined(ENABLE_EFFECTENGINE)
_availableCommands << "effects-update";
#endif
qRegisterMetaType<PriorityMuxer::InputsMap>("InputsMap");
}
bool JsonCB::subscribeFor(const QString& type, bool unsubscribe)
{
if(!_availableCommands.contains(type))
return false;
if(unsubscribe)
_subscribedCommands.removeAll(type);
else
_subscribedCommands << type;
if(type == "components-update")
{
if(unsubscribe)
disconnect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState);
else
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection);
}
if(type == "priorities-update")
{
if (unsubscribe)
disconnect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate);
else
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection);
}
if(type == "imageToLedMapping-update")
{
if(unsubscribe)
disconnect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange);
else
connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection);
}
if(type == "adjustment-update")
{
if(unsubscribe)
disconnect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange);
else
connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection);
}
if(type == "videomode-update")
{
if(unsubscribe)
disconnect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange);
else
connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange, Qt::UniqueConnection);
}
#if defined(ENABLE_EFFECTENGINE)
if(type == "effects-update")
{
if(unsubscribe)
disconnect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange);
else
connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange, Qt::UniqueConnection);
}
#endif
if(type == "settings-update")
{
if(unsubscribe)
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange);
else
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection);
}
if(type == "leds-update")
{
if(unsubscribe)
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange);
else
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection);
}
if(type == "instance-update")
{
if(unsubscribe)
disconnect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange);
else
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection);
}
if (type == "token-update")
{
if (unsubscribe)
disconnect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCB::handleTokenChange);
else
connect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCB::handleTokenChange, Qt::UniqueConnection);
}
return true;
}
void JsonCB::resetSubscriptions()
{
for(const auto & entry : getSubscribedCommands())
{
subscribeFor(entry, true);
}
}
void JsonCB::setSubscriptionsTo(Hyperion* hyperion)
{
assert(hyperion);
// get current subs
QStringList currSubs(getSubscribedCommands());
// stop subs
resetSubscriptions();
// update pointer
_hyperion = hyperion;
_componentRegister = _hyperion->getComponentRegister();
_prioMuxer = _hyperion->getMuxerInstance();
// re-apply subs
for(const auto & entry : currSubs)
{
subscribeFor(entry);
}
}
void JsonCB::doCallback(const QString& cmd, const QVariant& data)
{
QJsonObject obj;
obj["instance"] = _hyperion->getInstanceIndex();
obj["command"] = cmd;
if (data.userType() == QMetaType::QJsonArray)
obj["data"] = data.toJsonArray();
else
obj["data"] = data.toJsonObject();
emit newCallback(obj);
}
void JsonCB::handleComponentState(hyperion::Components comp, bool state)
{
QJsonObject data;
data["name"] = componentToIdString(comp);
data["enabled"] = state;
doCallback("components-update", QVariant(data));
}
void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs)
{
QJsonObject data;
QJsonArray priorities;
uint64_t now = QDateTime::currentMSecsSinceEpoch();
QList<int> activePriorities = activeInputs.keys();
activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY);
for (int priority : std::as_const(activePriorities)) {
const Hyperion::InputInfo& priorityInfo = activeInputs[priority];
QJsonObject item;
item["priority"] = priority;
if (priorityInfo.timeoutTime_ms > 0 )
{
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
}
// owner has optional informations to the component
if(!priorityInfo.owner.isEmpty())
{
item["owner"] = priorityInfo.owner;
}
item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId));
item["origin"] = priorityInfo.origin;
item["active"] = (priorityInfo.timeoutTime_ms >= -1);
item["visible"] = (priority == currentPriority);
if(priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty())
{
QJsonObject LEDcolor;
// add RGB Value to Array
QJsonArray RGBValue;
RGBValue.append(priorityInfo.ledColors.begin()->red);
RGBValue.append(priorityInfo.ledColors.begin()->green);
RGBValue.append(priorityInfo.ledColors.begin()->blue);
LEDcolor.insert("RGB", RGBValue);
uint16_t Hue;
float Saturation;
float Luminace;
// add HSL Value to Array
QJsonArray HSLValue;
ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red,
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
HSLValue.append(Hue);
HSLValue.append(Saturation);
HSLValue.append(Luminace);
LEDcolor.insert("HSL", HSLValue);
item["value"] = LEDcolor;
}
priorities.append(item);
}
data["priorities"] = priorities;
data["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled();
doCallback("priorities-update", QVariant(data));
}
void JsonCB::handleImageToLedsMappingChange(int mappingType)
{
QJsonObject data;
data["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(mappingType);
doCallback("imageToLedMapping-update", QVariant(data));
}
void JsonCB::handleAdjustmentChange()
{
QJsonArray adjustmentArray;
for (const QString& adjustmentId : _hyperion->getAdjustmentIds())
{
const ColorAdjustment * colorAdjustment = _hyperion->getAdjustment(adjustmentId);
if (colorAdjustment == nullptr)
{
continue;
}
QJsonObject adjustment;
adjustment["id"] = adjustmentId;
QJsonArray whiteAdjust;
whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentR());
whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentG());
whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentB());
adjustment.insert("white", whiteAdjust);
QJsonArray redAdjust;
redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentR());
redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentG());
redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentB());
adjustment.insert("red", redAdjust);
QJsonArray greenAdjust;
greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentR());
greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentG());
greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentB());
adjustment.insert("green", greenAdjust);
QJsonArray blueAdjust;
blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentR());
blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentG());
blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentB());
adjustment.insert("blue", blueAdjust);
QJsonArray cyanAdjust;
cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentR());
cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentG());
cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentB());
adjustment.insert("cyan", cyanAdjust);
QJsonArray magentaAdjust;
magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentR());
magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentG());
magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentB());
adjustment.insert("magenta", magentaAdjust);
QJsonArray yellowAdjust;
yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentR());
yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentG());
yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentB());
adjustment.insert("yellow", yellowAdjust);
adjustment["backlightThreshold"] = colorAdjustment->_rgbTransform.getBacklightThreshold();
adjustment["backlightColored"] = colorAdjustment->_rgbTransform.getBacklightColored();
adjustment["brightness"] = colorAdjustment->_rgbTransform.getBrightness();
adjustment["brightnessCompensation"] = colorAdjustment->_rgbTransform.getBrightnessCompensation();
adjustment["gammaRed"] = colorAdjustment->_rgbTransform.getGammaR();
adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG();
adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB();
adjustmentArray.append(adjustment);
}
doCallback("adjustment-update", QVariant(adjustmentArray));
}
void JsonCB::handleVideoModeChange(VideoMode mode)
{
QJsonObject data;
data["videomode"] = QString(videoMode2String(mode));
doCallback("videomode-update", QVariant(data));
}
#if defined(ENABLE_EFFECTENGINE)
void JsonCB::handleEffectListChange()
{
QJsonArray effectList;
QJsonObject effects;
const std::list<EffectDefinition> & effectsDefinitions = _hyperion->getEffects();
for (const EffectDefinition & effectDefinition : effectsDefinitions)
{
QJsonObject effect;
effect["name"] = effectDefinition.name;
effect["file"] = effectDefinition.file;
effect["script"] = effectDefinition.script;
effect["args"] = effectDefinition.args;
effectList.append(effect);
};
effects["effects"] = effectList;
doCallback("effects-update", QVariant(effects));
}
#endif
void JsonCB::handleSettingsChange(settings::type type, const QJsonDocument& data)
{
QJsonObject dat;
if(data.isObject())
dat[typeToString(type)] = data.object();
else
dat[typeToString(type)] = data.array();
doCallback("settings-update", QVariant(dat));
}
void JsonCB::handleLedsConfigChange(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("running", entry["running"].toBool());
arr.append(obj);
}
doCallback("instance-update", QVariant(arr));
}
void JsonCB::handleTokenChange(const QVector<AuthManager::AuthDefinition> &def)
{
QJsonArray arr;
for (const auto &entry : def)
{
QJsonObject sub;
sub["comment"] = entry.comment;
sub["id"] = entry.id;
sub["last_use"] = entry.lastUse;
arr.push_back(sub);
}
doCallback("token-update", QVariant(arr));
}

View File

@ -0,0 +1,459 @@
#include <api/JsonCallbacks.h>
#include <api/JsonInfo.h>
#include <api/JsonApiSubscription.h>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
#include <events/EventHandler.h>
#include <hyperion/ComponentRegister.h>
#include <hyperion/PriorityMuxer.h>
#include <utils/ColorSys.h>
#include <hyperion/ImageProcessor.h>
#include <QDateTime>
#include <QVariant>
#include <QImage>
#include <QBuffer>
using namespace hyperion;
JsonCallbacks::JsonCallbacks(Logger *log, const QString& peerAddress, QObject* parent)
: QObject(parent)
, _log (log)
, _hyperion(nullptr)
, _peerAddress (peerAddress)
, _componentRegister(nullptr)
, _prioMuxer(nullptr)
, _islogMsgStreamingActive(false)
{
qRegisterMetaType<PriorityMuxer::InputsMap>("InputsMap");
}
bool JsonCallbacks::subscribe(const Subscription::Type cmd)
{
switch (cmd) {
case Subscription::AdjustmentUpdate:
connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCallbacks::handleAdjustmentChange);
break;
case Subscription::ComponentsUpdate:
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCallbacks::handleComponentState);
break;
#if defined(ENABLE_EFFECTENGINE)
case Subscription::EffectsUpdate:
connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCallbacks::handleEffectListChange);
break;
#endif
case Subscription::EventUpdate:
connect(EventHandler::getInstance().data(), &EventHandler::signalEvent, this, &JsonCallbacks::handleEventUpdate);
break;
case Subscription::ImageToLedMappingUpdate:
connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCallbacks::handleImageToLedsMappingChange);
break;
case Subscription::ImageUpdate:
connect(_hyperion, &Hyperion::currentImage, this, &JsonCallbacks::handleImageUpdate);
break;
case Subscription::InstanceUpdate:
connect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCallbacks::handleInstanceChange);
break;
case Subscription::LedColorsUpdate:
connect(_hyperion, &Hyperion::rawLedColors, this, &JsonCallbacks::handleLedColorUpdate);
break;
case Subscription::LedsUpdate:
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCallbacks::handleLedsConfigChange);
break;
case Subscription::LogMsgUpdate:
if (!_islogMsgStreamingActive)
{
handleLogMessageUpdate (Logger::T_LOG_MESSAGE{}); // needed to trigger log sending
_islogMsgStreamingActive = true;
Debug(_log, "log streaming activated for client %s", _peerAddress.toStdString().c_str());
}
connect(LoggerManager::getInstance().data(), &LoggerManager::newLogMessage, this, &JsonCallbacks::handleLogMessageUpdate);
break;
case Subscription::PrioritiesUpdate:
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCallbacks::handlePriorityUpdate);
break;
case Subscription::SettingsUpdate:
connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCallbacks::handleSettingsChange);
break;
case Subscription::TokenUpdate:
connect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCallbacks::handleTokenChange, Qt::AutoConnection);
break;
case Subscription::VideomodeUpdate:
connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCallbacks::handleVideoModeChange);
break;
default:
return false;
}
_subscribedCommands.insert(cmd);
return true;
}
bool JsonCallbacks::subscribe(const QString& cmd)
{
JsonApiSubscription subscription = ApiSubscriptionRegister::getSubscriptionInfo(cmd);
if (subscription.cmd == Subscription::Unknown)
{
return false;
}
return subscribe(subscription.cmd);
}
QStringList JsonCallbacks::subscribe(const QJsonArray& subscriptions)
{
QJsonArray subsArr;
if (subscriptions.contains("all"))
{
for (const auto& entry : getCommands(false))
{
subsArr.append(entry);
}
}
else
{
subsArr = subscriptions;
}
QStringList invalidSubscriptions;
for (auto it = subsArr.begin(); it != subsArr.end(); ++it)
{
const QJsonValue& entry = *it;
if (!subscribe(entry.toString()))
{
invalidSubscriptions.append(entry.toString());
}
}
return invalidSubscriptions;
}
bool JsonCallbacks::unsubscribe(const Subscription::Type cmd)
{
_subscribedCommands.remove(cmd);
switch (cmd) {
case Subscription::AdjustmentUpdate:
disconnect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCallbacks::handleAdjustmentChange);
break;
case Subscription::ComponentsUpdate:
disconnect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCallbacks::handleComponentState);
break;
#if defined(ENABLE_EFFECTENGINE)
case Subscription::EffectsUpdate:
disconnect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCallbacks::handleEffectListChange);
break;
#endif
case Subscription::EventUpdate:
disconnect(EventHandler::getInstance().data(), &EventHandler::signalEvent, this, &JsonCallbacks::handleEventUpdate);
break;
case Subscription::ImageToLedMappingUpdate:
disconnect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCallbacks::handleImageToLedsMappingChange);
break;
case Subscription::ImageUpdate:
disconnect(_hyperion, &Hyperion::currentImage, this, &JsonCallbacks::handleImageUpdate);
break;
case Subscription::InstanceUpdate:
disconnect(HyperionIManager::getInstance(), &HyperionIManager::change, this, &JsonCallbacks::handleInstanceChange);
break;
case Subscription::LedColorsUpdate:
disconnect(_hyperion, &Hyperion::rawLedColors, this, &JsonCallbacks::handleLedColorUpdate);
break;
case Subscription::LedsUpdate:
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCallbacks::handleLedsConfigChange);
break;
case Subscription::LogMsgUpdate:
disconnect(LoggerManager::getInstance().data(), &LoggerManager::newLogMessage, this, &JsonCallbacks::handleLogMessageUpdate);
if (_islogMsgStreamingActive)
{
_islogMsgStreamingActive = false;
Debug(_log, "log streaming deactivated for client %s", _peerAddress.toStdString().c_str());
}
break;
case Subscription::PrioritiesUpdate:
disconnect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCallbacks::handlePriorityUpdate);
break;
case Subscription::SettingsUpdate:
disconnect(_hyperion, &Hyperion::settingsChanged, this, &JsonCallbacks::handleSettingsChange);
break;
case Subscription::TokenUpdate:
disconnect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCallbacks::handleTokenChange);
break;
case Subscription::VideomodeUpdate:
disconnect(_hyperion, &Hyperion::newVideoMode, this, &JsonCallbacks::handleVideoModeChange);
break;
default:
return false;
}
return true;
}
bool JsonCallbacks::unsubscribe(const QString& cmd)
{
JsonApiSubscription subscription = ApiSubscriptionRegister::getSubscriptionInfo(cmd);
if (subscription.cmd == Subscription::Unknown)
{
return false;
}
return unsubscribe(subscription.cmd);
}
QStringList JsonCallbacks::unsubscribe(const QJsonArray& subscriptions)
{
QJsonArray subsArr;
if (subscriptions.contains("all"))
{
for (const auto& entry : getCommands(false))
{
subsArr.append(entry);
}
}
else
{
subsArr = subscriptions;
}
QStringList invalidSubscriptions;
for (auto it = subsArr.begin(); it != subsArr.end(); ++it)
{
const QJsonValue& entry = *it;
if (!unsubscribe(entry.toString()))
{
invalidSubscriptions.append(entry.toString());
}
}
return invalidSubscriptions;
}
void JsonCallbacks::resetSubscriptions()
{
const QSet<Subscription::Type> currentSubscriptions = _subscribedCommands;
for (QSet<Subscription::Type>::const_iterator it = currentSubscriptions.constBegin(); it != currentSubscriptions.constEnd(); ++it)
{
unsubscribe(*it);
}
}
void JsonCallbacks::setSubscriptionsTo(Hyperion* hyperion)
{
assert(hyperion);
// get current subs
const QSet<Subscription::Type> currSubs(_subscribedCommands);
// stop subs
resetSubscriptions();
// update pointer
_hyperion = hyperion;
_componentRegister = _hyperion->getComponentRegister();
_prioMuxer = _hyperion->getMuxerInstance();
// re-apply subs
for(const auto & entry : currSubs)
{
subscribe(entry);
}
}
QStringList JsonCallbacks::getCommands(bool fullList) const
{
QStringList commands;
for (JsonApiSubscription subscription : ApiSubscriptionRegister::getSubscriptionLookup())
{
if (fullList || subscription.isAll)
{
commands << Subscription::toString(subscription.cmd);
}
}
return commands;
}
QStringList JsonCallbacks::getSubscribedCommands() const
{
QStringList commands;
for (Subscription::Type cmd : _subscribedCommands)
{
commands << Subscription::toString(cmd);
}
return commands;
}
void JsonCallbacks::doCallback(Subscription::Type cmd, const QVariant& data)
{
QJsonObject obj;
obj["command"] = Subscription::toString(cmd);
if (Subscription::isInstanceSpecific(cmd))
{
obj["instance"] = _hyperion->getInstanceIndex();
}
if (data.userType() == QMetaType::QJsonArray) {
obj["data"] = data.toJsonArray();
} else {
obj["data"] = data.toJsonObject();
}
emit newCallback(obj);
}
void JsonCallbacks::handleComponentState(hyperion::Components comp, bool state)
{
QJsonObject data;
data["name"] = componentToIdString(comp);
data["enabled"] = state;
doCallback(Subscription::ComponentsUpdate, QVariant(data));
}
void JsonCallbacks::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs)
{
QJsonObject data;
data["priorities"] = JsonInfo::getPrioritiestInfo(currentPriority, activeInputs);
data["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled();
doCallback(Subscription::PrioritiesUpdate, QVariant(data));
}
void JsonCallbacks::handleImageToLedsMappingChange(int mappingType)
{
QJsonObject data;
data["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(mappingType);
doCallback(Subscription::ImageToLedMappingUpdate, QVariant(data));
}
void JsonCallbacks::handleAdjustmentChange()
{
doCallback(Subscription::AdjustmentUpdate, JsonInfo::getAdjustmentInfo(_hyperion,_log));
}
void JsonCallbacks::handleVideoModeChange(VideoMode mode)
{
QJsonObject data;
data["videomode"] = QString(videoMode2String(mode));
doCallback(Subscription::VideomodeUpdate, QVariant(data));
}
#if defined(ENABLE_EFFECTENGINE)
void JsonCallbacks::handleEffectListChange()
{
QJsonObject effects;
effects["effects"] = JsonInfo::getEffects(_hyperion);
doCallback(Subscription::EffectsUpdate, QVariant(effects));
}
#endif
void JsonCallbacks::handleSettingsChange(settings::type type, const QJsonDocument& data)
{
QJsonObject dat;
if(data.isObject()) {
dat[typeToString(type)] = data.object();
} else {
dat[typeToString(type)] = data.array();
}
doCallback(Subscription::SettingsUpdate, QVariant(dat));
}
void JsonCallbacks::handleLedsConfigChange(settings::type type, const QJsonDocument& data)
{
if(type == settings::LEDS)
{
QJsonObject dat;
dat[typeToString(type)] = data.array();
doCallback(Subscription::LedsUpdate, QVariant(dat));
}
}
void JsonCallbacks::handleInstanceChange()
{
doCallback(Subscription::InstanceUpdate, JsonInfo::getInstanceInfo());
}
void JsonCallbacks::handleTokenChange(const QVector<AuthManager::AuthDefinition> &def)
{
QJsonArray arr;
for (const auto &entry : def)
{
QJsonObject sub;
sub["comment"] = entry.comment;
sub["id"] = entry.id;
sub["last_use"] = entry.lastUse;
arr.push_back(sub);
}
doCallback(Subscription::TokenUpdate, QVariant(arr));
}
void JsonCallbacks::handleLedColorUpdate(const std::vector<ColorRgb> &ledColors)
{
QJsonObject result;
QJsonArray leds;
for (const auto &color : ledColors)
{
leds << QJsonValue(color.red) << QJsonValue(color.green) << QJsonValue(color.blue);
}
result["leds"] = leds;
doCallback(Subscription::LedColorsUpdate, QVariant(result));
}
void JsonCallbacks::handleImageUpdate(const Image<ColorRgb> &image)
{
QImage jpgImage(reinterpret_cast<const uchar*>(image.memptr()), image.width(), image.height(), qsizetype(3) * image.width(), QImage::Format_RGB888);
QByteArray byteArray;
QBuffer buffer(&byteArray);
buffer.open(QIODevice::WriteOnly);
jpgImage.save(&buffer, "jpg");
QJsonObject result;
result["image"] = "data:image/jpg;base64," + QString(byteArray.toBase64());
doCallback(Subscription::ImageUpdate, QVariant(result));
}
void JsonCallbacks::handleLogMessageUpdate(const Logger::T_LOG_MESSAGE &msg)
{
QJsonObject result;
QJsonObject message;
QJsonArray messageArray;
if (!_islogMsgStreamingActive)
{
_islogMsgStreamingActive = true;
QMetaObject::invokeMethod(LoggerManager::getInstance().data(), "getLogMessageBuffer",
Qt::DirectConnection,
Q_RETURN_ARG(QJsonArray, messageArray),
Q_ARG(Logger::LogLevel, _log->getLogLevel()));
}
else
{
message["loggerName"] = msg.loggerName;
message["loggerSubName"] = msg.loggerSubName;
message["function"] = msg.function;
message["line"] = QString::number(msg.line);
message["fileName"] = msg.fileName;
message["message"] = msg.message;
message["levelString"] = msg.levelString;
message["utime"] = QString::number(msg.utime);
messageArray.append(message);
}
result.insert("messages", messageArray);
doCallback(Subscription::LogMsgUpdate, QVariant(result));
}
void JsonCallbacks::handleEventUpdate(const Event &event)
{
QJsonObject result;
result["event"] = eventToString(event);
doCallback(Subscription::EventUpdate, QVariant(result));
}

620
libsrc/api/JsonInfo.cpp Normal file
View File

@ -0,0 +1,620 @@
#include <api/JsonInfo.h>
#include <api/API.h>
#include <utils/ColorSys.h>
#include <hyperion/GrabberWrapper.h>
#include <leddevice/LedDeviceWrapper.h>
#include <utils/SysInfo.h>
#include <hyperion/AuthManager.h>
#include <QCoreApplication>
#include <QApplication>
#include <HyperionConfig.h> // Required to determine the cmake options
#include <hyperion/GrabberWrapper.h>
#include <grabber/GrabberConfig.h>
QJsonArray JsonInfo::getAdjustmentInfo(const Hyperion* hyperion, Logger* log)
{
QJsonArray adjustmentArray;
for (const QString &adjustmentId : hyperion->getAdjustmentIds())
{
const ColorAdjustment *colorAdjustment = hyperion->getAdjustment(adjustmentId);
if (colorAdjustment == nullptr)
{
Error(log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId));
continue;
}
QJsonObject adjustment;
adjustment["id"] = adjustmentId;
QJsonArray whiteAdjust;
whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentR());
whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentG());
whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentB());
adjustment.insert("white", whiteAdjust);
QJsonArray redAdjust;
redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentR());
redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentG());
redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentB());
adjustment.insert("red", redAdjust);
QJsonArray greenAdjust;
greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentR());
greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentG());
greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentB());
adjustment.insert("green", greenAdjust);
QJsonArray blueAdjust;
blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentR());
blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentG());
blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentB());
adjustment.insert("blue", blueAdjust);
QJsonArray cyanAdjust;
cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentR());
cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentG());
cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentB());
adjustment.insert("cyan", cyanAdjust);
QJsonArray magentaAdjust;
magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentR());
magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentG());
magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentB());
adjustment.insert("magenta", magentaAdjust);
QJsonArray yellowAdjust;
yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentR());
yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentG());
yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentB());
adjustment.insert("yellow", yellowAdjust);
adjustment["backlightThreshold"] = colorAdjustment->_rgbTransform.getBacklightThreshold();
adjustment["backlightColored"] = colorAdjustment->_rgbTransform.getBacklightColored();
adjustment["brightness"] = colorAdjustment->_rgbTransform.getBrightness();
adjustment["brightnessCompensation"] = colorAdjustment->_rgbTransform.getBrightnessCompensation();
adjustment["gammaRed"] = colorAdjustment->_rgbTransform.getGammaR();
adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG();
adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB();
adjustment["saturationGain"] = colorAdjustment->_okhsvTransform.getSaturationGain();
adjustment["brightnessGain"] = colorAdjustment->_okhsvTransform.getBrightnessGain();
adjustmentArray.append(adjustment);
}
return adjustmentArray;
}
QJsonArray JsonInfo::getPrioritiestInfo(const Hyperion* hyperion)
{
return getPrioritiestInfo(hyperion->getCurrentPriority(), hyperion->getPriorityInfo());
}
QJsonArray JsonInfo::getPrioritiestInfo(int currentPriority, const PriorityMuxer::InputsMap& activeInputs)
{
QJsonArray priorities;
int64_t now = QDateTime::currentMSecsSinceEpoch();
QList<int> activePriorities = activeInputs.keys();
activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY);
for(int priority : std::as_const(activePriorities))
{
const PriorityMuxer::InputInfo priorityInfo = activeInputs.value(priority);
QJsonObject item;
item["priority"] = priority;
if (priorityInfo.timeoutTime_ms > 0 )
{
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
}
// owner has optional informations to the component
if (!priorityInfo.owner.isEmpty())
{
item["owner"] = priorityInfo.owner;
}
item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId));
item["origin"] = priorityInfo.origin;
item["active"] = (priorityInfo.timeoutTime_ms >= -1);
item["visible"] = (priority == currentPriority);
if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty())
{
QJsonObject LEDcolor;
// add RGB Value to Array
QJsonArray RGBValue;
RGBValue.append(priorityInfo.ledColors.begin()->red);
RGBValue.append(priorityInfo.ledColors.begin()->green);
RGBValue.append(priorityInfo.ledColors.begin()->blue);
LEDcolor.insert("RGB", RGBValue);
uint16_t Hue;
float Saturation;
float Luminace;
// add HSL Value to Array
QJsonArray HSLValue;
ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red,
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
HSLValue.append(static_cast<double>(Hue));
HSLValue.append(static_cast<double>(Saturation));
HSLValue.append(static_cast<double>(Luminace));
LEDcolor.insert("HSL", HSLValue);
item["value"] = LEDcolor;
}
(priority == currentPriority)
? priorities.prepend(item)
: priorities.append(item);
}
return priorities;
}
QJsonArray JsonInfo::getEffects(const Hyperion* hyperion)
{
QJsonArray effects;
#if defined(ENABLE_EFFECTENGINE)
// collect effect info
const std::list<EffectDefinition> &effectsDefinitions = hyperion->getEffects();
for (const EffectDefinition &effectDefinition : effectsDefinitions)
{
QJsonObject effect;
effect["name"] = effectDefinition.name;
effect["file"] = effectDefinition.file;
effect["script"] = effectDefinition.script;
effect["args"] = effectDefinition.args;
effects.append(effect);
}
#endif
return effects;
}
QJsonObject JsonInfo::getAvailableLedDevices()
{
// get available led devices
QJsonObject ledDevices;
QJsonArray availableLedDevices;
for (const auto& dev : LedDeviceWrapper::getDeviceMap())
{
availableLedDevices.append(dev.first);
}
ledDevices["available"] = availableLedDevices;
return ledDevices;
}
QJsonArray JsonInfo::getAvailableScreenGrabbers()
{
QJsonArray availableScreenGrabbers;
for (const auto& grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::SCREEN))
{
availableScreenGrabbers.append(grabber);
}
return availableScreenGrabbers;
}
QJsonArray JsonInfo::getAvailableVideoGrabbers()
{
QJsonArray availableVideoGrabbers;
for (const auto& grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::VIDEO))
{
availableVideoGrabbers.append(grabber);
}
return availableVideoGrabbers;
}
QJsonArray JsonInfo::getAvailableAudioGrabbers()
{
QJsonArray availableAudioGrabbers;
for (const auto& grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::AUDIO))
{
availableAudioGrabbers.append(grabber);
}
return availableAudioGrabbers;
}
QJsonObject JsonInfo::getGrabbers(const Hyperion* hyperion)
{
QJsonObject grabbers;
// SCREEN
QJsonObject screenGrabbers;
if (GrabberWrapper::getInstance() != nullptr)
{
const QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(hyperion->getInstanceIndex(), GrabberTypeFilter::SCREEN);
QJsonArray activeGrabberNames;
for (const auto& grabberName : activeGrabbers)
{
activeGrabberNames.append(grabberName);
}
screenGrabbers["active"] = activeGrabberNames;
}
screenGrabbers["available"] = getAvailableScreenGrabbers();
// VIDEO
QJsonObject videoGrabbers;
if (GrabberWrapper::getInstance() != nullptr)
{
const QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(hyperion->getInstanceIndex(), GrabberTypeFilter::VIDEO);
QJsonArray activeGrabberNames;
for (const auto& grabberName : activeGrabbers)
{
activeGrabberNames.append(grabberName);
}
videoGrabbers["active"] = activeGrabberNames;
}
videoGrabbers["available"] = getAvailableVideoGrabbers();
// AUDIO
QJsonObject audioGrabbers;
if (GrabberWrapper::getInstance() != nullptr)
{
const QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(hyperion->getInstanceIndex(), GrabberTypeFilter::AUDIO);
QJsonArray activeGrabberNames;
for (const auto& grabberName : activeGrabbers)
{
activeGrabberNames.append(grabberName);
}
audioGrabbers["active"] = activeGrabberNames;
}
audioGrabbers["available"] = getAvailableAudioGrabbers() ;
grabbers.insert("screen", screenGrabbers);
grabbers.insert("video", videoGrabbers);
grabbers.insert("audio", audioGrabbers);
return grabbers;
}
QJsonObject JsonInfo::getCecInfo()
{
QJsonObject cecInfo;
#if defined(ENABLE_CEC)
cecInfo["enabled"] = true;
#else
cecInfo["enabled"] = false;
#endif
return cecInfo;
}
QJsonArray JsonInfo::getServices()
{
// get available services
QJsonArray services;
#if defined(ENABLE_BOBLIGHT_SERVER)
services.append("boblight");
#endif
#if defined(ENABLE_CEC)
services.append("cec");
#endif
#if defined(ENABLE_EFFECTENGINE)
services.append("effectengine");
#endif
#if defined(ENABLE_FORWARDER)
services.append("forwarder");
#endif
#if defined(ENABLE_FLATBUF_SERVER)
services.append("flatbuffer");
#endif
#if defined(ENABLE_PROTOBUF_SERVER)
services.append("protobuffer");
#endif
#if defined(ENABLE_MDNS)
services.append("mDNS");
#endif
services.append("SSDP");
if (!getAvailableScreenGrabbers().isEmpty() || !getAvailableVideoGrabbers().isEmpty() || services.contains("flatbuffer") || services.contains("protobuffer"))
{
services.append("borderdetection");
}
return services;
}
QJsonArray JsonInfo::getComponents(const Hyperion* hyperion)
{
// get available components
QJsonArray component;
std::map<hyperion::Components, bool> components = hyperion->getComponentRegister()->getRegister();
for (auto comp : components)
{
QJsonObject item;
item["name"] = QString::fromStdString(hyperion::componentToIdString(comp.first));
item["enabled"] = comp.second;
component.append(item);
}
return component;
}
QJsonArray JsonInfo::getInstanceInfo()
{
QJsonArray instanceInfo;
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("running", entry["running"].toBool());
instanceInfo.append(obj);
}
return instanceInfo;
}
QJsonArray JsonInfo::getTransformationInfo(const Hyperion* hyperion)
{
// TRANSFORM INFORMATION (DEFAULT VALUES)
QJsonArray transformArray;
for (const QString &transformId : hyperion->getAdjustmentIds())
{
QJsonObject transform;
QJsonArray blacklevel;
QJsonArray whitelevel;
QJsonArray gamma;
QJsonArray threshold;
transform["id"] = transformId;
transform["saturationGain"] = 1.0;
transform["brightnessGain"] = 1.0;
transform["saturationLGain"] = 1.0;
transform["luminanceGain"] = 1.0;
transform["luminanceMinimum"] = 0.0;
for (int i = 0; i < 3; i++)
{
blacklevel.append(0.0);
whitelevel.append(1.0);
gamma.append(2.50);
threshold.append(0.0);
}
transform.insert("blacklevel", blacklevel);
transform.insert("whitelevel", whitelevel);
transform.insert("gamma", gamma);
transform.insert("threshold", threshold);
transformArray.append(transform);
}
return transformArray;
}
QJsonArray JsonInfo::getActiveEffects(const Hyperion* hyperion)
{
// ACTIVE EFFECT INFO
QJsonArray activeEffects;
#if defined(ENABLE_EFFECTENGINE)
for (const ActiveEffectDefinition &activeEffectDefinition : hyperion->getActiveEffects())
{
if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1)
{
QJsonObject activeEffect;
activeEffect["script"] = activeEffectDefinition.script;
activeEffect["name"] = activeEffectDefinition.name;
activeEffect["priority"] = activeEffectDefinition.priority;
activeEffect["timeout"] = activeEffectDefinition.timeout;
activeEffect["args"] = activeEffectDefinition.args;
activeEffects.append(activeEffect);
}
}
#endif
return activeEffects;
}
QJsonArray JsonInfo::getActiveColors(const Hyperion* hyperion)
{
// ACTIVE STATIC LED COLOR
QJsonArray activeLedColors;
const Hyperion::InputInfo &priorityInfo = hyperion->getPriorityInfo(hyperion->getCurrentPriority());
if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty())
{
// check if LED Color not Black (0,0,0)
if ((priorityInfo.ledColors.begin()->red +
priorityInfo.ledColors.begin()->green +
priorityInfo.ledColors.begin()->blue !=
0))
{
QJsonObject LEDcolor;
// add RGB Value to Array
QJsonArray RGBValue;
RGBValue.append(priorityInfo.ledColors.begin()->red);
RGBValue.append(priorityInfo.ledColors.begin()->green);
RGBValue.append(priorityInfo.ledColors.begin()->blue);
LEDcolor.insert("RGB Value", RGBValue);
uint16_t Hue;
float Saturation;
float Luminace;
// add HSL Value to Array
QJsonArray HSLValue;
ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red,
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
HSLValue.append(static_cast<double>(Hue));
HSLValue.append(static_cast<double>(Saturation));
HSLValue.append(static_cast<double>(Luminace));
LEDcolor.insert("HSL Value", HSLValue);
activeLedColors.append(LEDcolor);
}
}
return activeLedColors;
}
QJsonObject JsonInfo::getSystemInfo(const Hyperion* hyperion)
{
QJsonObject info;
SysInfo::HyperionSysInfo data = SysInfo::get();
QJsonObject systemInfo;
systemInfo["kernelType"] = data.kernelType;
systemInfo["kernelVersion"] = data.kernelVersion;
systemInfo["architecture"] = data.architecture;
systemInfo["cpuModelName"] = data.cpuModelName;
systemInfo["cpuModelType"] = data.cpuModelType;
systemInfo["cpuHardware"] = data.cpuHardware;
systemInfo["cpuRevision"] = data.cpuRevision;
systemInfo["wordSize"] = data.wordSize;
systemInfo["productType"] = data.productType;
systemInfo["productVersion"] = data.productVersion;
systemInfo["prettyName"] = data.prettyName;
systemInfo["hostName"] = data.hostName;
systemInfo["domainName"] = data.domainName;
systemInfo["isUserAdmin"] = data.isUserAdmin;
systemInfo["qtVersion"] = data.qtVersion;
#if defined(ENABLE_EFFECTENGINE)
systemInfo["pyVersion"] = data.pyVersion;
#endif
info["system"] = systemInfo;
QJsonObject hyperionInfo;
hyperionInfo["version"] = QString(HYPERION_VERSION);
hyperionInfo["build"] = QString(HYPERION_BUILD_ID);
hyperionInfo["gitremote"] = QString(HYPERION_GIT_REMOTE);
hyperionInfo["time"] = QString(__DATE__ " " __TIME__);
hyperionInfo["id"] = AuthManager::getInstance()->getID();
hyperionInfo["rootPath"] = HyperionIManager::getInstance()->getRootPath();
hyperionInfo["readOnlyMode"] = hyperion->getReadOnlyMode();
QCoreApplication* app = QCoreApplication::instance();
hyperionInfo["isGuiMode"] = qobject_cast<QApplication*>(app) != nullptr;
info["hyperion"] = hyperionInfo;
return info;
}
QJsonObject JsonInfo::discoverSources(const QString& sourceType, const QJsonObject& params)
{
QJsonObject inputSourcesDiscovered;
inputSourcesDiscovered.insert("sourceType", sourceType);
if (sourceType == "video") {
QJsonArray videoInputs = discoverVideoInputs(params);
inputSourcesDiscovered["video_sources"] = videoInputs;
} else if (sourceType == "audio") {
QJsonArray audioInputs = discoverAudioInputs(params);
inputSourcesDiscovered["audio_sources"] = audioInputs;
} else if (sourceType == "screen") {
QJsonArray screenInputs = discoverScreenInputs(params);
inputSourcesDiscovered["video_sources"] = screenInputs;
}
return inputSourcesDiscovered;
}
template<typename GrabberType>
void JsonInfo::discoverGrabber(QJsonArray& inputs, const QJsonObject& params) const
{
GrabberType grabber;
QJsonValue discoveryResult = grabber.discover(params);
if (discoveryResult.isArray())
{
inputs = discoveryResult.toArray();
}
else
{
if (!discoveryResult.toObject().isEmpty())
{
inputs.append(discoveryResult);
}
}
}
QJsonArray JsonInfo::discoverVideoInputs(const QJsonObject& params) const
{
QJsonArray videoInputs;
#ifdef ENABLE_V4L2
discoverGrabber<V4L2Grabber>(videoInputs, params);
#endif
#ifdef ENABLE_MF
discoverGrabber<MFGrabber>(videoInputs, params);
#endif
return videoInputs;
}
QJsonArray JsonInfo::discoverAudioInputs(const QJsonObject& params) const
{
QJsonArray audioInputs;
#ifdef ENABLE_AUDIO
#ifdef WIN32
discoverGrabber<AudioGrabberWindows>(audioInputs, params);
#endif
#ifdef __linux__audioInputs
discoverGrabber<AudioGrabberLinux>(audioInputs, params);
#endif
#endif
return audioInputs;
}
QJsonArray JsonInfo::discoverScreenInputs(const QJsonObject& params) const
{
QJsonArray screenInputs;
#ifdef ENABLE_QT
discoverGrabber<QtGrabber>(screenInputs, params);
#endif
#ifdef ENABLE_DX
discoverGrabber<DirectXGrabber>(screenInputs, params);
#endif
#ifdef ENABLE_X11
discoverGrabber<X11Grabber>(screenInputs, params);
#endif
#ifdef ENABLE_XCB
discoverGrabber<XcbGrabber>(screenInputs, params);
#endif
#if defined(ENABLE_FB) && !defined(ENABLE_AMLOGIC)
discoverGrabber<FramebufferFrameGrabber>(screenInputs, params);
#endif
#ifdef ENABLE_DISPMANX
discoverGrabber<DispmanxFrameGrabber>(screenInputs, params);
#endif
#ifdef ENABLE_AMLOGIC
discoverGrabber<AmlogicGrabber>(screenInputs, params);
#endif
#ifdef ENABLE_OSX
discoverGrabber<OsxFrameGrabber>(screenInputs, params);
#endif
return screenInputs;
}

View File

@ -103,7 +103,7 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message)
if (it != effectsSchemas.end())
{
if (!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log))
if (!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log).first)
{
return "Error during arg validation against schema, please consult the Hyperion Log";
}
@ -298,12 +298,12 @@ bool EffectFileHandler::loadEffectDefinition(const QString& path, const QString&
// Read and parse the effect json config file
QJsonObject configEffect;
if (!JsonUtils::readFile(fileName, configEffect, _log)) {
if (!JsonUtils::readFile(fileName, configEffect, _log).first) {
return false;
}
// validate effect config with effect schema(path)
if (!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log)) {
if (!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log).first) {
return false;
}
@ -335,7 +335,7 @@ bool EffectFileHandler::loadEffectSchema(const QString& path, const QString& sch
{
// Read and parse the effect schema file
QJsonObject schemaEffect;
if (!JsonUtils::readFile(schemaFilePath, schemaEffect, _log))
if (!JsonUtils::readFile(schemaFilePath, schemaEffect, _log).first)
{
return false;
}

View File

@ -144,7 +144,7 @@ void EventHandler::handleEvent(Event event)
{
QObject *senderObj = QObject::sender();
QString senderObjectClass;
if (senderObj)
if (senderObj != nullptr)
{
senderObjectClass = senderObj->metaObject()->className();
} else
@ -179,13 +179,19 @@ void EventHandler::handleEvent(Event event)
break;
case Event::Reload:
emit signalEvent(Event::Reload);
Process::restartHyperion(10);
break;
case Event::Restart:
emit signalEvent(Event::Restart);
Process::restartHyperion(11);
break;
case Event::Quit:
emit signalEvent(Event::Quit);
break;
default:
Error(_log,"Unkonwn Event '%d' received", event);
break;

View File

@ -35,7 +35,7 @@ OsEventHandlerBase::OsEventHandlerBase()
_log = Logger::getInstance("EVENTS-OS");
QCoreApplication* app = QCoreApplication::instance();
if (!qobject_cast<QApplication*>(app))
if (qobject_cast<QApplication*>(app) == nullptr)
{
_isService = true;
}
@ -46,6 +46,7 @@ OsEventHandlerBase::OsEventHandlerBase()
OsEventHandlerBase::~OsEventHandlerBase()
{
quit();
QObject::disconnect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance().data(), &EventHandler::handleEvent);
OsEventHandlerBase::unregisterLockHandler();
@ -130,6 +131,11 @@ void OsEventHandlerBase::lock(bool isLocked)
}
}
void OsEventHandlerBase::quit()
{
emit signalEvent(Event::Quit);
}
#if defined(_WIN32)
OsEventHandlerWindows* OsEventHandlerWindows::getInstance()

View File

@ -15,7 +15,6 @@ AuthManager::AuthManager(QObject *parent, bool readonlyMode)
, _authTable(new AuthTable("", this, readonlyMode))
, _metaTable(new MetaTable(this, readonlyMode))
, _pendingRequests()
, _authRequired(true)
, _timer(new QTimer(this))
, _authBlockTimer(new QTimer(this))
{
@ -36,13 +35,13 @@ AuthManager::AuthManager(QObject *parent, bool readonlyMode)
connect(_authBlockTimer, &QTimer::timeout, this, &AuthManager::checkAuthBlockTimeout);
// init with default user and password
if (!_authTable->userExist("Hyperion"))
if (!_authTable->userExist(hyperion::DEFAULT_USER))
{
_authTable->createUser("Hyperion", "hyperion");
_authTable->createUser(hyperion::DEFAULT_USER, hyperion::DEFAULT_PASSWORD);
}
// update Hyperion user token on startup
_authTable->setUserToken("Hyperion");
_authTable->setUserToken(hyperion::DEFAULT_USER);
}
AuthManager::AuthDefinition AuthManager::createToken(const QString &comment)
@ -201,6 +200,8 @@ QVector<AuthManager::AuthDefinition> AuthManager::getPendingRequests() const
def.comment = entry.comment;
def.id = entry.id;
def.timeoutTime = entry.timeoutTime - QDateTime::currentMSecsSinceEpoch();
def.tan = entry.tan;
def.caller = nullptr;
finalVec.append(def);
}
return finalVec;
@ -208,20 +209,26 @@ QVector<AuthManager::AuthDefinition> AuthManager::getPendingRequests() const
bool AuthManager::renameToken(const QString &id, const QString &comment)
{
if (_authTable->renameToken(id, comment))
if (_authTable->idExist(id))
{
emit tokenChange(getTokenList());
return true;
if (_authTable->renameToken(id, comment))
{
emit tokenChange(getTokenList());
return true;
}
}
return false;
}
bool AuthManager::deleteToken(const QString &id)
{
if (_authTable->deleteToken(id))
if (_authTable->idExist(id))
{
emit tokenChange(getTokenList());
return true;
if (_authTable->deleteToken(id))
{
emit tokenChange(getTokenList());
return true;
}
}
return false;
}
@ -231,9 +238,7 @@ void AuthManager::handleSettingsUpdate(settings::type type, const QJsonDocument
if (type == settings::NETWORK)
{
const QJsonObject &obj = config.object();
_authRequired = obj["apiAuth"].toBool(true);
_localAuthRequired = obj["localApiAuth"].toBool(false);
_localAdminAuthRequired = obj["localAdminAuth"].toBool(true);
}
}

View File

@ -550,6 +550,11 @@ QList<int> Hyperion::getActivePriorities() const
return _muxer->getPriorities();
}
Hyperion::InputsMap Hyperion::getPriorityInfo() const
{
return _muxer->getInputInfo();
}
Hyperion::InputInfo Hyperion::getPriorityInfo(int priority) const
{
return _muxer->getInputInfo(priority);

View File

@ -45,6 +45,11 @@ QVector<QVariantMap> HyperionIManager::getInstanceData() const
return instances;
}
QList<quint8> HyperionIManager::getRunningInstanceIdx() const
{
return _runningInstances.keys();
}
void HyperionIManager::startAll()
{
for(const auto & entry : _instanceTable->getAllInstances(true))

View File

@ -128,6 +128,11 @@ bool PriorityMuxer::hasPriority(int priority) const
return (priority == PriorityMuxer::LOWEST_PRIORITY) ? true : _activeInputs.contains(priority);
}
PriorityMuxer::InputsMap PriorityMuxer::getInputInfo() const
{
return _activeInputs;
}
PriorityMuxer::InputInfo PriorityMuxer::getInputInfo(int priority) const
{
auto elemIt = _activeInputs.constFind(priority);

View File

@ -52,7 +52,7 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly
// get default config
QJsonObject defaultConfig;
if (!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
if (!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log).first)
{
throw std::runtime_error("Failed to read default config");
}

View File

@ -4,26 +4,13 @@
"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
"propertyOrder" : 1
},
"restirctedInternetAccessAPI" :
{
@ -36,7 +23,7 @@
"internetAccessAPI": true
}
},
"propertyOrder" : 3
"propertyOrder" : 2
},
"ipWhitelist" :
{
@ -53,7 +40,7 @@
"restirctedInternetAccessAPI": true
}
},
"propertyOrder" : 4
"propertyOrder" : 3
},
"localApiAuth" :
{
@ -66,15 +53,7 @@
"apiAuth": true
}
},
"propertyOrder" : 5
},
"localAdminAuth" :
{
"type" : "boolean",
"title" : "edt_conf_net_localAdminAuth_title",
"required" : true,
"default" : true,
"propertyOrder" : 5
"propertyOrder" : 4
}
},
"additionalProperties" : false

View File

@ -193,11 +193,17 @@ QJsonObject LedDeviceWrapper::getLedDeviceSchemas()
}
QJsonObject schema;
if(!JsonUtils::parse(schemaPath, data, schema, Logger::getInstance("LEDDEVICE")))
QPair<bool, QStringList> parsingResult = JsonUtils::parse(schemaPath, data, schema, Logger::getInstance("LEDDEVICE"));
if (!parsingResult.first)
{
throw std::runtime_error("ERROR: JSON schema wrong of file: " + item.toStdString());
QStringList errorList = parsingResult.second;
for (const auto& errorMessage : errorList) {
Debug(Logger::getInstance("LEDDEVICE"), "JSON parse error: %s ", QSTRING_CSTR(errorMessage));
}
throw std::runtime_error("ERROR: JSON schema is wrong for file: " + item.toStdString());
}
schemaJson = schema;
schemaJson["title"] = QString("edt_dev_spec_header_title");

View File

@ -8,25 +8,26 @@
#include <QRegularExpression>
#include <QJsonObject>
#include <QJsonParseError>
#include <QStringList>
namespace JsonUtils {
bool readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError)
QPair<bool, QStringList> readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError)
{
QString data;
if(!FileUtils::readFile(path, data, log, ignError))
return false;
{
return qMakePair(false, QStringList(QString("Error reading file: %1").arg(path)));
}
if(!parse(path, data, obj, log))
return false;
return true;
QPair<bool, QStringList> parsingResult = JsonUtils::parse(path, data, obj, log);
return parsingResult;
}
bool readSchema(const QString& path, QJsonObject& obj, Logger* log)
{
QJsonObject schema;
if(!readFile(path, schema, log))
if(!readFile(path, schema, log).first)
return false;
if(!resolveRefs(schema, obj, log))
@ -35,80 +36,89 @@ namespace JsonUtils {
return true;
}
bool parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log)
QPair<bool, QStringList> parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log)
{
QJsonDocument doc;
if(!parse(path, data, doc, log))
return false;
QPair<bool, QStringList> parsingResult = JsonUtils::parse(path, data, doc, log);
obj = doc.object();
return true;
return parsingResult;
}
bool parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log)
QPair<bool, QStringList> parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log)
{
QJsonDocument doc;
if(!parse(path, data, doc, log))
return false;
QPair<bool, QStringList> parsingResult = JsonUtils::parse(path, data, doc, log);
arr = doc.array();
return true;
return parsingResult;
}
bool parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log)
QPair<bool, QStringList> parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log)
{
//remove Comments in data
QString cleanData = data;
QStringList errorList;
QJsonParseError error;
doc = QJsonDocument::fromJson(cleanData.toUtf8(), &error);
doc = QJsonDocument::fromJson(data.toUtf8(), &error);
if (error.error != QJsonParseError::NoError)
{
// report to the user the failure and their locations in the document.
int errorLine(0), errorColumn(0);
int errorLine = 1;
int errorColumn = 1;
for( int i=0, count=qMin( error.offset,cleanData.size()); i<count; ++i )
int lastNewlineIndex = data.lastIndexOf("\n", error.offset - 1);
if (lastNewlineIndex != -1)
{
++errorColumn;
if(data.at(i) == '\n' )
{
errorColumn = 0;
++errorLine;
}
errorColumn = error.offset - lastNewlineIndex ;
}
Error(log, "Failed to parse json data from %s: Error: %s at Line: %i, Column: %i, Data: '%s'", QSTRING_CSTR(path), QSTRING_CSTR(error.errorString()), errorLine, errorColumn, QSTRING_CSTR(data));
return false;
errorLine += data.left(error.offset).count('\n');
const QString errorMessage = QString("JSON parse error: %1, line: %2, column: %3, Data: '%4'")
.arg(error.errorString())
.arg(errorLine)
.arg(errorColumn)
.arg(data);
errorList.push_back(errorMessage);
Error(log, "%s", QSTRING_CSTR(errorMessage));
return qMakePair(false, errorList);
}
return true;
return qMakePair(true, errorList);
}
bool validate(const QString& file, const QJsonObject& json, const QString& schemaPath, Logger* log)
QPair<bool, QStringList> validate(const QString& file, const QJsonObject& json, const QString& schemaPath, Logger* log)
{
// get the schema data
QJsonObject schema;
if(!readFile(schemaPath, schema, log))
return false;
if(!validate(file, json, schema, log))
return false;
return true;
QPair<bool, QStringList> readResult = readFile(schemaPath, schema, log);
if(!readResult.first)
{
return readResult;
}
QPair<bool, QStringList> validationResult = validate(file, json, schema, log);
return validationResult;
}
bool validate(const QString& file, const QJsonObject& json, const QJsonObject& schema, Logger* log)
QPair<bool, QStringList> validate(const QString& file, const QJsonObject& json, const QJsonObject& schema, Logger* log)
{
QStringList errorList;
QJsonSchemaChecker schemaChecker;
schemaChecker.setSchema(schema);
if (!schemaChecker.validate(json).first)
{
const QStringList & errors = schemaChecker.getMessages();
for (auto & error : errors)
const QStringList &errors = schemaChecker.getMessages();
for (const auto& error : errors)
{
Error(log, "While validating schema against json data of '%s':%s", QSTRING_CSTR(file), QSTRING_CSTR(error));
QString errorMessage = QString("JSON parse error: %1")
.arg(error);
errorList.push_back(errorMessage);
Error(log, "%s", QSTRING_CSTR(errorMessage));
}
return false;
return qMakePair(false, errorList);
}
return true;
return qMakePair(true, errorList);
}
bool write(const QString& filename, const QJsonObject& json, Logger* log)

View File

@ -1,13 +1,15 @@
#include <utils/NetOrigin.h>
#include <QJsonObject>
#include <QNetworkInterface>
NetOrigin* NetOrigin::instance = nullptr;
NetOrigin::NetOrigin(QObject* parent, Logger* log)
: QObject(parent)
, _log(log)
, _internetAccessAllowed(false)
, _isInternetAccessAllowed(false)
, _isInternetAccessRestricted(false)
, _ipWhitelist()
{
NetOrigin::instance = this;
@ -15,37 +17,73 @@ NetOrigin::NetOrigin(QObject* parent, Logger* log)
bool NetOrigin::accessAllowed(const QHostAddress& address, const QHostAddress& local) const
{
if(_internetAccessAllowed)
return true;
bool isAllowed {false};
if(_ipWhitelist.contains(address)) // v4 and v6
return true;
if(!isLocalAddress(address, local))
if(isLocalAddress(address, local))
{
Warning(_log,"Client connection with IP address '%s' has been rejected! It's not whitelisted, access denied.",QSTRING_CSTR(address.toString()));
return false;
isAllowed = true;
}
return true;
else
{
if(_isInternetAccessAllowed)
{
if (!_isInternetAccessRestricted)
{
isAllowed = true;
}
else
{
for (const QHostAddress &listAddress : _ipWhitelist)
{
if (address.isEqual(listAddress))
{
isAllowed = true;
break;
}
}
WarningIf(!isAllowed, _log,"Client connection from IP address '%s' has been rejected! It's not whitelisted.",QSTRING_CSTR(address.toString()));
}
}
}
return isAllowed;
}
bool NetOrigin::isLocalAddress(const QHostAddress& address, const QHostAddress& local) const
bool NetOrigin::isLocalAddress(const QHostAddress& ipAddress, const QHostAddress& /*local*/) const
{
if(address.protocol() == QAbstractSocket::IPv4Protocol)
QHostAddress address = ipAddress;
if (address.isLoopback() || address.isLinkLocal())
{
if(!address.isInSubnet(local, 24)) // 255.255.255.xxx; IPv4 0-32
{
return false;
return true;
}
//Convert to IPv4 to check, if an IPv6 address is an IPv4 mapped address
QHostAddress ipv4Address(address.toIPv4Address());
if (ipv4Address != QHostAddress::AnyIPv4) // ipv4Address is not "0.0.0.0"
{
address = ipv4Address;
}
QList<QNetworkInterface> allInterfaces = QNetworkInterface::allInterfaces();
for (const QNetworkInterface &networkInterface : allInterfaces) {
QList<QNetworkAddressEntry> addressEntries = networkInterface.addressEntries();
for (const QNetworkAddressEntry &localNetworkAddressEntry : addressEntries) {
QHostAddress localIP = localNetworkAddressEntry.ip();
if(localIP.protocol() != QAbstractSocket::NetworkLayerProtocol::IPv4Protocol)
{
continue;
}
bool isInSubnet = address.isInSubnet(localIP, localNetworkAddressEntry.prefixLength());
if (isInSubnet)
{
return true;
}
}
}
else if(address.protocol() == QAbstractSocket::IPv6Protocol)
{
if(!address.isInSubnet(local, 64)) // 2001:db8:abcd:0012:XXXX:XXXX:XXXX:XXXX; IPv6 0-128
{
return false;
}
}
return true;
return false;
}
void NetOrigin::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
@ -53,16 +91,19 @@ void NetOrigin::handleSettingsUpdate(settings::type type, const QJsonDocument& c
if(type == settings::NETWORK)
{
const QJsonObject& obj = config.object();
_internetAccessAllowed = obj["internetAccessAPI"].toBool(false);
_isInternetAccessAllowed = obj["internetAccessAPI"].toBool(false);
_isInternetAccessRestricted = obj["restirctedInternetAccessAPI"].toBool(false);
const QJsonArray arr = obj["ipWhitelist"].toArray();
const QJsonArray& arr = obj["ipWhitelist"].toArray();
_ipWhitelist.clear();
_ipWhitelist.clear();
for(const auto& e : arr)
for(const auto& item : std::as_const(arr))
{
const QString& entry = e.toString("");
const QString& entry = item.toString("");
if(entry.isEmpty())
{
continue;
}
QHostAddress host(entry);
if(host.isNull())

View File

@ -125,7 +125,7 @@ void JsonConnection::setEffect(const QString &effectName, const QString & effect
if (effectArgs.size() > 0)
{
QJsonObject effObj;
if(!JsonUtils::parse("hyperion-remote-args", effectArgs, effObj, _log))
if(!JsonUtils::parse("hyperion-remote-args", effectArgs, effObj, _log).first)
{
throw std::runtime_error("Error in effect arguments, abort");
}
@ -160,7 +160,7 @@ void JsonConnection::createEffect(const QString &effectName, const QString &effe
if (effectArgs.size() > 0)
{
QJsonObject effObj;
if(!JsonUtils::parse("hyperion-remote-args", effectScript, effObj, _log))
if(!JsonUtils::parse("hyperion-remote-args", effectScript, effObj, _log).first)
{
throw std::runtime_error("Error in effect arguments, abort");
}
@ -440,7 +440,7 @@ void JsonConnection::setConfig(const QString &jsonString)
if (jsonString.size() > 0)
{
QJsonObject configObj;
if(!JsonUtils::parse("hyperion-remote-args", jsonString, configObj, _log))
if(!JsonUtils::parse("hyperion-remote-args", jsonString, configObj, _log).first)
{
throw std::runtime_error("Error in configSet arguments, abort");
}

View File

@ -231,7 +231,7 @@ int main(int argc, char** argv)
ScreenshotHandler handler("screenshot.png", signalDetectionOffset);
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
grabber.prepare();
grabber.prepare();
grabber.start();
QCoreApplication::exec();
grabber.stop();

View File

@ -84,10 +84,8 @@ void SysTray::createTrayIcon()
restartAction = new QAction(tr("&Restart"), this);
restartAction->setIcon(QPixmap(":/restart.svg"));
connect(restartAction, &QAction::triggered, this , [=](){ Process::restartHyperion(12); });
connect(restartAction, &QAction::triggered, this , [=](){ emit signalEvent(Event::Restart); });
// TODO: Check if can be done with SystemEvents
suspendAction = new QAction(tr("&Suspend"), this);
suspendAction->setIcon(QPixmap(":/suspend.svg"));
connect(suspendAction, &QAction::triggered, this, [this]() { emit signalEvent(Event::Suspend); });
@ -129,7 +127,9 @@ void SysTray::createTrayIcon()
// add seperator if custom effects exists
if (!_trayIconEfxMenu->isEmpty())
{
_trayIconEfxMenu->addSeparator();
}
// build in effects
for (const auto &efx : efxs)