mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
acdf733936
* Creating Audio Grabber Creating Audio Grabber Creating Audio Grabber. Successfully began capturing audio in windows. Starting to implement a hard-coded UV Visualizer. Got Windows DirectSound Implementation working. Hardcoded basic VU Meter. Begin working on linux audio grabber implementation. Finished Linux Draft Implementation. Minor Mods to windows implementation. Windows: - Free memory used by device id. - Prevent starting audio if the grabber is disabled - More debug logging Linux: - Prevent starting audio if the grabber is disabled Added strings to english Removed "custom" from device selection Made hard-coded visualizer values configurable. wrote values to imageData with BGR priority to enable configurable values to be set in RGB format. created logic to support "Automatic" to enable the API to select the default device. Add language key for audio in "Remote Control" section. Removed audio configuration for number of channels. This was causing an error with some devices. Fixed logic to update capture while its active. Optimizing code . UI Tweaks Destructuring. Fixed build error on linux. Custom Effects - Clean-ups and Enhancements (#1163) * Cleanup EffectFileHandler * Support Custom Effect Schemas and align EffectFileHandler * Change back to colon prefix for system effects * WebSockets - Fix error in handling fragmented frames * Correct missing colon updates * Update json with image file location for custom gif effects * Image effect deletion - considere full filename is stored in JSON * Correct selection lists indentions Creating Audio Grabber Creating Audio Grabber Creating Audio Grabber. Successfully began capturing audio in windows. Starting to implement a hard-coded UV Visualizer. Got Windows DirectSound Implementation working. Hardcoded basic VU Meter. Begin working on linux audio grabber implementation. Finished Linux Draft Implementation. Minor Mods to windows implementation. Windows: - Free memory used by device id. - Prevent starting audio if the grabber is disabled - More debug logging Linux: - Prevent starting audio if the grabber is disabled Added strings to english Removed "custom" from device selection Made hard-coded visualizer values configurable. wrote values to imageData with BGR priority to enable configurable values to be set in RGB format. created logic to support "Automatic" to enable the API to select the default device. Add language key for audio in "Remote Control" section. Removed audio configuration for number of channels. This was causing an error with some devices. Fixed logic to update capture while its active. Optimizing code . UI Tweaks Destructuring. Fixed build error on linux. Commented setVideoMode from AudioGrabber. Linux Threading changes. Implementing new API Continuing to implement audio into new APIs Fixed Audio Grabber for DirectSound on Windows Fixed UI for Audio Grabber Configuration Default AUDIO to off unless specified. fixed missing #ifdef for audio grabber. Added logic to calculate a dynamic multiplier from the signal input. Updating linux api for discovering devices. Fixed HTML/JS issues with view. Fixed NPE in Windows. Disabled setting thread priority in linux. updated the schema options check to pass through hidden states and commented the change. Updated grabber start conditions Updated Audio grabber to instantiate similar to video grabber Updated windows grabber to set "started" flag to false when shutting down. Removed "tryStart" to prevent enabling audio capture unnecessarily. Fixing instance audio grabber device configuration Added configurable resolution Reduced tolerance to 5% Fixed issue where grabber failed for additional instances when "start" was called multiple times. Fixed resolution calculation Change averaging algorithm to prevent overflowing the sum. Updated logic to stop audio grabber when disabled. Fix integer casting and rounding. Restart grabber on configuration change. Fix missing include/grabber/AudioGrabber. Disable tolerance. Added configurable tolerance. Fixed tolerance algorithm. reset multiplier on configuration change. Line Endings Proposed change and questions/request to fix implementing more of LordGrey's suggestions. Fix mode for snd_pcm_open. Latest ALSA uses SND_PCM_NONBLOCK instead of SND_PCM_OPEN_NONBLOCK defaulted multiplier to 0 "auto" defaulted tolerance to 20% changed 100 to 100.0 for pixel value percentage calculation to fix value from being 0. missed a 100 as a double so precision isn't lost during math operation. Fix Windows grabber and further cleanups Enable Audio grabbing in standard build Remove empty methods Fix audio capture priority setting Remove unused code Clean-up default config Allow additional json-editor attributes Allow multiple effects and resetting to defaults Correct default values Allow to build for Qt < 5.14 Update CodeQL build dependency Update build dependencies Remove effect1 placeholder * Renamed uvMeter to VU Meter (Volume Unit) - Fixed issues flagged by code scanning bot. * Moved stop call into destructor of implementing class. * Removed commented linux audio channel configuration logic. --------- Co-authored-by: Michael Rochelle <michael@j2inn.com>
2063 lines
58 KiB
C++
2063 lines
58 KiB
C++
// project includes
|
|
#include <api/JsonAPI.h>
|
|
|
|
// Qt includes
|
|
#include <QResource>
|
|
#include <QDateTime>
|
|
#include <QImage>
|
|
#include <QBuffer>
|
|
#include <QByteArray>
|
|
#include <QTimer>
|
|
#include <QHostInfo>
|
|
#include <QMultiMap>
|
|
|
|
// hyperion includes
|
|
#include <leddevice/LedDeviceWrapper.h>
|
|
#include <leddevice/LedDevice.h>
|
|
#include <leddevice/LedDeviceFactory.h>
|
|
|
|
#include <HyperionConfig.h> // Required to determine the cmake options
|
|
|
|
#include <hyperion/GrabberWrapper.h>
|
|
#include <grabber/QtGrabber.h>
|
|
|
|
#include <utils/WeakConnect.h>
|
|
|
|
#if defined(ENABLE_MF)
|
|
#include <grabber/MFGrabber.h>
|
|
#elif defined(ENABLE_V4L2)
|
|
#include <grabber/V4L2Grabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_AUDIO)
|
|
#include <grabber/AudioGrabber.h>
|
|
|
|
#ifdef WIN32
|
|
#include <grabber/AudioGrabberWindows.h>
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
#include <grabber/AudioGrabberLinux.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(ENABLE_X11)
|
|
#include <grabber/X11Grabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_XCB)
|
|
#include <grabber/XcbGrabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_DX)
|
|
#include <grabber/DirectXGrabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_FB)
|
|
#include <grabber/FramebufferFrameGrabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_DISPMANX)
|
|
#include <grabber/DispmanxFrameGrabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_AMLOGIC)
|
|
#include <grabber/AmlogicGrabber.h>
|
|
#endif
|
|
|
|
#if defined(ENABLE_OSX)
|
|
#include <grabber/OsxFrameGrabber.h>
|
|
#endif
|
|
|
|
#include <utils/jsonschema/QJsonFactory.h>
|
|
#include <utils/jsonschema/QJsonSchemaChecker.h>
|
|
#include <HyperionConfig.h>
|
|
#include <utils/SysInfo.h>
|
|
#include <utils/ColorSys.h>
|
|
#include <utils/Process.h>
|
|
#include <utils/JsonUtils.h>
|
|
|
|
// ledmapping int <> string transform methods
|
|
#include <hyperion/ImageProcessor.h>
|
|
|
|
// api includes
|
|
#include <api/JsonCB.h>
|
|
|
|
// auth manager
|
|
#include <hyperion/AuthManager.h>
|
|
|
|
#ifdef ENABLE_MDNS
|
|
// mDNS discover
|
|
#include <mdns/MdnsBrowser.h>
|
|
#include <mdns/MdnsServiceRegister.h>
|
|
#else
|
|
// ssdp discover
|
|
#include <ssdp/SSDPDiscover.h>
|
|
#endif
|
|
|
|
using namespace hyperion;
|
|
|
|
// Constants
|
|
namespace { const bool verbose = false; }
|
|
|
|
JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject *parent, bool noListener)
|
|
: API(log, localConnection, parent)
|
|
{
|
|
_noListener = noListener;
|
|
_peerAddress = peerAddress;
|
|
_jsonCB = new JsonCB(this);
|
|
_streaming_logging_activated = false;
|
|
_ledStreamTimer = new QTimer(this);
|
|
|
|
Q_INIT_RESOURCE(JSONRPC_schemas);
|
|
}
|
|
|
|
void JsonAPI::initialize()
|
|
{
|
|
// init API, REQUIRED!
|
|
API::init();
|
|
|
|
// setup auth interface
|
|
connect(this, &API::onPendingTokenRequest, this, &JsonAPI::newPendingTokenRequest);
|
|
connect(this, &API::onTokenResponse, this, &JsonAPI::handleTokenResponse);
|
|
|
|
// listen for killed instances
|
|
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange);
|
|
|
|
// pipe callbacks from subscriptions to parent
|
|
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
|
|
|
// notify hyperion about a jsonMessageForward
|
|
if (_hyperion != nullptr)
|
|
{
|
|
// Initialise jsonCB with current instance
|
|
_jsonCB->setSubscriptionsTo(_hyperion);
|
|
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
|
}
|
|
|
|
//notify instance manager on suspend/resume/idle requests
|
|
connect(this, &JsonAPI::suspendAll, _instanceManager, &HyperionIManager::triggerSuspend);
|
|
connect(this, &JsonAPI::toggleSuspendAll, _instanceManager, &HyperionIManager::triggerToggleSuspend);
|
|
connect(this, &JsonAPI::idleAll, _instanceManager, &HyperionIManager::triggerIdle);
|
|
connect(this, &JsonAPI::toggleIdleAll, _instanceManager, &HyperionIManager::triggerToggleIdle);
|
|
}
|
|
|
|
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
|
|
{
|
|
if (API::setHyperionInstance(inst))
|
|
{
|
|
Debug(_log, "Client '%s' switch to Hyperion instance %d", QSTRING_CSTR(_peerAddress), inst);
|
|
// the JsonCB creates json messages you can subscribe to e.g. data change events
|
|
_jsonCB->setSubscriptionsTo(_hyperion);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void JsonAPI::handleMessage(const QString &messageString, const QString &httpAuthHeader)
|
|
{
|
|
const QString ident = "JsonRpc@" + _peerAddress;
|
|
QJsonObject message;
|
|
|
|
// parse the message
|
|
if (!JsonUtils::parse(ident, messageString, message, _log))
|
|
{
|
|
sendErrorReply("Errors during message parsing, please consult the Hyperion Log.");
|
|
return;
|
|
}
|
|
|
|
int tan = 0;
|
|
if (message.value("tan") != QJsonValue::Undefined)
|
|
tan = message["tan"].toInt();
|
|
|
|
// check basic message
|
|
if (!JsonUtils::validate(ident, message, ":schema", _log))
|
|
{
|
|
sendErrorReply("Errors during message validation, please consult the Hyperion Log.", "" /*command*/, tan);
|
|
return;
|
|
}
|
|
|
|
// check specific message
|
|
const QString command = message["command"].toString();
|
|
if (!JsonUtils::validate(ident, message, QString(":schema-%1").arg(command), _log))
|
|
{
|
|
sendErrorReply("Errors during specific message validation, please consult the Hyperion Log", command, tan);
|
|
return;
|
|
}
|
|
|
|
// client auth before everything else but not for http
|
|
if (!_noListener && command == "authorize")
|
|
{
|
|
handleAuthorizeCommand(message, command, tan);
|
|
return;
|
|
}
|
|
|
|
// check auth state
|
|
if (!API::isAuthorized())
|
|
{
|
|
// on the fly auth available for http from http Auth header
|
|
if (_noListener)
|
|
{
|
|
QString cToken = httpAuthHeader.mid(5).trimmed();
|
|
if (API::isTokenAuthorized(cToken))
|
|
goto proceed;
|
|
}
|
|
sendErrorReply("No Authorization", command, tan);
|
|
return;
|
|
}
|
|
proceed:
|
|
if (_hyperion == nullptr)
|
|
{
|
|
sendErrorReply("Service Unavailable", command, tan);
|
|
return;
|
|
}
|
|
|
|
// switch over all possible commands and handle them
|
|
if (command == "color")
|
|
handleColorCommand(message, command, tan);
|
|
else if (command == "image")
|
|
handleImageCommand(message, command, tan);
|
|
#if defined(ENABLE_EFFECTENGINE)
|
|
else if (command == "effect")
|
|
handleEffectCommand(message, command, tan);
|
|
else if (command == "create-effect")
|
|
handleCreateEffectCommand(message, command, tan);
|
|
else if (command == "delete-effect")
|
|
handleDeleteEffectCommand(message, command, tan);
|
|
#endif
|
|
else if (command == "sysinfo")
|
|
handleSysInfoCommand(message, command, tan);
|
|
else if (command == "serverinfo")
|
|
handleServerInfoCommand(message, command, tan);
|
|
else if (command == "clear")
|
|
handleClearCommand(message, command, tan);
|
|
else if (command == "adjustment")
|
|
handleAdjustmentCommand(message, command, tan);
|
|
else if (command == "sourceselect")
|
|
handleSourceSelectCommand(message, command, tan);
|
|
else if (command == "config")
|
|
handleConfigCommand(message, command, tan);
|
|
else if (command == "componentstate")
|
|
handleComponentStateCommand(message, command, tan);
|
|
else if (command == "ledcolors")
|
|
handleLedColorsCommand(message, command, tan);
|
|
else if (command == "logging")
|
|
handleLoggingCommand(message, command, tan);
|
|
else if (command == "processing")
|
|
handleProcessingCommand(message, command, tan);
|
|
else if (command == "videomode")
|
|
handleVideoModeCommand(message, command, tan);
|
|
else if (command == "instance")
|
|
handleInstanceCommand(message, command, tan);
|
|
else if (command == "leddevice")
|
|
handleLedDeviceCommand(message, command, tan);
|
|
else if (command == "inputsource")
|
|
handleInputSourceCommand(message, command, tan);
|
|
else if (command == "service")
|
|
handleServiceCommand(message, command, tan);
|
|
else if (command == "system")
|
|
handleSystemCommand(message, command, tan);
|
|
|
|
// BEGIN | The following commands are deprecated but used to ensure backward compatibility with hyperion Classic remote control
|
|
else if (command == "clearall")
|
|
handleClearallCommand(message, command, tan);
|
|
else if (command == "transform" || command == "correction" || command == "temperature")
|
|
sendErrorReply("The command " + command + "is deprecated, please use the Hyperion Web Interface to configure", command, tan);
|
|
// END
|
|
|
|
// handle not implemented commands
|
|
else
|
|
handleNotImplemented(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleColorCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
emit forwardJsonMessage(message);
|
|
int priority = message["priority"].toInt();
|
|
int duration = message["duration"].toInt(-1);
|
|
const QString origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress;
|
|
|
|
const QJsonArray &jsonColor = message["color"].toArray();
|
|
std::vector<uint8_t> colors;
|
|
// TODO faster copy
|
|
for (const auto &entry : jsonColor)
|
|
{
|
|
colors.emplace_back(uint8_t(entry.toInt()));
|
|
}
|
|
|
|
API::setColor(priority, colors, duration, origin);
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleImageCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
emit forwardJsonMessage(message);
|
|
|
|
API::ImageCmdData idata;
|
|
idata.priority = message["priority"].toInt();
|
|
idata.origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress;
|
|
idata.duration = message["duration"].toInt(-1);
|
|
idata.width = message["imagewidth"].toInt();
|
|
idata.height = message["imageheight"].toInt();
|
|
idata.scale = message["scale"].toInt(-1);
|
|
idata.format = message["format"].toString();
|
|
idata.imgName = message["name"].toString("");
|
|
idata.data = QByteArray::fromBase64(QByteArray(message["imagedata"].toString().toUtf8()));
|
|
QString replyMsg;
|
|
|
|
if (!API::setImage(idata, COMP_IMAGE, replyMsg))
|
|
{
|
|
sendErrorReply(replyMsg, command, tan);
|
|
return;
|
|
}
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
#if defined(ENABLE_EFFECTENGINE)
|
|
void JsonAPI::handleEffectCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
emit forwardJsonMessage(message);
|
|
|
|
EffectCmdData dat;
|
|
dat.priority = message["priority"].toInt();
|
|
dat.duration = message["duration"].toInt(-1);
|
|
dat.pythonScript = message["pythonScript"].toString();
|
|
dat.origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress;
|
|
dat.effectName = message["effect"].toObject()["name"].toString();
|
|
dat.data = message["imageData"].toString("").toUtf8();
|
|
dat.args = message["effect"].toObject()["args"].toObject();
|
|
|
|
if (API::setEffect(dat))
|
|
sendSuccessReply(command, tan);
|
|
else
|
|
sendErrorReply("Effect '" + dat.effectName + "' not found", command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleCreateEffectCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
const QString resultMsg = API::saveEffect(message);
|
|
resultMsg.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(resultMsg, command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleDeleteEffectCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
const QString res = API::deleteEffect(message["name"].toString());
|
|
res.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(res, command, tan);
|
|
}
|
|
#endif
|
|
|
|
void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, int tan)
|
|
{
|
|
// create result
|
|
QJsonObject result;
|
|
QJsonObject info;
|
|
result["success"] = true;
|
|
result["command"] = command;
|
|
result["tan"] = tan;
|
|
|
|
SysInfo::HyperionSysInfo data = SysInfo::get();
|
|
QJsonObject system;
|
|
system["kernelType"] = data.kernelType;
|
|
system["kernelVersion"] = data.kernelVersion;
|
|
system["architecture"] = data.architecture;
|
|
system["cpuModelName"] = data.cpuModelName;
|
|
system["cpuModelType"] = data.cpuModelType;
|
|
system["cpuHardware"] = data.cpuHardware;
|
|
system["cpuRevision"] = data.cpuRevision;
|
|
system["wordSize"] = data.wordSize;
|
|
system["productType"] = data.productType;
|
|
system["productVersion"] = data.productVersion;
|
|
system["prettyName"] = data.prettyName;
|
|
system["hostName"] = data.hostName;
|
|
system["domainName"] = data.domainName;
|
|
system["isUserAdmin"] = data.isUserAdmin;
|
|
system["qtVersion"] = data.qtVersion;
|
|
#if defined(ENABLE_EFFECTENGINE)
|
|
system["pyVersion"] = data.pyVersion;
|
|
#endif
|
|
info["system"] = system;
|
|
|
|
QJsonObject hyperion;
|
|
hyperion["version"] = QString(HYPERION_VERSION);
|
|
hyperion["build"] = QString(HYPERION_BUILD_ID);
|
|
hyperion["gitremote"] = QString(HYPERION_GIT_REMOTE);
|
|
hyperion["time"] = QString(__DATE__ " " __TIME__);
|
|
hyperion["id"] = _authManager->getID();
|
|
hyperion["rootPath"] = _instanceManager->getRootPath();
|
|
hyperion["readOnlyMode"] = _hyperion->getReadOnlyMode();
|
|
|
|
info["hyperion"] = hyperion;
|
|
|
|
// send the result
|
|
result["info"] = info;
|
|
emit callbackMessage(result);
|
|
}
|
|
|
|
void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
QJsonObject info;
|
|
|
|
// collect priority information
|
|
QJsonArray priorities;
|
|
uint64_t now = QDateTime::currentMSecsSinceEpoch();
|
|
QList<int> activePriorities = _hyperion->getActivePriorities();
|
|
activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY);
|
|
int currentPriority = _hyperion->getCurrentPriority();
|
|
|
|
for(int priority : qAsConst(activePriorities))
|
|
{
|
|
const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(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;
|
|
}
|
|
|
|
(priority == currentPriority)
|
|
? priorities.prepend(item)
|
|
: priorities.append(item);
|
|
}
|
|
|
|
info["priorities"] = priorities;
|
|
info["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled();
|
|
|
|
// collect adjustment information
|
|
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);
|
|
}
|
|
|
|
info["adjustment"] = adjustmentArray;
|
|
|
|
#if defined(ENABLE_EFFECTENGINE)
|
|
// collect effect info
|
|
QJsonArray 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;
|
|
effects.append(effect);
|
|
}
|
|
|
|
info["effects"] = effects;
|
|
#endif
|
|
|
|
// get available led devices
|
|
QJsonObject ledDevices;
|
|
QJsonArray availableLedDevices;
|
|
for (auto dev : LedDeviceWrapper::getDeviceMap())
|
|
{
|
|
availableLedDevices.append(dev.first);
|
|
}
|
|
|
|
ledDevices["available"] = availableLedDevices;
|
|
info["ledDevices"] = ledDevices;
|
|
|
|
QJsonObject grabbers;
|
|
// SCREEN
|
|
QJsonObject screenGrabbers;
|
|
if (GrabberWrapper::getInstance() != nullptr)
|
|
{
|
|
QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex(), GrabberTypeFilter::SCREEN);
|
|
QJsonArray activeGrabberNames;
|
|
for (auto grabberName : activeGrabbers)
|
|
{
|
|
activeGrabberNames.append(grabberName);
|
|
}
|
|
|
|
screenGrabbers["active"] = activeGrabberNames;
|
|
}
|
|
QJsonArray availableScreenGrabbers;
|
|
for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::SCREEN))
|
|
{
|
|
availableScreenGrabbers.append(grabber);
|
|
}
|
|
screenGrabbers["available"] = availableScreenGrabbers;
|
|
|
|
// VIDEO
|
|
QJsonObject videoGrabbers;
|
|
if (GrabberWrapper::getInstance() != nullptr)
|
|
{
|
|
QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex(), GrabberTypeFilter::VIDEO);
|
|
QJsonArray activeGrabberNames;
|
|
for (auto grabberName : activeGrabbers)
|
|
{
|
|
activeGrabberNames.append(grabberName);
|
|
}
|
|
|
|
videoGrabbers["active"] = activeGrabberNames;
|
|
}
|
|
QJsonArray availableVideoGrabbers;
|
|
for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::VIDEO))
|
|
{
|
|
availableVideoGrabbers.append(grabber);
|
|
}
|
|
videoGrabbers["available"] = availableVideoGrabbers;
|
|
|
|
// AUDIO
|
|
QJsonObject audioGrabbers;
|
|
if (GrabberWrapper::getInstance() != nullptr)
|
|
{
|
|
QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex(), GrabberTypeFilter::AUDIO);
|
|
|
|
QJsonArray activeGrabberNames;
|
|
for (auto grabberName : activeGrabbers)
|
|
{
|
|
activeGrabberNames.append(grabberName);
|
|
}
|
|
|
|
audioGrabbers["active"] = activeGrabberNames;
|
|
}
|
|
QJsonArray availableAudioGrabbers;
|
|
for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::AUDIO))
|
|
{
|
|
availableAudioGrabbers.append(grabber);
|
|
}
|
|
audioGrabbers["available"] = availableAudioGrabbers;
|
|
|
|
grabbers.insert("screen", screenGrabbers);
|
|
grabbers.insert("video", videoGrabbers);
|
|
grabbers.insert("audio", audioGrabbers);
|
|
|
|
info["grabbers"] = grabbers;
|
|
|
|
info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode()));
|
|
|
|
QJsonObject cecInfo;
|
|
#if defined(ENABLE_CEC)
|
|
cecInfo["enabled"] = true;
|
|
#else
|
|
cecInfo["enabled"] = false;
|
|
#endif
|
|
info["cec"] = cecInfo;
|
|
|
|
// 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 (!availableScreenGrabbers.isEmpty() || !availableVideoGrabbers.isEmpty() || services.contains("flatbuffer") || services.contains("protobuffer"))
|
|
{
|
|
services.append("borderdetection");
|
|
}
|
|
|
|
info["services"] = services;
|
|
|
|
// 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);
|
|
}
|
|
|
|
info["components"] = component;
|
|
info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType());
|
|
|
|
// add instance info
|
|
QJsonArray instanceInfo;
|
|
for (const auto &entry : API::getAllInstanceData())
|
|
{
|
|
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);
|
|
}
|
|
info["instance"] = instanceInfo;
|
|
|
|
// add leds configs
|
|
info["leds"] = _hyperion->getSetting(settings::LEDS).array();
|
|
|
|
// BEGIN | The following entries are deprecated but used to ensure backward compatibility with hyperion Classic remote control
|
|
// TODO Output the real transformation information instead of default
|
|
|
|
// HOST NAME
|
|
info["hostname"] = QHostInfo::localHostName();
|
|
|
|
// TRANSFORM INFORMATION (DEFAULT VALUES)
|
|
QJsonArray transformArray;
|
|
for (const QString &transformId : _hyperion->getAdjustmentIds())
|
|
{
|
|
QJsonObject transform;
|
|
QJsonArray blacklevel, whitelevel, gamma, 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);
|
|
}
|
|
info["transform"] = transformArray;
|
|
|
|
#if defined(ENABLE_EFFECTENGINE)
|
|
// ACTIVE EFFECT INFO
|
|
QJsonArray activeEffects;
|
|
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);
|
|
}
|
|
}
|
|
info["activeEffects"] = activeEffects;
|
|
#endif
|
|
|
|
// 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, 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 Value", HSLValue);
|
|
|
|
activeLedColors.append(LEDcolor);
|
|
}
|
|
}
|
|
info["activeLedColor"] = activeLedColors;
|
|
|
|
// END
|
|
|
|
sendSuccessDataReply(QJsonDocument(info), command, tan);
|
|
|
|
// AFTER we send the info, the client might want to subscribe to future updates
|
|
if (message.contains("subscribe"))
|
|
{
|
|
// check if listeners are allowed
|
|
if (_noListener)
|
|
return;
|
|
|
|
QJsonArray subsArr = message["subscribe"].toArray();
|
|
// catch the all keyword and build a list of all cmds
|
|
if (subsArr.contains("all"))
|
|
{
|
|
subsArr = QJsonArray();
|
|
for (const auto& entry : _jsonCB->getCommands())
|
|
{
|
|
subsArr.append(entry);
|
|
}
|
|
}
|
|
|
|
for (const QJsonValueRef entry : subsArr)
|
|
{
|
|
// config callbacks just if auth is set
|
|
if ((entry == "settings-update" || entry == "token-update") && !API::isAdminAuthorized())
|
|
continue;
|
|
// silent failure if a subscribe type is not found
|
|
_jsonCB->subscribeFor(entry.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleClearCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
emit forwardJsonMessage(message);
|
|
int priority = message["priority"].toInt();
|
|
QString replyMsg;
|
|
|
|
if (!API::clearPriority(priority, replyMsg))
|
|
{
|
|
sendErrorReply(replyMsg, command, tan);
|
|
return;
|
|
}
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleClearallCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
emit forwardJsonMessage(message);
|
|
QString replyMsg;
|
|
API::clearPriority(-1, replyMsg);
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
const QJsonObject &adjustment = message["adjustment"].toObject();
|
|
|
|
const QString adjustmentId = adjustment["id"].toString(_hyperion->getAdjustmentIds().first());
|
|
ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId);
|
|
if (colorAdjustment == nullptr)
|
|
{
|
|
Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str());
|
|
return;
|
|
}
|
|
|
|
if (adjustment.contains("red"))
|
|
{
|
|
const QJsonArray &values = adjustment["red"].toArray();
|
|
colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
|
|
if (adjustment.contains("green"))
|
|
{
|
|
const QJsonArray &values = adjustment["green"].toArray();
|
|
colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
|
|
if (adjustment.contains("blue"))
|
|
{
|
|
const QJsonArray &values = adjustment["blue"].toArray();
|
|
colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
if (adjustment.contains("cyan"))
|
|
{
|
|
const QJsonArray &values = adjustment["cyan"].toArray();
|
|
colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
if (adjustment.contains("magenta"))
|
|
{
|
|
const QJsonArray &values = adjustment["magenta"].toArray();
|
|
colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
if (adjustment.contains("yellow"))
|
|
{
|
|
const QJsonArray &values = adjustment["yellow"].toArray();
|
|
colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
if (adjustment.contains("white"))
|
|
{
|
|
const QJsonArray &values = adjustment["white"].toArray();
|
|
colorAdjustment->_rgbWhiteAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
|
|
}
|
|
|
|
if (adjustment.contains("gammaRed"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setGamma(adjustment["gammaRed"].toDouble(), colorAdjustment->_rgbTransform.getGammaG(), colorAdjustment->_rgbTransform.getGammaB());
|
|
}
|
|
if (adjustment.contains("gammaGreen"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setGamma(colorAdjustment->_rgbTransform.getGammaR(), adjustment["gammaGreen"].toDouble(), colorAdjustment->_rgbTransform.getGammaB());
|
|
}
|
|
if (adjustment.contains("gammaBlue"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setGamma(colorAdjustment->_rgbTransform.getGammaR(), colorAdjustment->_rgbTransform.getGammaG(), adjustment["gammaBlue"].toDouble());
|
|
}
|
|
|
|
if (adjustment.contains("backlightThreshold"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setBacklightThreshold(adjustment["backlightThreshold"].toDouble());
|
|
}
|
|
if (adjustment.contains("backlightColored"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setBacklightColored(adjustment["backlightColored"].toBool());
|
|
}
|
|
if (adjustment.contains("brightness"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setBrightness(adjustment["brightness"].toInt());
|
|
}
|
|
if (adjustment.contains("brightnessCompensation"))
|
|
{
|
|
colorAdjustment->_rgbTransform.setBrightnessCompensation(adjustment["brightnessCompensation"].toInt());
|
|
}
|
|
|
|
if (adjustment.contains("saturationGain"))
|
|
{
|
|
colorAdjustment->_okhsvTransform.setSaturationGain(adjustment["saturationGain"].toDouble());
|
|
}
|
|
|
|
if (adjustment.contains("brightnessGain"))
|
|
{
|
|
colorAdjustment->_okhsvTransform.setBrightnessGain(adjustment["brightnessGain"].toDouble());
|
|
}
|
|
|
|
// commit the changes
|
|
_hyperion->adjustmentsUpdated();
|
|
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleSourceSelectCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
if (message.contains("auto"))
|
|
{
|
|
API::setSourceAutoSelect(message["auto"].toBool(false));
|
|
}
|
|
else if (message.contains("priority"))
|
|
{
|
|
API::setVisiblePriority(message["priority"].toInt());
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("Priority request is invalid", command, tan);
|
|
return;
|
|
}
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
QString subcommand = message["subcommand"].toString("");
|
|
QString full_command = command + "-" + subcommand;
|
|
|
|
if (subcommand == "getschema")
|
|
{
|
|
handleSchemaGetCommand(message, full_command, tan);
|
|
}
|
|
else if (subcommand == "getconfig")
|
|
{
|
|
if (_adminAuthorized)
|
|
sendSuccessDataReply(QJsonDocument(_hyperion->getQJsonConfig()), full_command, tan);
|
|
else
|
|
sendErrorReply("No Authorization", command, tan);
|
|
}
|
|
else if (subcommand == "setconfig")
|
|
{
|
|
if (_adminAuthorized)
|
|
handleConfigSetCommand(message, full_command, tan);
|
|
else
|
|
sendErrorReply("No Authorization", command, tan);
|
|
}
|
|
else if (subcommand == "restoreconfig")
|
|
{
|
|
if (_adminAuthorized)
|
|
handleConfigRestoreCommand(message, full_command, tan);
|
|
else
|
|
sendErrorReply("No Authorization", command, tan);
|
|
}
|
|
else if (subcommand == "reload")
|
|
{
|
|
if (_adminAuthorized)
|
|
{
|
|
Debug(_log, "Restarting due to RPC command");
|
|
|
|
Process::restartHyperion(10);
|
|
|
|
sendSuccessReply(command + "-" + subcommand, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("No Authorization", command, tan);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("unknown or missing subcommand", full_command, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleConfigSetCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
if (message.contains("config"))
|
|
{
|
|
QJsonObject config = message["config"].toObject();
|
|
if (API::isHyperionEnabled())
|
|
{
|
|
if ( API::saveSettings(config) )
|
|
{
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("Save settings failed", command, tan);
|
|
}
|
|
}
|
|
else
|
|
sendErrorReply("Saving configuration while Hyperion is disabled isn't possible", command, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleConfigRestoreCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
if (message.contains("config"))
|
|
{
|
|
QJsonObject config = message["config"].toObject();
|
|
if (API::isHyperionEnabled())
|
|
{
|
|
if ( API::restoreSettings(config) )
|
|
{
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("Restore settings failed", command, tan);
|
|
}
|
|
}
|
|
else
|
|
sendErrorReply("Restoring configuration while Hyperion is disabled isn't possible", command, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleSchemaGetCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
// create result
|
|
QJsonObject schemaJson, alldevices, properties;
|
|
|
|
// make sure the resources are loaded (they may be left out after static linking)
|
|
Q_INIT_RESOURCE(resource);
|
|
|
|
// read the hyperion json schema from the resource
|
|
QString schemaFile = ":/hyperion-schema";
|
|
|
|
try
|
|
{
|
|
schemaJson = QJsonFactory::readSchema(schemaFile);
|
|
}
|
|
catch (const std::runtime_error &error)
|
|
{
|
|
throw std::runtime_error(error.what());
|
|
}
|
|
|
|
// collect all LED Devices
|
|
properties = schemaJson["properties"].toObject();
|
|
alldevices = LedDeviceWrapper::getLedDeviceSchemas();
|
|
properties.insert("alldevices", alldevices);
|
|
|
|
#if defined(ENABLE_EFFECTENGINE)
|
|
// collect all available effect schemas
|
|
QJsonArray schemaList;
|
|
const std::list<EffectSchema>& effectsSchemas = _hyperion->getEffectSchemas();
|
|
for (const EffectSchema& effectSchema : effectsSchemas)
|
|
{
|
|
QJsonObject schema;
|
|
schema.insert("script", effectSchema.pyFile);
|
|
schema.insert("schemaLocation", effectSchema.schemaFile);
|
|
schema.insert("schemaContent", effectSchema.pySchema);
|
|
if (effectSchema.pyFile.startsWith(':'))
|
|
{
|
|
schema.insert("type", "system");
|
|
}
|
|
else
|
|
{
|
|
schema.insert("type", "custom");
|
|
}
|
|
schemaList.append(schema);
|
|
}
|
|
properties.insert("effectSchemas", schemaList);
|
|
#endif
|
|
|
|
schemaJson.insert("properties", properties);
|
|
|
|
// send the result
|
|
sendSuccessDataReply(QJsonDocument(schemaJson), command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleComponentStateCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
const QJsonObject &componentState = message["componentstate"].toObject();
|
|
QString comp = componentState["component"].toString("invalid");
|
|
bool compState = componentState["state"].toBool(true);
|
|
QString replyMsg;
|
|
|
|
if (!API::setComponentState(comp, compState, replyMsg))
|
|
{
|
|
sendErrorReply(replyMsg, command, tan);
|
|
return;
|
|
}
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
// create result
|
|
QString subcommand = message["subcommand"].toString("");
|
|
|
|
// max 20 Hz (50ms) interval for streaming (default: 10 Hz (100ms))
|
|
qint64 streaming_interval = qMax(message["interval"].toInt(100), 50);
|
|
|
|
if (subcommand == "ledstream-start")
|
|
{
|
|
_streaming_leds_reply["success"] = true;
|
|
_streaming_leds_reply["command"] = command + "-ledstream-update";
|
|
_streaming_leds_reply["tan"] = tan;
|
|
|
|
connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector<ColorRgb> &ledValues) {
|
|
_currentLedValues = ledValues;
|
|
|
|
// necessary because Qt::UniqueConnection for lambdas does not work until 5.9
|
|
// see: https://bugreports.qt.io/browse/QTBUG-52438
|
|
if (!_ledStreamConnection)
|
|
_ledStreamConnection = connect(_ledStreamTimer, &QTimer::timeout, this, [=]() {
|
|
emit streamLedcolorsUpdate(_currentLedValues);
|
|
},
|
|
Qt::UniqueConnection);
|
|
|
|
// start the timer
|
|
if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval)
|
|
_ledStreamTimer->start(streaming_interval);
|
|
},
|
|
Qt::UniqueConnection);
|
|
// push once
|
|
_hyperion->update();
|
|
}
|
|
else if (subcommand == "ledstream-stop")
|
|
{
|
|
disconnect(_hyperion, &Hyperion::rawLedColors, this, 0);
|
|
_ledStreamTimer->stop();
|
|
disconnect(_ledStreamConnection);
|
|
}
|
|
else if (subcommand == "imagestream-start")
|
|
{
|
|
_streaming_image_reply["success"] = true;
|
|
_streaming_image_reply["command"] = command + "-imagestream-update";
|
|
_streaming_image_reply["tan"] = tan;
|
|
|
|
connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection);
|
|
}
|
|
else if (subcommand == "imagestream-stop")
|
|
{
|
|
disconnect(_hyperion, &Hyperion::currentImage, this, 0);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
sendSuccessReply(command + "-" + subcommand, tan);
|
|
}
|
|
|
|
void JsonAPI::handleLoggingCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
// create result
|
|
QString subcommand = message["subcommand"].toString("");
|
|
|
|
if (API::isAdminAuthorized())
|
|
{
|
|
_streaming_logging_reply["success"] = true;
|
|
_streaming_logging_reply["command"] = command;
|
|
_streaming_logging_reply["tan"] = tan;
|
|
|
|
if (subcommand == "start")
|
|
{
|
|
if (!_streaming_logging_activated)
|
|
{
|
|
_streaming_logging_reply["command"] = command + "-update";
|
|
connect(LoggerManager::getInstance(), &LoggerManager::newLogMessage, this, &JsonAPI::incommingLogMessage);
|
|
|
|
emit incommingLogMessage (Logger::T_LOG_MESSAGE{}); // needed to trigger log sending
|
|
Debug(_log, "log streaming activated for client %s", _peerAddress.toStdString().c_str());
|
|
}
|
|
}
|
|
else if (subcommand == "stop")
|
|
{
|
|
if (_streaming_logging_activated)
|
|
{
|
|
disconnect(LoggerManager::getInstance(), &LoggerManager::newLogMessage, this, &JsonAPI::incommingLogMessage);
|
|
_streaming_logging_activated = false;
|
|
Debug(_log, "log streaming deactivated for client %s", _peerAddress.toStdString().c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
sendSuccessReply(command + "-" + subcommand, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("No Authorization", command + "-" + subcommand, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleProcessingCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
API::setLedMappingType(ImageProcessor::mappingTypeToInt(message["mappingType"].toString("multicolor_mean")));
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleVideoModeCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
API::setVideoMode(parse3DMode(message["videoMode"].toString("2D")));
|
|
sendSuccessReply(command, tan);
|
|
}
|
|
|
|
void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
const QString &subc = message["subcommand"].toString().trimmed();
|
|
const QString &id = message["id"].toString().trimmed();
|
|
const QString &password = message["password"].toString().trimmed();
|
|
const QString &newPassword = message["newPassword"].toString().trimmed();
|
|
const QString &comment = message["comment"].toString().trimmed();
|
|
|
|
// catch test if auth is required
|
|
if (subc == "tokenRequired")
|
|
{
|
|
QJsonObject req;
|
|
req["required"] = !API::isAuthorized();
|
|
|
|
sendSuccessDataReply(QJsonDocument(req), command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// catch test if admin auth is required
|
|
if (subc == "adminRequired")
|
|
{
|
|
QJsonObject req;
|
|
req["adminRequired"] = !API::isAdminAuthorized();
|
|
sendSuccessDataReply(QJsonDocument(req), command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// default hyperion password is a security risk, replace it asap
|
|
if (subc == "newPasswordRequired")
|
|
{
|
|
QJsonObject req;
|
|
req["newPasswordRequired"] = API::hasHyperionDefaultPw();
|
|
sendSuccessDataReply(QJsonDocument(req), command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// catch logout
|
|
if (subc == "logout")
|
|
{
|
|
// disconnect all kind of data callbacks
|
|
JsonAPI::stopDataConnections(); // TODO move to API
|
|
API::logout();
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// change password
|
|
if (subc == "newPassword")
|
|
{
|
|
// use password, newPassword
|
|
if (API::isAdminAuthorized())
|
|
{
|
|
if (API::updateHyperionPassword(password, newPassword))
|
|
{
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
sendErrorReply("Failed to update user password", command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// token created from ui
|
|
if (subc == "createToken")
|
|
{
|
|
// use comment
|
|
// for user authorized sessions
|
|
AuthManager::AuthDefinition def;
|
|
const QString createTokenResult = API::createToken(comment, def);
|
|
if (createTokenResult.isEmpty())
|
|
{
|
|
QJsonObject newTok;
|
|
newTok["comment"] = def.comment;
|
|
newTok["id"] = def.id;
|
|
newTok["token"] = def.token;
|
|
|
|
sendSuccessDataReply(QJsonDocument(newTok), command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
sendErrorReply(createTokenResult, command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// rename Token
|
|
if (subc == "renameToken")
|
|
{
|
|
// use id/comment
|
|
const QString renameTokenResult = API::renameToken(id, comment);
|
|
if (renameTokenResult.isEmpty())
|
|
{
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
sendErrorReply(renameTokenResult, command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// delete token
|
|
if (subc == "deleteToken")
|
|
{
|
|
// use id
|
|
const QString deleteTokenResult = API::deleteToken(id);
|
|
if (deleteTokenResult.isEmpty())
|
|
{
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
sendErrorReply(deleteTokenResult, command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// catch token request
|
|
if (subc == "requestToken")
|
|
{
|
|
// use id/comment
|
|
const bool &acc = message["accept"].toBool(true);
|
|
if (acc)
|
|
API::setNewTokenRequest(comment, id, tan);
|
|
else
|
|
API::cancelNewTokenRequest(comment, id);
|
|
// client should wait for answer
|
|
return;
|
|
}
|
|
|
|
// get pending token requests
|
|
if (subc == "getPendingTokenRequests")
|
|
{
|
|
QVector<AuthManager::AuthDefinition> vec;
|
|
if (API::getPendingTokenRequests(vec))
|
|
{
|
|
QJsonArray arr;
|
|
for (const auto &entry : qAsConst(vec))
|
|
{
|
|
QJsonObject obj;
|
|
obj["comment"] = entry.comment;
|
|
obj["id"] = entry.id;
|
|
obj["timeout"] = int(entry.timeoutTime);
|
|
arr.append(obj);
|
|
}
|
|
sendSuccessDataReply(QJsonDocument(arr), command + "-" + subc, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// accept/deny token request
|
|
if (subc == "answerRequest")
|
|
{
|
|
// use id
|
|
const bool &accept = message["accept"].toBool(false);
|
|
if (!API::handlePendingTokenRequest(id, accept))
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// get token list
|
|
if (subc == "getTokenList")
|
|
{
|
|
QVector<AuthManager::AuthDefinition> defVect;
|
|
if (API::getTokenList(defVect))
|
|
{
|
|
QJsonArray tArr;
|
|
for (const auto &entry : defVect)
|
|
{
|
|
QJsonObject subO;
|
|
subO["comment"] = entry.comment;
|
|
subO["id"] = entry.id;
|
|
subO["last_use"] = entry.lastUse;
|
|
|
|
tArr.append(subO);
|
|
}
|
|
sendSuccessDataReply(QJsonDocument(tArr), command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// login
|
|
if (subc == "login")
|
|
{
|
|
const QString &token = message["token"].toString().trimmed();
|
|
|
|
// catch token
|
|
if (!token.isEmpty())
|
|
{
|
|
// userToken is longer
|
|
if (token.size() > 36)
|
|
{
|
|
if (API::isUserTokenAuthorized(token))
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
else
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
|
|
return;
|
|
}
|
|
// usual app token is 36
|
|
if (token.size() == 36)
|
|
{
|
|
if (API::isTokenAuthorized(token))
|
|
{
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// password
|
|
// use password
|
|
if (password.size() >= 8)
|
|
{
|
|
QString userTokenRep;
|
|
if (API::isUserAuthorized(password) && API::getUserToken(userTokenRep))
|
|
{
|
|
// Return the current valid Hyperion user token
|
|
QJsonObject obj;
|
|
obj["token"] = userTokenRep;
|
|
sendSuccessDataReply(QJsonDocument(obj), command + "-" + subc, tan);
|
|
}
|
|
else
|
|
sendErrorReply("No Authorization", command + "-" + subc, tan);
|
|
}
|
|
else
|
|
sendErrorReply("Password too short", command + "-" + subc, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
const QString &subc = message["subcommand"].toString();
|
|
const quint8 &inst = static_cast<quint8>(message["instance"].toInt());
|
|
const QString &name = message["name"].toString();
|
|
|
|
if (subc == "switchTo")
|
|
{
|
|
if (handleInstanceSwitch(inst))
|
|
{
|
|
QJsonObject obj;
|
|
obj["instance"] = inst;
|
|
sendSuccessDataReply(QJsonDocument(obj), command + "-" + subc, tan);
|
|
}
|
|
else
|
|
sendErrorReply("Selected Hyperion instance isn't running", command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
if (subc == "startInstance")
|
|
{
|
|
//Only send update once
|
|
weakConnect(this, &API::onStartInstanceResponse, [this, command, subc] (int tan)
|
|
{
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
});
|
|
|
|
if (!API::startInstance(inst, tan))
|
|
sendErrorReply("Can't start Hyperion instance index " + QString::number(inst), command + "-" + subc, tan);
|
|
|
|
return;
|
|
}
|
|
|
|
if (subc == "stopInstance")
|
|
{
|
|
// silent fail
|
|
API::stopInstance(inst);
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
if (subc == "deleteInstance")
|
|
{
|
|
QString replyMsg;
|
|
if (API::deleteInstance(inst, replyMsg))
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
else
|
|
sendErrorReply(replyMsg, command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
// create and save name requires name
|
|
if (name.isEmpty())
|
|
sendErrorReply("Name string required for this command", command + "-" + subc, tan);
|
|
|
|
if (subc == "createInstance")
|
|
{
|
|
QString replyMsg = API::createInstance(name);
|
|
if (replyMsg.isEmpty())
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
else
|
|
sendErrorReply(replyMsg, command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
|
|
if (subc == "saveName")
|
|
{
|
|
QString replyMsg = API::setInstanceName(inst, name);
|
|
if (replyMsg.isEmpty())
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
else
|
|
sendErrorReply(replyMsg, command + "-" + subc, tan);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
|
|
|
const QString &subc = message["subcommand"].toString().trimmed();
|
|
const QString &devType = message["ledDeviceType"].toString().trimmed();
|
|
|
|
QString full_command = command + "-" + subc;
|
|
|
|
// TODO: Validate that device type is a valid one
|
|
|
|
{
|
|
QJsonObject config;
|
|
config.insert("type", devType);
|
|
LedDevice* ledDevice = nullptr;
|
|
|
|
if (subc == "discover")
|
|
{
|
|
ledDevice = LedDeviceFactory::construct(config);
|
|
const QJsonObject ¶ms = message["params"].toObject();
|
|
const QJsonObject devicesDiscovered = ledDevice->discover(params);
|
|
|
|
Debug(_log, "response: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
|
|
|
sendSuccessDataReply(QJsonDocument(devicesDiscovered), full_command, tan);
|
|
}
|
|
else if (subc == "getProperties")
|
|
{
|
|
ledDevice = LedDeviceFactory::construct(config);
|
|
const QJsonObject ¶ms = message["params"].toObject();
|
|
const QJsonObject deviceProperties = ledDevice->getProperties(params);
|
|
|
|
Debug(_log, "response: [%s]", QString(QJsonDocument(deviceProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
|
|
|
sendSuccessDataReply(QJsonDocument(deviceProperties), full_command, tan);
|
|
}
|
|
else if (subc == "identify")
|
|
{
|
|
ledDevice = LedDeviceFactory::construct(config);
|
|
const QJsonObject ¶ms = message["params"].toObject();
|
|
ledDevice->identify(params);
|
|
|
|
sendSuccessReply(full_command, tan);
|
|
}
|
|
else if (subc == "addAuthorization")
|
|
{
|
|
ledDevice = LedDeviceFactory::construct(config);
|
|
const QJsonObject& params = message["params"].toObject();
|
|
const QJsonObject response = ledDevice->addAuthorization(params);
|
|
|
|
Debug(_log, "response: [%s]", QString(QJsonDocument(response).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
|
|
|
sendSuccessDataReply(QJsonDocument(response), full_command, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("Unknown or missing subcommand", full_command, tan);
|
|
}
|
|
|
|
delete ledDevice;
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString& command, int tan)
|
|
{
|
|
DebugIf(verbose, _log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
|
|
|
const QString& subc = message["subcommand"].toString().trimmed();
|
|
const QString& sourceType = message["sourceType"].toString().trimmed();
|
|
|
|
QString full_command = command + "-" + subc;
|
|
|
|
// TODO: Validate that source type is a valid one
|
|
{
|
|
if (subc == "discover")
|
|
{
|
|
QJsonObject inputSourcesDiscovered;
|
|
inputSourcesDiscovered.insert("sourceType", sourceType);
|
|
QJsonArray videoInputs;
|
|
QJsonArray audioInputs;
|
|
|
|
#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
|
|
|
|
if (sourceType == "video" )
|
|
{
|
|
#if defined(ENABLE_MF)
|
|
MFGrabber* grabber = new MFGrabber();
|
|
#elif defined(ENABLE_V4L2)
|
|
V4L2Grabber* grabber = new V4L2Grabber();
|
|
#endif
|
|
QJsonObject params;
|
|
videoInputs = grabber->discover(params);
|
|
delete grabber;
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined(ENABLE_AUDIO)
|
|
if (sourceType == "audio")
|
|
{
|
|
AudioGrabber* grabber;
|
|
#ifdef WIN32
|
|
grabber = new AudioGrabberWindows();
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
grabber = new AudioGrabberLinux();
|
|
#endif
|
|
QJsonObject params;
|
|
audioInputs = grabber->discover(params);
|
|
delete grabber;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
DebugIf(verbose, _log, "sourceType: [%s]", QSTRING_CSTR(sourceType));
|
|
|
|
if (sourceType == "screen")
|
|
{
|
|
QJsonObject params;
|
|
|
|
QJsonObject device;
|
|
#ifdef ENABLE_QT
|
|
QtGrabber* qtgrabber = new QtGrabber();
|
|
device = qtgrabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete qtgrabber;
|
|
#endif
|
|
|
|
#ifdef ENABLE_DX
|
|
DirectXGrabber* dxgrabber = new DirectXGrabber();
|
|
device = dxgrabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete dxgrabber;
|
|
#endif
|
|
|
|
#ifdef ENABLE_X11
|
|
X11Grabber* x11Grabber = new X11Grabber();
|
|
device = x11Grabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete x11Grabber;
|
|
#endif
|
|
|
|
#ifdef ENABLE_XCB
|
|
XcbGrabber* xcbGrabber = new XcbGrabber();
|
|
device = xcbGrabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete xcbGrabber;
|
|
#endif
|
|
|
|
//Ignore FB for Amlogic, as it is embedded in the Amlogic grabber itself
|
|
#if defined(ENABLE_FB) && !defined(ENABLE_AMLOGIC)
|
|
|
|
FramebufferFrameGrabber* fbGrabber = new FramebufferFrameGrabber();
|
|
device = fbGrabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete fbGrabber;
|
|
#endif
|
|
|
|
#if defined(ENABLE_DISPMANX)
|
|
DispmanxFrameGrabber* dispmanx = new DispmanxFrameGrabber();
|
|
if (dispmanx->isAvailable())
|
|
{
|
|
device = dispmanx->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
}
|
|
delete dispmanx;
|
|
#endif
|
|
|
|
#if defined(ENABLE_AMLOGIC)
|
|
AmlogicGrabber* amlGrabber = new AmlogicGrabber();
|
|
device = amlGrabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete amlGrabber;
|
|
#endif
|
|
|
|
#if defined(ENABLE_OSX)
|
|
OsxFrameGrabber* osxGrabber = new OsxFrameGrabber();
|
|
device = osxGrabber->discover(params);
|
|
if (!device.isEmpty() )
|
|
{
|
|
videoInputs.append(device);
|
|
}
|
|
delete osxGrabber;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
inputSourcesDiscovered["video_sources"] = videoInputs;
|
|
inputSourcesDiscovered["audio_sources"] = audioInputs;
|
|
|
|
DebugIf(verbose, _log, "response: [%s]", QString(QJsonDocument(inputSourcesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
|
|
|
sendSuccessDataReply(QJsonDocument(inputSourcesDiscovered), full_command, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("Unknown or missing subcommand", full_command, tan);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleServiceCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
DebugIf(verbose, _log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
|
|
|
const QString &subc = message["subcommand"].toString().trimmed();
|
|
const QString type = message["serviceType"].toString().trimmed();
|
|
|
|
QString full_command = command + "-" + subc;
|
|
|
|
if (subc == "discover")
|
|
{
|
|
QByteArray serviceType;
|
|
|
|
QJsonObject servicesDiscovered;
|
|
QJsonObject servicesOfType;
|
|
QJsonArray serviceList;
|
|
|
|
#ifdef ENABLE_MDNS
|
|
QString discoveryMethod("mDNS");
|
|
serviceType = MdnsServiceRegister::getServiceType(type);
|
|
#else
|
|
QString discoveryMethod("ssdp");
|
|
#endif
|
|
if (!serviceType.isEmpty())
|
|
{
|
|
#ifdef ENABLE_MDNS
|
|
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
|
|
Qt::QueuedConnection, Q_ARG(QByteArray, serviceType));
|
|
|
|
serviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(serviceType, MdnsServiceRegister::getServiceNameFilter(type), DEFAULT_DISCOVER_TIMEOUT);
|
|
#endif
|
|
servicesOfType.insert(type, serviceList);
|
|
|
|
servicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
|
servicesDiscovered.insert("services", servicesOfType);
|
|
|
|
sendSuccessDataReply(QJsonDocument(servicesDiscovered), full_command, tan);
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply(QString("Discovery of service type [%1] via %2 not supported").arg(type, discoveryMethod), full_command, tan);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendErrorReply("Unknown or missing subcommand", full_command, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleSystemCommand(const QJsonObject &message, const QString &command, int tan)
|
|
{
|
|
DebugIf(verbose, _log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
|
|
|
const QString &subc = message["subcommand"].toString().trimmed();
|
|
|
|
if (subc == "suspend")
|
|
{
|
|
emit suspendAll(true);
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else if (subc == "resume")
|
|
{
|
|
emit suspendAll(false);
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else if (subc == "restart")
|
|
{
|
|
Process::restartHyperion(11);
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else if (subc == "toggleSuspend")
|
|
{
|
|
emit toggleSuspendAll();
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else if (subc == "idle")
|
|
{
|
|
emit idleAll(true);
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else if (subc == "toggleIdle")
|
|
{
|
|
emit toggleIdleAll();
|
|
sendSuccessReply(command + "-" + subc, tan);
|
|
}
|
|
else
|
|
{
|
|
QString full_command = command + "-" + subc;
|
|
sendErrorReply("Unknown or missing subcommand", full_command, tan);
|
|
}
|
|
}
|
|
|
|
void JsonAPI::handleNotImplemented(const QString &command, int tan)
|
|
{
|
|
sendErrorReply("Command not implemented", command, tan);
|
|
}
|
|
|
|
void JsonAPI::sendSuccessReply(const QString &command, int tan)
|
|
{
|
|
// create reply
|
|
QJsonObject reply;
|
|
reply["instance"] = _hyperion->getInstanceIndex();
|
|
reply["success"] = true;
|
|
reply["command"] = command;
|
|
reply["tan"] = tan;
|
|
|
|
// send reply
|
|
emit callbackMessage(reply);
|
|
}
|
|
|
|
void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &command, int tan)
|
|
{
|
|
QJsonObject reply;
|
|
reply["instance"] = _hyperion->getInstanceIndex();
|
|
reply["success"] = true;
|
|
reply["command"] = command;
|
|
reply["tan"] = tan;
|
|
if (doc.isArray())
|
|
reply["info"] = doc.array();
|
|
else
|
|
reply["info"] = doc.object();
|
|
|
|
emit callbackMessage(reply);
|
|
}
|
|
|
|
void JsonAPI::sendErrorReply(const QString &error, const QString &command, int tan)
|
|
{
|
|
// create reply
|
|
QJsonObject reply;
|
|
reply["instance"] = _hyperion->getInstanceIndex();
|
|
reply["success"] = false;
|
|
reply["error"] = error;
|
|
reply["command"] = command;
|
|
reply["tan"] = tan;
|
|
|
|
// send reply
|
|
emit callbackMessage(reply);
|
|
}
|
|
|
|
void JsonAPI::streamLedcolorsUpdate(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;
|
|
_streaming_leds_reply["result"] = result;
|
|
|
|
// send the result
|
|
emit callbackMessage(_streaming_leds_reply);
|
|
}
|
|
|
|
void JsonAPI::setImage(const Image<ColorRgb> &image)
|
|
{
|
|
QImage jpgImage((const uint8_t *)image.memptr(), image.width(), image.height(), 3 * image.width(), QImage::Format_RGB888);
|
|
QByteArray ba;
|
|
QBuffer buffer(&ba);
|
|
buffer.open(QIODevice::WriteOnly);
|
|
jpgImage.save(&buffer, "jpg");
|
|
|
|
QJsonObject result;
|
|
result["image"] = "data:image/jpg;base64," + QString(ba.toBase64());
|
|
_streaming_image_reply["result"] = result;
|
|
emit callbackMessage(_streaming_image_reply);
|
|
}
|
|
|
|
void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg)
|
|
{
|
|
QJsonObject result, message;
|
|
QJsonArray messageArray;
|
|
|
|
if (!_streaming_logging_activated)
|
|
{
|
|
_streaming_logging_activated = true;
|
|
const QList<Logger::T_LOG_MESSAGE> *logBuffer = LoggerManager::getInstance()->getLogMessageBuffer();
|
|
for (int i = 0; i < logBuffer->length(); i++)
|
|
{
|
|
//Only present records of the current log-level
|
|
if ( logBuffer->at(i).level >= _log->getLogLevel())
|
|
{
|
|
message["loggerName"] = logBuffer->at(i).loggerName;
|
|
message["loggerSubName"] = logBuffer->at(i).loggerSubName;
|
|
message["function"] = logBuffer->at(i).function;
|
|
message["line"] = QString::number(logBuffer->at(i).line);
|
|
message["fileName"] = logBuffer->at(i).fileName;
|
|
message["message"] = logBuffer->at(i).message;
|
|
message["levelString"] = logBuffer->at(i).levelString;
|
|
message["utime"] = QString::number(logBuffer->at(i).utime);
|
|
|
|
messageArray.append(message);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
_streaming_logging_reply["result"] = result;
|
|
|
|
// send the result
|
|
emit callbackMessage(_streaming_logging_reply);
|
|
}
|
|
|
|
void JsonAPI::newPendingTokenRequest(const QString &id, const QString &comment)
|
|
{
|
|
QJsonObject obj;
|
|
obj["comment"] = comment;
|
|
obj["id"] = id;
|
|
obj["timeout"] = 180000;
|
|
|
|
sendSuccessDataReply(QJsonDocument(obj), "authorize-tokenRequest", 1);
|
|
}
|
|
|
|
void JsonAPI::handleTokenResponse(bool success, const QString &token, const QString &comment, const QString &id, const int &tan)
|
|
{
|
|
const QString cmd = "authorize-requestToken";
|
|
QJsonObject result;
|
|
result["token"] = token;
|
|
result["comment"] = comment;
|
|
result["id"] = id;
|
|
|
|
if (success)
|
|
sendSuccessDataReply(QJsonDocument(result), cmd, tan);
|
|
else
|
|
sendErrorReply("Token request timeout or denied", cmd, tan);
|
|
}
|
|
|
|
void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name)
|
|
{
|
|
switch (state)
|
|
{
|
|
case InstanceState::H_ON_STOP:
|
|
if (_hyperion->getInstanceIndex() == instance)
|
|
{
|
|
handleInstanceSwitch();
|
|
}
|
|
break;
|
|
|
|
case InstanceState::H_STARTED:
|
|
case InstanceState::H_STOPPED:
|
|
case InstanceState::H_CREATED:
|
|
case InstanceState::H_DELETED:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JsonAPI::stopDataConnections()
|
|
{
|
|
LoggerManager::getInstance()->disconnect();
|
|
_streaming_logging_activated = false;
|
|
_jsonCB->resetSubscriptions();
|
|
// led stream colors
|
|
disconnect(_hyperion, &Hyperion::rawLedColors, this, 0);
|
|
_ledStreamTimer->stop();
|
|
disconnect(_ledStreamConnection);
|
|
}
|