mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Merge remote-tracking branch 'origin/master' into temperture
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
|
||||
|
||||
add_subdirectory(hyperion)
|
||||
add_subdirectory(commandline)
|
||||
@@ -32,19 +32,21 @@ add_subdirectory(db)
|
||||
add_subdirectory(api)
|
||||
add_subdirectory(ssdp)
|
||||
|
||||
if(ENABLE_CEC)
|
||||
add_subdirectory(cec)
|
||||
endif()
|
||||
|
||||
add_subdirectory(events)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
add_subdirectory(mdns)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_EFFECTENGINE)
|
||||
add_subdirectory(effectengine)
|
||||
add_subdirectory(python)
|
||||
endif()
|
||||
|
||||
if(ENABLE_CEC)
|
||||
add_subdirectory(cec)
|
||||
endif()
|
||||
|
||||
if(ENABLE_EXPERIMENTAL)
|
||||
add_subdirectory(experimental)
|
||||
endif()
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
# Define the current source locations
|
||||
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/api)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/api)
|
||||
|
||||
FILE ( GLOB_RECURSE Api_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
set(Api_RESOURCES ${CURRENT_SOURCE_DIR}/JSONRPC_schemas.qrc )
|
||||
|
||||
add_library(hyperion-api
|
||||
${Api_SOURCES}
|
||||
${Api_RESOURCES}
|
||||
${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}/libsrc/api/JsonAPI.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/api/API.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/api/JsonCB.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/api/JSONRPC_schemas.qrc
|
||||
)
|
||||
|
||||
if(ENABLE_DX)
|
||||
include_directories(${DIRECTX9_INCLUDE_DIRS})
|
||||
target_link_libraries(hyperion-api ${DIRECTX9_LIBRARIES})
|
||||
endif(ENABLE_DX)
|
||||
|
||||
target_link_libraries(hyperion-api
|
||||
hyperion
|
||||
hyperion-utils
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
${DIRECTX9_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories(hyperion-api PRIVATE
|
||||
${DIRECTX9_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <QTimer>
|
||||
#include <QHostInfo>
|
||||
#include <QMultiMap>
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
|
||||
// hyperion includes
|
||||
#include <leddevice/LedDeviceWrapper.h>
|
||||
@@ -19,54 +21,55 @@
|
||||
#include <HyperionConfig.h> // Required to determine the cmake options
|
||||
|
||||
#include <hyperion/GrabberWrapper.h>
|
||||
#include <grabber/QtGrabber.h>
|
||||
#include <grabber/qt/QtGrabber.h>
|
||||
|
||||
#include <utils/WeakConnect.h>
|
||||
#include <events/EventEnum.h>
|
||||
|
||||
#if defined(ENABLE_MF)
|
||||
#include <grabber/MFGrabber.h>
|
||||
#include <grabber/video/mediafoundation/MFGrabber.h>
|
||||
#elif defined(ENABLE_V4L2)
|
||||
#include <grabber/V4L2Grabber.h>
|
||||
#include <grabber/video/v4l2/V4L2Grabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_AUDIO)
|
||||
#include <grabber/AudioGrabber.h>
|
||||
#include <grabber/audio/AudioGrabber.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <grabber/AudioGrabberWindows.h>
|
||||
#include <grabber/audio/AudioGrabberWindows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <grabber/AudioGrabberLinux.h>
|
||||
#include <grabber/audio/AudioGrabberLinux.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_X11)
|
||||
#include <grabber/X11Grabber.h>
|
||||
#include <grabber/x11/X11Grabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_XCB)
|
||||
#include <grabber/XcbGrabber.h>
|
||||
#include <grabber/xcb/XcbGrabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_DX)
|
||||
#include <grabber/DirectXGrabber.h>
|
||||
#include <grabber/directx/DirectXGrabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FB)
|
||||
#include <grabber/FramebufferFrameGrabber.h>
|
||||
#include <grabber/framebuffer/FramebufferFrameGrabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_DISPMANX)
|
||||
#include <grabber/DispmanxFrameGrabber.h>
|
||||
#include <grabber/dispmanx/DispmanxFrameGrabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_AMLOGIC)
|
||||
#include <grabber/AmlogicGrabber.h>
|
||||
#include <grabber/amlogic/AmlogicGrabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_OSX)
|
||||
#include <grabber/OsxFrameGrabber.h>
|
||||
#include <grabber/osx/OsxFrameGrabber.h>
|
||||
#endif
|
||||
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
@@ -83,6 +86,7 @@
|
||||
|
||||
// api includes
|
||||
#include <api/JsonCB.h>
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
// auth manager
|
||||
#include <hyperion/AuthManager.h>
|
||||
@@ -111,6 +115,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject
|
||||
_ledStreamTimer = new QTimer(this);
|
||||
|
||||
Q_INIT_RESOURCE(JSONRPC_schemas);
|
||||
|
||||
qRegisterMetaType<Event>("Event");
|
||||
}
|
||||
|
||||
void JsonAPI::initialize()
|
||||
@@ -136,14 +142,13 @@ void JsonAPI::initialize()
|
||||
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);
|
||||
//notify eventhadler on suspend/resume/idle requests
|
||||
connect(this, &JsonAPI::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
|
||||
connect(_ledStreamTimer, &QTimer::timeout, this, &JsonAPI::streamLedColorsUpdate, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
|
||||
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool /*forced*/)
|
||||
{
|
||||
if (API::setHyperionInstance(inst))
|
||||
{
|
||||
@@ -387,6 +392,9 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command,
|
||||
hyperion["rootPath"] = _instanceManager->getRootPath();
|
||||
hyperion["readOnlyMode"] = _hyperion->getReadOnlyMode();
|
||||
|
||||
QCoreApplication* app = QCoreApplication::instance();
|
||||
hyperion["isGuiMode"] = qobject_cast<QApplication*>(app) ? true : false;
|
||||
|
||||
info["hyperion"] = hyperion;
|
||||
|
||||
// send the result
|
||||
@@ -405,7 +413,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY);
|
||||
int currentPriority = _hyperion->getCurrentPriority();
|
||||
|
||||
for(int priority : qAsConst(activePriorities))
|
||||
for(int priority : std::as_const(activePriorities))
|
||||
{
|
||||
const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority);
|
||||
|
||||
@@ -1026,8 +1034,7 @@ void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &com
|
||||
if (_adminAuthorized)
|
||||
{
|
||||
Debug(_log, "Restarting due to RPC command");
|
||||
|
||||
Process::restartHyperion(10);
|
||||
emit signalEvent(Event::Reload);
|
||||
|
||||
sendSuccessReply(command + "-" + subcommand, tan);
|
||||
}
|
||||
@@ -1153,6 +1160,11 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject &message, const QStr
|
||||
sendSuccessReply(command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::streamLedColorsUpdate()
|
||||
{
|
||||
emit streamLedcolorsUpdate(_currentLedValues);
|
||||
}
|
||||
|
||||
void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString &command, int tan)
|
||||
{
|
||||
// create result
|
||||
@@ -1168,21 +1180,21 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString &
|
||||
_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);
|
||||
if (ledValues != _currentLedValues)
|
||||
{
|
||||
_currentLedValues = ledValues;
|
||||
if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval)
|
||||
{
|
||||
_ledStreamTimer->start(streaming_interval);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_ledStreamTimer->stop();
|
||||
}
|
||||
});
|
||||
|
||||
// start the timer
|
||||
if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval)
|
||||
_ledStreamTimer->start(streaming_interval);
|
||||
},
|
||||
Qt::UniqueConnection);
|
||||
// push once
|
||||
_hyperion->update();
|
||||
}
|
||||
@@ -1401,7 +1413,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
if (API::getPendingTokenRequests(vec))
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &entry : qAsConst(vec))
|
||||
for (const auto &entry : std::as_const(vec))
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["comment"] = entry.comment;
|
||||
@@ -1859,32 +1871,37 @@ void JsonAPI::handleSystemCommand(const QJsonObject &message, const QString &com
|
||||
|
||||
if (subc == "suspend")
|
||||
{
|
||||
emit suspendAll(true);
|
||||
emit signalEvent(Event::Suspend);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else if (subc == "resume")
|
||||
{
|
||||
emit suspendAll(false);
|
||||
emit signalEvent(Event::Resume);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else if (subc == "restart")
|
||||
{
|
||||
Process::restartHyperion(11);
|
||||
emit signalEvent(Event::Restart);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else if (subc == "toggleSuspend")
|
||||
{
|
||||
emit toggleSuspendAll();
|
||||
emit signalEvent(Event::ToggleSuspend);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else if (subc == "idle")
|
||||
{
|
||||
emit idleAll(true);
|
||||
emit signalEvent(Event::Idle);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else if (subc == "resumeIdle")
|
||||
{
|
||||
emit signalEvent(Event::ResumeIdle);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else if (subc == "toggleIdle")
|
||||
{
|
||||
emit toggleIdleAll();
|
||||
emit signalEvent(Event::ToggleIdle);
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
}
|
||||
else
|
||||
@@ -1980,24 +1997,10 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg)
|
||||
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);
|
||||
}
|
||||
}
|
||||
QMetaObject::invokeMethod(LoggerManager::getInstance(), "getLogMessageBuffer",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QJsonArray, messageArray),
|
||||
Q_ARG(Logger::LogLevel, _log->getLogLevel()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -199,7 +199,7 @@ void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::Inpu
|
||||
|
||||
activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY);
|
||||
|
||||
for (int priority : qAsConst(activePriorities)) {
|
||||
for (int priority : std::as_const(activePriorities)) {
|
||||
|
||||
const Hyperion::InputInfo& priorityInfo = activeInputs[priority];
|
||||
|
||||
|
||||
@@ -35,12 +35,7 @@ BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent)
|
||||
// listen for component state changes
|
||||
connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest);
|
||||
|
||||
_detector = new BlackBorderDetector(_oldThreshold);
|
||||
}
|
||||
|
||||
BlackBorderProcessor::~BlackBorderProcessor()
|
||||
{
|
||||
delete _detector;
|
||||
_detector = std::make_unique<BlackBorderDetector>(_oldThreshold);
|
||||
}
|
||||
|
||||
void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
@@ -66,10 +61,7 @@ void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJson
|
||||
if (fabs(_oldThreshold - newThreshold) > std::numeric_limits<double>::epsilon())
|
||||
{
|
||||
_oldThreshold = newThreshold;
|
||||
|
||||
delete _detector;
|
||||
|
||||
_detector = new BlackBorderDetector(newThreshold);
|
||||
_detector = std::make_unique<BlackBorderDetector>(_oldThreshold);
|
||||
}
|
||||
|
||||
Debug(Logger::getInstance("BLACKBORDER", "I"+QString::number(_hyperion->getInstanceIndex())), "Set mode to: %s", QSTRING_CSTR(_detectionMode));
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/blackborder)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/blackborder)
|
||||
|
||||
FILE ( GLOB Blackborder_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(blackborder ${Blackborder_SOURCES} )
|
||||
add_library(blackborder
|
||||
${CMAKE_SOURCE_DIR}/include/blackborder/BlackBorderDetector.h
|
||||
${CMAKE_SOURCE_DIR}/include/blackborder/BlackBorderProcessor.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/blackborder/BlackBorderDetector.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/blackborder/BlackBorderProcessor.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(blackborder
|
||||
hyperion-utils
|
||||
|
||||
@@ -63,6 +63,7 @@ void BoblightServer::stop()
|
||||
return;
|
||||
|
||||
qDeleteAll(_openConnections);
|
||||
_openConnections.clear();
|
||||
|
||||
_server->close();
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/boblightserver)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/boblightserver)
|
||||
|
||||
FILE ( GLOB BoblightServer_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(boblightserver ${BoblightServer_SOURCES} )
|
||||
add_library(boblightserver
|
||||
${CMAKE_SOURCE_DIR}/include/boblightserver/BoblightServer.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/boblightserver/BoblightServer.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/boblightserver/BoblightClientConnection.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/boblightserver/BoblightClientConnection.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(boblightserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -4,21 +4,31 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include <libcec/cecloader.h>
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
/* Enable to turn on detailed CEC logs */
|
||||
#define NO_VERBOSE_CEC
|
||||
#define NOVERBOSE_CEC
|
||||
|
||||
CECHandler::CECHandler()
|
||||
CECHandler::CECHandler(const QJsonDocument& config, QObject * parent)
|
||||
: QObject(parent)
|
||||
, _config(config)
|
||||
, _isInitialised(false)
|
||||
, _isOpen(false)
|
||||
, _isEnabled(false)
|
||||
, _buttonReleaseDelayMs(CEC_BUTTON_TIMEOUT)
|
||||
, _buttonRepeatRateMs(0)
|
||||
, _doubleTapTimeoutMs(CEC_DOUBLE_TAP_TIMEOUT_MS)
|
||||
, _cecEventActionMap()
|
||||
{
|
||||
qRegisterMetaType<CECEvent>("CECEvent");
|
||||
qRegisterMetaType<Event>("Event");
|
||||
|
||||
_logger = Logger::getInstance("CEC");
|
||||
_logger = Logger::getInstance("EVENTS-CEC");
|
||||
|
||||
_cecCallbacks = getCallbacks();
|
||||
_cecConfig = getConfig();
|
||||
@@ -28,66 +38,160 @@ CECHandler::CECHandler()
|
||||
|
||||
CECHandler::~CECHandler()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::CECEVENTS)
|
||||
{
|
||||
if (_isInitialised)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
|
||||
_isEnabled = obj["enable"].toBool(false);
|
||||
Debug(_logger, "CEC Event handling is %s", _isEnabled? "enabled" : "disabled");
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
_buttonReleaseDelayMs = obj["buttonReleaseDelayMs"].toInt(CEC_BUTTON_TIMEOUT);
|
||||
_buttonRepeatRateMs = obj["buttonRepeatRateMs"].toInt(0);
|
||||
_doubleTapTimeoutMs = obj["doubleTapTimeoutMs"].toInt(CEC_DOUBLE_TAP_TIMEOUT_MS);
|
||||
|
||||
Debug(_logger, "Remote button press release time : %dms",_buttonReleaseDelayMs);
|
||||
Debug(_logger, "Remote button press repeat rate : %dms",_buttonRepeatRateMs);
|
||||
Debug(_logger, "Remote button press delay before repeating : %dms",_doubleTapTimeoutMs);
|
||||
|
||||
_cecConfig.iButtonReleaseDelayMs = static_cast<uint32_t>(_buttonReleaseDelayMs);
|
||||
_cecConfig.iButtonRepeatRateMs = static_cast<uint32_t>(_buttonRepeatRateMs);
|
||||
_cecConfig.iDoubleTapTimeoutMs = static_cast<uint32_t>(_doubleTapTimeoutMs);
|
||||
|
||||
_cecEventActionMap.clear();
|
||||
const QJsonArray actionItems = obj["actions"].toArray();
|
||||
if (!actionItems.isEmpty())
|
||||
{
|
||||
for (const QJsonValue &item : actionItems)
|
||||
{
|
||||
QString cecEvent = item.toObject().value("event").toString();
|
||||
QString action = item.toObject().value("action").toString();
|
||||
_cecEventActionMap.insert(cecEvent, stringToEvent(action));
|
||||
Debug(_logger, "CEC-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(cecEvent), QSTRING_CSTR(action));
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cecEventActionMap.isEmpty())
|
||||
{
|
||||
enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_logger, "No CEC events to listen to are configured currently.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CECHandler::start()
|
||||
{
|
||||
if (_cecAdapter)
|
||||
return true;
|
||||
|
||||
std::string library = std::string("" CEC_LIBRARY);
|
||||
_cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() : nullptr);
|
||||
if(!_cecAdapter)
|
||||
_isInitialised = false;
|
||||
if (_cecAdapter == nullptr)
|
||||
{
|
||||
Error(_logger, "Failed to loading libcec.so");
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "CEC handler started");
|
||||
|
||||
auto adapters = getAdapters();
|
||||
if (adapters.isEmpty())
|
||||
{
|
||||
Error(_logger, "Failed to find CEC adapter");
|
||||
UnloadLibCec(_cecAdapter);
|
||||
_cecAdapter = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "Auto detecting CEC adapter");
|
||||
bool opened = false;
|
||||
for (const auto & adapter : adapters)
|
||||
{
|
||||
printAdapter(adapter);
|
||||
|
||||
if (!opened && openAdapter(adapter))
|
||||
_cecAdapter = LibCecInitialise(&_cecConfig);
|
||||
if(_cecAdapter == nullptr)
|
||||
{
|
||||
Info(_logger, "CEC Handler initialized with adapter : %s", adapter.strComName);
|
||||
|
||||
opened = true;
|
||||
Error(_logger, "Failed loading libCEC library. CEC is not supported.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_isInitialised = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opened)
|
||||
{
|
||||
UnloadLibCec(_cecAdapter);
|
||||
_cecAdapter = nullptr;
|
||||
handleSettingsUpdate(settings::CECEVENTS,_config);
|
||||
}
|
||||
|
||||
return opened;
|
||||
return _isInitialised;
|
||||
}
|
||||
|
||||
void CECHandler::stop()
|
||||
{
|
||||
if (_cecAdapter)
|
||||
if (_cecAdapter != nullptr)
|
||||
{
|
||||
Info(_logger, "Stopping CEC handler");
|
||||
|
||||
_cecAdapter->Close();
|
||||
UnloadLibCec(_cecAdapter);
|
||||
_cecAdapter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool CECHandler::enable()
|
||||
{
|
||||
if (_isInitialised)
|
||||
{
|
||||
if (!_isOpen)
|
||||
{
|
||||
const auto adapters = getAdapters();
|
||||
if (adapters.isEmpty())
|
||||
{
|
||||
Error(_logger, "Failed to find any CEC adapter. CEC event handling will be disabled.");
|
||||
_cecAdapter->Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "Auto detecting CEC adapter");
|
||||
bool opened {false};
|
||||
for (const auto & adapter : adapters)
|
||||
{
|
||||
printAdapter(adapter);
|
||||
if (!opened)
|
||||
{
|
||||
if (openAdapter(adapter))
|
||||
{
|
||||
|
||||
Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType));
|
||||
opened = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!opened)
|
||||
{
|
||||
Error(_logger, "Could not initialize any CEC adapter.");
|
||||
_cecAdapter->Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
_isOpen=true;
|
||||
QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
#ifdef VERBOSE_CEC
|
||||
std::cout << "Found Devices: " << scan().toStdString() << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (_isOpen && !_cecAdapter->SetConfiguration(&_cecConfig))
|
||||
{
|
||||
Error(_logger, "Failed setting remote button press timing parameters");
|
||||
}
|
||||
|
||||
Info(_logger, "CEC handler enabled");
|
||||
|
||||
}
|
||||
|
||||
return _isOpen;
|
||||
}
|
||||
|
||||
void CECHandler::disable()
|
||||
{
|
||||
if (_isInitialised)
|
||||
{
|
||||
QObject::disconnect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
_cecAdapter->Close();
|
||||
_isOpen=false;
|
||||
Info(_logger, "CEC handler disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +201,6 @@ CECConfig CECHandler::getConfig() const
|
||||
|
||||
const std::string name("HyperionCEC");
|
||||
name.copy(configuration.strDeviceName, std::min(name.size(), sizeof(configuration.strDeviceName)));
|
||||
|
||||
configuration.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_RECORDING_DEVICE);
|
||||
configuration.clientVersion = CEC::LIBCEC_VERSION_CURRENT;
|
||||
|
||||
@@ -121,11 +224,11 @@ CECCallbacks CECHandler::getCallbacks() const
|
||||
|
||||
QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
||||
{
|
||||
if (!_cecAdapter)
|
||||
if (_cecAdapter == nullptr)
|
||||
return {};
|
||||
|
||||
QVector<CECAdapterDescriptor> descriptors(16);
|
||||
int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), descriptors.size(), nullptr, true /*quickscan*/);
|
||||
int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), static_cast<uint8_t>(descriptors.size()), nullptr, true /*quickscan*/);
|
||||
descriptors.resize(size);
|
||||
|
||||
return descriptors;
|
||||
@@ -133,15 +236,14 @@ QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
||||
|
||||
bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
||||
{
|
||||
if (!_cecAdapter)
|
||||
if (_cecAdapter == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_cecAdapter->Open(descriptor.strComName))
|
||||
{
|
||||
Error(_logger, "%s", QSTRING_CSTR(QString("Failed to open the CEC adaper on port %1")
|
||||
.arg(descriptor.strComName))
|
||||
);
|
||||
|
||||
Error(_logger, "CEC adapter '%s', type: %s failed to open.", descriptor.strComName, _cecAdapter->ToString(descriptor.adapterType));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -149,28 +251,44 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
||||
|
||||
void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const
|
||||
{
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("CEC Adapter:")));
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tName : %1").arg(descriptor.strComName)));
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tPath : %1").arg(descriptor.strComPath)));
|
||||
Debug(_logger, "CEC Adapter:");
|
||||
Debug(_logger, "\tName : %s", descriptor.strComName);
|
||||
Debug(_logger, "\tPath : %s", descriptor.strComPath);
|
||||
if (descriptor.iVendorId != 0)
|
||||
{
|
||||
Debug(_logger, "\tVendor id: %04x", descriptor.iVendorId);
|
||||
}
|
||||
if (descriptor.iProductId != 0)
|
||||
{
|
||||
Debug(_logger, "\tProduct id: %04x", descriptor.iProductId);
|
||||
}
|
||||
if (descriptor.iFirmwareVersion != 0)
|
||||
{
|
||||
Debug(_logger, "\tFirmware id: %d", descriptor.iFirmwareVersion);
|
||||
}
|
||||
if (descriptor.adapterType != CEC::ADAPTERTYPE_UNKNOWN)
|
||||
{
|
||||
Debug(_logger, "\tType : %s", _cecAdapter->ToString(descriptor.adapterType));
|
||||
}
|
||||
}
|
||||
|
||||
QString CECHandler::scan() const
|
||||
{
|
||||
if (!_cecAdapter)
|
||||
if (_cecAdapter == nullptr)
|
||||
return {};
|
||||
|
||||
Info(_logger, "Starting CEC scan");
|
||||
|
||||
QJsonArray devices;
|
||||
CECLogicalAddresses addresses = _cecAdapter->GetActiveDevices();
|
||||
for (int address = CEC::CECDEVICE_TV; address <= CEC::CECDEVICE_BROADCAST; ++address)
|
||||
for (uint8_t address = CEC::CECDEVICE_TV; address <= CEC::CECDEVICE_BROADCAST; ++address)
|
||||
{
|
||||
if (addresses[address])
|
||||
if (addresses[address] != 0)
|
||||
{
|
||||
CECLogicalAddress logicalAddress = (CECLogicalAddress)address;
|
||||
CECLogicalAddress logicalAddress = static_cast<CECLogicalAddress>(address);
|
||||
|
||||
QJsonObject device;
|
||||
CECVendorId vendor = (CECVendorId)_cecAdapter->GetDeviceVendorId(logicalAddress);
|
||||
CECVendorId vendor = static_cast<CECVendorId>(_cecAdapter->GetDeviceVendorId(logicalAddress));
|
||||
CECPowerStatus power = _cecAdapter->GetDevicePowerStatus(logicalAddress);
|
||||
|
||||
device["name" ] = _cecAdapter->GetDeviceOSDName(logicalAddress).c_str();
|
||||
@@ -181,46 +299,48 @@ QString CECHandler::scan() const
|
||||
devices << device;
|
||||
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4")
|
||||
.arg(device["name"].toString(),
|
||||
device["vendor"].toString(),
|
||||
device["address"].toString(),
|
||||
device["power"].toString()))
|
||||
);
|
||||
.arg(device["name"].toString(),
|
||||
device["vendor"].toString(),
|
||||
device["address"].toString(),
|
||||
device["power"].toString())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return QJsonDocument(devices).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
||||
void CECHandler::triggerAction(const QString& cecEvent)
|
||||
{
|
||||
Event action = _cecEventActionMap.value(cecEvent, Event::Unknown);
|
||||
if ( action != Event::Unknown )
|
||||
{
|
||||
Debug(_logger, "CEC-Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(cecEvent), eventToString(action) );
|
||||
emit signalEvent(action);
|
||||
}
|
||||
}
|
||||
|
||||
void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
switch (message->level)
|
||||
{
|
||||
case CEC::CEC_LOG_ERROR:
|
||||
Error(handler->_logger, QString("%1")
|
||||
.arg(message->message)
|
||||
.toLocal8Bit());
|
||||
Error(handler->_logger, "%s", message->message);
|
||||
break;
|
||||
case CEC::CEC_LOG_WARNING:
|
||||
Warning(handler->_logger, QString("%1")
|
||||
.arg(message->message)
|
||||
.toLocal8Bit());
|
||||
Warning(handler->_logger, "%s", message->message);
|
||||
break;
|
||||
case CEC::CEC_LOG_TRAFFIC:
|
||||
case CEC::CEC_LOG_NOTICE:
|
||||
Info(handler->_logger, QString("%1")
|
||||
.arg(message->message)
|
||||
.toLocal8Bit());
|
||||
Info(handler->_logger, "%s", message->message);
|
||||
break;
|
||||
case CEC::CEC_LOG_DEBUG:
|
||||
Debug(handler->_logger, QString("%1")
|
||||
.arg(message->message)
|
||||
.toLocal8Bit());
|
||||
Debug(handler->_logger, "%s", message->message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -230,29 +350,36 @@ void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
|
||||
|
||||
void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key)
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
CECAdapter * adapter = handler->_cecAdapter;
|
||||
|
||||
Debug(handler->_logger, QString("CECHandler::onCecKeyPress: %1")
|
||||
.arg(adapter->ToString(key->keycode))
|
||||
.toLocal8Bit());
|
||||
#ifdef VERBOSE_CEC
|
||||
Debug(handler->_logger, "CECHandler::onCecKeyPress: %s", adapter->ToString(key->keycode));
|
||||
#endif
|
||||
switch (key->keycode) {
|
||||
case CEC::CEC_USER_CONTROL_CODE_F1_BLUE:
|
||||
case CEC::CEC_USER_CONTROL_CODE_F2_RED:
|
||||
case CEC::CEC_USER_CONTROL_CODE_F3_GREEN:
|
||||
case CEC::CEC_USER_CONTROL_CODE_F4_YELLOW:
|
||||
handler->triggerAction(adapter->ToString(key->keycode));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter data)
|
||||
void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter /* data */)
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
Error(handler->_logger, QString("CECHandler::onCecAlert: %1")
|
||||
.arg(alert)
|
||||
.toLocal8Bit());
|
||||
Error(handler->_logger, QSTRING_CSTR(QString("CECHandler::onCecAlert: %1")
|
||||
.arg(alert)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -260,12 +387,10 @@ void CECHandler::onCecConfigurationChanged(void * context, const CECConfig * con
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
Debug(handler->_logger, QString("CECHandler::onCecConfigurationChanged: %1")
|
||||
.arg(configuration->strDeviceName)
|
||||
.toLocal8Bit());
|
||||
Debug(handler->_logger, "CECHandler::onCecConfigurationChanged: %s", configuration->strDeviceName);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -278,9 +403,7 @@ int CECHandler::onCecMenuStateChanged(void * context, const CECMenuState state)
|
||||
|
||||
CECAdapter * adapter = handler->_cecAdapter;
|
||||
|
||||
Debug(handler->_logger, QString("CECHandler::onCecMenuStateChanged: %1")
|
||||
.arg(adapter->ToString(state))
|
||||
.toLocal8Bit());
|
||||
Debug(handler->_logger, "CECHandler::onCecMenuStateChanged: %s", adapter->ToString(state));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -294,28 +417,25 @@ void CECHandler::onCecCommandReceived(void * context, const CECCommand * command
|
||||
CECAdapter * adapter = handler->_cecAdapter;
|
||||
|
||||
#ifdef VERBOSE_CEC
|
||||
Debug(handler->_logger, QString("CECHandler::onCecCommandReceived: %1 (%2 > %3)")
|
||||
.arg(adapter->ToString(command->opcode))
|
||||
.arg(adapter->ToString(command->initiator))
|
||||
.arg(adapter->ToString(command->destination))
|
||||
.toLocal8Bit());
|
||||
Debug(handler->_logger, "CECHandler::onCecCommandReceived: %s %s > %s)",
|
||||
adapter->ToString(command->opcode),
|
||||
adapter->ToString(command->initiator),
|
||||
adapter->ToString(command->destination)
|
||||
);
|
||||
#endif
|
||||
/* We do NOT check sender */
|
||||
// if (address == CEC::CECDEVICE_TV)
|
||||
//if (address == CEC::CECDEVICE_TV)
|
||||
{
|
||||
if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH)
|
||||
switch (command->opcode) {
|
||||
case CEC::CEC_OPCODE_STANDBY:
|
||||
case CEC::CEC_OPCODE_SET_STREAM_PATH:
|
||||
{
|
||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1")
|
||||
.arg(adapter->ToString(command->initiator)))
|
||||
);
|
||||
emit handler->cecEvent(CECEvent::On);
|
||||
handler->triggerAction(adapter->ToString(command->opcode));
|
||||
}
|
||||
if (command->opcode == CEC::CEC_OPCODE_STANDBY)
|
||||
{
|
||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source deactivated: %1")
|
||||
.arg(adapter->ToString(command->initiator)))
|
||||
);
|
||||
emit handler->cecEvent(CECEvent::Off);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,15 +447,14 @@ void CECHandler::onCecSourceActivated(void * context, const CECLogicalAddress ad
|
||||
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
CECAdapter * adapter = handler->_cecAdapter;
|
||||
|
||||
Debug(handler->_logger, QString("CEC source %1 : %2")
|
||||
.arg(activated ? "activated" : "deactivated")
|
||||
.arg(adapter->ToString(address))
|
||||
.toLocal8Bit());
|
||||
Debug(handler->_logger, QSTRING_CSTR(QString("CEC source %1 : %2")
|
||||
.arg(activated ? "activated" : "deactivated",
|
||||
adapter->ToString(address))
|
||||
));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
# Define the current source locations
|
||||
find_package(CEC REQUIRED)
|
||||
if(CEC_FOUND)
|
||||
list(GET CEC_LIBRARIES 0 CEC_LIBRARIES)
|
||||
add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}")
|
||||
else()
|
||||
message(FATAL_ERROR "libCEC not found")
|
||||
endif()
|
||||
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/cec)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/cec)
|
||||
|
||||
FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
add_library(cechandler ${CEC_SOURCES})
|
||||
add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}")
|
||||
|
||||
include_directories(${CEC_INCLUDE_DIRS})
|
||||
add_library(cechandler
|
||||
${CMAKE_SOURCE_DIR}/include/cec/CECHandler.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/cec/CECHandler.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(cechandler
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/commandline)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/commandline)
|
||||
|
||||
FILE ( GLOB Parser_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(commandline ${Parser_SOURCES} )
|
||||
add_library(commandline
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/BooleanOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/ColorOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/ColorsOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/DoubleOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/ImageOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/IntOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/Option.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/Parser.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/RegularExpressionOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/SwitchOption.h
|
||||
${CMAKE_SOURCE_DIR}/include/commandline/ValidatorOption.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/BooleanOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/ColorOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/ColorsOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/DoubleOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/ImageOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/IntOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/Option.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/Parser.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/RegularExpressionOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/SwitchOption.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/commandline/ValidatorOption.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(commandline
|
||||
hyperion
|
||||
|
||||
@@ -5,6 +5,7 @@ using namespace commandline;
|
||||
Parser::~Parser()
|
||||
{
|
||||
qDeleteAll(_options);
|
||||
_options.clear();
|
||||
}
|
||||
|
||||
bool Parser::parse(const QStringList &arguments)
|
||||
@@ -14,19 +15,22 @@ bool Parser::parse(const QStringList &arguments)
|
||||
return false;
|
||||
}
|
||||
|
||||
for(Option * option : _options)
|
||||
for(Option * option : std::as_const(_options))
|
||||
{
|
||||
QString value = this->value(*option);
|
||||
if (!option->validate(*this, value)) {
|
||||
const QString error = option->getError();
|
||||
if (!error.isEmpty()) {
|
||||
_errorText = tr("\"%1\" is not a valid option for %2, %3").arg(value, option->name(), error);
|
||||
if (!option->valueName().isEmpty())
|
||||
{
|
||||
QString value = this->value(*option);
|
||||
if (!option->validate(*this, value)) {
|
||||
const QString error = option->getError();
|
||||
if (!error.isEmpty()) {
|
||||
_errorText = tr("\"%1\" is not a valid option for %2, %3").arg(value, option->name(), error);
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorText = tr("\"%1\" is not a valid option for %2").arg(value, option->name());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorText = tr("\"%1\" is not a valid option for %2").arg(value, option->name());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/db)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/db)
|
||||
|
||||
FILE ( GLOB DB_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(database
|
||||
${DB_SOURCES}
|
||||
${CMAKE_SOURCE_DIR}/include/db/AuthTable.h
|
||||
${CMAKE_SOURCE_DIR}/include/db/DBManager.h
|
||||
${CMAKE_SOURCE_DIR}/include/db/InstanceTable.h
|
||||
${CMAKE_SOURCE_DIR}/include/db/MetaTable.h
|
||||
${CMAKE_SOURCE_DIR}/include/db/SettingsTable.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/db/DBManager.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(database
|
||||
hyperion
|
||||
hyperion-utils
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QThreadStorage>
|
||||
#include <QUuid>
|
||||
#include <QDir>
|
||||
#include <QMetaType>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdexcept>
|
||||
@@ -425,15 +426,15 @@ void DBManager::doAddBindValue(QSqlQuery& query, const QVariantList& variants) c
|
||||
auto t = variant.userType();
|
||||
switch(t)
|
||||
{
|
||||
case QVariant::UInt:
|
||||
case QVariant::Int:
|
||||
case QVariant::Bool:
|
||||
case QMetaType::UInt:
|
||||
case QMetaType::Int:
|
||||
case QMetaType::Bool:
|
||||
query.addBindValue(variant.toInt());
|
||||
break;
|
||||
case QVariant::Double:
|
||||
case QMetaType::Double:
|
||||
query.addBindValue(variant.toFloat());
|
||||
break;
|
||||
case QVariant::ByteArray:
|
||||
case QMetaType::QByteArray:
|
||||
query.addBindValue(variant.toByteArray());
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,47 +1,30 @@
|
||||
if (NOT CMAKE_VERSION VERSION_LESS "3.12")
|
||||
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
|
||||
else()
|
||||
find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) # Maps PythonLibs to the PythonInterp version of the main cmake
|
||||
endif()
|
||||
file(GLOB effectFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/effects/*)
|
||||
set(HYPERION_EFFECTS_RES "")
|
||||
foreach(f ${effectFiles})
|
||||
get_filename_component(fname ${f} NAME)
|
||||
set(HYPERION_EFFECTS_RES "${HYPERION_EFFECTS_RES}\n\t\t<file alias=\"/effects/${fname}\">${f}</file>")
|
||||
endforeach()
|
||||
|
||||
# Include the python directory. Also include the parent (which is for example /usr/include)
|
||||
# which may be required when it is not includes by the (cross-) compiler by default.
|
||||
if (NOT CMAKE_VERSION VERSION_LESS "3.12")
|
||||
include_directories(${Python3_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}/..)
|
||||
else()
|
||||
include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..)
|
||||
endif()
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/effectengine)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/effectengine)
|
||||
|
||||
FILE ( GLOB EffectEngineSOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
FILE ( GLOB effectFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/effects/* )
|
||||
|
||||
SET ( HYPERION_EFFECTS_RES "")
|
||||
FOREACH( f ${effectFiles} )
|
||||
GET_FILENAME_COMPONENT(fname ${f} NAME)
|
||||
SET(HYPERION_EFFECTS_RES "${HYPERION_EFFECTS_RES}\n\t\t<file alias=\"/effects/${fname}\">${f}</file>")
|
||||
ENDFOREACH()
|
||||
CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/EffectEngine.qrc.in ${CMAKE_BINARY_DIR}/EffectEngine.qrc )
|
||||
|
||||
SET(EffectEngine_RESOURCES ${CMAKE_BINARY_DIR}/EffectEngine.qrc)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/EffectEngine.qrc.in ${CMAKE_BINARY_DIR}/EffectEngine.qrc)
|
||||
|
||||
add_library(effectengine
|
||||
${EffectEngine_RESOURCES}
|
||||
${EffectEngineSOURCES}
|
||||
${CMAKE_BINARY_DIR}/EffectEngine.qrc
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/ActiveEffectDefinition.h
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/Effect.h
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/EffectDefinition.h
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/EffectEngine.h
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/EffectFileHandler.h
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/EffectModule.h
|
||||
${CMAKE_SOURCE_DIR}/include/effectengine/EffectSchema.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/effectengine/Effect.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/effectengine/EffectEngine.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/effectengine/EffectFileHandler.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/effectengine/EffectModule.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(effectengine
|
||||
hyperion
|
||||
python
|
||||
hyperion
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
|
||||
if (NOT CMAKE_VERSION VERSION_LESS "3.12")
|
||||
target_link_libraries( effectengine ${Python3_LIBRARIES} )
|
||||
else()
|
||||
target_link_libraries( effectengine ${PYTHON_LIBRARIES} )
|
||||
endif()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="effect-schema">${CURRENT_SOURCE_DIR}/EffectDefinition.schema.json</file>
|
||||
<file alias="effect-schema">${CMAKE_CURRENT_SOURCE_DIR}/EffectDefinition.schema.json</file>
|
||||
${HYPERION_EFFECTS_RES}
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -224,7 +224,7 @@ void EffectFileHandler::updateEffects()
|
||||
}
|
||||
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString& path : qAsConst(efxPathList))
|
||||
for (const QString& path : std::as_const(efxPathList))
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
@@ -241,8 +241,8 @@ void EffectFileHandler::updateEffects()
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString& filename : qAsConst(filenames))
|
||||
const QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString& filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
@@ -268,8 +268,8 @@ void EffectFileHandler::updateEffects()
|
||||
|
||||
QString schemaPath = path + "schema" + '/';
|
||||
directory.setPath(schemaPath);
|
||||
QStringList schemaFileNames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString& schemaFileName : qAsConst(schemaFileNames))
|
||||
const QStringList schemaFileNames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString& schemaFileName : schemaFileNames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, directory.filePath(schemaFileName), pyEffect))
|
||||
@@ -282,7 +282,7 @@ void EffectFileHandler::updateEffects()
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& item : qAsConst(availableEffects))
|
||||
for (const auto& item : std::as_const(availableEffects))
|
||||
{
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
27
libsrc/events/CMakeLists.txt
Normal file
27
libsrc/events/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
add_library(events
|
||||
${CMAKE_SOURCE_DIR}/include/events/EventEnum.h
|
||||
${CMAKE_SOURCE_DIR}/include/events/EventHandler.h
|
||||
${CMAKE_SOURCE_DIR}/include/events/OsEventHandler.h
|
||||
${CMAKE_SOURCE_DIR}/include/events/EventScheduler.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/events/EventHandler.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/events/OsEventHandler.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/events/EventScheduler.cpp
|
||||
)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET)
|
||||
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
||||
target_link_libraries(events Qt${QT_VERSION_MAJOR}::DBus)
|
||||
target_compile_definitions(events PRIVATE HYPERION_HAS_DBUS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(events
|
||||
hyperion-utils
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set_source_files_properties(OsEventHandler.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
|
||||
target_link_libraries(events "-framework AppKit")
|
||||
endif()
|
||||
186
libsrc/events/EventHandler.cpp
Normal file
186
libsrc/events/EventHandler.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Process.h>
|
||||
#include <hyperion/HyperionIManager.h>
|
||||
|
||||
EventHandler::EventHandler()
|
||||
: _isSuspended(false)
|
||||
, _isIdle(false)
|
||||
{
|
||||
qRegisterMetaType<Event>("Event");
|
||||
_log = Logger::getInstance("EVENTS");
|
||||
|
||||
QObject::connect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent);
|
||||
}
|
||||
|
||||
EventHandler::~EventHandler()
|
||||
{
|
||||
QObject::disconnect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent);
|
||||
}
|
||||
|
||||
EventHandler* EventHandler::getInstance()
|
||||
{
|
||||
static EventHandler instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void EventHandler::suspend()
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
|
||||
void EventHandler::suspend(bool sleep)
|
||||
{
|
||||
if (sleep)
|
||||
{
|
||||
if (!_isSuspended)
|
||||
{
|
||||
_isSuspended = true;
|
||||
Info(_log, "Suspend event received - Hyperion is going to sleep");
|
||||
emit signalEvent(Event::Suspend);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Suspend event ignored - already suspended");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isSuspended || _isIdle)
|
||||
{
|
||||
Info(_log, "Resume event received - Hyperion is going into working mode");
|
||||
emit signalEvent(Event::Resume);
|
||||
_isSuspended = false;
|
||||
_isIdle = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Resume event ignored - not in suspend nor idle mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::resume()
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
|
||||
void EventHandler::toggleSuspend()
|
||||
{
|
||||
Debug(_log, "Toggle suspend event received");
|
||||
if (!_isSuspended)
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::idle()
|
||||
{
|
||||
idle(true);
|
||||
}
|
||||
|
||||
void EventHandler::idle(bool isIdle)
|
||||
{
|
||||
if (!_isSuspended)
|
||||
{
|
||||
if (isIdle)
|
||||
{
|
||||
if (!_isIdle)
|
||||
{
|
||||
_isIdle = true;
|
||||
Info(_log, "Idle event received");
|
||||
emit signalEvent(Event::Idle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isIdle)
|
||||
{
|
||||
Info(_log, "Resume from idle event recevied");
|
||||
emit signalEvent(Event::ResumeIdle);
|
||||
_isIdle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Idle event ignored - Hyperion is suspended");
|
||||
}
|
||||
}
|
||||
void EventHandler::resumeIdle()
|
||||
{
|
||||
idle(false);
|
||||
}
|
||||
|
||||
void EventHandler::toggleIdle()
|
||||
{
|
||||
Debug(_log, "Toggle idle event received");
|
||||
if (!_isIdle)
|
||||
{
|
||||
idle(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
idle(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::handleEvent(Event event)
|
||||
{
|
||||
QObject *senderObj = QObject::sender();
|
||||
QString senderObjectClass;
|
||||
if (senderObj)
|
||||
{
|
||||
senderObjectClass = senderObj->metaObject()->className();
|
||||
} else
|
||||
{
|
||||
senderObjectClass = "unknown sender";
|
||||
}
|
||||
Debug(_log,"%s Event [%d] received from %s", eventToString(event), event, QSTRING_CSTR(senderObjectClass));
|
||||
|
||||
switch (event) {
|
||||
case Event::Suspend:
|
||||
suspend();
|
||||
break;
|
||||
|
||||
case Event::Resume:
|
||||
resume();
|
||||
break;
|
||||
|
||||
case Event::ToggleSuspend:
|
||||
toggleSuspend();
|
||||
break;
|
||||
|
||||
case Event::Idle:
|
||||
idle(true);
|
||||
break;
|
||||
|
||||
case Event::ResumeIdle:
|
||||
idle(false);
|
||||
break;
|
||||
|
||||
case Event::ToggleIdle:
|
||||
toggleIdle();
|
||||
break;
|
||||
|
||||
case Event::Reload:
|
||||
Process::restartHyperion(10);
|
||||
break;
|
||||
|
||||
case Event::Restart:
|
||||
Process::restartHyperion(11);
|
||||
break;
|
||||
|
||||
default:
|
||||
Error(_log,"Unkonwn Event '%d' received", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
153
libsrc/events/EventScheduler.cpp
Normal file
153
libsrc/events/EventScheduler.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "events/EventScheduler.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <events/EventHandler.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
EventScheduler::EventScheduler()
|
||||
: _isEnabled(false)
|
||||
{
|
||||
qRegisterMetaType<Event>("Event");
|
||||
_log = Logger::getInstance("EVENTS-SCHED");
|
||||
|
||||
QObject::connect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
}
|
||||
|
||||
EventScheduler::~EventScheduler()
|
||||
{
|
||||
QObject::disconnect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
clearTimers();
|
||||
Info(_log, "Event scheduler stopped");
|
||||
}
|
||||
|
||||
void EventScheduler::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::SCHEDEVENTS)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
|
||||
_isEnabled = obj["enable"].toBool(false);
|
||||
Debug(_log, "Event scheduler is %s", _isEnabled? "enabled" : "disabled");
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
_scheduledEvents.clear();
|
||||
|
||||
const QJsonArray actionItems = obj["actions"].toArray();
|
||||
if (!actionItems.isEmpty())
|
||||
{
|
||||
timeEvent timeEvent;
|
||||
for (const QJsonValue &item : actionItems)
|
||||
{
|
||||
QString action = item.toObject().value("action").toString();
|
||||
timeEvent.action = stringToEvent(action);
|
||||
|
||||
QString event = item.toObject().value("event").toString();
|
||||
timeEvent.time = QTime::fromString(event,"hh:mm");
|
||||
if (timeEvent.time.isValid())
|
||||
{
|
||||
_scheduledEvents.append(timeEvent);
|
||||
Debug(_log, "Time-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action));
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Error in configured time : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_scheduledEvents.isEmpty())
|
||||
{
|
||||
enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "No scheduled events to listen to are configured currently.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
disable();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool EventScheduler::enable()
|
||||
{
|
||||
bool enabled {false};
|
||||
|
||||
clearTimers();
|
||||
for (int i = 0; i < _scheduledEvents.size(); ++i)
|
||||
{
|
||||
QTimer* timer = new QTimer(this);
|
||||
timer->setTimerType(Qt::PreciseTimer);
|
||||
_timers.append(timer);
|
||||
|
||||
// Calculate the milliseconds until the next occurrence of the scheduled time
|
||||
int milliseconds = getMillisecondsToNextScheduledTime(_scheduledEvents.at(i).time);
|
||||
timer->start(milliseconds);
|
||||
|
||||
QObject::connect(timer, &QTimer::timeout, this, [this, i]() { handleEvent(i); });
|
||||
}
|
||||
|
||||
enabled = true;
|
||||
Info(_log, "Event scheduler started");
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void EventScheduler::disable()
|
||||
{
|
||||
Info(_log, "Disabling event scheduler");
|
||||
clearTimers();
|
||||
}
|
||||
|
||||
void EventScheduler::clearTimers()
|
||||
{
|
||||
for (QTimer *timer : std::as_const(_timers)) {
|
||||
timer->disconnect();
|
||||
timer->stop();
|
||||
delete timer;
|
||||
}
|
||||
_timers.clear();
|
||||
}
|
||||
|
||||
void EventScheduler::handleEvent(int timerIndex)
|
||||
{
|
||||
QTime time = _scheduledEvents.at(timerIndex).time;
|
||||
Event action = _scheduledEvents.at(timerIndex).action;
|
||||
Debug(_log, "Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(time.toString()), eventToString(action) );
|
||||
if ( action != Event::Unknown )
|
||||
{
|
||||
emit signalEvent(action);
|
||||
}
|
||||
|
||||
// Retrigger the timer for the next occurrence
|
||||
QTimer* timer = _timers.at(timerIndex);
|
||||
int milliseconds = getMillisecondsToNextScheduledTime(time);
|
||||
timer->start(milliseconds);
|
||||
}
|
||||
|
||||
int EventScheduler::getMillisecondsToNextScheduledTime(const QTime& scheduledTime)
|
||||
{
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
QTime currentTime = currentDateTime.time();
|
||||
|
||||
QDateTime nextOccurrence = currentDateTime;
|
||||
nextOccurrence.setTime(scheduledTime);
|
||||
|
||||
// If the scheduled time has already passed for today, schedule it for tomorrow
|
||||
if (currentTime > scheduledTime)
|
||||
{
|
||||
nextOccurrence = nextOccurrence.addDays(1);
|
||||
}
|
||||
|
||||
int milliseconds = currentDateTime.msecsTo(nextOccurrence);
|
||||
return (milliseconds > 0) ? milliseconds : 0;
|
||||
}
|
||||
667
libsrc/events/OsEventHandler.cpp
Normal file
667
libsrc/events/OsEventHandler.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
#include "events/OsEventHandler.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
|
||||
#include <events/EventHandler.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <QWidget>
|
||||
#include <windows.h>
|
||||
#include <wtsapi32.h>
|
||||
#include <powerbase.h>
|
||||
|
||||
#pragma comment( lib, "wtsapi32.lib" )
|
||||
#pragma comment(lib, "PowrProf.lib")
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
OsEventHandlerBase::OsEventHandlerBase()
|
||||
: _isSuspendEnabled(false)
|
||||
, _isLockEnabled(false)
|
||||
, _isSuspendOnLock(false)
|
||||
, _isSuspendRegistered(false)
|
||||
, _isLockRegistered(false)
|
||||
, _isService(false)
|
||||
{
|
||||
qRegisterMetaType<Event>("Event");
|
||||
_log = Logger::getInstance("EVENTS-OS");
|
||||
|
||||
QCoreApplication* app = QCoreApplication::instance();
|
||||
if (!qobject_cast<QApplication*>(app))
|
||||
{
|
||||
_isService = true;
|
||||
}
|
||||
QObject::connect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
}
|
||||
|
||||
OsEventHandlerBase::~OsEventHandlerBase()
|
||||
{
|
||||
QObject::disconnect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
|
||||
OsEventHandlerBase::unregisterLockHandler();
|
||||
OsEventHandlerBase::unregisterOsEventHandler();
|
||||
|
||||
Info(_log, "Operating System event handler stopped");
|
||||
}
|
||||
|
||||
void OsEventHandlerBase::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if (type == settings::OSEVENTS)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
|
||||
//Suspend on lock or go into idle mode
|
||||
bool prevIsSuspendOnLock = _isSuspendOnLock;
|
||||
_isSuspendOnLock = obj["suspendOnLockEnable"].toBool(false);
|
||||
|
||||
//Handle OS event related configurations
|
||||
_isSuspendEnabled = obj["suspendEnable"].toBool(true);
|
||||
if (_isSuspendEnabled)
|
||||
{
|
||||
// Listen to suspend/resume/idle events received by the OS
|
||||
registerOsEventHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
unregisterOsEventHandler();
|
||||
}
|
||||
|
||||
if (!_isService)
|
||||
{
|
||||
_isLockEnabled = obj["lockEnable"].toBool(true);
|
||||
if (_isLockEnabled || _isSuspendOnLock != prevIsSuspendOnLock)
|
||||
{
|
||||
// Listen to lock/screensaver events received by the OS
|
||||
registerLockHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
unregisterLockHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsEventHandlerBase::suspend(bool sleep)
|
||||
{
|
||||
if (sleep)
|
||||
{
|
||||
emit signalEvent(Event::Suspend);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit signalEvent(Event::Resume);
|
||||
}
|
||||
}
|
||||
|
||||
void OsEventHandlerBase::lock(bool isLocked)
|
||||
{
|
||||
if (isLocked)
|
||||
{
|
||||
if (_isSuspendOnLock)
|
||||
{
|
||||
emit signalEvent(Event::Suspend);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit signalEvent(Event::Idle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isSuspendOnLock)
|
||||
{
|
||||
emit signalEvent(Event::Resume);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit signalEvent(Event::ResumeIdle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
OsEventHandlerWindows* OsEventHandlerWindows::getInstance()
|
||||
{
|
||||
static OsEventHandlerWindows instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
OsEventHandlerWindows::OsEventHandlerWindows()
|
||||
: _notifyHandle(NULL),
|
||||
_widget(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
OsEventHandlerWindows::~OsEventHandlerWindows()
|
||||
{
|
||||
unregisterLockHandler();
|
||||
unregisterOsEventHandler();
|
||||
|
||||
delete _widget;
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, qintptr* /*result*/)
|
||||
#else
|
||||
bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, long int* /*result*/)
|
||||
#endif
|
||||
{
|
||||
|
||||
MSG* msg = static_cast<MSG*>(message);
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_WTSSESSION_CHANGE:
|
||||
switch (msg->wParam)
|
||||
{
|
||||
case WTS_SESSION_LOCK:
|
||||
emit lock(true);
|
||||
return true;
|
||||
break;
|
||||
case WTS_SESSION_UNLOCK:
|
||||
emit lock(false);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OsEventHandlerWindows::handleSuspendResumeEvent(bool sleep)
|
||||
{
|
||||
if (sleep)
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
}
|
||||
|
||||
ULONG OsEventHandlerWindows::handlePowerNotifications(PVOID Context, ULONG Type, PVOID Setting)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case PBT_APMRESUMESUSPEND:
|
||||
getInstance()->handleSuspendResumeEvent(false);
|
||||
break;
|
||||
case PBT_APMSUSPEND:
|
||||
getInstance()->handleSuspendResumeEvent(true);
|
||||
break;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool OsEventHandlerWindows::registerOsEventHandler()
|
||||
{
|
||||
bool isRegistered{ _isSuspendRegistered };
|
||||
if (!_isSuspendRegistered)
|
||||
{
|
||||
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS notificationsParameters = { handlePowerNotifications, NULL };
|
||||
if (PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, (HANDLE)¬ificationsParameters, &_notifyHandle) == ERROR_SUCCESS)
|
||||
{
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for suspend/resume events!");
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isSuspendRegistered = true;
|
||||
}
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerWindows::unregisterOsEventHandler()
|
||||
{
|
||||
if (_isSuspendRegistered)
|
||||
{
|
||||
if (_notifyHandle != NULL)
|
||||
{
|
||||
PowerUnregisterSuspendResumeNotification(_notifyHandle);
|
||||
}
|
||||
_notifyHandle = NULL;
|
||||
_isSuspendRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OsEventHandlerWindows::registerLockHandler()
|
||||
{
|
||||
bool isRegistered{ _isLockRegistered };
|
||||
if (!_isLockRegistered)
|
||||
{
|
||||
if (!_isService)
|
||||
{
|
||||
_widget = new QWidget();
|
||||
if (_widget)
|
||||
{
|
||||
auto handle = reinterpret_cast<HWND> (_widget->winId());
|
||||
if (WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION))
|
||||
{
|
||||
QCoreApplication::instance()->installNativeEventFilter(this);
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for lock/unlock events!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isLockRegistered = true;
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerWindows::unregisterLockHandler()
|
||||
{
|
||||
if (_isLockRegistered)
|
||||
{
|
||||
if (!_isService)
|
||||
{
|
||||
auto handle = reinterpret_cast<HWND> (_widget->winId());
|
||||
QCoreApplication::instance()->removeNativeEventFilter(this);
|
||||
WTSUnRegisterSessionNotification(handle);
|
||||
_isLockRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
#include <csignal>
|
||||
|
||||
OsEventHandlerLinux* OsEventHandlerLinux::getInstance()
|
||||
{
|
||||
static OsEventHandlerLinux instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
OsEventHandlerLinux::OsEventHandlerLinux()
|
||||
{
|
||||
signal(SIGUSR1, static_signaleHandler);
|
||||
signal(SIGUSR2, static_signaleHandler);
|
||||
}
|
||||
|
||||
void OsEventHandlerLinux::handleSignal(int signum)
|
||||
{
|
||||
if (signum == SIGUSR1)
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
else if (signum == SIGUSR2)
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HYPERION_HAS_DBUS)
|
||||
#include <QDBusConnection>
|
||||
|
||||
struct dBusSignals
|
||||
{
|
||||
QString service;
|
||||
QString path;
|
||||
QString interface;
|
||||
QString name;
|
||||
};
|
||||
|
||||
typedef QMultiMap<QString, dBusSignals> DbusSignalsMap;
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const DbusSignalsMap dbusSignals = {
|
||||
//system signals
|
||||
{"Suspend", {"org.freedesktop.login1","/org/freedesktop/login1","org.freedesktop.login1.Manager","PrepareForSleep"}},
|
||||
|
||||
//Session signals
|
||||
{"ScreenSaver", {"org.freedesktop.ScreenSaver","/org/freedesktop/ScreenSaver","org.freedesktop.ScreenSaver","ActiveChanged"}},
|
||||
{"ScreenSaver", {"org.gnome.ScreenSaver","/org/gnome/ScreenSaver","org.gnome.ScreenSaver","ActiveChanged"}},
|
||||
};
|
||||
} //End of constants
|
||||
|
||||
bool OsEventHandlerLinux::registerOsEventHandler()
|
||||
{
|
||||
|
||||
bool isRegistered{ _isSuspendRegistered };
|
||||
if (!_isSuspendRegistered)
|
||||
{
|
||||
QDBusConnection systemBus = QDBusConnection::systemBus();
|
||||
if (!systemBus.isConnected())
|
||||
{
|
||||
Info(_log, "The suspend/resume feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
QString service = dbusSignals.find("Suspend").value().service;
|
||||
if (systemBus.connect(service,
|
||||
dbusSignals.find("Suspend").value().path,
|
||||
dbusSignals.find("Suspend").value().interface,
|
||||
dbusSignals.find("Suspend").value().name,
|
||||
this, SLOT(suspend(bool))))
|
||||
{
|
||||
Debug(_log, "Registered for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isSuspendRegistered = true;
|
||||
}
|
||||
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerLinux::unregisterOsEventHandler()
|
||||
{
|
||||
if (_isSuspendRegistered)
|
||||
{
|
||||
QDBusConnection systemBus = QDBusConnection::systemBus();
|
||||
if (!systemBus.isConnected())
|
||||
{
|
||||
Info(_log, "The suspend/resume feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
QString service = dbusSignals.find("Suspend").value().service;
|
||||
if (systemBus.disconnect(service,
|
||||
dbusSignals.find("Suspend").value().path,
|
||||
dbusSignals.find("Suspend").value().interface,
|
||||
dbusSignals.find("Suspend").value().name,
|
||||
this, SLOT(suspend(bool))))
|
||||
{
|
||||
Debug(_log, "Unregistered for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
_isSuspendRegistered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OsEventHandlerLinux::registerLockHandler()
|
||||
{
|
||||
bool isRegistered{ _isLockRegistered };
|
||||
|
||||
if (!_isLockRegistered)
|
||||
{
|
||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||
if (!sessionBus.isConnected())
|
||||
{
|
||||
Info(_log, "The lock/unlock feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
||||
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
||||
QString service = iter.value().service;
|
||||
if (sessionBus.connect(service,
|
||||
iter.value().path,
|
||||
iter.value().interface,
|
||||
iter.value().name,
|
||||
this, SLOT(lock(bool))))
|
||||
{
|
||||
Debug(_log, "Registered for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isLockRegistered = true;
|
||||
}
|
||||
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerLinux::unregisterLockHandler()
|
||||
{
|
||||
bool isUnregistered{ false };
|
||||
|
||||
if (_isLockRegistered)
|
||||
{
|
||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||
if (!sessionBus.isConnected())
|
||||
{
|
||||
Info(_log, "The lock/unlock feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
||||
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
||||
QString service = iter.value().service;
|
||||
if (sessionBus.disconnect(service,
|
||||
iter.value().path,
|
||||
iter.value().interface,
|
||||
iter.value().name,
|
||||
this, SLOT(lock(bool))))
|
||||
{
|
||||
Debug(_log, "Unregistered for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
isUnregistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (isUnregistered)
|
||||
{
|
||||
_isLockRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HYPERION_HAS_DBUS
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
OsEventHandlerMacOS::OsEventHandlerMacOS()
|
||||
: _sleepEventHandler(nullptr)
|
||||
, _lockEventHandler(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@interface SleepEvents : NSObject
|
||||
{
|
||||
OsEventHandlerMacOS* _eventHandler;
|
||||
}
|
||||
- (id)initSleepEvents : (OsEventHandlerMacOS*)osEventHandler;
|
||||
@end
|
||||
|
||||
@implementation SleepEvents
|
||||
- (id)initSleepEvents:(OsEventHandlerMacOS*)osEventHandler
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_eventHandler = osEventHandler;
|
||||
id notifCenter = [[NSWorkspace sharedWorkspace]notificationCenter];
|
||||
[notifCenter addObserver : self selector : @selector(receiveSleepWake:) name:NSWorkspaceWillSleepNotification object : nil] ;
|
||||
[notifCenter addObserver : self selector : @selector(receiveSleepWake:) name:NSWorkspaceDidWakeNotification object : nil] ;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[[NSWorkspace sharedWorkspace]notificationCenter] removeObserver:self];
|
||||
_eventHandler = nullptr;
|
||||
[super dealloc] ;
|
||||
}
|
||||
|
||||
- (void)receiveSleepWake:(NSNotification*)notification
|
||||
{
|
||||
if (!_eventHandler) return;
|
||||
if (notification.name == NSWorkspaceWillSleepNotification)
|
||||
{
|
||||
_eventHandler->suspend(true);
|
||||
}
|
||||
else if (notification.name == NSWorkspaceDidWakeNotification)
|
||||
{
|
||||
_eventHandler->suspend(false);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
bool OsEventHandlerMacOS::registerOsEventHandler()
|
||||
{
|
||||
bool isRegistered{ _isSuspendRegistered };
|
||||
if (!_isSuspendRegistered)
|
||||
{
|
||||
_sleepEventHandler = [[SleepEvents alloc]initSleepEvents:this];
|
||||
if (_sleepEventHandler)
|
||||
{
|
||||
Debug(_log, "Registered for suspend/resume events");
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for suspend/resume events");
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isSuspendRegistered = true;
|
||||
}
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerMacOS::unregisterOsEventHandler()
|
||||
{
|
||||
if (_isSuspendRegistered && _sleepEventHandler)
|
||||
{
|
||||
[(SleepEvents*)_sleepEventHandler release] , _sleepEventHandler = nil;
|
||||
if (!_sleepEventHandler)
|
||||
{
|
||||
Debug(_log, "Unregistered for suspend/resume events");
|
||||
_isSuspendRegistered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for suspend/resume events");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@interface LockEvents : NSObject
|
||||
{
|
||||
OsEventHandlerMacOS* _eventHandler;
|
||||
}
|
||||
- (id)initLockEvents : (OsEventHandlerMacOS*)osEventHandler;
|
||||
@end
|
||||
|
||||
@implementation LockEvents
|
||||
- (id)initLockEvents:(OsEventHandlerMacOS*)osEventHandler
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_eventHandler = osEventHandler;
|
||||
id defCenter = [NSDistributedNotificationCenter defaultCenter];
|
||||
[defCenter addObserver : self selector : @selector(receiveLockUnlock:) name:@"com.apple.screenIsLocked" object:nil] ;
|
||||
[defCenter addObserver : self selector : @selector(receiveLockUnlock:) name:@"com.apple.screenIsUnlocked" object:nil] ;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[[NSWorkspace sharedWorkspace]notificationCenter] removeObserver:self];
|
||||
_eventHandler = nullptr;
|
||||
[super dealloc] ;
|
||||
}
|
||||
|
||||
- (void)receiveLockUnlock:(NSNotification*)notification
|
||||
{
|
||||
if (!_eventHandler) return;
|
||||
if (CFEqual(notification.name, CFSTR("com.apple.screenIsLocked")))
|
||||
{
|
||||
_eventHandler->lock(true);
|
||||
}
|
||||
else if (CFEqual(notification.name, CFSTR("com.apple.screenIsUnlocked")))
|
||||
{
|
||||
_eventHandler->lock(false);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
bool OsEventHandlerMacOS::registerLockHandler()
|
||||
{
|
||||
bool isRegistered{ _isLockRegistered };
|
||||
if (!_isLockRegistered)
|
||||
{
|
||||
_lockEventHandler = [[LockEvents alloc]initLockEvents:this];
|
||||
if (_lockEventHandler)
|
||||
{
|
||||
Debug(_log, "Registered for lock/unlock events");
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for lock/unlock events!");
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isLockRegistered = true;
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerMacOS::unregisterLockHandler()
|
||||
{
|
||||
if (_isLockRegistered && _lockEventHandler)
|
||||
{
|
||||
[(LockEvents*)_lockEventHandler release] , _lockEventHandler = nil;
|
||||
if (!_lockEventHandler)
|
||||
{
|
||||
Debug(_log, "Unregistered for lock/unlock events");
|
||||
_isLockRegistered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for lock/unlock events");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,69 +1,54 @@
|
||||
# set (compiled) Flatbuffer schema names
|
||||
set(FBS_Request "hyperion_request")
|
||||
set(FBS_Reply "hyperion_reply")
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/flatbufserver)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${FLATBUFFERS_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(Flatbuffer_GENERATED_FBS
|
||||
hyperion_reply_generated.h
|
||||
hyperion_request_generated.h
|
||||
)
|
||||
|
||||
set(Flatbuffer_FBS
|
||||
${CURRENT_SOURCE_DIR}/hyperion_reply.fbs
|
||||
${CURRENT_SOURCE_DIR}/hyperion_request.fbs
|
||||
)
|
||||
FOREACH(FBS_FILE ${Flatbuffer_FBS})
|
||||
compile_flattbuffer_schema(${FBS_FILE} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
ENDFOREACH(FBS_FILE)
|
||||
# define and compile flatbuffer schemas
|
||||
list(APPEND Compiled_FBS ${FBS_Request}_generated.h)
|
||||
list(APPEND Compiled_FBS ${FBS_Reply}_generated.h)
|
||||
compile_flattbuffer_schema(${CMAKE_CURRENT_SOURCE_DIR}/${FBS_Request}.fbs ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
compile_flattbuffer_schema(${CMAKE_CURRENT_SOURCE_DIR}/${FBS_Reply}.fbs ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# let cmake know about new generated source files
|
||||
set_source_files_properties(
|
||||
${Flatbuffer_GENERATED_FBS} PROPERTIES GENERATED TRUE
|
||||
)
|
||||
|
||||
### Split flatbufconnect from flatbufserver as flatbufserver relates to HyperionDaemon
|
||||
set_source_files_properties(${Compiled_FBS} PROPERTIES GENERATED TRUE)
|
||||
|
||||
# split flatbufconnect from flatbufserver as flatbufserver relates to HyperionDaemon
|
||||
if(ENABLE_FLATBUF_CONNECT)
|
||||
add_library(flatbufconnect
|
||||
${CURRENT_HEADER_DIR}/FlatBufferConnection.h
|
||||
${CURRENT_SOURCE_DIR}/FlatBufferConnection.cpp
|
||||
${FLATBUFSERVER_SOURCES}
|
||||
${Flatbuffer_GENERATED_FBS}
|
||||
add_library(flatbufconnect
|
||||
${CMAKE_SOURCE_DIR}/include/flatbufserver/FlatBufferConnection.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferConnection.cpp
|
||||
${Compiled_FBS}
|
||||
)
|
||||
|
||||
)
|
||||
target_link_libraries(flatbufconnect
|
||||
hyperion-utils
|
||||
flatbuffers
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
)
|
||||
target_link_libraries(flatbufconnect
|
||||
hyperion-utils
|
||||
flatbuffers
|
||||
)
|
||||
|
||||
target_include_directories(flatbufconnect PUBLIC
|
||||
${FLATBUFFERS_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_FLATBUF_SERVER)
|
||||
add_library(flatbufserver
|
||||
${CURRENT_HEADER_DIR}/FlatBufferServer.h
|
||||
${CURRENT_SOURCE_DIR}/FlatBufferServer.cpp
|
||||
${CURRENT_SOURCE_DIR}/FlatBufferClient.h
|
||||
${CURRENT_SOURCE_DIR}/FlatBufferClient.cpp
|
||||
${FLATBUFSERVER_SOURCES}
|
||||
${Flatbuffer_GENERATED_FBS}
|
||||
)
|
||||
add_library(flatbufserver
|
||||
${CMAKE_SOURCE_DIR}/include/flatbufserver/FlatBufferServer.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferServer.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferClient.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferClient.cpp
|
||||
${Compiled_FBS}
|
||||
)
|
||||
|
||||
target_link_libraries(flatbufserver
|
||||
hyperion-utils
|
||||
flatbuffers
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(flatbufserver mdns)
|
||||
endif()
|
||||
target_link_libraries(flatbufserver
|
||||
hyperion-utils
|
||||
flatbuffers
|
||||
)
|
||||
|
||||
target_include_directories(flatbufserver PUBLIC
|
||||
${FLATBUFFERS_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(flatbufserver mdns)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/forwarder)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/forwarder)
|
||||
|
||||
if(ENABLE_FLATBUF_CONNECT)
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver
|
||||
add_library(forwarder
|
||||
${CMAKE_SOURCE_DIR}/include/forwarder/MessageForwarder.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/forwarder/MessageForwarder.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
FILE ( GLOB Forwarder_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(forwarder ${Forwarder_SOURCES} )
|
||||
|
||||
target_link_libraries(forwarder
|
||||
hyperion
|
||||
hyperion-utils
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
if(ENABLE_FLATBUF_CONNECT)
|
||||
|
||||
@@ -269,7 +269,7 @@ int MessageForwarder::startJsonTargets(const QJsonObject& config)
|
||||
|
||||
if (!_jsonTargets.isEmpty())
|
||||
{
|
||||
for (const auto& targetHost : qAsConst(_jsonTargets))
|
||||
for (const auto& targetHost : std::as_const(_jsonTargets))
|
||||
{
|
||||
Info(_log, "Forwarding now to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
|
||||
}
|
||||
@@ -286,7 +286,7 @@ void MessageForwarder::stopJsonTargets()
|
||||
if (!_jsonTargets.isEmpty())
|
||||
{
|
||||
disconnect(_hyperion, &Hyperion::forwardJsonMessage, nullptr, nullptr);
|
||||
for (const auto& targetHost : qAsConst(_jsonTargets))
|
||||
for (const auto& targetHost : std::as_const(_jsonTargets))
|
||||
{
|
||||
Info(_log, "Stopped forwarding to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
|
||||
}
|
||||
@@ -373,7 +373,7 @@ int MessageForwarder::startFlatbufferTargets(const QJsonObject& config)
|
||||
|
||||
if (!_flatbufferTargets.isEmpty())
|
||||
{
|
||||
for (const auto& targetHost : qAsConst(_flatbufferTargets))
|
||||
for (const auto& targetHost : std::as_const(_flatbufferTargets))
|
||||
{
|
||||
Info(_log, "Forwarding now to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
|
||||
}
|
||||
@@ -399,7 +399,7 @@ void MessageForwarder::stopFlatbufferTargets()
|
||||
_messageForwarderFlatBufHelper = nullptr;
|
||||
}
|
||||
|
||||
for (const auto& targetHost : qAsConst(_flatbufferTargets))
|
||||
for (const auto& targetHost : std::as_const(_flatbufferTargets))
|
||||
{
|
||||
Info(_log, "Stopped forwarding to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
|
||||
}
|
||||
@@ -412,7 +412,7 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject& message)
|
||||
if (_forwarder_enabled)
|
||||
{
|
||||
QTcpSocket client;
|
||||
for (const auto& targetHost : qAsConst(_jsonTargets))
|
||||
for (const auto& targetHost : std::as_const(_jsonTargets))
|
||||
{
|
||||
client.connectToHost(targetHost.host, targetHost.port);
|
||||
if (client.waitForConnected(CONNECT_TIMEOUT.count()))
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
if (ENABLE_AMLOGIC)
|
||||
if(ENABLE_AMLOGIC)
|
||||
add_subdirectory(amlogic)
|
||||
endif (ENABLE_AMLOGIC)
|
||||
|
||||
if (ENABLE_DISPMANX)
|
||||
if(ENABLE_DISPMANX)
|
||||
add_subdirectory(dispmanx)
|
||||
endif (ENABLE_DISPMANX)
|
||||
|
||||
if (ENABLE_FB)
|
||||
if(ENABLE_FB)
|
||||
add_subdirectory(framebuffer)
|
||||
endif (ENABLE_FB)
|
||||
|
||||
if (ENABLE_OSX)
|
||||
if(ENABLE_OSX)
|
||||
add_subdirectory(osx)
|
||||
endif(ENABLE_OSX)
|
||||
|
||||
if (ENABLE_V4L2 OR ENABLE_MF)
|
||||
if(ENABLE_V4L2 OR ENABLE_MF)
|
||||
add_subdirectory(video)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (ENABLE_X11)
|
||||
if(ENABLE_X11)
|
||||
add_subdirectory(x11)
|
||||
endif(ENABLE_X11)
|
||||
|
||||
if (ENABLE_XCB)
|
||||
if(ENABLE_XCB)
|
||||
add_subdirectory(xcb)
|
||||
endif(ENABLE_XCB)
|
||||
|
||||
if (ENABLE_QT)
|
||||
if(ENABLE_QT)
|
||||
add_subdirectory(qt)
|
||||
endif(ENABLE_QT)
|
||||
|
||||
if (ENABLE_DX)
|
||||
if(ENABLE_DX)
|
||||
add_subdirectory(directx)
|
||||
endif(ENABLE_DX)
|
||||
|
||||
if (ENABLE_AUDIO)
|
||||
if(ENABLE_AUDIO)
|
||||
add_subdirectory(audio)
|
||||
endif()
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
// Local includes
|
||||
#include <utils/Logger.h>
|
||||
#include <grabber/AmlogicGrabber.h>
|
||||
#include <grabber/amlogic/AmlogicGrabber.h>
|
||||
#include "Amvideocap.h"
|
||||
|
||||
// Constants
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/AmlogicWrapper.h>
|
||||
#include <grabber/amlogic/AmlogicWrapper.h>
|
||||
|
||||
AmlogicWrapper::AmlogicWrapper(int pixelDecimation, int updateRate_Hz)
|
||||
: GrabberWrapper("Amlogic", &_grabber, updateRate_Hz)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
INCLUDE (CheckIncludeFiles)
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic)
|
||||
|
||||
FILE ( GLOB AmlogicSOURCES "${CURRENT_HEADER_DIR}/Amlogic*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(amlogic-grabber ${AmlogicSOURCES} )
|
||||
add_library(amlogic-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/amlogic/AmlogicGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/amlogic/AmlogicWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/AmlogicGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/AmlogicWrapper.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/Amvideocap.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/ion.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/meson_ion.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/IonBuffer.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/IonBuffer.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(amlogic-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES})
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/AudioGrabber.h>
|
||||
#include <grabber/audio/AudioGrabber.h>
|
||||
#include <math.h>
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
@@ -9,6 +9,15 @@
|
||||
// Constants
|
||||
namespace {
|
||||
const uint16_t RESOLUTION = 255;
|
||||
|
||||
//Constants vuMeter
|
||||
const QJsonArray DEFAULT_HOTCOLOR { 255,0,0 };
|
||||
const QJsonArray DEFAULT_WARNCOLOR { 255,255,0 };
|
||||
const QJsonArray DEFAULT_SAFECOLOR { 0,255,0 };
|
||||
const int DEFAULT_WARNVALUE { 80 };
|
||||
const int DEFAULT_SAFEVALUE { 45 };
|
||||
const int DEFAULT_MULTIPLIER { 0 };
|
||||
const int DEFAULT_TOLERANCE { 20 };
|
||||
}
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
|
||||
@@ -28,12 +37,12 @@ AudioGrabber::AudioGrabber()
|
||||
, _deviceProperties()
|
||||
, _device("none")
|
||||
, _hotColor(QColorConstants::Red)
|
||||
, _warnValue(80)
|
||||
, _warnValue(DEFAULT_WARNVALUE)
|
||||
, _warnColor(QColorConstants::Yellow)
|
||||
, _safeValue(45)
|
||||
, _safeValue(DEFAULT_SAFEVALUE)
|
||||
, _safeColor(QColorConstants::Green)
|
||||
, _multiplier(0)
|
||||
, _tolerance(20)
|
||||
, _multiplier(DEFAULT_MULTIPLIER)
|
||||
, _tolerance(DEFAULT_TOLERANCE)
|
||||
, _dynamicMultiplier(INT16_MAX)
|
||||
, _started(false)
|
||||
{
|
||||
@@ -61,18 +70,27 @@ void AudioGrabber::setDevice(const QString& device)
|
||||
|
||||
void AudioGrabber::setConfiguration(const QJsonObject& config)
|
||||
{
|
||||
QJsonArray hotColorArray = config["hotColor"].toArray(QJsonArray::fromVariantList(QList<QVariant>({ QVariant(255), QVariant(0), QVariant(0) })));
|
||||
QJsonArray warnColorArray = config["warnColor"].toArray(QJsonArray::fromVariantList(QList<QVariant>({ QVariant(255), QVariant(255), QVariant(0) })));
|
||||
QJsonArray safeColorArray = config["safeColor"].toArray(QJsonArray::fromVariantList(QList<QVariant>({ QVariant(0), QVariant(255), QVariant(0) })));
|
||||
QString audioEffect = config["audioEffect"].toString();
|
||||
QJsonObject audioEffectConfig = config[audioEffect].toObject();
|
||||
|
||||
_hotColor = QColor(hotColorArray.at(0).toInt(), hotColorArray.at(1).toInt(), hotColorArray.at(2).toInt());
|
||||
_warnColor = QColor(warnColorArray.at(0).toInt(), warnColorArray.at(1).toInt(), warnColorArray.at(2).toInt());
|
||||
_safeColor = QColor(safeColorArray.at(0).toInt(), safeColorArray.at(1).toInt(), safeColorArray.at(2).toInt());
|
||||
if (audioEffect == "vuMeter")
|
||||
{
|
||||
QJsonArray hotColorArray = audioEffectConfig.value("hotColor").toArray(DEFAULT_HOTCOLOR);
|
||||
QJsonArray warnColorArray = audioEffectConfig.value("warnColor").toArray(DEFAULT_WARNCOLOR);
|
||||
QJsonArray safeColorArray = audioEffectConfig.value("safeColor").toArray(DEFAULT_SAFECOLOR);
|
||||
|
||||
_warnValue = config["warnValue"].toInt(80);
|
||||
_safeValue = config["safeValue"].toInt(45);
|
||||
_multiplier = config["multiplier"].toDouble(0);
|
||||
_tolerance = config["tolerance"].toInt(20);
|
||||
_hotColor = QColor(hotColorArray.at(0).toInt(), hotColorArray.at(1).toInt(), hotColorArray.at(2).toInt());
|
||||
_warnColor = QColor(warnColorArray.at(0).toInt(), warnColorArray.at(1).toInt(), warnColorArray.at(2).toInt());
|
||||
_safeColor = QColor(safeColorArray.at(0).toInt(), safeColorArray.at(1).toInt(), safeColorArray.at(2).toInt());
|
||||
_warnValue = audioEffectConfig["warnValue"].toInt(DEFAULT_WARNVALUE);
|
||||
_safeValue = audioEffectConfig["safeValue"].toInt(DEFAULT_SAFEVALUE);
|
||||
_multiplier = audioEffectConfig["multiplier"].toDouble(DEFAULT_MULTIPLIER);
|
||||
_tolerance = audioEffectConfig["tolerance"].toInt(DEFAULT_MULTIPLIER);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Unknow Audio-Effect: \"%s\" configured", QSTRING_CSTR(audioEffect));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioGrabber::resetMultiplier()
|
||||
@@ -160,7 +178,7 @@ void AudioGrabber::processAudioFrame(int16_t* buffer, int length)
|
||||
}
|
||||
|
||||
// Convert to Image<ColorRGB>
|
||||
Image<ColorRgb> finalImage (static_cast<unsigned>(image.width()), static_cast<unsigned>(image.height()));
|
||||
Image<ColorRgb> finalImage (image.width(),image.height());
|
||||
for (int y = 0; y < image.height(); y++)
|
||||
{
|
||||
memcpy((unsigned char*)finalImage.memptr() + y * image.width() * 3, static_cast<unsigned char*>(image.scanLine(y)), image.width() * 3);
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
#include <grabber/AudioGrabberLinux.h>
|
||||
#include <grabber/audio/AudioGrabberLinux.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
typedef void* (*THREADFUNCPTR)(void*);
|
||||
static void * AudioThreadRunner(void* params)
|
||||
{
|
||||
AudioGrabberLinux* This = static_cast<AudioGrabberLinux*>(params);
|
||||
|
||||
Debug(This->getLog(), "Audio Thread Started");
|
||||
|
||||
snd_pcm_sframes_t framesAvailable = 0;
|
||||
|
||||
while (This->_isRunning.load(std::memory_order_acquire))
|
||||
{
|
||||
snd_pcm_wait(This->_captureDevice, 1000);
|
||||
|
||||
if ((framesAvailable = snd_pcm_avail(This->_captureDevice)) > 0)
|
||||
This->processAudioBuffer(framesAvailable);
|
||||
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
Debug(This->getLog(), "Audio Thread Shutting Down");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AudioGrabberLinux::AudioGrabberLinux()
|
||||
: AudioGrabber()
|
||||
@@ -121,7 +141,7 @@ bool AudioGrabberLinux::configureCaptureInterface()
|
||||
snd_pcm_close(_captureDevice);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ((error = snd_pcm_hw_params_set_access(_captureDevice, _captureDeviceConfig, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
{
|
||||
Error(_log, "Failed to configure interleaved mode: %s", snd_strerror(error));
|
||||
@@ -129,7 +149,7 @@ bool AudioGrabberLinux::configureCaptureInterface()
|
||||
snd_pcm_close(_captureDevice);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ((error = snd_pcm_hw_params_set_format(_captureDevice, _captureDeviceConfig, SND_PCM_FORMAT_S16_LE)) < 0)
|
||||
{
|
||||
Error(_log, "Failed to configure capture format: %s", snd_strerror(error));
|
||||
@@ -169,7 +189,7 @@ bool AudioGrabberLinux::configureCaptureInterface()
|
||||
snd_pcm_close(_captureDevice);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -189,11 +209,6 @@ bool AudioGrabberLinux::start()
|
||||
_isRunning.store(true, std::memory_order_release);
|
||||
|
||||
pthread_attr_t threadAttributes;
|
||||
int threadPriority = 1;
|
||||
|
||||
sched_param schedulerParameter;
|
||||
schedulerParameter.sched_priority = threadPriority;
|
||||
|
||||
if (pthread_attr_init(&threadAttributes) != 0)
|
||||
{
|
||||
Debug(_log, "Failed to create thread attributes");
|
||||
@@ -201,7 +216,7 @@ bool AudioGrabberLinux::start()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pthread_create(&_audioThread, &threadAttributes, static_cast<THREADFUNCPTR>(&AudioThreadRunner), static_cast<void*>(this)) != 0)
|
||||
if (pthread_create(&_audioThread, &threadAttributes, &AudioThreadRunner, static_cast<void*>(this)) != 0)
|
||||
{
|
||||
Debug(_log, "Failed to create audio capture thread");
|
||||
stop();
|
||||
@@ -239,7 +254,7 @@ void AudioGrabberLinux::processAudioBuffer(snd_pcm_sframes_t frames)
|
||||
ssize_t bytes = snd_pcm_frames_to_bytes(_captureDevice, frames);
|
||||
|
||||
int16_t * buffer = static_cast<int16_t*>(calloc(static_cast<size_t>(bytes / 2), sizeof(int16_t)));
|
||||
|
||||
|
||||
if (frames == 0)
|
||||
{
|
||||
buffer[0] = 0;
|
||||
@@ -293,25 +308,3 @@ QString AudioGrabberLinux::getDeviceName(const QString& devicePath) const
|
||||
|
||||
return _deviceProperties.value(devicePath).name;
|
||||
}
|
||||
|
||||
static void * AudioThreadRunner(void* params)
|
||||
{
|
||||
AudioGrabberLinux* This = static_cast<AudioGrabberLinux*>(params);
|
||||
|
||||
Debug(This->getLog(), "Audio Thread Started");
|
||||
|
||||
snd_pcm_sframes_t framesAvailable = 0;
|
||||
|
||||
while (This->_isRunning.load(std::memory_order_acquire))
|
||||
{
|
||||
snd_pcm_wait(This->_captureDevice, 1000);
|
||||
|
||||
if ((framesAvailable = snd_pcm_avail(This->_captureDevice)) > 0)
|
||||
This->processAudioBuffer(framesAvailable);
|
||||
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
Debug(This->getLog(), "Audio Thread Shutting Down");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <grabber/AudioGrabberWindows.h>
|
||||
#include <grabber/audio/AudioGrabberWindows.h>
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include <QImage>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
@@ -61,11 +64,14 @@ bool AudioGrabberWindows::configureCaptureInterface()
|
||||
// wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec,
|
||||
// nBlockAlign, wBitsPerSample, cbSize
|
||||
|
||||
notificationSize = max(1024, audioFormat.nAvgBytesPerSec / 8);
|
||||
#ifdef WIN32
|
||||
#undef max
|
||||
#endif
|
||||
notificationSize = std::max(static_cast<DWORD>(1024), static_cast<DWORD>(audioFormat.nAvgBytesPerSec / 8));
|
||||
notificationSize -= notificationSize % audioFormat.nBlockAlign;
|
||||
|
||||
bufferCaptureSize = notificationSize * AUDIO_NOTIFICATION_COUNT;
|
||||
|
||||
|
||||
DSCBUFFERDESC bufferDesc;
|
||||
bufferDesc.dwSize = sizeof(DSCBUFFERDESC);
|
||||
bufferDesc.dwFlags = 0;
|
||||
@@ -74,7 +80,7 @@ bool AudioGrabberWindows::configureCaptureInterface()
|
||||
bufferDesc.lpwfxFormat = &audioFormat;
|
||||
bufferDesc.dwFXCount = 0;
|
||||
bufferDesc.lpDSCFXDesc = NULL;
|
||||
|
||||
|
||||
// Create Capture Device's Buffer
|
||||
LPDIRECTSOUNDCAPTUREBUFFER preBuffer;
|
||||
if (FAILED(recordingDevice->CreateCaptureBuffer(&bufferDesc, &preBuffer, NULL)))
|
||||
@@ -95,7 +101,7 @@ bool AudioGrabberWindows::configureCaptureInterface()
|
||||
}
|
||||
|
||||
preBuffer->Release();
|
||||
|
||||
|
||||
// Create Notifications
|
||||
LPDIRECTSOUNDNOTIFY8 notify;
|
||||
|
||||
@@ -106,7 +112,7 @@ bool AudioGrabberWindows::configureCaptureInterface()
|
||||
recordingBuffer->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create Events
|
||||
notificationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
@@ -127,11 +133,11 @@ bool AudioGrabberWindows::configureCaptureInterface()
|
||||
positionNotify[i].dwOffset = (notificationSize * i) + notificationSize - 1;
|
||||
positionNotify[i].hEventNotify = notificationEvent;
|
||||
}
|
||||
|
||||
|
||||
// Set Notifications
|
||||
notify->SetNotificationPositions(AUDIO_NOTIFICATION_COUNT, positionNotify);
|
||||
notify->Release();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -156,12 +162,12 @@ bool AudioGrabberWindows::start()
|
||||
}
|
||||
|
||||
Info(_log, "Capture audio from %s", QSTRING_CSTR(getDeviceName(_device)));
|
||||
|
||||
|
||||
if (!this->configureCaptureInterface())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (FAILED(recordingBuffer->Start(DSCBSTART_LOOPING)))
|
||||
{
|
||||
Error(_log, "Failed starting audio capture from '%s'", QSTRING_CSTR(getDeviceName(_device)));
|
||||
@@ -208,7 +214,7 @@ void AudioGrabberWindows::stop()
|
||||
{
|
||||
Error(_log, "Audio capture failed to stop: '%s'", QSTRING_CSTR(getDeviceName(_device)));
|
||||
}
|
||||
|
||||
|
||||
if (FAILED(recordingBuffer->Release()))
|
||||
{
|
||||
Error(_log, "Failed to release recording buffer: '%s'", QSTRING_CSTR(getDeviceName(_device)));
|
||||
@@ -300,7 +306,7 @@ void AudioGrabberWindows::processAudioBuffer()
|
||||
|
||||
// Buffer wrapped around, read second position
|
||||
if (capturedAudio2 != NULL)
|
||||
{
|
||||
{
|
||||
bufferCapturePosition += capturedAudio2Length;
|
||||
bufferCapturePosition %= bufferCaptureSize; // Circular Buffer
|
||||
}
|
||||
@@ -312,13 +318,13 @@ void AudioGrabberWindows::processAudioBuffer()
|
||||
{
|
||||
CopyMemory(readBuffer + capturedAudioLength, capturedAudio2, capturedAudio2Length);
|
||||
}
|
||||
|
||||
|
||||
// Release Buffer Lock
|
||||
recordingBuffer->Unlock(capturedAudio, capturedAudioLength, capturedAudio2, capturedAudio2Length);
|
||||
|
||||
|
||||
// Process Audio Frame
|
||||
this->processAudioFrame(readBuffer, frameSize);
|
||||
|
||||
|
||||
delete[] readBuffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <grabber/AudioWrapper.h>
|
||||
#include <grabber/audio/AudioWrapper.h>
|
||||
#include <hyperion/GrabberWrapper.h>
|
||||
#include <QObject>
|
||||
#include <QMetaType>
|
||||
|
||||
AudioWrapper::AudioWrapper()
|
||||
: GrabberWrapper("AudioGrabber", &_grabber)
|
||||
, _grabber()
|
||||
{
|
||||
// register the image type
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
# Define the current source locations
|
||||
SET( CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber )
|
||||
SET( CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio )
|
||||
|
||||
|
||||
if (WIN32)
|
||||
FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Windows.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Windows.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp")
|
||||
elseif(${CMAKE_SYSTEM} MATCHES "Linux")
|
||||
FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Linux.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Linux.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp")
|
||||
elseif (APPLE)
|
||||
#TODO
|
||||
#FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Apple.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Apple.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp")
|
||||
if(WIN32)
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
set(AUDIO_GRABBER_SOURCES
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioGrabberWindows.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioGrabberWindows.cpp
|
||||
)
|
||||
elseif(CMAKE_HOST_UNIX AND NOT APPLE)
|
||||
set(AUDIO_GRABBER_SOURCES
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioGrabberLinux.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioGrabberLinux.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library( audio-grabber ${AUDIO_GRABBER_SOURCES} )
|
||||
add_library(audio-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioWrapper.cpp
|
||||
${AUDIO_GRABBER_SOURCES}
|
||||
)
|
||||
|
||||
set(AUDIO_LIBS hyperion)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
set(AUDIO_LIBS ${AUDIO_LIBS} DSound)
|
||||
elseif(${CMAKE_SYSTEM} MATCHES "Linux")
|
||||
find_package(ALSA REQUIRED)
|
||||
if (ALSA_FOUND)
|
||||
include_directories(${ALSA_INCLUDE_DIRS})
|
||||
set(AUDIO_LIBS ${AUDIO_LIBS} ${ALSA_LIBRARIES})
|
||||
endif(ALSA_FOUND)
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
set(AUDIO_LIBS ${AUDIO_LIBS} Threads::Threads) # PRIVATE
|
||||
if(WIN32)
|
||||
set(AUDIO_LIBS DSound)
|
||||
elseif(CMAKE_HOST_UNIX AND NOT APPLE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(ALSA REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
set(AUDIO_LIBS ${ALSA_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
target_link_libraries(audio-grabber
|
||||
hyperion
|
||||
${AUDIO_LIBS}
|
||||
)
|
||||
|
||||
target_link_libraries(audio-grabber ${AUDIO_LIBS} ${QT_LIBRARIES})
|
||||
if(CMAKE_HOST_UNIX AND NOT APPLE)
|
||||
target_include_directories(audio-grabber PUBLIC ${ALSA_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# Define the current source locations
|
||||
SET( CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber )
|
||||
SET( CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/directx )
|
||||
find_package(DirectX9 REQUIRED)
|
||||
|
||||
include_directories(${DIRECTX9_INCLUDE_DIRS})
|
||||
|
||||
FILE ( GLOB DIRECTX_GRAB_SOURCES "${CURRENT_HEADER_DIR}/DirectX*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library( directx-grabber ${DIRECTX_GRAB_SOURCES} )
|
||||
add_library(directx-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/directx/DirectXGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/directx/DirectXWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/directx/DirectXGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/directx/DirectXWrapper.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(directx-grabber
|
||||
hyperion
|
||||
${DIRECTX9_LIBRARIES}
|
||||
hyperion
|
||||
${DIRECTX9_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories(directx-grabber PUBLIC
|
||||
${DIRECTX9_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <windows.h>
|
||||
#include <grabber/DirectXGrabber.h>
|
||||
#include <grabber/directx/DirectXGrabber.h>
|
||||
|
||||
#pragma comment(lib, "d3d9.lib")
|
||||
#pragma comment(lib,"d3dx9.lib")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/DirectXWrapper.h>
|
||||
#include <grabber/directx/DirectXWrapper.h>
|
||||
|
||||
DirectXWrapper::DirectXWrapper( int updateRate_Hz,
|
||||
int display,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Find the BCM-package (VC control)
|
||||
if( "${PLATFORM}" MATCHES rpi)
|
||||
if("${PLATFORM}" MATCHES rpi)
|
||||
find_package(BCM)
|
||||
if(BCM_FOUND)
|
||||
add_definitions(-DBCM_FOUND)
|
||||
@@ -9,21 +9,20 @@ else()
|
||||
set(BCM_LIBRARY "")
|
||||
endif()
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
|
||||
add_library(dispmanx-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/dispmanx/DispmanxFrameGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/dispmanx/DispmanxWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx/DispmanxWrapper.cpp
|
||||
)
|
||||
|
||||
FILE ( GLOB DispmanxGrabberSOURCES "${CURRENT_HEADER_DIR}/Dispmanx*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(dispmanx-grabber ${DispmanxGrabberSOURCES})
|
||||
add_definitions(-DBCM_LIBRARY="${BCM_LIBRARY}")
|
||||
|
||||
target_link_libraries(dispmanx-grabber
|
||||
hyperion
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
target_include_directories(dispmanx-grabber PUBLIC
|
||||
${BCM_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(dispmanx-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace {
|
||||
} //End of constants
|
||||
|
||||
// Local includes
|
||||
#include "grabber/DispmanxFrameGrabber.h"
|
||||
#include "grabber/dispmanx/DispmanxFrameGrabber.h"
|
||||
|
||||
DispmanxFrameGrabber::DispmanxFrameGrabber()
|
||||
: Grabber("DISPMANXGRABBER")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/DispmanxWrapper.h>
|
||||
#include <grabber/dispmanx/DispmanxWrapper.h>
|
||||
|
||||
DispmanxWrapper::DispmanxWrapper( int updateRate_Hz,
|
||||
int pixelDecimation
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/framebuffer)
|
||||
|
||||
FILE ( GLOB FramebufferGrabberSOURCES "${CURRENT_HEADER_DIR}/Framebuffer*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(framebuffer-grabber ${FramebufferGrabberSOURCES} )
|
||||
add_library(framebuffer-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/framebuffer/FramebufferFrameGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/framebuffer/FramebufferWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/framebuffer/FramebufferWrapper.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(framebuffer-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES})
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ const char DISCOVERY_FILEPATTERN[] = "fb?";
|
||||
} //End of constants
|
||||
|
||||
// Local includes
|
||||
#include <grabber/FramebufferFrameGrabber.h>
|
||||
#include <grabber/framebuffer/FramebufferFrameGrabber.h>
|
||||
|
||||
FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device)
|
||||
: Grabber("FRAMEBUFFERGRABBER")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/FramebufferWrapper.h>
|
||||
#include <grabber/framebuffer/FramebufferWrapper.h>
|
||||
|
||||
FramebufferWrapper::FramebufferWrapper( int updateRate_Hz,
|
||||
const QString & device,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/osx)
|
||||
|
||||
FILE ( GLOB OsxGrabberSOURCES "${CURRENT_HEADER_DIR}/Osx*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(osx-grabber ${OsxGrabberSOURCES} )
|
||||
add_library(osx-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/osx/OsxFrameGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/osx/OsxWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxFrameGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxWrapper.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(osx-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES})
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <iostream>
|
||||
|
||||
// Local includes
|
||||
#include <grabber/OsxFrameGrabber.h>
|
||||
#include <grabber/osx/OsxFrameGrabber.h>
|
||||
|
||||
//Qt
|
||||
#include <QJsonObject>
|
||||
@@ -124,6 +124,7 @@ bool OsxFrameGrabber::setDisplayIndex(int index)
|
||||
CGImageRelease(image);
|
||||
}
|
||||
}
|
||||
delete[] activeDspys;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -201,7 +202,7 @@ QJsonObject OsxFrameGrabber::discover(const QJsonObject& params)
|
||||
defaults["video_input"] = video_inputs_default;
|
||||
inputsDiscovered["default"] = defaults;
|
||||
}
|
||||
delete [] activeDspys;
|
||||
delete[] activeDspys;
|
||||
}
|
||||
|
||||
if (inputsDiscovered.isEmpty())
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
#ifndef __APPLE__
|
||||
#include <grabber/OsxFrameGrabberMock.h>
|
||||
|
||||
unsigned __osx_frame_counter = 0;
|
||||
const int __screenWidth = 800;
|
||||
const int __screenHeight = 600;
|
||||
|
||||
CGError CGGetActiveDisplayList(uint32_t maxDisplays, CGDirectDisplayID *activeDisplays, uint32_t *displayCount)
|
||||
{
|
||||
if (maxDisplays == 0 || activeDisplays == nullptr)
|
||||
{
|
||||
*displayCount = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayCount = &maxDisplays;
|
||||
if (activeDisplays != nullptr)
|
||||
{
|
||||
for (CGDirectDisplayID i = 0; i < maxDisplays; ++i)
|
||||
{
|
||||
activeDisplays[i] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return kCGErrorFailure;
|
||||
}
|
||||
}
|
||||
return kCGErrorSuccess;
|
||||
}
|
||||
|
||||
CGImageRef CGDisplayCreateImage(CGDirectDisplayID display)
|
||||
{
|
||||
CGImageRef image = new CGImage(__screenWidth / (display+1), __screenHeight / (display+1));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void CGImageRelease(CGImageRef image)
|
||||
{
|
||||
delete image;
|
||||
}
|
||||
|
||||
CGImageRef CGImageGetDataProvider(CGImageRef image)
|
||||
{
|
||||
__osx_frame_counter++;
|
||||
if (__osx_frame_counter > 100)
|
||||
{
|
||||
__osx_frame_counter = 0;
|
||||
}
|
||||
|
||||
ColorRgb color[4] = {ColorRgb::RED, ColorRgb::BLUE, ColorRgb::GREEN, ColorRgb::WHITE};
|
||||
if (__osx_frame_counter < 25)
|
||||
{
|
||||
color[0] = ColorRgb::WHITE;
|
||||
color[1] = ColorRgb::RED;
|
||||
color[2] = ColorRgb::BLUE;
|
||||
color[3] = ColorRgb::GREEN;
|
||||
}
|
||||
else if(__osx_frame_counter < 50)
|
||||
{
|
||||
color[1] = ColorRgb::WHITE;
|
||||
color[2] = ColorRgb::RED;
|
||||
color[3] = ColorRgb::BLUE;
|
||||
color[0] = ColorRgb::GREEN;
|
||||
}
|
||||
else if(__osx_frame_counter < 75)
|
||||
{
|
||||
color[2] = ColorRgb::WHITE;
|
||||
color[3] = ColorRgb::RED;
|
||||
color[0] = ColorRgb::BLUE;
|
||||
color[1] = ColorRgb::GREEN;
|
||||
}
|
||||
unsigned w = image->width();
|
||||
unsigned h = image->height();
|
||||
|
||||
for (unsigned y=0; y<h; y++)
|
||||
{
|
||||
for (unsigned x=0; x<w; x++)
|
||||
{
|
||||
unsigned id = 0;
|
||||
if (x < w/2 && y < h/2) id = 1;
|
||||
if (x < w/2 && y >= h/2) id = 2;
|
||||
if (x >= w/2 && y < h/2) id = 3;
|
||||
|
||||
image->memptr()[y*w + x] = color[id];
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
CFDataRef CGDataProviderCopyData(CGImageRef image)
|
||||
{
|
||||
const unsigned indexMax = image->width() * image->height() * CGImageGetBitsPerPixel(image);
|
||||
CFDataRef data = new CFData[indexMax];
|
||||
int lineLength = CGImageGetBytesPerRow(image);
|
||||
|
||||
for (unsigned y=0; y<image->height(); y++)
|
||||
{
|
||||
for (unsigned x=0; x<image->width(); x++)
|
||||
{
|
||||
int index = lineLength * y + x * CGImageGetBitsPerPixel(image);
|
||||
|
||||
data[index ] = (*image)(x,y).blue;
|
||||
data[index+1] = (*image)(x,y).green;
|
||||
data[index+2] = (*image)(x,y).red;
|
||||
data[index+3] = 0;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
unsigned char* CFDataGetBytePtr(CFDataRef imgData)
|
||||
{
|
||||
return imgData;
|
||||
}
|
||||
|
||||
unsigned CGImageGetWidth(CGImageRef image)
|
||||
{
|
||||
return image->width();
|
||||
}
|
||||
|
||||
unsigned CGImageGetHeight(CGImageRef image)
|
||||
{
|
||||
return image->height();
|
||||
}
|
||||
|
||||
unsigned CGImageGetBytesPerRow(CGImageRef image)
|
||||
{
|
||||
return image->width()*CGImageGetBitsPerPixel(image);
|
||||
}
|
||||
|
||||
unsigned CGImageGetBitsPerPixel(CGImageRef)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void CFRelease(CFDataRef imgData)
|
||||
{
|
||||
delete imgData;
|
||||
}
|
||||
|
||||
CGDisplayModeRef CGDisplayCopyDisplayMode(CGDirectDisplayID display)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
CGRect CGDisplayBounds(CGDirectDisplayID display)
|
||||
{
|
||||
CGRect rect;
|
||||
rect.size.width = __screenWidth / (display+1);
|
||||
rect.size.height = __screenHeight / (display+1);
|
||||
return rect;
|
||||
}
|
||||
void CGDisplayModeRelease(CGDisplayModeRef mode)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/OsxWrapper.h>
|
||||
#include <grabber/osx/OsxWrapper.h>
|
||||
|
||||
OsxWrapper::OsxWrapper( int updateRate_Hz,
|
||||
int display,
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/qt)
|
||||
|
||||
|
||||
FILE ( GLOB QT_GRAB_SOURCES "${CURRENT_HEADER_DIR}/Qt*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(qt-grabber ${QT_GRAB_SOURCES} )
|
||||
add_library(qt-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/qt/QtGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/qt/QtWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/qt/QtGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/qt/QtWrapper.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(qt-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// proj
|
||||
#include <grabber/QtGrabber.h>
|
||||
#include <grabber/qt/QtGrabber.h>
|
||||
|
||||
// qt
|
||||
#include <QPixmap>
|
||||
@@ -22,8 +22,8 @@ const bool verbose = false;
|
||||
QtGrabber::QtGrabber(int display, int cropLeft, int cropRight, int cropTop, int cropBottom)
|
||||
: Grabber("QTGRABBER", cropLeft, cropRight, cropTop, cropBottom)
|
||||
, _display(display)
|
||||
, _calculatedWidth(0)
|
||||
, _calculatedHeight(0)
|
||||
, _screenWidth(0)
|
||||
, _screenHeight(0)
|
||||
, _src_x(0)
|
||||
, _src_y(0)
|
||||
, _src_x_max(0)
|
||||
@@ -103,7 +103,7 @@ bool QtGrabber::setupDisplay()
|
||||
|
||||
Info(_log, "Available Displays:");
|
||||
int index = 0;
|
||||
for (auto* screen : qAsConst(screens))
|
||||
for (auto* screen : std::as_const(screens))
|
||||
{
|
||||
const QRect geo = screen->geometry();
|
||||
Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.width(), geo.height(), geo.x(), geo.y(), geo.x() + geo.width(), geo.y() + geo.height(), screen->depth());
|
||||
@@ -226,15 +226,15 @@ int QtGrabber::grabFrame(Image<ColorRgb>& image)
|
||||
QPixmap originalPixmap = grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
|
||||
#else
|
||||
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
|
||||
#endif
|
||||
#endif
|
||||
if (originalPixmap.isNull())
|
||||
{
|
||||
rc = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat(QImage::Format_RGB888);
|
||||
image.resize(static_cast<uint>(_calculatedWidth), static_cast<uint>(_calculatedHeight));
|
||||
QImage imageFrame = originalPixmap.toImage().scaled(_width, _height).convertToFormat(QImage::Format_RGB888);
|
||||
image.resize(_width, _height);
|
||||
|
||||
for (int y = 0; y < imageFrame.height(); y++)
|
||||
{
|
||||
@@ -263,27 +263,27 @@ int QtGrabber::updateScreenDimensions(bool force)
|
||||
{
|
||||
geo = _screen->geometry();
|
||||
}
|
||||
if (!force && _width == geo.width() && _height == geo.height())
|
||||
if (!force && _screenWidth == geo.width() && _height == geo.height())
|
||||
{
|
||||
// No update required
|
||||
return 0;
|
||||
}
|
||||
|
||||
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, geo.width(), geo.height());
|
||||
_width = geo.width();
|
||||
_height = geo.height();
|
||||
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geo.width(), geo.height());
|
||||
_screenWidth = geo.width();
|
||||
_screenHeight = geo.height();
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
// Image scaling is performed by Qt
|
||||
width = (_width > (_cropLeft + _cropRight))
|
||||
? ((_width - _cropLeft - _cropRight) / _pixelDecimation)
|
||||
: (_width / _pixelDecimation);
|
||||
width = (_screenWidth > (_cropLeft + _cropRight))
|
||||
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation)
|
||||
: (_screenWidth / _pixelDecimation);
|
||||
|
||||
height = (_height > (_cropTop + _cropBottom))
|
||||
? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
|
||||
: (_height / _pixelDecimation);
|
||||
height = (_screenHeight > (_cropTop + _cropBottom))
|
||||
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation)
|
||||
: (_screenHeight / _pixelDecimation);
|
||||
|
||||
// calculate final image dimensions and adjust top/left cropping in 3D modes
|
||||
if (_isVirtual)
|
||||
@@ -300,33 +300,33 @@ int QtGrabber::updateScreenDimensions(bool force)
|
||||
switch (_videoMode)
|
||||
{
|
||||
case VideoMode::VIDEO_3DSBS:
|
||||
_calculatedWidth = width / 2;
|
||||
_calculatedHeight = height;
|
||||
_width = width / 2;
|
||||
_height = height;
|
||||
_src_x = _src_x + (_cropLeft / 2);
|
||||
_src_y = _src_y + _cropTop;
|
||||
_src_x_max = (_width / 2) - _cropRight - _cropLeft;
|
||||
_src_y_max = _height - _cropBottom - _cropTop;
|
||||
_src_x_max = (_screenWidth / 2) - _cropRight - _cropLeft;
|
||||
_src_y_max = _screenHeight - _cropBottom - _cropTop;
|
||||
break;
|
||||
case VideoMode::VIDEO_3DTAB:
|
||||
_calculatedWidth = width;
|
||||
_calculatedHeight = height / 2;
|
||||
_width = width;
|
||||
_height = height / 2;
|
||||
_src_x = _src_x + _cropLeft;
|
||||
_src_y = _src_y + (_cropTop / 2);
|
||||
_src_x_max = _width - _cropRight - _cropLeft;
|
||||
_src_y_max = (_height / 2) - _cropBottom - _cropTop;
|
||||
_src_x_max = _screenWidth - _cropRight - _cropLeft;
|
||||
_src_y_max = (_screenHeight / 2) - _cropBottom - _cropTop;
|
||||
break;
|
||||
case VideoMode::VIDEO_2D:
|
||||
default:
|
||||
_calculatedWidth = width;
|
||||
_calculatedHeight = height;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_src_x = _src_x + _cropLeft;
|
||||
_src_y = _src_y + _cropTop;
|
||||
_src_x_max = _width - _cropRight - _cropLeft;
|
||||
_src_y_max = _height - _cropBottom - _cropTop;
|
||||
_src_x_max = _screenWidth - _cropRight - _cropLeft;
|
||||
_src_y_max = _screenHeight - _cropBottom - _cropTop;
|
||||
break;
|
||||
}
|
||||
|
||||
Info(_log, "Update output image resolution to [%dx%d]", _calculatedWidth, _calculatedHeight);
|
||||
Info(_log, "Update output image resolution to [%dx%d]", _width, _height);
|
||||
Debug(_log, "Grab screen area: %d,%d,%d,%d", _src_x, _src_y, _src_x_max, _src_y_max);
|
||||
|
||||
return 1;
|
||||
@@ -433,10 +433,10 @@ QJsonObject QtGrabber::discover(const QJsonObject& params)
|
||||
|
||||
if (screens.at(0)->size() != screens.at(0)->virtualSize())
|
||||
{
|
||||
QJsonObject in;
|
||||
in["name"] = "All Displays";
|
||||
in["inputIdx"] = screens.size();
|
||||
in["virtual"] = true;
|
||||
QJsonObject input;
|
||||
input["name"] = "All Displays";
|
||||
input["inputIdx"] = screens.size();
|
||||
input["virtual"] = true;
|
||||
|
||||
QJsonArray formats;
|
||||
QJsonObject format;
|
||||
@@ -454,15 +454,19 @@ QJsonObject QtGrabber::discover(const QJsonObject& params)
|
||||
format["resolutions"] = resolutionArray;
|
||||
formats.append(format);
|
||||
|
||||
in["formats"] = formats;
|
||||
video_inputs.append(in);
|
||||
input["formats"] = formats;
|
||||
video_inputs.append(input);
|
||||
}
|
||||
inputsDiscovered["video_inputs"] = video_inputs;
|
||||
|
||||
QJsonObject defaults, video_inputs_default, resolution_default;
|
||||
QJsonObject resolution_default;
|
||||
resolution_default["fps"] = _fps;
|
||||
|
||||
QJsonObject video_inputs_default;
|
||||
video_inputs_default["resolution"] = resolution_default;
|
||||
video_inputs_default["inputIdx"] = 0;
|
||||
|
||||
QJsonObject defaults;
|
||||
defaults["video_input"] = video_inputs_default;
|
||||
inputsDiscovered["default"] = defaults;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/QtWrapper.h>
|
||||
#include <grabber/qt/QtWrapper.h>
|
||||
|
||||
QtWrapper::QtWrapper( int updateRate_Hz,
|
||||
int display,
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
# Common cmake definition for external video grabber
|
||||
|
||||
# Add Turbo JPEG library
|
||||
if (ENABLE_V4L2 OR ENABLE_MF)
|
||||
find_package(TurboJPEG)
|
||||
if (TURBOJPEG_FOUND)
|
||||
add_definitions(-DHAVE_TURBO_JPEG)
|
||||
message( STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}")
|
||||
include_directories(${TurboJPEG_INCLUDE_DIRS})
|
||||
else ()
|
||||
message( STATUS "Turbo JPEG library not found, MJPEG camera format won't work.")
|
||||
endif ()
|
||||
endif()
|
||||
set(MF-grabber mediafoundation)
|
||||
set(V4L2-grabber v4l2)
|
||||
|
||||
# Define the wrapper/header/source locations and collect them
|
||||
SET(WRAPPER_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/video)
|
||||
SET(HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
if (ENABLE_MF)
|
||||
if(ENABLE_MF)
|
||||
project(mf-grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/mediafoundation)
|
||||
FILE (GLOB SOURCES "${WRAPPER_DIR}/*.cpp" "${HEADER_DIR}/Video*.h" "${HEADER_DIR}/MF*.h" "${HEADER_DIR}/Encoder*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp")
|
||||
set(grabber_project MF)
|
||||
set(MediaFoundationSourceReaderCallBack ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/mediafoundation/MFSourceReaderCB.h)
|
||||
elseif(ENABLE_V4L2)
|
||||
project(v4l2-grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/v4l2)
|
||||
FILE (GLOB SOURCES "${WRAPPER_DIR}/*.cpp" "${HEADER_DIR}/Video*.h" "${HEADER_DIR}/V4L2*.h" "${HEADER_DIR}/Encoder*.h" "${CURRENT_SOURCE_DIR}/*.cpp")
|
||||
set(grabber_project V4L2)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME} ${SOURCES})
|
||||
target_link_libraries(${PROJECT_NAME} hyperion ${QT_LIBRARIES})
|
||||
add_library(${PROJECT_NAME}
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/video/EncoderThread.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/video/VideoWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/video/EncoderThread.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/video/VideoWrapper.cpp
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/video/${${grabber_project}-grabber}/${grabber_project}Grabber.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/video/${${grabber_project}-grabber}/${grabber_project}Grabber.cpp
|
||||
${MediaFoundationSourceReaderCallBack}
|
||||
)
|
||||
|
||||
if(TURBOJPEG_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} ${TurboJPEG_LIBRARY})
|
||||
target_link_libraries(${PROJECT_NAME} hyperion)
|
||||
|
||||
# Add Turbo JPEG library
|
||||
if(ENABLE_V4L2 OR ENABLE_MF)
|
||||
find_package(TurboJPEG)
|
||||
if(TURBOJPEG_FOUND)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_TURBO_JPEG)
|
||||
message(STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}")
|
||||
target_link_libraries(${PROJECT_NAME} ${TurboJPEG_LIBRARY})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${TurboJPEG_INCLUDE_DIRS})
|
||||
else ()
|
||||
message(STATUS "Turbo JPEG library not found, MJPEG camera format won't work.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "grabber/EncoderThread.h"
|
||||
#include "grabber/video/EncoderThread.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
@@ -318,7 +318,7 @@ void EncoderThread::processImageMjpeg()
|
||||
}
|
||||
}
|
||||
|
||||
Image<ColorRgb> srcImage(static_cast<unsigned>(_width), static_cast<unsigned>(_height));
|
||||
Image<ColorRgb> srcImage(_width, _height);
|
||||
|
||||
if (tjDecompress2(_tjInstance, _localData , _size,
|
||||
reinterpret_cast<unsigned char*>(srcImage.memptr()), _width, 0, _height,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <QMetaType>
|
||||
|
||||
#include <grabber/VideoWrapper.h>
|
||||
#include <grabber/video/VideoWrapper.h>
|
||||
|
||||
// qt includes
|
||||
#include <QTimer>
|
||||
@@ -15,10 +15,13 @@ VideoWrapper::VideoWrapper()
|
||||
{
|
||||
// register the image type
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
qRegisterMetaType<Event>("Event");
|
||||
|
||||
// Handle the image in the captured thread (Media Foundation/V4L2) using a direct connection
|
||||
connect(&_grabber, SIGNAL(newFrame(const Image<ColorRgb>&)), this, SLOT(newFrame(const Image<ColorRgb>&)), Qt::DirectConnection);
|
||||
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
|
||||
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
}
|
||||
|
||||
VideoWrapper::~VideoWrapper()
|
||||
@@ -37,15 +40,6 @@ void VideoWrapper::stop()
|
||||
GrabberWrapper::stop();
|
||||
}
|
||||
|
||||
#if defined(ENABLE_CEC) && !defined(ENABLE_MF)
|
||||
|
||||
void VideoWrapper::handleCecEvent(CECEvent event)
|
||||
{
|
||||
_grabber.handleCecEvent(event);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::V4L2 && _grabberName.startsWith("V4L2"))
|
||||
@@ -100,11 +94,6 @@ void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument
|
||||
obj["hardware_saturation"].toInt(0),
|
||||
obj["hardware_hue"].toInt(0));
|
||||
|
||||
#if defined(ENABLE_CEC) && defined(ENABLE_V4L2)
|
||||
// CEC Standby
|
||||
_grabber.setCecDetectionEnable(obj["cecDetection"].toBool(true));
|
||||
#endif
|
||||
|
||||
// Software frame skipping
|
||||
_grabber.setFpsSoftwareDecimation(obj["fpsSoftwareDecimation"].toInt(1));
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "MFSourceReaderCB.h"
|
||||
#include "grabber/MFGrabber.h"
|
||||
#include "grabber/video/mediafoundation/MFGrabber.h"
|
||||
|
||||
// Constants
|
||||
namespace { const bool verbose = false; }
|
||||
@@ -537,7 +537,7 @@ void MFGrabber::process_image(const void *frameImageBuffer, int size)
|
||||
Error(_log, "Frame too small: %d != %d", size, _frameByteSize);
|
||||
else if (_threadManager != nullptr)
|
||||
{
|
||||
for (unsigned long i = 0; i < _threadManager->_threadCount; i++)
|
||||
for (int i = 0; i < _threadManager->_threadCount; i++)
|
||||
{
|
||||
if (!_threadManager->_threads[i]->isBusy())
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#pragma comment (lib, "strmiids.lib")
|
||||
#pragma comment (lib, "wmcodecdspuuid.lib")
|
||||
|
||||
#include <grabber/MFGrabber.h>
|
||||
#include <grabber/video/mediafoundation/MFGrabber.h>
|
||||
|
||||
#define SAFE_RELEASE(x) if(x) { x->Release(); x = nullptr; }
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QSet>
|
||||
|
||||
#include "grabber/V4L2Grabber.h"
|
||||
#include "grabber/video/v4l2/V4L2Grabber.h"
|
||||
|
||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
@@ -79,8 +79,7 @@ V4L2Grabber::V4L2Grabber()
|
||||
, _currentFrame(0)
|
||||
, _noSignalCounterThreshold(40)
|
||||
, _noSignalThresholdColor(ColorRgb{0,0,0})
|
||||
, _cecDetectionEnabled(true)
|
||||
, _cecStandbyActivated(false)
|
||||
, _standbyActivated(false)
|
||||
, _signalDetectionEnabled(true)
|
||||
, _noSignalDetected(false)
|
||||
, _noSignalCounter(0)
|
||||
@@ -1035,7 +1034,7 @@ bool V4L2Grabber::process_image(const void *p, int size)
|
||||
|
||||
void V4L2Grabber::newThreadFrame(Image<ColorRgb> image)
|
||||
{
|
||||
if (_cecDetectionEnabled && _cecStandbyActivated)
|
||||
if (_standbyActivated)
|
||||
return;
|
||||
|
||||
if (_signalDetectionEnabled)
|
||||
@@ -1203,16 +1202,6 @@ void V4L2Grabber::setSignalDetectionEnable(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
void V4L2Grabber::setCecDetectionEnable(bool enable)
|
||||
{
|
||||
if (_cecDetectionEnabled != enable)
|
||||
{
|
||||
_cecDetectionEnabled = enable;
|
||||
if(_initialized)
|
||||
Info(_log, "%s", QSTRING_CSTR(QString("CEC detection is now %1").arg(enable ? "enabled" : "disabled")));
|
||||
}
|
||||
}
|
||||
|
||||
bool V4L2Grabber::reload(bool force)
|
||||
{
|
||||
if (_reload || force)
|
||||
@@ -1231,26 +1220,6 @@ bool V4L2Grabber::reload(bool force)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_CEC)
|
||||
|
||||
void V4L2Grabber::handleCecEvent(CECEvent event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case CECEvent::On :
|
||||
Debug(_log,"CEC on event received");
|
||||
_cecStandbyActivated = false;
|
||||
return;
|
||||
case CECEvent::Off :
|
||||
Debug(_log,"CEC off event received");
|
||||
_cecStandbyActivated = true;
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QJsonArray V4L2Grabber::discover(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
@@ -1290,11 +1259,11 @@ QJsonArray V4L2Grabber::discover(const QJsonObject& params)
|
||||
format["format"] = pixelFormatToString(encodingFormat);
|
||||
|
||||
QMap<std::pair<int, int>, QSet<int>> combined = QMap<std::pair<int, int>, QSet<int>>();
|
||||
for (auto enc : input.value().encodingFormats.values(encodingFormat))
|
||||
for (const auto &enc : input.value().encodingFormats.values(encodingFormat))
|
||||
{
|
||||
std::pair<int, int> width_height{enc.width, enc.height};
|
||||
auto &com = combined[width_height];
|
||||
for (auto framerate : qAsConst(enc.framerates))
|
||||
for (auto framerate : enc.framerates)
|
||||
{
|
||||
com.insert(framerate);
|
||||
}
|
||||
@@ -1326,7 +1295,7 @@ QJsonArray V4L2Grabber::discover(const QJsonObject& params)
|
||||
device["video_inputs"] = video_inputs;
|
||||
|
||||
QJsonObject controls, controls_default;
|
||||
for (const auto &control : qAsConst(_deviceControls[device_property.key()]))
|
||||
for (const auto &control : std::as_const(_deviceControls[device_property.key()]))
|
||||
{
|
||||
QJsonObject property;
|
||||
property["minValue"] = control.minValue;
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/x11)
|
||||
|
||||
# Find X11
|
||||
find_package(X11 REQUIRED)
|
||||
|
||||
include_directories( ${X11_INCLUDES} )
|
||||
|
||||
if(APPLE)
|
||||
include_directories("/opt/X11/include")
|
||||
endif(APPLE)
|
||||
|
||||
FILE ( GLOB X11_SOURCES "${CURRENT_HEADER_DIR}/X11*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(x11-grabber ${X11_SOURCES} )
|
||||
add_library(x11-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/x11/X11Grabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/x11/X11Wrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/x11/X11Grabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/x11/X11Wrapper.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(x11-grabber
|
||||
hyperion
|
||||
${X11_LIBRARIES}
|
||||
${X11_Xrandr_LIB}
|
||||
${X11_Xrender_LIB}
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND X11_INCLUDES "/opt/X11/include")
|
||||
endif()
|
||||
|
||||
target_include_directories(x11-grabber PUBLIC
|
||||
${X11_INCLUDES}
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <utils/Logger.h>
|
||||
#include <grabber/X11Grabber.h>
|
||||
#include <grabber/x11/X11Grabber.h>
|
||||
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
@@ -18,16 +18,15 @@ X11Grabber::X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom)
|
||||
, _dstFormat(nullptr)
|
||||
, _srcPicture(None)
|
||||
, _dstPicture(None)
|
||||
, _calculatedWidth(0)
|
||||
, _calculatedHeight(0)
|
||||
, _screenWidth(0)
|
||||
, _screenHeight(0)
|
||||
, _src_x(cropLeft)
|
||||
, _src_y(cropTop)
|
||||
, _XShmAvailable(false)
|
||||
, _XRenderAvailable(false)
|
||||
, _XRandRAvailable(false)
|
||||
, _xShmAvailable(false)
|
||||
, _xRenderAvailable(false)
|
||||
, _xRandRAvailable(false)
|
||||
, _isWayland (false)
|
||||
, _logger{}
|
||||
, _image(0,0)
|
||||
{
|
||||
_logger = Logger::getInstance("X11");
|
||||
|
||||
@@ -53,17 +52,17 @@ void X11Grabber::freeResources()
|
||||
{
|
||||
XDestroyImage(_xImage);
|
||||
}
|
||||
if (_XRandRAvailable)
|
||||
if (_xRandRAvailable)
|
||||
{
|
||||
qApp->removeNativeEventFilter(this);
|
||||
}
|
||||
if(_XShmAvailable)
|
||||
if(_xShmAvailable)
|
||||
{
|
||||
XShmDetach(_x11Display, &_shminfo);
|
||||
shmdt(_shminfo.shmaddr);
|
||||
shmctl(_shminfo.shmid, IPC_RMID, 0);
|
||||
shmctl(_shminfo.shmid, IPC_RMID, nullptr);
|
||||
}
|
||||
if (_XRenderAvailable)
|
||||
if (_xRenderAvailable)
|
||||
{
|
||||
XRenderFreePicture(_x11Display, _srcPicture);
|
||||
XRenderFreePicture(_x11Display, _dstPicture);
|
||||
@@ -73,41 +72,41 @@ void X11Grabber::freeResources()
|
||||
|
||||
void X11Grabber::setupResources()
|
||||
{
|
||||
if (_XRandRAvailable)
|
||||
if (_xRandRAvailable)
|
||||
{
|
||||
qApp->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
if(_XShmAvailable)
|
||||
if(_xShmAvailable)
|
||||
{
|
||||
_xImage = XShmCreateImage(_x11Display, _windowAttr.visual, _windowAttr.depth, ZPixmap, NULL, &_shminfo, _calculatedWidth, _calculatedHeight);
|
||||
_xImage = XShmCreateImage(_x11Display, _windowAttr.visual, _windowAttr.depth, ZPixmap, nullptr, &_shminfo, _width, _height);
|
||||
_shminfo.shmid = shmget(IPC_PRIVATE, (size_t) _xImage->bytes_per_line * _xImage->height, IPC_CREAT|0777);
|
||||
_xImage->data = (char*)shmat(_shminfo.shmid,0,0);
|
||||
_xImage->data = (char*)shmat(_shminfo.shmid,nullptr,0);
|
||||
_shminfo.shmaddr = _xImage->data;
|
||||
_shminfo.readOnly = False;
|
||||
XShmAttach(_x11Display, &_shminfo);
|
||||
}
|
||||
|
||||
if (_XRenderAvailable)
|
||||
if (_xRenderAvailable)
|
||||
{
|
||||
_useImageResampler = false;
|
||||
_imageResampler.setHorizontalPixelDecimation(1);
|
||||
_imageResampler.setVerticalPixelDecimation(1);
|
||||
|
||||
if(_XShmPixmapAvailable)
|
||||
if(_xShmPixmapAvailable)
|
||||
{
|
||||
_pixmap = XShmCreatePixmap(_x11Display, _window, _xImage->data, &_shminfo, _calculatedWidth, _calculatedHeight, _windowAttr.depth);
|
||||
_pixmap = XShmCreatePixmap(_x11Display, _window, _xImage->data, &_shminfo, _width, _height, _windowAttr.depth);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pixmap = XCreatePixmap(_x11Display, _window, _calculatedWidth, _calculatedHeight, _windowAttr.depth);
|
||||
_pixmap = XCreatePixmap(_x11Display, _window, _width, _height, _windowAttr.depth);
|
||||
}
|
||||
_srcFormat = XRenderFindVisualFormat(_x11Display, _windowAttr.visual);
|
||||
_dstFormat = XRenderFindVisualFormat(_x11Display, _windowAttr.visual);
|
||||
_srcPicture = XRenderCreatePicture(_x11Display, _window, _srcFormat, CPRepeat, &_pictAttr);
|
||||
_dstPicture = XRenderCreatePicture(_x11Display, _pixmap, _dstFormat, CPRepeat, &_pictAttr);
|
||||
|
||||
XRenderSetPictureFilter(_x11Display, _srcPicture, FilterBilinear, NULL, 0);
|
||||
XRenderSetPictureFilter(_x11Display, _srcPicture, FilterBilinear, nullptr, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -162,19 +161,20 @@ bool X11Grabber::setupDisplay()
|
||||
{
|
||||
_window = DefaultRootWindow(_x11Display);
|
||||
|
||||
int dummy, pixmaps_supported;
|
||||
int dummy;
|
||||
int pixmaps_supported;
|
||||
|
||||
_XRandRAvailable = XRRQueryExtension(_x11Display, &_XRandREventBase, &dummy);
|
||||
_XRenderAvailable = XRenderQueryExtension(_x11Display, &dummy, &dummy);
|
||||
_XShmAvailable = XShmQueryExtension(_x11Display);
|
||||
_xRandRAvailable = (XRRQueryExtension(_x11Display, &_xRandREventBase, &dummy) != 0);
|
||||
_xRenderAvailable = (XRenderQueryExtension(_x11Display, &dummy, &dummy) != 0);
|
||||
_xShmAvailable = (XShmQueryExtension(_x11Display) != 0);
|
||||
XShmQueryVersion(_x11Display, &dummy, &dummy, &pixmaps_supported);
|
||||
_XShmPixmapAvailable = pixmaps_supported && XShmPixmapFormat(_x11Display) == ZPixmap;
|
||||
_xShmPixmapAvailable = (pixmaps_supported != 0) && XShmPixmapFormat(_x11Display) == ZPixmap;
|
||||
|
||||
Info(_log, "%s", QSTRING_CSTR(QString("XRandR=[%1] XRender=[%2] XShm=[%3] XPixmap=[%4]")
|
||||
.arg(_XRandRAvailable ? "available" : "unavailable",
|
||||
_XRenderAvailable ? "available" : "unavailable",
|
||||
_XShmAvailable ? "available" : "unavailable",
|
||||
_XShmPixmapAvailable ? "available" : "unavailable"))
|
||||
.arg(_xRandRAvailable ? "available" : "unavailable",
|
||||
_xRenderAvailable ? "available" : "unavailable",
|
||||
_xShmAvailable ? "available" : "unavailable",
|
||||
_xShmPixmapAvailable ? "available" : "unavailable"))
|
||||
);
|
||||
|
||||
result = (updateScreenDimensions(true) >=0);
|
||||
@@ -186,12 +186,17 @@ bool X11Grabber::setupDisplay()
|
||||
|
||||
int X11Grabber::grabFrame(Image<ColorRgb> & image, bool forceUpdate)
|
||||
{
|
||||
if (!_isEnabled) return 0;
|
||||
if (!_isEnabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (forceUpdate)
|
||||
{
|
||||
updateScreenDimensions(forceUpdate);
|
||||
}
|
||||
|
||||
if (_XRenderAvailable)
|
||||
if (_xRenderAvailable)
|
||||
{
|
||||
double scale_x = static_cast<double>(_windowAttr.width / _pixelDecimation) / static_cast<double>(_windowAttr.width);
|
||||
double scale_y = static_cast<double>(_windowAttr.height / _pixelDecimation) / static_cast<double>(_windowAttr.height);
|
||||
@@ -224,20 +229,20 @@ int X11Grabber::grabFrame(Image<ColorRgb> & image, bool forceUpdate)
|
||||
// src_y = cropTop, mask_x, mask_y, dest_x, dest_y, width, height
|
||||
XRenderComposite(
|
||||
_x11Display, PictOpSrc, _srcPicture, None, _dstPicture, ( _src_x/_pixelDecimation),
|
||||
(_src_y/_pixelDecimation), 0, 0, 0, 0, _calculatedWidth, _calculatedHeight);
|
||||
(_src_y/_pixelDecimation), 0, 0, 0, 0, _width, _height);
|
||||
|
||||
XSync(_x11Display, False);
|
||||
|
||||
if (_XShmAvailable)
|
||||
if (_xShmAvailable)
|
||||
{
|
||||
XShmGetImage(_x11Display, _pixmap, _xImage, 0, 0, AllPlanes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_xImage = XGetImage(_x11Display, _pixmap, 0, 0, _calculatedWidth, _calculatedHeight, AllPlanes, ZPixmap);
|
||||
_xImage = XGetImage(_x11Display, _pixmap, 0, 0, _width, _height, AllPlanes, ZPixmap);
|
||||
}
|
||||
}
|
||||
else if (_XShmAvailable)
|
||||
else if (_xShmAvailable)
|
||||
{
|
||||
// use xshm
|
||||
XShmGetImage(_x11Display, _window, _xImage, _src_x, _src_y, AllPlanes);
|
||||
@@ -245,7 +250,7 @@ int X11Grabber::grabFrame(Image<ColorRgb> & image, bool forceUpdate)
|
||||
else
|
||||
{
|
||||
// all things done by xgetimage
|
||||
_xImage = XGetImage(_x11Display, _window, _src_x, _src_y, _calculatedWidth, _calculatedHeight, AllPlanes, ZPixmap);
|
||||
_xImage = XGetImage(_x11Display, _window, _src_x, _src_y, _width, _height, AllPlanes, ZPixmap);
|
||||
}
|
||||
|
||||
if (_xImage == nullptr)
|
||||
@@ -268,46 +273,46 @@ int X11Grabber::updateScreenDimensions(bool force)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!force && _width == _windowAttr.width && _height == _windowAttr.height)
|
||||
if (!force && _screenWidth == _windowAttr.width && _screenHeight == _windowAttr.height)
|
||||
{
|
||||
// No update required
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_width || _height)
|
||||
if ((_screenWidth != 0) || _screenHeight != 0)
|
||||
{
|
||||
freeResources();
|
||||
}
|
||||
|
||||
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, _windowAttr.width, _windowAttr.height);
|
||||
_width = _windowAttr.width;
|
||||
_height = _windowAttr.height;
|
||||
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, _windowAttr.width, _windowAttr.height);
|
||||
_screenWidth = _windowAttr.width;
|
||||
_screenHeight = _windowAttr.height;
|
||||
|
||||
int width=0;
|
||||
int height=0;
|
||||
|
||||
// Image scaling is performed by XRender when available, otherwise by ImageResampler
|
||||
if (_XRenderAvailable)
|
||||
if (_xRenderAvailable)
|
||||
{
|
||||
width = (_width > (_cropLeft + _cropRight))
|
||||
? ((_width - _cropLeft - _cropRight) / _pixelDecimation)
|
||||
: _width / _pixelDecimation;
|
||||
width = (_screenWidth > (_cropLeft + _cropRight))
|
||||
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation)
|
||||
: _screenWidth / _pixelDecimation;
|
||||
|
||||
height = (_height > (_cropTop + _cropBottom))
|
||||
? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
|
||||
: _height / _pixelDecimation;
|
||||
height = (_screenHeight > (_cropTop + _cropBottom))
|
||||
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation)
|
||||
: _screenHeight / _pixelDecimation;
|
||||
|
||||
Info(_log, "Using XRender for grabbing");
|
||||
}
|
||||
else
|
||||
{
|
||||
width = (_width > (_cropLeft + _cropRight))
|
||||
? (_width - _cropLeft - _cropRight)
|
||||
: _width;
|
||||
width = (_screenWidth > (_cropLeft + _cropRight))
|
||||
? (_screenWidth - _cropLeft - _cropRight)
|
||||
: _screenWidth;
|
||||
|
||||
height = (_height > (_cropTop + _cropBottom))
|
||||
? (_height - _cropTop - _cropBottom)
|
||||
: _height;
|
||||
height = (_screenHeight > (_cropTop + _cropBottom))
|
||||
? (_screenHeight - _cropTop - _cropBottom)
|
||||
: _screenHeight;
|
||||
|
||||
Info(_log, "Using XGetImage for grabbing");
|
||||
}
|
||||
@@ -316,29 +321,28 @@ int X11Grabber::updateScreenDimensions(bool force)
|
||||
switch (_videoMode)
|
||||
{
|
||||
case VideoMode::VIDEO_3DSBS:
|
||||
_calculatedWidth = width /2;
|
||||
_calculatedHeight = height;
|
||||
_width = width /2;
|
||||
_height = height;
|
||||
_src_x = _cropLeft / 2;
|
||||
_src_y = _cropTop;
|
||||
break;
|
||||
case VideoMode::VIDEO_3DTAB:
|
||||
_calculatedWidth = width;
|
||||
_calculatedHeight = height / 2;
|
||||
_width = width;
|
||||
_height = height / 2;
|
||||
_src_x = _cropLeft;
|
||||
_src_y = _cropTop / 2;
|
||||
break;
|
||||
case VideoMode::VIDEO_2D:
|
||||
default:
|
||||
_calculatedWidth = width;
|
||||
_calculatedHeight = height;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_src_x = _cropLeft;
|
||||
_src_y = _cropTop;
|
||||
break;
|
||||
}
|
||||
|
||||
Info(_log, "Update output image resolution: [%dx%d] to [%dx%d]", _image.width(), _image.height(), _calculatedWidth, _calculatedHeight);
|
||||
|
||||
_image.resize(_calculatedWidth, _calculatedHeight);
|
||||
Info(_log, "Update output image resolution to [%dx%d]", _width, _height);
|
||||
_image.resize(_width, _height);
|
||||
setupResources();
|
||||
|
||||
return 1;
|
||||
@@ -384,14 +388,14 @@ bool X11Grabber::nativeEventFilter(const QByteArray & eventType, void * message,
|
||||
bool X11Grabber::nativeEventFilter(const QByteArray & eventType, void * message, long int * /*result*/)
|
||||
#endif
|
||||
{
|
||||
if (!_XRandRAvailable || eventType != "xcb_generic_event_t") {
|
||||
if (!_xRandRAvailable || eventType != "xcb_generic_event_t") {
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_generic_event_t *e = static_cast<xcb_generic_event_t*>(message);
|
||||
const uint8_t xEventType = XCB_EVENT_RESPONSE_TYPE(e);
|
||||
xcb_generic_event_t *event = static_cast<xcb_generic_event_t*>(message);
|
||||
const uint8_t xEventType = XCB_EVENT_RESPONSE_TYPE(event);
|
||||
|
||||
if (xEventType == _XRandREventBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
|
||||
if (xEventType == _xRandREventBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
|
||||
{
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
@@ -428,7 +432,7 @@ QJsonObject X11Grabber::discover(const QJsonObject& params)
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonObject in;
|
||||
QJsonObject input;
|
||||
|
||||
QString displayName;
|
||||
char* name;
|
||||
@@ -440,8 +444,8 @@ QJsonObject X11Grabber::discover(const QJsonObject& params)
|
||||
displayName = QString("Display:%1").arg(i);
|
||||
}
|
||||
|
||||
in["name"] = displayName;
|
||||
in["inputIdx"] = i;
|
||||
input["name"] = displayName;
|
||||
input["inputIdx"] = i;
|
||||
|
||||
QJsonArray formats;
|
||||
QJsonArray resolutionArray;
|
||||
@@ -457,19 +461,22 @@ QJsonObject X11Grabber::discover(const QJsonObject& params)
|
||||
format["resolutions"] = resolutionArray;
|
||||
formats.append(format);
|
||||
|
||||
in["formats"] = formats;
|
||||
video_inputs.append(in);
|
||||
input["formats"] = formats;
|
||||
video_inputs.append(input);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !video_inputs.isEmpty() )
|
||||
{
|
||||
inputsDiscovered["video_inputs"] = video_inputs;
|
||||
|
||||
QJsonObject defaults, video_inputs_default, resolution_default;
|
||||
QJsonObject resolution_default;
|
||||
resolution_default["fps"] = _fps;
|
||||
|
||||
QJsonObject video_inputs_default;
|
||||
video_inputs_default["resolution"] = resolution_default;
|
||||
video_inputs_default["inputIdx"] = 0;
|
||||
|
||||
QJsonObject defaults;
|
||||
defaults["video_input"] = video_inputs_default;
|
||||
inputsDiscovered["default"] = defaults;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/X11Wrapper.h>
|
||||
#include <grabber/x11/X11Wrapper.h>
|
||||
|
||||
X11Wrapper::X11Wrapper( int updateRate_Hz,
|
||||
int pixelDecimation,
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb)
|
||||
|
||||
find_package(XCB COMPONENTS SHM IMAGE RENDER RANDR REQUIRED)
|
||||
|
||||
include_directories(${XCB_INCLUDE_DIRS})
|
||||
|
||||
FILE (GLOB XCB_SOURCES "${CURRENT_HEADER_DIR}/Xcb*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(xcb-grabber ${XCB_SOURCES})
|
||||
add_library(xcb-grabber
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/xcb/XcbGrabber.h
|
||||
${CMAKE_SOURCE_DIR}/include/grabber/xcb/XcbWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbCommandExecutor.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbCommands.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbGrabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbWrapper.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(xcb-grabber
|
||||
hyperion
|
||||
${XCB_LIBRARIES}
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
if (NOT APPLE)
|
||||
target_link_libraries(
|
||||
xcb-grabber
|
||||
)
|
||||
endif()
|
||||
target_include_directories(xcb-grabber PUBLIC
|
||||
${XCB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <utils/Logger.h>
|
||||
#include <grabber/XcbGrabber.h>
|
||||
#include <grabber/xcb/XcbGrabber.h>
|
||||
|
||||
#include "XcbCommands.h"
|
||||
#include "XcbCommandExecutor.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <grabber/XcbWrapper.h>
|
||||
#include <grabber/xcb/XcbWrapper.h>
|
||||
|
||||
XcbWrapper::XcbWrapper( int updateRate_Hz,
|
||||
int pixelDecimation,
|
||||
|
||||
@@ -1,29 +1,58 @@
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
|
||||
|
||||
if(ENABLE_FLATBUF_SERVER)
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver
|
||||
)
|
||||
endif()
|
||||
|
||||
FILE ( GLOB Hyperion_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
SET(Hyperion_RESOURCES ${CURRENT_SOURCE_DIR}/resource.qrc)
|
||||
|
||||
add_library(hyperion
|
||||
${Hyperion_SOURCES}
|
||||
${Hyperion_RESOURCES}
|
||||
# Authorization Manager
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/AuthManager.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/AuthManager.cpp
|
||||
# Background Effect Handler
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/BGEffectHandler.h
|
||||
# Capture Control class
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/CaptureCont.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/CaptureCont.cpp
|
||||
# Color Adjustment
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/ColorAdjustment.h
|
||||
# Component Register
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/ComponentRegister.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/ComponentRegister.cpp
|
||||
# Grabber/Wrapper classes
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/Grabber.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/Grabber.cpp
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/GrabberWrapper.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/GrabberWrapper.cpp
|
||||
# Hyperion + Resources
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/Hyperion.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/Hyperion.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/resource.qrc
|
||||
# Instance Manager
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/HyperionIManager.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/HyperionIManager.cpp
|
||||
# Image Processor
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/ImageProcessor.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/ImageProcessor.cpp
|
||||
# ImageToLedsMap class
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/ImageToLedsMap.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/ImageToLedsMap.cpp
|
||||
# Led String
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/LedString.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/LedString.cpp
|
||||
# Linear Color Smoothing
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/LinearColorSmoothing.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/LinearColorSmoothing.cpp
|
||||
# Led Color Transform
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/MultiColorAdjustment.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/MultiColorAdjustment.cpp
|
||||
# Priority Muxer
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/PriorityMuxer.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/PriorityMuxer.cpp
|
||||
# Settings Manager
|
||||
${CMAKE_SOURCE_DIR}/include/hyperion/SettingsManager.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/hyperion/SettingsManager.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(hyperion
|
||||
blackborder
|
||||
events
|
||||
hyperion-utils
|
||||
leddevice
|
||||
database
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
if(ENABLE_BOBLIGHT_SERVER)
|
||||
|
||||
@@ -97,7 +97,7 @@ void CaptureCont::setSystemCaptureEnable(bool enable)
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, 0);
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, nullptr);
|
||||
_hyperion->clear(_systemCaptPrio);
|
||||
_systemInactiveTimer->stop();
|
||||
_systemCaptName = "";
|
||||
@@ -120,7 +120,7 @@ void CaptureCont::setV4LCaptureEnable(bool enable)
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, this, 0);
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, this, nullptr);
|
||||
_hyperion->clear(_v4lCaptPrio);
|
||||
_v4lInactiveTimer->stop();
|
||||
_v4lCaptName = "";
|
||||
@@ -143,7 +143,7 @@ void CaptureCont::setAudioCaptureEnable(bool enable)
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, this, 0);
|
||||
disconnect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, this, nullptr);
|
||||
_hyperion->clear(_audioCaptPrio);
|
||||
_audioInactiveTimer->stop();
|
||||
_audioCaptName = "";
|
||||
|
||||
@@ -61,7 +61,7 @@ ComponentRegister::ComponentRegister(Hyperion* hyperion)
|
||||
vect << COMP_FORWARDER;
|
||||
#endif
|
||||
|
||||
for(auto e : qAsConst(vect))
|
||||
for(auto e : std::as_const(vect))
|
||||
{
|
||||
_componentStates.emplace(e, (e == COMP_ALL));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
// utils includes
|
||||
#include <utils/GlobalSignals.h>
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
// qt
|
||||
#include <QTimer>
|
||||
@@ -26,33 +27,43 @@ bool GrabberWrapper::GLOBAL_GRABBER_AUDIO_ENABLE = false;
|
||||
GrabberWrapper::GrabberWrapper(const QString& grabberName, Grabber * ggrabber, int updateRate_Hz)
|
||||
: _grabberName(grabberName)
|
||||
, _log(Logger::getInstance(grabberName.toUpper()))
|
||||
, _timer(new QTimer(this))
|
||||
, _timer(nullptr)
|
||||
, _updateInterval_ms(1000/updateRate_Hz)
|
||||
, _ggrabber(ggrabber)
|
||||
, _image(0,0)
|
||||
{
|
||||
GrabberWrapper::instance = this;
|
||||
|
||||
_timer.reset(new QTimer(this));
|
||||
|
||||
// Configure the timer to generate events every n milliseconds
|
||||
_timer->setTimerType(Qt::PreciseTimer);
|
||||
_timer->setInterval(_updateInterval_ms);
|
||||
|
||||
connect(_timer, &QTimer::timeout, this, &GrabberWrapper::action);
|
||||
connect(_timer.get(), &QTimer::timeout, this, &GrabberWrapper::action);
|
||||
|
||||
// connect the image forwarding
|
||||
if (_grabberName.startsWith("V4L"))
|
||||
{
|
||||
connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setV4lImage);
|
||||
}
|
||||
else if (_grabberName.startsWith("Audio"))
|
||||
{
|
||||
connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setAudioImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setSystemImage);
|
||||
}
|
||||
|
||||
// listen for source requests
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::requestSource, this, &GrabberWrapper::handleSourceRequest);
|
||||
|
||||
QObject::connect(EventHandler::getInstance(), &EventHandler::signalEvent, this, &GrabberWrapper::handleEvent);
|
||||
}
|
||||
|
||||
GrabberWrapper::~GrabberWrapper()
|
||||
{
|
||||
_timer->stop();
|
||||
GrabberWrapper::instance = nullptr;
|
||||
}
|
||||
|
||||
@@ -83,6 +94,11 @@ void GrabberWrapper::stop()
|
||||
}
|
||||
}
|
||||
|
||||
void GrabberWrapper::handleEvent(Event event)
|
||||
{
|
||||
_ggrabber->handleEvent(event);
|
||||
}
|
||||
|
||||
bool GrabberWrapper::isActive() const
|
||||
{
|
||||
return _timer->isActive();
|
||||
@@ -95,19 +111,25 @@ QStringList GrabberWrapper::getActive(int inst, GrabberTypeFilter type) const
|
||||
if (type == GrabberTypeFilter::SCREEN || type == GrabberTypeFilter::ALL)
|
||||
{
|
||||
if (GRABBER_SYS_CLIENTS.contains(inst))
|
||||
{
|
||||
result << GRABBER_SYS_CLIENTS.value(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == GrabberTypeFilter::VIDEO || type == GrabberTypeFilter::ALL)
|
||||
{
|
||||
if (GRABBER_V4L_CLIENTS.contains(inst))
|
||||
{
|
||||
result << GRABBER_V4L_CLIENTS.value(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == GrabberTypeFilter::AUDIO || type == GrabberTypeFilter::ALL)
|
||||
{
|
||||
if (GRABBER_AUDIO_CLIENTS.contains(inst))
|
||||
{
|
||||
result << GRABBER_AUDIO_CLIENTS.value(inst);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -199,7 +221,9 @@ void GrabberWrapper::updateTimer(int interval)
|
||||
_timer->setInterval(_updateInterval_ms);
|
||||
|
||||
if(timerWasActive)
|
||||
{
|
||||
_timer->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,40 +284,64 @@ void GrabberWrapper::handleSourceRequest(hyperion::Components component, int hyp
|
||||
!_grabberName.startsWith("Audio"))
|
||||
{
|
||||
if (listen)
|
||||
{
|
||||
GRABBER_SYS_CLIENTS.insert(hyperionInd, _grabberName);
|
||||
}
|
||||
else
|
||||
{
|
||||
GRABBER_SYS_CLIENTS.remove(hyperionInd);
|
||||
}
|
||||
|
||||
if (GRABBER_SYS_CLIENTS.empty() || !getSysGrabberState())
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
start();
|
||||
}
|
||||
}
|
||||
else if (component == hyperion::Components::COMP_V4L &&
|
||||
_grabberName.startsWith("V4L"))
|
||||
{
|
||||
if (listen)
|
||||
{
|
||||
GRABBER_V4L_CLIENTS.insert(hyperionInd, _grabberName);
|
||||
}
|
||||
else
|
||||
{
|
||||
GRABBER_V4L_CLIENTS.remove(hyperionInd);
|
||||
}
|
||||
|
||||
if (GRABBER_V4L_CLIENTS.empty() || !getV4lGrabberState())
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
start();
|
||||
}
|
||||
}
|
||||
else if (component == hyperion::Components::COMP_AUDIO &&
|
||||
_grabberName.startsWith("Audio"))
|
||||
{
|
||||
if (listen)
|
||||
{
|
||||
GRABBER_AUDIO_CLIENTS.insert(hyperionInd, _grabberName);
|
||||
}
|
||||
else
|
||||
{
|
||||
GRABBER_AUDIO_CLIENTS.remove(hyperionInd);
|
||||
}
|
||||
|
||||
if (GRABBER_AUDIO_CLIENTS.empty())
|
||||
if (GRABBER_AUDIO_CLIENTS.empty() || !getAudioGrabberState())
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ Hyperion::Hyperion(quint8 instance, bool readonlyMode)
|
||||
, _instIndex(instance)
|
||||
, _settingsManager(new SettingsManager(instance, this, readonlyMode))
|
||||
, _componentRegister(nullptr)
|
||||
, _ledString(hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
|
||||
, _ledString(LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())))
|
||||
, _imageProcessor(nullptr)
|
||||
, _muxer(nullptr)
|
||||
, _raw2ledAdjustment(hyperion::createLedColorsAdjustment(static_cast<int>(_ledString.leds().size()), getSetting(settings::COLOR).object()))
|
||||
@@ -207,13 +207,16 @@ void Hyperion::stop()
|
||||
|
||||
void Hyperion::freeObjects()
|
||||
{
|
||||
//delete Background effect first that it does not kick in when other priorities are stopped
|
||||
delete _BGEffectHandler;
|
||||
//Disconnect Background effect first that it does not kick in when other priorities are stopped
|
||||
_BGEffectHandler->disconnect();
|
||||
|
||||
//Remove all priorities to switch off all leds
|
||||
clear(-1,true);
|
||||
|
||||
// delete components on exit of hyperion core
|
||||
|
||||
delete _BGEffectHandler;
|
||||
|
||||
#if defined(ENABLE_BOBLIGHT_SERVER)
|
||||
delete _boblightServer;
|
||||
#endif
|
||||
@@ -274,7 +277,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co
|
||||
#endif
|
||||
|
||||
// ledstring, img processor, muxer, ledGridSize (effect-engine image based effects), _ledBuffer and ByteOrder of ledstring
|
||||
_ledString = hyperion::createLedString(leds, hyperion::createColorOrder(getSetting(settings::DEVICE).object()));
|
||||
_ledString = LedString::createLedString(leds, hyperion::createColorOrder(getSetting(settings::DEVICE).object()));
|
||||
_imageProcessor->setLedString(_ledString);
|
||||
_muxer->updateLedColorsLength(static_cast<int>(_ledString.leds().size()));
|
||||
_ledGridSize = hyperion::getLedLayoutGridSize(leds);
|
||||
@@ -314,7 +317,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co
|
||||
// force ledString update, if device ByteOrder changed
|
||||
if(_ledDeviceWrapper->getColorOrder() != dev["colorOrder"].toString("rgb"))
|
||||
{
|
||||
_ledString = hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(dev));
|
||||
_ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(dev));
|
||||
_imageProcessor->setLedString(_ledString);
|
||||
|
||||
_ledStringColorOrder.clear();
|
||||
@@ -710,6 +713,18 @@ void Hyperion::update()
|
||||
else
|
||||
{
|
||||
_ledBuffer = priorityInfo.ledColors;
|
||||
|
||||
if (_ledString.hasBlackListedLeds())
|
||||
{
|
||||
for (unsigned long id : _ledString.blacklistedLedIds())
|
||||
{
|
||||
if (id > _ledBuffer.size()-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
_ledBuffer.at(id) = ColorRgb::BLACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// emit rawLedColors before transform
|
||||
|
||||
@@ -63,23 +63,38 @@ void HyperionIManager::stopAll()
|
||||
}
|
||||
}
|
||||
|
||||
void HyperionIManager::suspend()
|
||||
void HyperionIManager::handleEvent(Event event)
|
||||
{
|
||||
Info(_log,"Suspend all instances and enabled components");
|
||||
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
||||
for(const auto instance : instCopy)
|
||||
{
|
||||
emit instance->suspendRequest(true);
|
||||
Debug(_log,"%s Event [%d] received", eventToString(event), event);
|
||||
switch (event) {
|
||||
case Event::Suspend:
|
||||
toggleSuspend(true);
|
||||
break;
|
||||
|
||||
case Event::Resume:
|
||||
toggleSuspend(false);
|
||||
break;
|
||||
|
||||
case Event::Idle:
|
||||
toggleIdle(true);
|
||||
break;
|
||||
|
||||
case Event::ResumeIdle:
|
||||
toggleIdle(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HyperionIManager::resume()
|
||||
void HyperionIManager::toggleSuspend(bool isSuspend)
|
||||
{
|
||||
Info(_log,"Resume all instances and enabled components");
|
||||
Info(_log,"Put all instances in %s state", isSuspend ? "suspend" : "working");
|
||||
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
||||
for(const auto instance : instCopy)
|
||||
{
|
||||
emit instance->suspendRequest(false);
|
||||
emit instance->suspendRequest(isSuspend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,92 @@
|
||||
// STL includes
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
// hyperion includes
|
||||
#include <hyperion/LedString.h>
|
||||
|
||||
// QT includes
|
||||
#include <QJsonObject>
|
||||
|
||||
std::vector<Led>& LedString::leds()
|
||||
{
|
||||
return mLeds;
|
||||
return _leds;
|
||||
}
|
||||
|
||||
const std::vector<Led>& LedString::leds() const
|
||||
{
|
||||
return mLeds;
|
||||
return _leds;
|
||||
}
|
||||
|
||||
std::vector<int>& LedString::blacklistedLedIds()
|
||||
{
|
||||
return _blacklistedLedIds;
|
||||
}
|
||||
|
||||
const std::vector<int>& LedString::blacklistedLedIds() const
|
||||
{
|
||||
return _blacklistedLedIds;
|
||||
}
|
||||
|
||||
bool LedString::hasBlackListedLeds()
|
||||
{
|
||||
|
||||
if (_blacklistedLedIds.size() > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the 'led-string' with the integration area definition per led and the color
|
||||
* ordering of the RGB channels
|
||||
* @param ledsConfig The configuration of the led areas
|
||||
* @param deviceOrder The default RGB channel ordering
|
||||
* @return The constructed ledstring
|
||||
*/
|
||||
LedString LedString::createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder)
|
||||
{
|
||||
LedString ledString;
|
||||
const QString deviceOrderStr = colorOrderToString(deviceOrder);
|
||||
|
||||
for (signed i = 0; i < ledConfigArray.size(); ++i)
|
||||
{
|
||||
const QJsonObject& ledConfig = ledConfigArray[i].toObject();
|
||||
Led led;
|
||||
|
||||
led.minX_frac = qMax(0.0, qMin(1.0, ledConfig["hmin"].toDouble()));
|
||||
led.maxX_frac = qMax(0.0, qMin(1.0, ledConfig["hmax"].toDouble()));
|
||||
led.minY_frac = qMax(0.0, qMin(1.0, ledConfig["vmin"].toDouble()));
|
||||
led.maxY_frac = qMax(0.0, qMin(1.0, ledConfig["vmax"].toDouble()));
|
||||
// Fix if the user swapped min and max
|
||||
if (led.minX_frac > led.maxX_frac)
|
||||
{
|
||||
std::swap(led.minX_frac, led.maxX_frac);
|
||||
}
|
||||
if (led.minY_frac > led.maxY_frac)
|
||||
{
|
||||
std::swap(led.minY_frac, led.maxY_frac);
|
||||
}
|
||||
|
||||
// Get the order of the rgb channels for this led (default is device order)
|
||||
led.colorOrder = stringToColorOrder(ledConfig["colorOrder"].toString(deviceOrderStr));
|
||||
|
||||
led.isBlacklisted = false;
|
||||
if (led.minX_frac < std::numeric_limits<double>::epsilon() &&
|
||||
led.maxX_frac < std::numeric_limits<double>::epsilon() &&
|
||||
led.minY_frac < std::numeric_limits<double>::epsilon() &&
|
||||
led.maxY_frac < std::numeric_limits<double>::epsilon()
|
||||
)
|
||||
{
|
||||
led.isBlacklisted = true;
|
||||
ledString.blacklistedLedIds().push_back(i);
|
||||
}
|
||||
ledString.leds().push_back(led);
|
||||
}
|
||||
return ledString;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion
|
||||
, _enabled(false)
|
||||
, _enabledSystemCfg(false)
|
||||
, _smoothingType(SmoothingType::Linear)
|
||||
, tempValues(std::vector<uint64_t>(0, 0L))
|
||||
, tempValues(std::vector<uint64_t>())
|
||||
{
|
||||
QString subComponent = hyperion->property("instance").toString();
|
||||
_log= Logger::getInstance("SMOOTHING", subComponent);
|
||||
@@ -87,7 +87,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion
|
||||
|
||||
// add pause on cfg 1
|
||||
SmoothingCfg cfg {true, 0, 0};
|
||||
_cfgList.append(cfg);
|
||||
_cfgList.append(std::move(cfg));
|
||||
|
||||
// listen for comp changes
|
||||
connect(_hyperion, &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange);
|
||||
@@ -592,7 +592,7 @@ unsigned LinearColorSmoothing::addConfig(int settlingTime_ms, double ledUpdateFr
|
||||
ledUpdateFrequency_hz,
|
||||
updateDelay
|
||||
};
|
||||
_cfgList.append(cfg);
|
||||
_cfgList.append(std::move(cfg));
|
||||
|
||||
DebugIf(verbose && _enabled, _log,"%s", QSTRING_CSTR(getConfig(_cfgList.count()-1)));
|
||||
|
||||
|
||||
@@ -21,19 +21,19 @@ using namespace semver;
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const char DEFAULT_VERSION[] = "2.0.0-alpha.8";
|
||||
const char DEFAULT_VERSION[] = "2.0.0-alpha.8";
|
||||
} //End of constants
|
||||
|
||||
QJsonObject SettingsManager::schemaJson;
|
||||
|
||||
SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonlyMode)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("SETTINGSMGR", "I"+QString::number(instance)))
|
||||
, _instance(instance)
|
||||
, _sTable(new SettingsTable(instance, this))
|
||||
, _configVersion(DEFAULT_VERSION)
|
||||
, _previousVersion(DEFAULT_VERSION)
|
||||
, _readonlyMode(readonlyMode)
|
||||
, _log(Logger::getInstance("SETTINGSMGR", "I" + QString::number(instance)))
|
||||
, _instance(instance)
|
||||
, _sTable(new SettingsTable(instance, this))
|
||||
, _configVersion(DEFAULT_VERSION)
|
||||
, _previousVersion(DEFAULT_VERSION)
|
||||
, _readonlyMode(readonlyMode)
|
||||
{
|
||||
_sTable->setReadonlyMode(_readonlyMode);
|
||||
// get schema
|
||||
@@ -58,9 +58,9 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly
|
||||
}
|
||||
|
||||
// transform json to string lists
|
||||
QStringList keyList = defaultConfig.keys();
|
||||
const QStringList keyList = defaultConfig.keys();
|
||||
QStringList defValueList;
|
||||
for (const auto& key : qAsConst(keyList))
|
||||
for (const auto& key : keyList)
|
||||
{
|
||||
if (defaultConfig[key].isObject())
|
||||
{
|
||||
@@ -73,7 +73,7 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly
|
||||
}
|
||||
|
||||
// fill database with default data if required
|
||||
for (const auto& key : qAsConst(keyList))
|
||||
for (const auto& key : keyList)
|
||||
{
|
||||
QString val = defValueList.takeFirst();
|
||||
// prevent overwrite
|
||||
@@ -86,7 +86,7 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly
|
||||
// need to validate all data in database construct the entire data object
|
||||
// TODO refactor schemaChecker to accept QJsonArray in validate(); QJsonDocument container? To validate them per entry...
|
||||
QJsonObject dbConfig;
|
||||
for (const auto& key : qAsConst(keyList))
|
||||
for (const auto& key : keyList)
|
||||
{
|
||||
QJsonDocument doc = _sTable->getSettingsRecord(key);
|
||||
if (doc.isArray())
|
||||
@@ -242,9 +242,9 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct)
|
||||
_qconfig = config;
|
||||
|
||||
// extract keys and data
|
||||
QStringList keyList = config.keys();
|
||||
const QStringList keyList = config.keys();
|
||||
QStringList newValueList;
|
||||
for (const auto& key : qAsConst(keyList))
|
||||
for (const auto& key : keyList)
|
||||
{
|
||||
if (config[key].isObject())
|
||||
{
|
||||
@@ -258,7 +258,7 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct)
|
||||
|
||||
bool rc = true;
|
||||
// compare database data with new data to emit/save changes accordingly
|
||||
for (const auto& key : qAsConst(keyList))
|
||||
for (const auto& key : keyList)
|
||||
{
|
||||
QString data = newValueList.takeFirst();
|
||||
if (_sTable->getSettingsRecordString(key) != data)
|
||||
@@ -269,7 +269,15 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct)
|
||||
}
|
||||
else
|
||||
{
|
||||
emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(data.toLocal8Bit()));
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toUtf8(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
Error(_log, "Error parsing JSON: %s", QSTRING_CSTR(error.errorString()));
|
||||
rc = false;
|
||||
}
|
||||
else {
|
||||
emit settingsChanged(settings::stringToType(key), jsonDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -618,10 +626,10 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
||||
QJsonArray json;
|
||||
if (newForwarderConfig.contains("json"))
|
||||
{
|
||||
QJsonArray oldJson = newForwarderConfig["json"].toArray();
|
||||
const QJsonArray oldJson = newForwarderConfig["json"].toArray();
|
||||
QJsonObject newJsonConfig;
|
||||
|
||||
for (const QJsonValue& value : qAsConst(oldJson))
|
||||
for (const QJsonValue& value : oldJson)
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
@@ -661,10 +669,10 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
||||
QJsonArray flatbuffer;
|
||||
if (newForwarderConfig.contains("flat"))
|
||||
{
|
||||
QJsonArray oldFlatbuffer = newForwarderConfig["flat"].toArray();
|
||||
const QJsonArray oldFlatbuffer = newForwarderConfig["flat"].toArray();
|
||||
QJsonObject newFlattbufferConfig;
|
||||
|
||||
for (const QJsonValue& value : qAsConst(oldFlatbuffer))
|
||||
for (const QJsonValue& value : oldFlatbuffer)
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
@@ -715,6 +723,7 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
||||
}
|
||||
|
||||
//Migration steps for versions <= 2.0.13
|
||||
_previousVersion = targetVersion;
|
||||
targetVersion.setVersion("2.0.13");
|
||||
if (_previousVersion <= targetVersion)
|
||||
{
|
||||
@@ -730,12 +739,12 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
||||
{
|
||||
QString type = newDeviceConfig["type"].toString();
|
||||
|
||||
const QStringList serialDevices {"adalight", "dmx", "atmo", "sedu", "tpm2", "karate"};
|
||||
if ( serialDevices.contains(type ))
|
||||
const QStringList serialDevices{ "adalight", "dmx", "atmo", "sedu", "tpm2", "karate" };
|
||||
if (serialDevices.contains(type))
|
||||
{
|
||||
if (!newDeviceConfig.contains("rateList"))
|
||||
{
|
||||
newDeviceConfig["rateList"] = "CUSTOM";
|
||||
newDeviceConfig["rateList"] = "CUSTOM";
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
@@ -766,6 +775,164 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Migration steps for versions <= 2.0.16
|
||||
_previousVersion = targetVersion;
|
||||
targetVersion.setVersion("2.0.16");
|
||||
if (_previousVersion <= targetVersion)
|
||||
{
|
||||
Info(_log, "Instance [%u]: Migrate from version [%s] to version [%s] or later", _instance, _previousVersion.getVersion().c_str(), targetVersion.getVersion().c_str());
|
||||
|
||||
// Have Hostname/IP-address separate from port for LED-Devices
|
||||
if (config.contains("device"))
|
||||
{
|
||||
QJsonObject newDeviceConfig = config["device"].toObject();
|
||||
|
||||
if (newDeviceConfig.contains("type"))
|
||||
{
|
||||
QString type = newDeviceConfig["type"].toString();
|
||||
|
||||
if (type == "philipshue")
|
||||
{
|
||||
if (newDeviceConfig.contains("groupId"))
|
||||
{
|
||||
if (newDeviceConfig["groupId"].isDouble())
|
||||
{
|
||||
int groupID = newDeviceConfig["groupId"].toInt();
|
||||
newDeviceConfig["groupId"] = QString::number(groupID);
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newDeviceConfig.contains("lightIds"))
|
||||
{
|
||||
QJsonArray lightIds = newDeviceConfig.value("lightIds").toArray();
|
||||
// Iterate through the JSON array and update integer values to strings
|
||||
for (int i = 0; i < lightIds.size(); ++i) {
|
||||
QJsonValue value = lightIds.at(i);
|
||||
if (value.isDouble())
|
||||
{
|
||||
int lightId = value.toInt();
|
||||
lightIds.replace(i, QString::number(lightId));
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
newDeviceConfig["lightIds"] = lightIds;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (type == "nanoleaf")
|
||||
{
|
||||
if (newDeviceConfig.contains("panelStartPos"))
|
||||
{
|
||||
newDeviceConfig.remove("panelStartPos");
|
||||
migrated = true;
|
||||
}
|
||||
|
||||
if (newDeviceConfig.contains("panelOrderTopDown"))
|
||||
{
|
||||
int panelOrderTopDown;
|
||||
if (newDeviceConfig["panelOrderTopDown"].isDouble())
|
||||
{
|
||||
panelOrderTopDown = newDeviceConfig["panelOrderTopDown"].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
panelOrderTopDown = newDeviceConfig["panelOrderTopDown"].toString().toInt();
|
||||
}
|
||||
|
||||
newDeviceConfig.remove("panelOrderTopDown");
|
||||
if (panelOrderTopDown == 0)
|
||||
{
|
||||
newDeviceConfig["panelOrderTopDown"] = "top2down";
|
||||
migrated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (panelOrderTopDown == 1)
|
||||
{
|
||||
newDeviceConfig["panelOrderTopDown"] = "bottom2up";
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newDeviceConfig.contains("panelOrderLeftRight"))
|
||||
{
|
||||
int panelOrderLeftRight;
|
||||
if (newDeviceConfig["panelOrderLeftRight"].isDouble())
|
||||
{
|
||||
panelOrderLeftRight = newDeviceConfig["panelOrderLeftRight"].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
panelOrderLeftRight = newDeviceConfig["panelOrderLeftRight"].toString().toInt();
|
||||
}
|
||||
|
||||
newDeviceConfig.remove("panelOrderLeftRight");
|
||||
if (panelOrderLeftRight == 0)
|
||||
{
|
||||
newDeviceConfig["panelOrderLeftRight"] = "left2right";
|
||||
migrated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (panelOrderLeftRight == 1)
|
||||
{
|
||||
newDeviceConfig["panelOrderLeftRight"] = "right2left";
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (migrated)
|
||||
{
|
||||
config["device"] = newDeviceConfig;
|
||||
Debug(_log, "LED-Device records migrated");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("cecEvents"))
|
||||
{
|
||||
bool isCECEnabled {false};
|
||||
if (config.contains("grabberV4L2"))
|
||||
{
|
||||
QJsonObject newGrabberV4L2Config = config["grabberV4L2"].toObject();
|
||||
if (newGrabberV4L2Config.contains("cecDetection"))
|
||||
{
|
||||
isCECEnabled = newGrabberV4L2Config.value("cecDetection").toBool(false);
|
||||
newGrabberV4L2Config.remove("cecDetection");
|
||||
config["grabberV4L2"] = newGrabberV4L2Config;
|
||||
|
||||
QJsonObject newGCecEventsConfig = config["cecEvents"].toObject();
|
||||
newGCecEventsConfig["enable"] = isCECEnabled;
|
||||
if (!newGCecEventsConfig.contains("actions"))
|
||||
{
|
||||
QJsonObject action1
|
||||
{
|
||||
{"action", "Suspend"},
|
||||
{"event", "standby"}
|
||||
};
|
||||
QJsonObject action2
|
||||
{
|
||||
{"action", "Resume"},
|
||||
{"event", "set stream path"}
|
||||
};
|
||||
|
||||
QJsonArray actions { action1, action2 };
|
||||
newGCecEventsConfig.insert("actions",actions);
|
||||
}
|
||||
config["cecEvents"] = newGCecEventsConfig;
|
||||
|
||||
migrated = true;
|
||||
Debug(_log, "CEC configuration records migrated");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return migrated;
|
||||
|
||||
@@ -90,6 +90,18 @@
|
||||
"leds":
|
||||
{
|
||||
"$ref": "schema-leds.json"
|
||||
},
|
||||
"osEvents":
|
||||
{
|
||||
"$ref": "schema-osEvents.json"
|
||||
},
|
||||
"cecEvents":
|
||||
{
|
||||
"$ref": "schema-cecEvents.json"
|
||||
},
|
||||
"schedEvents":
|
||||
{
|
||||
"$ref": "schema-schedEvents.json"
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
|
||||
@@ -23,5 +23,9 @@
|
||||
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
||||
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
||||
<file alias="schema-network.json">schema/schema-network.json</file>
|
||||
<file alias="schema-eventActions.json">schema/schema-eventActions.json</file>
|
||||
<file alias="schema-schedEvents.json">schema/schema-schedEvents.json</file>
|
||||
<file alias="schema-osEvents.json">schema/schema-osEvents.json</file>
|
||||
<file alias="schema-cecEvents.json">schema/schema-cecEvents.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
95
libsrc/hyperion/schema/schema-cecEvents.json
Normal file
95
libsrc/hyperion/schema/schema-cecEvents.json
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_general_enable_title",
|
||||
"default": false,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"buttonReleaseDelayMs": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"title": "edt_conf_cec_button_release_delay_ms_title",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 500,
|
||||
"step": 50,
|
||||
"default": 0,
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"buttonRepeatRateMs": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"title": "edt_conf_cec_button_repeat_rate_ms_title",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 250,
|
||||
"step": 10,
|
||||
"default": 0,
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"doubleTapTimeoutMs": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"title": "edt_conf_cec_double_tap_timeout_ms_title",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 50,
|
||||
"maximum": 1000,
|
||||
"step": 50,
|
||||
"default": 200,
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_cec_actions_header_title",
|
||||
"minItems": 0,
|
||||
"required": false,
|
||||
"propertyOrder": 5,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"title": "edt_conf_cec_actions_header_item_title",
|
||||
"properties": {
|
||||
"event": {
|
||||
"type": "string",
|
||||
"title": "edt_conf_cec_event_title",
|
||||
"enum": [
|
||||
"standby",
|
||||
"set stream path",
|
||||
"F1 (blue)",
|
||||
"F2 (red)",
|
||||
"F3 (green)",
|
||||
"F4 (yellow)"
|
||||
],
|
||||
"options": {
|
||||
"enum_titles": [
|
||||
"edt_conf_enum_cec_opcode_standby",
|
||||
"edt_conf_enum_cec_opcode_set stream path",
|
||||
"edt_conf_enum_cec_key_f1_blue",
|
||||
"edt_conf_enum_cec_key_f2_red",
|
||||
"edt_conf_enum_cec_key_f3_green",
|
||||
"edt_conf_enum_cec_key_f4_yellow"
|
||||
]
|
||||
},
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"action": {
|
||||
"$ref": "schema-eventActions.json"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
25
libsrc/hyperion/schema/schema-eventActions.json
Normal file
25
libsrc/hyperion/schema/schema-eventActions.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "string",
|
||||
"title": "edt_conf_action_title",
|
||||
"enum": [
|
||||
"Suspend",
|
||||
"Resume",
|
||||
"ToggleSuspend",
|
||||
"Idle",
|
||||
"ResumeIdle",
|
||||
"ToggleIdle",
|
||||
"Restart"
|
||||
],
|
||||
"options": {
|
||||
"enum_titles": [
|
||||
"edt_conf_enum_action_suspend",
|
||||
"edt_conf_enum_action_resume",
|
||||
"edt_conf_enum_action_toggleSuspend",
|
||||
"edt_conf_enum_action_idle",
|
||||
"edt_conf_enum_action_resumeIdle",
|
||||
"edt_conf_enum_action_toggleIdle",
|
||||
"edt_conf_enum_action_restart"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,21 +212,13 @@
|
||||
"required": true,
|
||||
"propertyOrder": 23
|
||||
},
|
||||
"cecDetection": {
|
||||
"type": "boolean",
|
||||
"title": "edt_conf_v4l2_cecDetection_title",
|
||||
"default": false,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 24
|
||||
},
|
||||
"signalDetection": {
|
||||
"type": "boolean",
|
||||
"title": "edt_conf_v4l2_signalDetection_title",
|
||||
"default": false,
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 25
|
||||
"propertyOrder": 24
|
||||
},
|
||||
"redSignalThreshold": {
|
||||
"type": "integer",
|
||||
@@ -242,7 +234,7 @@
|
||||
},
|
||||
"access": "expert",
|
||||
"required": true,
|
||||
"propertyOrder": 26
|
||||
"propertyOrder": 25
|
||||
},
|
||||
"greenSignalThreshold": {
|
||||
"type": "integer",
|
||||
@@ -258,7 +250,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 27
|
||||
"propertyOrder": 26
|
||||
},
|
||||
"blueSignalThreshold": {
|
||||
"type": "integer",
|
||||
@@ -274,7 +266,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 28
|
||||
"propertyOrder": 27
|
||||
},
|
||||
"noSignalCounterThreshold": {
|
||||
"type": "integer",
|
||||
@@ -289,7 +281,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 29
|
||||
"propertyOrder": 28
|
||||
},
|
||||
"sDVOffsetMin": {
|
||||
"type": "number",
|
||||
@@ -305,7 +297,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 30
|
||||
"propertyOrder": 29
|
||||
},
|
||||
"sDVOffsetMax": {
|
||||
"type": "number",
|
||||
@@ -321,7 +313,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 31
|
||||
"propertyOrder": 30
|
||||
},
|
||||
"sDHOffsetMin": {
|
||||
"type": "number",
|
||||
@@ -337,7 +329,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 32
|
||||
"propertyOrder": 31
|
||||
},
|
||||
"sDHOffsetMax": {
|
||||
"type": "number",
|
||||
@@ -353,7 +345,7 @@
|
||||
},
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 33
|
||||
"propertyOrder": 32
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
||||
34
libsrc/hyperion/schema/schema-osEvents.json
Normal file
34
libsrc/hyperion/schema/schema-osEvents.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_os_events_heading_title",
|
||||
"properties": {
|
||||
"suspendEnable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_os_events_suspendEnable_title",
|
||||
"default": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"lockEnable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_os_events_lockEnable_title",
|
||||
"default": true,
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"suspendOnLockEnable": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"title": "edt_conf_os_events_suspendOnLockEnable_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"lockEnable": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 3
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
40
libsrc/hyperion/schema/schema-schedEvents.json
Normal file
40
libsrc/hyperion/schema/schema-schedEvents.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_general_enable_title",
|
||||
"default": false,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_sched_actions_header_title",
|
||||
"minItems": 0,
|
||||
"required": false,
|
||||
"propertyOrder": 2,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"title": "edt_conf_sched_actions_header_item_title",
|
||||
"properties": {
|
||||
"event": {
|
||||
"type": "string",
|
||||
"format": "time",
|
||||
"default": "23:00",
|
||||
"title": "edt_conf_time_event_title",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"action": {
|
||||
"$ref": "schema-eventActions.json"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"port" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_general_port_title",
|
||||
"title" : "edt_conf_webc_port_title",
|
||||
"minimum" : 80,
|
||||
"maximum" : 65535,
|
||||
"default" : 8090,
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/jsonserver)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/jsonserver)
|
||||
|
||||
FILE ( GLOB JsonServer_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(jsonserver ${JsonServer_SOURCES} )
|
||||
add_library(jsonserver
|
||||
${CMAKE_SOURCE_DIR}/include/jsonserver/JsonServer.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/jsonserver/JsonServer.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/jsonserver/JsonClientConnection.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/jsonserver/JsonClientConnection.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(jsonserver
|
||||
hyperion-api
|
||||
hyperion
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(jsonserver mdns)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ JsonServer::JsonServer(const QJsonDocument& config)
|
||||
JsonServer::~JsonServer()
|
||||
{
|
||||
qDeleteAll(_openConnections);
|
||||
_openConnections.clear();
|
||||
}
|
||||
|
||||
void JsonServer::initServer()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/leddevice)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/leddevice)
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/leddevice)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/leddevice)
|
||||
|
||||
if ( ENABLE_DEV_NETWORK )
|
||||
if(ENABLE_DEV_NETWORK)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network REQUIRED)
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_SERIAL )
|
||||
if(ENABLE_DEV_SERIAL)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS SerialPort REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -21,7 +21,7 @@ include_directories(
|
||||
dev_tinker
|
||||
)
|
||||
|
||||
FILE ( GLOB Leddevice_SOURCES
|
||||
file (GLOB Leddevice_SOURCES
|
||||
"${CURRENT_HEADER_DIR}/*.h"
|
||||
"${CURRENT_SOURCE_DIR}/*.h"
|
||||
"${CURRENT_SOURCE_DIR}/*.cpp"
|
||||
@@ -29,49 +29,48 @@ FILE ( GLOB Leddevice_SOURCES
|
||||
"${CURRENT_SOURCE_DIR}/dev_other/*.cpp"
|
||||
)
|
||||
|
||||
if ( ENABLE_OSX OR WIN32 )
|
||||
if(ENABLE_OSX OR WIN32)
|
||||
list(REMOVE_ITEM Leddevice_SOURCES "${CURRENT_SOURCE_DIR}/dev_other/LedDevicePiBlaster.h")
|
||||
list(REMOVE_ITEM Leddevice_SOURCES "${CURRENT_SOURCE_DIR}/dev_other/LedDevicePiBlaster.cpp")
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_NETWORK )
|
||||
FILE ( GLOB Leddevice_NETWORK_SOURCES "${CURRENT_SOURCE_DIR}/dev_net/*.h" "${CURRENT_SOURCE_DIR}/dev_net/*.cpp")
|
||||
if(ENABLE_DEV_NETWORK)
|
||||
file (GLOB Leddevice_NETWORK_SOURCES "${CURRENT_SOURCE_DIR}/dev_net/*.h" "${CURRENT_SOURCE_DIR}/dev_net/*.cpp")
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_SERIAL )
|
||||
FILE ( GLOB Leddevice_SERIAL_SOURCES "${CURRENT_SOURCE_DIR}/dev_serial/*.h" "${CURRENT_SOURCE_DIR}/dev_serial/*.cpp")
|
||||
if(ENABLE_DEV_SERIAL)
|
||||
file (GLOB Leddevice_SERIAL_SOURCES "${CURRENT_SOURCE_DIR}/dev_serial/*.h" "${CURRENT_SOURCE_DIR}/dev_serial/*.cpp")
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_SPI )
|
||||
FILE ( GLOB Leddevice_SPI_SOURCES "${CURRENT_SOURCE_DIR}/dev_spi/*.h" "${CURRENT_SOURCE_DIR}/dev_spi/*.cpp")
|
||||
if(ENABLE_DEV_SPI)
|
||||
file (GLOB Leddevice_SPI_SOURCES "${CURRENT_SOURCE_DIR}/dev_spi/*.h" "${CURRENT_SOURCE_DIR}/dev_spi/*.cpp")
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_TINKERFORGE )
|
||||
FILE ( GLOB Leddevice_TINKER_SOURCES "${CURRENT_SOURCE_DIR}/dev_tinker/*.h" "${CURRENT_SOURCE_DIR}/dev_tinker/*.cpp")
|
||||
if(ENABLE_DEV_TINKERFORGE)
|
||||
file (GLOB Leddevice_TINKER_SOURCES "${CURRENT_SOURCE_DIR}/dev_tinker/*.h" "${CURRENT_SOURCE_DIR}/dev_tinker/*.cpp")
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_USB_HID )
|
||||
if(ENABLE_DEV_USB_HID)
|
||||
find_package(libusb-1.0 REQUIRED)
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/include/hidapi
|
||||
${LIBUSB_1_INCLUDE_DIRS}
|
||||
)
|
||||
FILE ( GLOB Leddevice_USB_HID_SOURCES "${CURRENT_SOURCE_DIR}/dev_hid/*.h" "${CURRENT_SOURCE_DIR}/dev_hid/*.cpp")
|
||||
file (GLOB Leddevice_USB_HID_SOURCES "${CURRENT_SOURCE_DIR}/dev_hid/*.h" "${CURRENT_SOURCE_DIR}/dev_hid/*.cpp")
|
||||
endif()
|
||||
|
||||
if ( ENABLE_DEV_WS281XPWM )
|
||||
include_directories(../../dependencies/external/rpi_ws281x)
|
||||
FILE ( GLOB Leddevice_PWM_SOURCES "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.h" "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.cpp")
|
||||
if(ENABLE_DEV_WS281XPWM)
|
||||
file (GLOB Leddevice_PWM_SOURCES "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.h" "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.cpp")
|
||||
endif()
|
||||
|
||||
set(LedDevice_RESOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSchemas.qrc )
|
||||
set(LedDevice_RESOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSchemas.qrc)
|
||||
|
||||
SET( Leddevice_SOURCES
|
||||
set(Leddevice_SOURCES
|
||||
${Leddevice_SOURCES}
|
||||
${LedDevice_RESOURCES}
|
||||
${Leddevice_NETWORK_SOURCES}
|
||||
${Leddevice_PWM_SOURCES}
|
||||
${Leddevice_SERIAL_SOURCES}
|
||||
${Leddevice_SERIAL_SOURCES}
|
||||
${Leddevice_SPI_SOURCES}
|
||||
${Leddevice_TINKER_SOURCES}
|
||||
${Leddevice_USB_HID_SOURCES}
|
||||
@@ -79,20 +78,20 @@ SET( Leddevice_SOURCES
|
||||
|
||||
# auto generate header file that include all available leddevice headers
|
||||
# auto generate cpp file for register() calls
|
||||
FILE ( WRITE "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#pragma once\n\n//this file is autogenerated, don't touch it\n\n" )
|
||||
FILE ( WRITE "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "//this file is autogenerated, don't touch it\n\n" )
|
||||
FOREACH( f ${Leddevice_SOURCES} )
|
||||
# MESSAGE (STATUS "Add led device: ${f}")
|
||||
if ( "${f}" MATCHES "dev_.*/Led.evice.+h$" )
|
||||
file (WRITE "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#pragma once\n\n//this file is autogenerated, don't touch it\n\n")
|
||||
file (WRITE "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "//this file is autogenerated, don't touch it\n\n")
|
||||
foreach(f ${Leddevice_SOURCES})
|
||||
# message (STATUS "Add led device: ${f}")
|
||||
if("${f}" MATCHES "dev_.*/Led.evice.+h$")
|
||||
GET_FILENAME_COMPONENT(fname ${f} NAME)
|
||||
FILE ( APPEND "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#include \"${fname}\"\n" )
|
||||
STRING( SUBSTRING ${fname} 9 -1 dname)
|
||||
STRING( REPLACE ".h" "" dname "${dname}" )
|
||||
FILE ( APPEND "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "REGISTER(${dname});\n" )
|
||||
file (APPEND "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#include \"${fname}\"\n")
|
||||
string(SUBSTRING ${fname} 9 -1 dname)
|
||||
string(REPLACE ".h" "" dname "${dname}")
|
||||
file (APPEND "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "REGISTER(${dname});\n")
|
||||
endif()
|
||||
ENDFOREACH()
|
||||
endforeach()
|
||||
|
||||
add_library(leddevice ${CMAKE_BINARY_DIR}/LedDevice_headers.h ${Leddevice_SOURCES} )
|
||||
add_library(leddevice ${CMAKE_BINARY_DIR}/LedDevice_headers.h ${Leddevice_SOURCES})
|
||||
|
||||
target_link_libraries(leddevice
|
||||
hyperion
|
||||
@@ -106,19 +105,19 @@ endif()
|
||||
|
||||
if(ENABLE_DEV_NETWORK)
|
||||
target_link_libraries(leddevice Qt${QT_VERSION_MAJOR}::Network ssdp)
|
||||
|
||||
if (NOT DEFAULT_USE_SYSTEM_MBEDTLS_LIBS)
|
||||
if (MBEDTLS_LIBRARIES)
|
||||
|
||||
if(NOT DEFAULT_USE_SYSTEM_MBEDTLS_LIBS)
|
||||
if(MBEDTLS_LIBRARIES)
|
||||
include_directories(${MBEDTLS_INCLUDE_DIR})
|
||||
target_link_libraries(leddevice ${MBEDTLS_LIBRARIES})
|
||||
target_include_directories(leddevice PRIVATE ${MBEDTLS_INCLUDE_DIR})
|
||||
endif (MBEDTLS_LIBRARIES)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
string(REGEX MATCH "[0-9]+|-([A-Za-z0-9_.]+)" MBEDTLS_MAJOR ${MBEDTLS_VERSION})
|
||||
if (MBEDTLS_MAJOR EQUAL "3")
|
||||
if(MBEDTLS_MAJOR EQUAL "3")
|
||||
target_compile_definitions(leddevice PRIVATE USE_MBEDTLS3)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
target_compile_features(leddevice PRIVATE cxx_std_20)
|
||||
endif()
|
||||
endif()
|
||||
@@ -133,10 +132,11 @@ if(ENABLE_DEV_TINKERFORGE)
|
||||
endif()
|
||||
|
||||
if(ENABLE_DEV_WS281XPWM)
|
||||
target_include_directories(leddevice PUBLIC "${CMAKE_SOURCE_DIR}/dependencies/external/rpi_ws281x")
|
||||
target_link_libraries(leddevice ws281x)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEV_USB_HID)
|
||||
if(ENABLE_DEV_USB_HID)
|
||||
if(APPLE)
|
||||
target_link_libraries(leddevice ${LIBUSB_1_LIBRARIES} hidapi-mac)
|
||||
else()
|
||||
@@ -150,17 +150,18 @@ if (ENABLE_DEV_USB_HID)
|
||||
#include <sys/time.h>
|
||||
|
||||
int main() {
|
||||
struct timespec t;
|
||||
return clock_gettime(CLOCK_REALTIME, &t);
|
||||
struct timespec t;
|
||||
return clock_gettime(CLOCK_REALTIME, &t);
|
||||
}
|
||||
" GLIBC_HAS_CLOCK_GETTIME)
|
||||
IF(NOT GLIBC_HAS_CLOCK_GETTIME)
|
||||
if(NOT GLIBC_HAS_CLOCK_GETTIME)
|
||||
target_link_libraries(leddevice rt)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(leddevice mdns)
|
||||
|
||||
target_link_libraries(leddevice mdns)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ int LedDevice::rewriteLEDs()
|
||||
|
||||
int LedDevice::writeBlack(int numberOfWrites)
|
||||
{
|
||||
Debug(_log, "Set LED strip to black to switch of LEDs");
|
||||
Debug(_log, "Set LED strip to black to switch LEDs off");
|
||||
return writeColor(ColorRgb::BLACK, numberOfWrites);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//std includes
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
|
||||
// Qt includes
|
||||
#include <QNetworkReply>
|
||||
@@ -21,88 +22,79 @@
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_AUTH_TOKEN[] = "token";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
const char CONFIG_BRIGHTNESS[] = "brightness";
|
||||
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
|
||||
// Configuration settings
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_AUTH_TOKEN[] = "token";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
const char CONFIG_BRIGHTNESS[] = "brightness";
|
||||
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
|
||||
|
||||
const char CONFIG_PANEL_ORDER_TOP_DOWN[] = "panelOrderTopDown";
|
||||
const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] = "panelOrderLeftRight";
|
||||
const char CONFIG_PANEL_START_POS[] = "panelStartPos";
|
||||
const char CONFIG_PANEL_ORDER_TOP_DOWN[] = "panelOrderTopDown";
|
||||
const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] = "panelOrderLeftRight";
|
||||
|
||||
const bool DEFAULT_IS_RESTORE_STATE = true;
|
||||
const bool DEFAULT_IS_BRIGHTNESS_OVERWRITE = true;
|
||||
const int BRI_MAX = 100;
|
||||
const bool DEFAULT_IS_RESTORE_STATE = true;
|
||||
const bool DEFAULT_IS_BRIGHTNESS_OVERWRITE = true;
|
||||
const int BRI_MAX = 100;
|
||||
|
||||
// Panel configuration settings
|
||||
const char PANEL_LAYOUT[] = "layout";
|
||||
const char PANEL_NUM[] = "numPanels";
|
||||
const char PANEL_ID[] = "panelId";
|
||||
const char PANEL_POSITIONDATA[] = "positionData";
|
||||
const char PANEL_SHAPE_TYPE[] = "shapeType";
|
||||
const char PANEL_POS_X[] = "x";
|
||||
const char PANEL_POS_Y[] = "y";
|
||||
// Panel configuration settings
|
||||
const char PANEL_GLOBALORIENTATION[] = "globalOrientation";
|
||||
const char PANEL_GLOBALORIENTATION_VALUE[] = "value";
|
||||
const char PANEL_LAYOUT[] = "layout";
|
||||
const char PANEL_NUM[] = "numPanels";
|
||||
const char PANEL_ID[] = "panelId";
|
||||
const char PANEL_POSITIONDATA[] = "positionData";
|
||||
const char PANEL_SHAPE_TYPE[] = "shapeType";
|
||||
const char PANEL_POS_X[] = "x";
|
||||
const char PANEL_POS_Y[] = "y";
|
||||
|
||||
// List of State Information
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_BRI[] = "brightness";
|
||||
const char STATE_HUE[] = "hue";
|
||||
const char STATE_SAT[] = "sat";
|
||||
const char STATE_CT[] = "ct";
|
||||
const char STATE_COLORMODE[] = "colorMode";
|
||||
const QStringList COLOR_MODES {"hs", "ct", "effect"};
|
||||
const char STATE_VALUE[] = "value";
|
||||
// List of State Information
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_BRI[] = "brightness";
|
||||
const char STATE_HUE[] = "hue";
|
||||
const char STATE_SAT[] = "sat";
|
||||
const char STATE_CT[] = "ct";
|
||||
const char STATE_COLORMODE[] = "colorMode";
|
||||
const QStringList COLOR_MODES{ "hs", "ct", "effect" };
|
||||
const char STATE_VALUE[] = "value";
|
||||
|
||||
// Device Data elements
|
||||
const char DEV_DATA_NAME[] = "name";
|
||||
const char DEV_DATA_MODEL[] = "model";
|
||||
const char DEV_DATA_MANUFACTURER[] = "manufacturer";
|
||||
const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
|
||||
// Device Data elements
|
||||
const char DEV_DATA_NAME[] = "name";
|
||||
const char DEV_DATA_MODEL[] = "model";
|
||||
const char DEV_DATA_MANUFACTURER[] = "manufacturer";
|
||||
const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
|
||||
|
||||
// Nanoleaf Stream Control elements
|
||||
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222;
|
||||
// Nanoleaf Stream Control elements
|
||||
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222;
|
||||
|
||||
// Nanoleaf OpenAPI URLs
|
||||
const int API_DEFAULT_PORT = 16021;
|
||||
const char API_BASE_PATH[] = "/api/v1/%1/";
|
||||
const char API_ROOT[] = "";
|
||||
const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
|
||||
const char API_STATE[] = "state";
|
||||
const char API_PANELLAYOUT[] = "panelLayout";
|
||||
const char API_EFFECT[] = "effects";
|
||||
// Nanoleaf OpenAPI URLs
|
||||
const int API_DEFAULT_PORT = 16021;
|
||||
const char API_BASE_PATH[] = "/api/v1/%1/";
|
||||
const char API_ROOT[] = "";
|
||||
const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
|
||||
const char API_STATE[] = "state";
|
||||
const char API_PANELLAYOUT[] = "panelLayout";
|
||||
const char API_EFFECT[] = "effects";
|
||||
const char API_IDENTIFY[] = "identify";
|
||||
const char API_ADD_USER[] = "new";
|
||||
const char API_EFFECT_SELECT[] = "select";
|
||||
|
||||
const char API_EFFECT_SELECT[] = "select";
|
||||
//Nanoleaf Control data stream
|
||||
const int STREAM_FRAME_PANEL_NUM_SIZE = 2;
|
||||
const int STREAM_FRAME_PANEL_INFO_SIZE = 8;
|
||||
|
||||
//Nanoleaf Control data stream
|
||||
const int STREAM_FRAME_PANEL_NUM_SIZE = 2;
|
||||
const int STREAM_FRAME_PANEL_INFO_SIZE = 8;
|
||||
// Nanoleaf ssdp services
|
||||
const char SSDP_ID[] = "ssdp:all";
|
||||
const char SSDP_FILTER_HEADER[] = "ST";
|
||||
const char SSDP_NANOLEAF[] = "nanoleaf:nl*";
|
||||
const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
|
||||
|
||||
const double ROTATION_STEPS_DEGREE = 15.0;
|
||||
|
||||
// Nanoleaf ssdp services
|
||||
const char SSDP_ID[] = "ssdp:all";
|
||||
const char SSDP_FILTER_HEADER[] = "ST";
|
||||
const char SSDP_NANOLEAF[] = "nanoleaf:nl*";
|
||||
const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
|
||||
} //End of constants
|
||||
|
||||
// Nanoleaf Panel Shapetypes
|
||||
enum SHAPETYPES {
|
||||
TRIANGLE = 0,
|
||||
RHYTM = 1,
|
||||
SQUARE = 2,
|
||||
CONTROL_SQUARE_PRIMARY = 3,
|
||||
CONTROL_SQUARE_PASSIVE = 4,
|
||||
POWER_SUPPLY= 5,
|
||||
HEXAGON_SHAPES = 7,
|
||||
TRIANGE_SHAPES = 8,
|
||||
MINI_TRIANGE_SHAPES = 8,
|
||||
SHAPES_CONTROLLER = 12
|
||||
};
|
||||
|
||||
// Nanoleaf external control versions
|
||||
enum EXTCONTROLVERSIONS {
|
||||
EXTCTRLVER_V1 = 1,
|
||||
@@ -111,18 +103,16 @@ enum EXTCONTROLVERSIONS {
|
||||
|
||||
LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject& deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
, _restApi(nullptr)
|
||||
, _apiPort(API_DEFAULT_PORT)
|
||||
, _topDown(true)
|
||||
, _leftRight(true)
|
||||
, _startPos(0)
|
||||
, _endPos(0)
|
||||
, _extControlVersion(EXTCTRLVER_V2)
|
||||
, _panelLedCount(0)
|
||||
, _restApi(nullptr)
|
||||
, _apiPort(API_DEFAULT_PORT)
|
||||
, _topDown(true)
|
||||
, _leftRight(true)
|
||||
, _extControlVersion(EXTCTRLVER_V2)
|
||||
, _panelLedCount(0)
|
||||
{
|
||||
#ifdef ENABLE_MDNS
|
||||
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
|
||||
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
|
||||
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -139,7 +129,7 @@ LedDeviceNanoleaf::~LedDeviceNanoleaf()
|
||||
|
||||
bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
{
|
||||
bool isInitOK {false};
|
||||
bool isInitOK{ false };
|
||||
|
||||
// Overwrite non supported/required features
|
||||
setLatchTime(0);
|
||||
@@ -150,9 +140,9 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
Info(_log, "Device Nanoleaf does not require rewrites. Refresh time is ignored.");
|
||||
}
|
||||
|
||||
DebugIf(verbose,_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
if ( ProviderUdp::init(deviceConfig) )
|
||||
if (ProviderUdp::init(deviceConfig))
|
||||
{
|
||||
//Set hostname as per configuration and default port
|
||||
_hostName = deviceConfig[CONFIG_HOST].toString();
|
||||
@@ -164,36 +154,66 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
|
||||
_brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX);
|
||||
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName) );
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName));
|
||||
Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
|
||||
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
|
||||
Debug(_log, "Set Brightness to : %d", _brightness);
|
||||
|
||||
// Read panel organisation configuration
|
||||
if (deviceConfig[CONFIG_PANEL_ORDER_TOP_DOWN].isString())
|
||||
{
|
||||
_topDown = deviceConfig[CONFIG_PANEL_ORDER_TOP_DOWN].toString().toInt() == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_topDown = deviceConfig[CONFIG_PANEL_ORDER_TOP_DOWN].toInt() == 0;
|
||||
}
|
||||
|
||||
if (deviceConfig[CONFIG_PANEL_ORDER_LEFT_RIGHT].isString())
|
||||
{
|
||||
_leftRight = deviceConfig[CONFIG_PANEL_ORDER_LEFT_RIGHT].toString().toInt() == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_leftRight = deviceConfig[CONFIG_PANEL_ORDER_LEFT_RIGHT].toInt() == 0;
|
||||
}
|
||||
_startPos = deviceConfig[CONFIG_PANEL_START_POS].toInt(0);
|
||||
_topDown = deviceConfig[CONFIG_PANEL_ORDER_TOP_DOWN].toString("top2down") == "top2down";
|
||||
_leftRight = deviceConfig[CONFIG_PANEL_ORDER_LEFT_RIGHT].toString("left2right") == "left2right";
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::getHwLedCount(const QJsonObject& jsonLayout) const
|
||||
{
|
||||
int hwLedCount{ 0 };
|
||||
|
||||
const QJsonArray positionData = jsonLayout[PANEL_POSITIONDATA].toArray();
|
||||
for (const QJsonValue& value : positionData)
|
||||
{
|
||||
QJsonObject panelObj = value.toObject();
|
||||
int panelId = panelObj[PANEL_ID].toInt();
|
||||
int panelshapeType = panelObj[PANEL_SHAPE_TYPE].toInt();
|
||||
|
||||
DebugIf(verbose, _log, "Panel [%d] - Type: [%d]", panelId, panelshapeType);
|
||||
|
||||
if (hasLEDs(static_cast<SHAPETYPES>(panelshapeType)))
|
||||
{
|
||||
++hwLedCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugIf(verbose, _log, "Rhythm/Shape/Lines Controller panel skipped.");
|
||||
}
|
||||
}
|
||||
return hwLedCount;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::hasLEDs(const SHAPETYPES& panelshapeType) const
|
||||
{
|
||||
bool hasLED {true};
|
||||
// Skip non LED panel types
|
||||
switch (panelshapeType)
|
||||
{
|
||||
case SHAPES_CONTROLLER:
|
||||
case LINES_CONECTOR:
|
||||
case CONTROLLER_CAP:
|
||||
case POWER_CONNECTOR:
|
||||
case RHYTM:
|
||||
DebugIf(verbose, _log, "Rhythm/Shape/Lines Controller panel skipped.");
|
||||
hasLED = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return hasLED;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
{
|
||||
bool isInitOK = true;
|
||||
@@ -206,7 +226,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Getting device details failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
this->setInError(errorReason);
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
@@ -225,37 +245,71 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
|
||||
// Get panel details from /panelLayout/layout
|
||||
QJsonObject jsonPanelLayout = jsonAllPanelInfo[API_PANELLAYOUT].toObject();
|
||||
|
||||
const QJsonObject globalOrientation = jsonPanelLayout[PANEL_GLOBALORIENTATION].toObject();
|
||||
int orientation = globalOrientation[PANEL_GLOBALORIENTATION_VALUE].toInt();
|
||||
|
||||
int degreesToRotate {orientation};
|
||||
bool isRotated {false};
|
||||
if (degreesToRotate > 0)
|
||||
{
|
||||
isRotated = true;
|
||||
int degreeRounded = static_cast<int>(round(degreesToRotate / ROTATION_STEPS_DEGREE) * ROTATION_STEPS_DEGREE);
|
||||
degreesToRotate = (degreeRounded +360) % 360;
|
||||
}
|
||||
|
||||
//Nanoleaf orientation is counter-clockwise
|
||||
degreesToRotate *= -1;
|
||||
|
||||
double radians = (degreesToRotate * std::acos(-1)) / 180;
|
||||
DebugIf(verbose, _log, "globalOrientation: %d, degreesToRotate: %d, radians: %0.2f", orientation, degreesToRotate, radians);
|
||||
|
||||
QJsonObject jsonLayout = jsonPanelLayout[PANEL_LAYOUT].toObject();
|
||||
|
||||
_panelLedCount = getHwLedCount(jsonLayout);
|
||||
_devConfig["hardwareLedCount"] = _panelLedCount;
|
||||
|
||||
int panelNum = jsonLayout[PANEL_NUM].toInt();
|
||||
const QJsonArray positionData = jsonLayout[PANEL_POSITIONDATA].toArray();
|
||||
|
||||
std::map<int, std::map<int, int>> panelMap;
|
||||
|
||||
// Loop over all children.
|
||||
for(const QJsonValue & value : positionData)
|
||||
for (const QJsonValue& value : positionData)
|
||||
{
|
||||
QJsonObject panelObj = value.toObject();
|
||||
|
||||
int panelId = panelObj[PANEL_ID].toInt();
|
||||
int panelX = panelObj[PANEL_POS_X].toInt();
|
||||
int panelY = panelObj[PANEL_POS_Y].toInt();
|
||||
int panelshapeType = panelObj[PANEL_SHAPE_TYPE].toInt();
|
||||
int posX = panelObj[PANEL_POS_X].toInt();
|
||||
int posY = panelObj[PANEL_POS_Y].toInt();
|
||||
|
||||
DebugIf(verbose,_log, "Panel [%d] (%d,%d) - Type: [%d]", panelId, panelX, panelY, panelshapeType);
|
||||
|
||||
// Skip Rhythm and Shapes controller panels
|
||||
if (panelshapeType != RHYTM && panelshapeType != SHAPES_CONTROLLER)
|
||||
int panelX;
|
||||
int panelY;
|
||||
if (isRotated)
|
||||
{
|
||||
panelMap[panelY][panelX] = panelId;
|
||||
panelX = static_cast<int>(round(posX * cos(radians) - posY * sin(radians)));
|
||||
panelY = static_cast<int>(round(posX * sin(radians) + posY * cos(radians)));
|
||||
}
|
||||
else
|
||||
{ // Reset non support/required features
|
||||
Info(_log, "Rhythm/Shape Controller panel skipped.");
|
||||
{
|
||||
panelX = posX;
|
||||
panelY = posY;
|
||||
}
|
||||
|
||||
if (hasLEDs(static_cast<SHAPETYPES>(panelshapeType)))
|
||||
{
|
||||
panelMap[panelY][panelX] = panelId;
|
||||
DebugIf(verbose, _log, "Use Panel [%d] (%d,%d) - Type: [%d]", panelId, panelX, panelY, panelshapeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugIf(verbose, _log, "Skip Panel [%d] (%d,%d) - Type: [%d]", panelId, panelX, panelY, panelshapeType);
|
||||
}
|
||||
}
|
||||
|
||||
// Travers panels top down
|
||||
_panelIds.clear();
|
||||
for (auto posY = panelMap.crbegin(); posY != panelMap.crend(); ++posY)
|
||||
{
|
||||
// Sort panels left to right
|
||||
@@ -263,7 +317,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
{
|
||||
for (auto posX = posY->second.cbegin(); posX != posY->second.cend(); ++posX)
|
||||
{
|
||||
DebugIf(verbose3, _log, "panelMap[%d][%d]=%d", posY->first, posX->first, posX->second);
|
||||
DebugIf(verbose, _log, "panelMap[%d][%d]=%d", posY->first, posX->first, posX->second);
|
||||
|
||||
if (_topDown)
|
||||
{
|
||||
@@ -280,7 +334,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
// Sort panels right to left
|
||||
for (auto posX = posY->second.crbegin(); posX != posY->second.crend(); ++posX)
|
||||
{
|
||||
DebugIf(verbose3, _log, "panelMap[%d][%d]=%d", posY->first, posX->first, posX->second);
|
||||
DebugIf(verbose, _log, "panelMap[%d][%d]=%d", posY->first, posX->first, posX->second);
|
||||
|
||||
if (_topDown)
|
||||
{
|
||||
@@ -294,27 +348,22 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
this->_panelLedCount = _panelIds.size();
|
||||
_devConfig["hardwareLedCount"] = _panelLedCount;
|
||||
|
||||
Debug(_log, "PanelsNum : %d", panelNum);
|
||||
Debug(_log, "PanelLedCount : %d", _panelLedCount);
|
||||
Debug(_log, "Sort Top>Down : %d", _topDown);
|
||||
Debug(_log, "Sort Left>Right: %d", _leftRight);
|
||||
|
||||
DebugIf(verbose, _log, "PanelMap size : %d", panelMap.size());
|
||||
DebugIf(verbose, _log, "PanelIds count : %d", _panelIds.size());
|
||||
|
||||
// Check. if enough panels were found.
|
||||
int configuredLedCount = this->getLedCount();
|
||||
_endPos = _startPos + configuredLedCount - 1;
|
||||
|
||||
Debug(_log, "Sort Top>Down : %d", _topDown);
|
||||
Debug(_log, "Sort Left>Right: %d", _leftRight);
|
||||
Debug(_log, "Start Panel Pos: %d", _startPos);
|
||||
Debug(_log, "End Panel Pos : %d", _endPos);
|
||||
|
||||
if (_panelLedCount < configuredLedCount)
|
||||
{
|
||||
QString errorReason = QString("Not enough panels [%1] for configured LEDs [%2] found!")
|
||||
.arg(_panelLedCount)
|
||||
.arg(configuredLedCount);
|
||||
this->setInError(errorReason);
|
||||
.arg(_panelLedCount)
|
||||
.arg(configuredLedCount);
|
||||
this->setInError(errorReason, false);
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
@@ -324,15 +373,16 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
Info(_log, "%s: More panels [%d] than configured LEDs [%d].", QSTRING_CSTR(this->getActiveDeviceType()), _panelLedCount, configuredLedCount);
|
||||
}
|
||||
|
||||
// Check, if start position + number of configured LEDs is greater than number of panels available
|
||||
if (_endPos >= _panelLedCount)
|
||||
//Check that panel count matches working list created for processing
|
||||
if (_panelLedCount != _panelIds.size())
|
||||
{
|
||||
QString errorReason = QString("Start panel [%1] out of range. Start panel position can be max [%2] given [%3] panel available!")
|
||||
.arg(_startPos).arg(_panelLedCount - configuredLedCount).arg(_panelLedCount);
|
||||
|
||||
this->setInError(errorReason);
|
||||
QString errorReason = QString("Number of available panels [%1] do not match panel-ID look-up list [%2]!")
|
||||
.arg(_panelLedCount)
|
||||
.arg(_panelIds.size());
|
||||
this->setInError(errorReason, false);
|
||||
isInitOK = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return isInitOK;
|
||||
@@ -340,7 +390,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
|
||||
bool LedDeviceNanoleaf::openRestAPI()
|
||||
{
|
||||
bool isInitOK {true};
|
||||
bool isInitOK{ true };
|
||||
|
||||
if (_restApi == nullptr)
|
||||
{
|
||||
@@ -360,7 +410,7 @@ int LedDeviceNanoleaf::open()
|
||||
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
if ( openRestAPI() )
|
||||
if (openRestAPI())
|
||||
{
|
||||
// Read LedDevice configuration and validate against device configuration
|
||||
if (initLedsConfiguration())
|
||||
@@ -415,7 +465,7 @@ QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
|
||||
MdnsServiceRegister::getServiceType(_activeDeviceType),
|
||||
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
|
||||
DEFAULT_DISCOVER_TIMEOUT
|
||||
);
|
||||
);
|
||||
#else
|
||||
QString discoveryMethod("ssdp");
|
||||
deviceList = discover();
|
||||
@@ -424,25 +474,25 @@ QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
QJsonObject properties;
|
||||
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = params["token"].toString("");
|
||||
_authToken = params[CONFIG_AUTH_TOKEN].toString("");
|
||||
|
||||
Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
|
||||
Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName));
|
||||
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
if ( openRestAPI() )
|
||||
if (openRestAPI())
|
||||
{
|
||||
QString filter = params["filter"].toString("");
|
||||
_restApi->setPath(filter);
|
||||
@@ -453,7 +503,14 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Warning(_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
properties.insert("properties", response.getBody().object());
|
||||
QJsonObject propertiesDetails = response.getBody().object();
|
||||
if (!propertiesDetails.isEmpty())
|
||||
{
|
||||
QJsonObject jsonLayout = propertiesDetails.value(API_PANELLAYOUT).toObject().value(PANEL_LAYOUT).toObject();
|
||||
_panelLedCount = getHwLedCount(jsonLayout);
|
||||
propertiesDetails.insert("ledCount", getHwLedCount(jsonLayout));
|
||||
}
|
||||
properties.insert("properties", propertiesDetails);
|
||||
}
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
@@ -463,21 +520,19 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
|
||||
void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
|
||||
_authToken = params["token"].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = params[CONFIG_AUTH_TOKEN].toString("");
|
||||
|
||||
Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
|
||||
Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName));
|
||||
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
if ( openRestAPI() )
|
||||
if (openRestAPI())
|
||||
{
|
||||
_restApi->setPath("identify");
|
||||
|
||||
// Perform request
|
||||
_restApi->setPath(API_IDENTIFY);
|
||||
httpResponse response = _restApi->put();
|
||||
if (response.error())
|
||||
{
|
||||
@@ -487,6 +542,36 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::addAuthorization(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QJsonDocument(params).toJson(QJsonDocument::Compact).constData());
|
||||
QJsonObject responseBody;
|
||||
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
|
||||
Info(_log, "Generate user authorization token for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName));
|
||||
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
if (openRestAPI())
|
||||
{
|
||||
_restApi->setBasePath(QString(API_BASE_PATH).arg(API_ADD_USER));
|
||||
httpResponse response = _restApi->post();
|
||||
if (response.error())
|
||||
{
|
||||
Warning(_log, "%s generating user authorization token failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Generated user authorization token: \"%s\"", QSTRING_CSTR(response.getBody().object().value("auth_token").toString()));
|
||||
responseBody = response.getBody().object();
|
||||
}
|
||||
}
|
||||
}
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::powerOn()
|
||||
{
|
||||
bool on = false;
|
||||
@@ -496,12 +581,12 @@ bool LedDeviceNanoleaf::powerOn()
|
||||
{
|
||||
QJsonObject newState;
|
||||
|
||||
QJsonObject onValue { {STATE_VALUE, true} };
|
||||
QJsonObject onValue{ {STATE_VALUE, true} };
|
||||
newState.insert(STATE_ON, onValue);
|
||||
|
||||
if ( _isBrightnessOverwrite)
|
||||
if (_isBrightnessOverwrite)
|
||||
{
|
||||
QJsonObject briValue { {STATE_VALUE, _brightness} };
|
||||
QJsonObject briValue{ {STATE_VALUE, _brightness} };
|
||||
newState.insert(STATE_BRI, briValue);
|
||||
}
|
||||
|
||||
@@ -511,9 +596,10 @@ bool LedDeviceNanoleaf::powerOn()
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Power-on request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
this->setInError(errorReason);
|
||||
on = false;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
on = true;
|
||||
}
|
||||
|
||||
@@ -529,7 +615,7 @@ bool LedDeviceNanoleaf::powerOff()
|
||||
{
|
||||
QJsonObject newState;
|
||||
|
||||
QJsonObject onValue { {STATE_VALUE, false} };
|
||||
QJsonObject onValue{ {STATE_VALUE, false} };
|
||||
newState.insert(STATE_ON, onValue);
|
||||
|
||||
//Power-off the Nanoleaf device physically
|
||||
@@ -538,7 +624,7 @@ bool LedDeviceNanoleaf::powerOff()
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Power-off request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
this->setInError(errorReason);
|
||||
off = false;
|
||||
}
|
||||
}
|
||||
@@ -549,12 +635,12 @@ bool LedDeviceNanoleaf::storeState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
if (_isRestoreOrigState)
|
||||
{
|
||||
_restApi->setPath(API_STATE);
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(response.getErrorReason());
|
||||
setInError(errorReason);
|
||||
@@ -563,7 +649,7 @@ bool LedDeviceNanoleaf::storeState()
|
||||
else
|
||||
{
|
||||
_originalStateProperties = response.getBody().object();
|
||||
DebugIf(verbose, _log, "state: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "state: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QJsonObject isOn = _originalStateProperties.value(STATE_ON).toObject();
|
||||
if (!isOn.isEmpty())
|
||||
@@ -579,7 +665,7 @@ bool LedDeviceNanoleaf::storeState()
|
||||
|
||||
_originalColorMode = _originalStateProperties[STATE_COLORMODE].toString();
|
||||
|
||||
switch(COLOR_MODES.indexOf(_originalColorMode)) {
|
||||
switch (COLOR_MODES.indexOf(_originalColorMode)) {
|
||||
case 0:
|
||||
{
|
||||
// hs
|
||||
@@ -611,7 +697,7 @@ bool LedDeviceNanoleaf::storeState()
|
||||
_restApi->setPath(API_EFFECT);
|
||||
|
||||
httpResponse responseEffects = _restApi->get();
|
||||
if ( responseEffects.error() )
|
||||
if (responseEffects.error())
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(responseEffects.getErrorReason());
|
||||
setInError(errorReason);
|
||||
@@ -620,7 +706,7 @@ bool LedDeviceNanoleaf::storeState()
|
||||
else
|
||||
{
|
||||
QJsonObject effects = responseEffects.getBody().object();
|
||||
DebugIf(verbose, _log, "effects: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "effects: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
_originalEffect = effects[API_EFFECT_SELECT].toString();
|
||||
_originalIsDynEffect = _originalEffect == "*Dynamic*" || _originalEffect == "*Solid*";
|
||||
}
|
||||
@@ -641,21 +727,21 @@ bool LedDeviceNanoleaf::restoreState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
if (_isRestoreOrigState)
|
||||
{
|
||||
QJsonObject newState;
|
||||
switch(COLOR_MODES.indexOf(_originalColorMode)) {
|
||||
switch (COLOR_MODES.indexOf(_originalColorMode)) {
|
||||
case 0:
|
||||
{ // hs
|
||||
QJsonObject hueValue { {STATE_VALUE, _originalHue} };
|
||||
QJsonObject hueValue{ {STATE_VALUE, _originalHue} };
|
||||
newState.insert(STATE_HUE, hueValue);
|
||||
QJsonObject satValue { {STATE_VALUE, _originalSat} };
|
||||
QJsonObject satValue{ {STATE_VALUE, _originalSat} };
|
||||
newState.insert(STATE_SAT, satValue);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{ // ct
|
||||
QJsonObject ctValue { {STATE_VALUE, _originalCt} };
|
||||
QJsonObject ctValue{ {STATE_VALUE, _originalCt} };
|
||||
newState.insert(STATE_CT, ctValue);
|
||||
break;
|
||||
}
|
||||
@@ -667,37 +753,38 @@ bool LedDeviceNanoleaf::restoreState()
|
||||
newEffect[API_EFFECT_SELECT] = _originalEffect;
|
||||
_restApi->setPath(API_EFFECT);
|
||||
httpResponse response = _restApi->put(newEffect);
|
||||
if ( response.error() )
|
||||
if (response.error())
|
||||
{
|
||||
Warning (_log, "%s restoring effect failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
Warning(_log, "%s restoring effect failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
} else {
|
||||
Warning (_log, "%s restoring effect failed with error: Cannot restore dynamic or solid effect. Device is switched off", QSTRING_CSTR(_activeDeviceType));
|
||||
}
|
||||
else {
|
||||
Warning(_log, "%s restoring effect failed with error: Cannot restore dynamic or solid effect. Device is switched off", QSTRING_CSTR(_activeDeviceType));
|
||||
_originalIsOn = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Warning (_log, "%s restoring failed with error: Unknown ColorMode", QSTRING_CSTR(_activeDeviceType));
|
||||
Warning(_log, "%s restoring failed with error: Unknown ColorMode", QSTRING_CSTR(_activeDeviceType));
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (!_originalIsDynEffect)
|
||||
{
|
||||
QJsonObject briValue { {STATE_VALUE, _originalBri} };
|
||||
QJsonObject briValue{ {STATE_VALUE, _originalBri} };
|
||||
newState.insert(STATE_BRI, briValue);
|
||||
}
|
||||
|
||||
QJsonObject onValue { {STATE_VALUE, _originalIsOn} };
|
||||
QJsonObject onValue{ {STATE_VALUE, _originalIsOn} };
|
||||
newState.insert(STATE_ON, onValue);
|
||||
|
||||
_restApi->setPath(API_STATE);
|
||||
|
||||
httpResponse response = _restApi->put(newState);
|
||||
|
||||
if ( response.error() )
|
||||
if (response.error())
|
||||
{
|
||||
Warning (_log, "%s restoring state failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
Warning(_log, "%s restoring state failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
@@ -722,7 +809,7 @@ bool LedDeviceNanoleaf::changeToExternalControlMode(QJsonDocument& resp)
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Change to external control mode failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
this->setInError(errorReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -758,29 +845,24 @@ int LedDeviceNanoleaf::write(const std::vector<ColorRgb>& ledValues)
|
||||
|
||||
ColorRgb color;
|
||||
|
||||
//Maintain LED counter independent from PanelCounter
|
||||
int ledCounter = 0;
|
||||
for (int panelCounter = 0; panelCounter < _panelLedCount; panelCounter++)
|
||||
for (int panelCounter = 0; panelCounter < _panelLedCount; ++panelCounter)
|
||||
{
|
||||
// Set panelID
|
||||
int panelID = _panelIds[panelCounter];
|
||||
qToBigEndian<quint16>(static_cast<quint16>(panelID), udpbuffer.data() + i);
|
||||
i += 2;
|
||||
|
||||
// Set panels configured
|
||||
if (panelCounter >= _startPos && panelCounter <= _endPos) {
|
||||
color = static_cast<ColorRgb>(ledValues.at(ledCounter));
|
||||
++ledCounter;
|
||||
// Set panel's color LEDs
|
||||
if (panelCounter < this->getLedCount()) {
|
||||
color = static_cast<ColorRgb>(ledValues.at(panelCounter));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set panels not configured to black
|
||||
color = ColorRgb::BLACK;
|
||||
DebugIf(verbose3, _log, "[%d] >= panelLedCount [%d] => Set to BLACK", panelCounter, _panelLedCount);
|
||||
DebugIf(verbose3, _log, "[%u] >= panelLedCount [%u] => Set to BLACK", panelCounter, _panelLedCount);
|
||||
}
|
||||
|
||||
// Set panelID
|
||||
qToBigEndian<quint16>(static_cast<quint16>(panelID), udpbuffer.data() + i);
|
||||
i += 2;
|
||||
|
||||
// Set panel's color LEDs
|
||||
udpbuffer[i++] = static_cast<char>(color.red);
|
||||
udpbuffer[i++] = static_cast<char>(color.green);
|
||||
udpbuffer[i++] = static_cast<char>(color.blue);
|
||||
@@ -799,7 +881,7 @@ int LedDeviceNanoleaf::write(const std::vector<ColorRgb>& ledValues)
|
||||
if (verbose3)
|
||||
{
|
||||
Debug(_log, "UDP-Address [%s], UDP-Port [%u], udpBufferSize[%d], Bytes to send [%d]", QSTRING_CSTR(_address.toString()), _port, udpBufferSize, i);
|
||||
Debug( _log, "packet: [%s]", QSTRING_CSTR(toHex(udpbuffer, 64)));
|
||||
Debug(_log, "packet: [%s]", QSTRING_CSTR(toHex(udpbuffer, 64)));
|
||||
}
|
||||
|
||||
retVal = writeBytes(udpbuffer);
|
||||
|
||||
@@ -87,6 +87,20 @@ public:
|
||||
///
|
||||
void identify(const QJsonObject& params) override;
|
||||
|
||||
/// @brief Add an API-token to the Nanoleaf device
|
||||
///
|
||||
/// Following parameters are required
|
||||
/// @code
|
||||
/// {
|
||||
/// "host" : "hostname or IP",
|
||||
/// }
|
||||
///@endcode
|
||||
///
|
||||
/// @param[in] params Parameters to query device
|
||||
/// @return A JSON structure holding the authorization keys
|
||||
///
|
||||
QJsonObject addAuthorization(const QJsonObject& params) override;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
@@ -147,6 +161,27 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
// Nanoleaf Panel Shapetypes
|
||||
enum SHAPETYPES {
|
||||
TRIANGLE = 0,
|
||||
RHYTM = 1,
|
||||
SQUARE = 2,
|
||||
CONTROL_SQUARE_PRIMARY = 3,
|
||||
CONTROL_SQUARE_PASSIVE = 4,
|
||||
POWER_SUPPLY = 5,
|
||||
HEXAGON_SHAPES = 7,
|
||||
TRIANGE_SHAPES = 8,
|
||||
MINI_TRIANGE_SHAPES = 9,
|
||||
SHAPES_CONTROLLER = 12,
|
||||
ELEMENTS_HEXAGONS = 14,
|
||||
ELEMENTS_HEXAGONS_CORNER = 15,
|
||||
LINES_CONECTOR = 16,
|
||||
LIGHT_LINES = 17,
|
||||
LIGHT_LINES_SINGLZONE = 18,
|
||||
CONTROLLER_CAP = 19,
|
||||
POWER_CONNECTOR = 20
|
||||
};
|
||||
|
||||
///
|
||||
/// @brief Initialise the access to the REST-API wrapper
|
||||
///
|
||||
@@ -182,6 +217,20 @@ private:
|
||||
///
|
||||
QJsonArray discover();
|
||||
|
||||
///
|
||||
/// @brief Get number of panels that can be used as LEds.
|
||||
///
|
||||
/// @return Number of usable LED panels
|
||||
///
|
||||
int getHwLedCount(const QJsonObject& jsonLayout) const;
|
||||
|
||||
///
|
||||
/// @brief Check, if panelshape type has LEDs
|
||||
///
|
||||
/// @return True, if panel shape type has LEDs
|
||||
///
|
||||
bool hasLEDs(const SHAPETYPES& panelshapeType) const;
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
int _apiPort;
|
||||
@@ -189,8 +238,6 @@ private:
|
||||
|
||||
bool _topDown;
|
||||
bool _leftRight;
|
||||
int _startPos;
|
||||
int _endPos;
|
||||
|
||||
//Nanoleaf device details
|
||||
QString _deviceModel;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,31 +16,6 @@
|
||||
#include "ProviderRestApi.h"
|
||||
#include "ProviderUdpSSL.h"
|
||||
|
||||
//Streaming message header and payload definition
|
||||
const uint8_t HEADER[] =
|
||||
{
|
||||
'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', //protocol
|
||||
0x01, 0x00, //version 1.0
|
||||
0x01, //sequence number 1
|
||||
0x00, 0x00, //Reserved write 0’s
|
||||
0x01, //xy Brightness
|
||||
0x00, // Reserved, write 0’s
|
||||
};
|
||||
|
||||
const uint8_t PAYLOAD_PER_LIGHT[] =
|
||||
{
|
||||
0x01, 0x00, 0x06, //light ID
|
||||
//color: 16 bpc
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
/*
|
||||
(message.R >> 8) & 0xff, message.R & 0xff,
|
||||
(message.G >> 8) & 0xff, message.G & 0xff,
|
||||
(message.B >> 8) & 0xff, message.B & 0xff
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* A XY color point in the color space of the hue system without brightness.
|
||||
*/
|
||||
@@ -145,12 +120,18 @@ public:
|
||||
/// Constructs the light.
|
||||
///
|
||||
/// @param log the logger
|
||||
/// @param bridge the bridge
|
||||
/// @param useApiV2 make use of Hue API version 2
|
||||
/// @param id the light id
|
||||
/// @param lightAttributes the light's attributes as provied by the Hue Bridge
|
||||
/// @param onBlackTimeToPowerOff Timeframe of Black output that triggers powering off the light
|
||||
/// @param onBlackTimeToPowerOn Timeframe of non Black output that triggers powering on the light
|
||||
///
|
||||
PhilipsHueLight(Logger* log, int id, QJsonObject values, int ledidx,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
PhilipsHueLight(Logger* log, bool useApiV2, const QString& id, const QJsonObject& lightAttributes,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
|
||||
void setDeviceDetails(const QJsonObject& details);
|
||||
void setEntertainmentSrvDetails(const QJsonObject& details);
|
||||
|
||||
///
|
||||
/// @param on
|
||||
@@ -167,7 +148,14 @@ public:
|
||||
///
|
||||
void setColor(const CiColor& color);
|
||||
|
||||
int getId() const;
|
||||
QString getId() const;
|
||||
QString getdeviceId() const;
|
||||
QString getProduct() const;
|
||||
QString getModel() const;
|
||||
QString getName() const;
|
||||
QString getArcheType() const;
|
||||
|
||||
int getMaxSegments() const;
|
||||
|
||||
bool getOnOffState() const;
|
||||
int getTransitionTime() const;
|
||||
@@ -179,7 +167,7 @@ public:
|
||||
CiColorTriangle getColorSpace() const;
|
||||
|
||||
void saveOriginalState(const QJsonObject& values);
|
||||
QString getOriginalState() const;
|
||||
QJsonObject getOriginalState() const;
|
||||
|
||||
bool isBusy();
|
||||
bool isBlack(bool isBlack);
|
||||
@@ -189,24 +177,30 @@ public:
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
/// light id
|
||||
int _id;
|
||||
int _ledidx;
|
||||
bool _useApiV2;
|
||||
|
||||
QString _id;
|
||||
QString _deviceId;
|
||||
QString _product;
|
||||
QString _model;
|
||||
QString _name;
|
||||
QString _archeType;
|
||||
QString _gamutType;
|
||||
|
||||
int _maxSegments;
|
||||
|
||||
bool _on;
|
||||
int _transitionTime;
|
||||
CiColor _color;
|
||||
bool _hasColor;
|
||||
/// darkes blue color in hue lamp GAMUT = black
|
||||
CiColor _colorBlack;
|
||||
/// The model id of the hue lamp which is used to determine the color space.
|
||||
QString _modelId;
|
||||
QString _lightname;
|
||||
CiColorTriangle _colorSpace;
|
||||
|
||||
/// The json string of the original state.
|
||||
QJsonObject _originalStateJSON;
|
||||
|
||||
QString _originalState;
|
||||
QJsonObject _originalState;
|
||||
CiColor _originalColor;
|
||||
qint64 _lastSendColorTime;
|
||||
qint64 _lastBlackTime;
|
||||
@@ -242,23 +236,40 @@ public:
|
||||
QJsonDocument get(const QString& route);
|
||||
|
||||
///
|
||||
/// @brief Perform a REST-API POST
|
||||
/// @brief Perform a REST-API GET
|
||||
///
|
||||
/// @param route the route of the POST request.
|
||||
/// @param content the content of the POST request.
|
||||
/// @param routeElements the route's elements of the GET request.
|
||||
///
|
||||
QJsonDocument put(const QString& route, const QString& content, bool supressError = false);
|
||||
/// @return the content of the GET request.
|
||||
///
|
||||
QJsonDocument get(const QStringList& routeElements);
|
||||
|
||||
QJsonDocument getLightState( int lightId);
|
||||
void setLightState( int lightId = 0, const QString &state = "");
|
||||
///
|
||||
/// @brief Perform a REST-API PUT
|
||||
///
|
||||
/// @param routeElements the route's elements of the PUT request.
|
||||
/// @param content the content of the PUT request.
|
||||
/// @param supressError Treat an error as a warning
|
||||
///
|
||||
/// @return the content of the PUT request.
|
||||
///
|
||||
QJsonDocument put(const QStringList& routeElements, const QJsonObject& content, bool supressError = false);
|
||||
|
||||
QMap<int,QJsonObject> getLightMap() const;
|
||||
QJsonDocument retrieveBridgeDetails();
|
||||
QJsonObject getDeviceDetails(const QString& deviceId);
|
||||
QJsonObject getEntertainmentSrvDetails(const QString& deviceId);
|
||||
|
||||
QMap<int,QJsonObject> getGroupMap() const;
|
||||
QJsonObject getLightDetails(const QString& lightId);
|
||||
QJsonDocument setLightState(const QString& lightId, const QJsonObject& state);
|
||||
|
||||
QString getGroupName(int groupId = 0) const;
|
||||
QMap<QString,QJsonObject> getDevicesMap() const;
|
||||
QMap<QString,QJsonObject> getLightMap() const;
|
||||
QMap<QString,QJsonObject> getGroupMap() const;
|
||||
QMap<QString,QJsonObject> getEntertainmentMap() const;
|
||||
|
||||
QJsonArray getGroupLights(int groupId = 0) const;
|
||||
QString getGroupName(const QString& groupId) const;
|
||||
QStringList getGroupLights(const QString& groupId) const;
|
||||
int getGroupChannelsCount(const QString& groupId) const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -289,7 +300,7 @@ protected:
|
||||
///
|
||||
/// @param[in] response from Hue-Bridge in JSON-format
|
||||
/// @param[in] suppressError Treat an error as a warning
|
||||
///
|
||||
///
|
||||
/// return True, Hue Bridge reports error
|
||||
///
|
||||
bool checkApiError(const QJsonDocument& response, bool supressError = false);
|
||||
@@ -338,23 +349,41 @@ protected:
|
||||
///
|
||||
QJsonObject addAuthorization(const QJsonObject& params) override;
|
||||
|
||||
bool isApiEntertainmentReady(const QString& apiVersion);
|
||||
bool isAPIv2Ready (int swVersion);
|
||||
|
||||
int getFirmwareVerion() { return _deviceFirmwareVersion; }
|
||||
void setBridgeDetails( const QJsonDocument &doc, bool isLogging = false );
|
||||
|
||||
void setBaseApiEnvironment(bool apiV2 = true, const QString& path = "");
|
||||
|
||||
QJsonDocument getGroupDetails( const QString& groupId );
|
||||
QJsonDocument setGroupState( const QString& groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
|
||||
bool initDevicesMap();
|
||||
bool initLightsMap();
|
||||
bool initGroupsMap();
|
||||
bool initEntertainmentSrvsMap();
|
||||
|
||||
void log(const char* msg, const char* type, ...) const;
|
||||
|
||||
bool configureSsl();
|
||||
const int * getCiphersuites() const override;
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
int _apiPort;
|
||||
/// User name for the API ("newdeveloper")
|
||||
QString _authToken;
|
||||
QString _applicationID;
|
||||
|
||||
bool _useHueEntertainmentAPI;
|
||||
bool _useEntertainmentAPI;
|
||||
bool _useApiV2;
|
||||
bool _isAPIv2Ready;
|
||||
|
||||
QJsonDocument getGroupState( int groupId );
|
||||
QJsonDocument setGroupState( int groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
bool initMaps();
|
||||
|
||||
void log(const char* msg, const char* type, ...) const;
|
||||
|
||||
const int * getCiphersuites() const override;
|
||||
bool _isDiyHue;
|
||||
|
||||
private:
|
||||
|
||||
@@ -364,16 +393,25 @@ private:
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonArray discover();
|
||||
QJsonArray discoverSsdp();
|
||||
|
||||
QJsonDocument getAllBridgeInfos();
|
||||
void setBridgeConfig( const QJsonDocument &doc );
|
||||
QJsonDocument retrieveDeviceDetails(const QString& deviceId = "");
|
||||
QJsonDocument retrieveLightDetails(const QString& lightId = "");
|
||||
QJsonDocument retrieveGroupDetails(const QString& groupId = "");
|
||||
QJsonDocument retrieveEntertainmentSrvDetails(const QString& deviceId = "");
|
||||
|
||||
bool retrieveApplicationId();
|
||||
|
||||
void setDevicesMap( const QJsonDocument &doc );
|
||||
void setLightsMap( const QJsonDocument &doc );
|
||||
void setGroupMap( const QJsonDocument &doc );
|
||||
void setEntertainmentSrvMap( const QJsonDocument &doc );
|
||||
|
||||
//Philips Hue Bridge details
|
||||
QString _deviceName;
|
||||
QString _deviceBridgeId;
|
||||
QString _deviceModel;
|
||||
QString _deviceFirmwareVersion;
|
||||
int _deviceFirmwareVersion;
|
||||
QString _deviceAPIVersion;
|
||||
|
||||
uint _api_major;
|
||||
@@ -382,8 +420,12 @@ private:
|
||||
|
||||
bool _isHueEntertainmentReady;
|
||||
|
||||
QMap<int,QJsonObject> _lightsMap;
|
||||
QMap<int,QJsonObject> _groupsMap;
|
||||
QMap<QString,QJsonObject> _devicesMap;
|
||||
QMap<QString,QJsonObject> _lightsMap;
|
||||
QMap<QString,QJsonObject> _groupsMap;
|
||||
QMap<QString,QJsonObject> _entertainmentMap;
|
||||
|
||||
int _lightsCount;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -440,7 +482,7 @@ public:
|
||||
///
|
||||
/// @return Number of device's LEDs
|
||||
///
|
||||
unsigned int getLightsCount() const { return _lightsCount; }
|
||||
int getLightsCount() const { return _lightsCount; }
|
||||
|
||||
void setOnOffState(PhilipsHueLight& light, bool on, bool force = false);
|
||||
void setTransitionTime(PhilipsHueLight& light);
|
||||
@@ -547,18 +589,18 @@ private:
|
||||
|
||||
bool setLights();
|
||||
|
||||
/// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
|
||||
/// creates new PhilipsHueLight(s) based on user lightId with bridge feedback
|
||||
///
|
||||
/// @param map Map of lightid/value pairs of bridge
|
||||
/// @param map Map of lightId/value pairs of bridge
|
||||
///
|
||||
bool updateLights(const QMap<int, QJsonObject> &map);
|
||||
bool updateLights(const QMap<QString, QJsonObject> &map);
|
||||
|
||||
///
|
||||
/// @brief Set the number of LEDs supported by the device.
|
||||
///
|
||||
/// @rparam[in] Number of device's LEDs
|
||||
//
|
||||
void setLightsCount( unsigned int lightsCount);
|
||||
void setLightsCount(int lightsCount);
|
||||
|
||||
bool openStream();
|
||||
bool getStreamGroupState();
|
||||
@@ -566,10 +608,8 @@ private:
|
||||
bool startStream();
|
||||
bool stopStream();
|
||||
|
||||
void writeStream(bool flush = false);
|
||||
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
|
||||
|
||||
QByteArray prepareStreamData() const;
|
||||
int writeStreamData(const std::vector<ColorRgb>& ledValues, bool flush = false);
|
||||
|
||||
///
|
||||
bool _switchOffOnBlack;
|
||||
@@ -582,12 +622,15 @@ private:
|
||||
bool _isInitLeds;
|
||||
|
||||
/// Array of the light ids.
|
||||
std::vector<int> _lightIds;
|
||||
QStringList _lightIds;
|
||||
/// Array to save the lamps.
|
||||
std::vector<PhilipsHueLight> _lights;
|
||||
|
||||
int _lightsCount;
|
||||
int _groupId;
|
||||
int _channelsCount;
|
||||
QString _groupId;
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
int _blackLightsTimeout;
|
||||
double _blackLevel;
|
||||
@@ -595,15 +638,5 @@ private:
|
||||
int _onBlackTimeToPowerOn;
|
||||
bool _candyGamma;
|
||||
|
||||
// TODO: Check what is the correct class
|
||||
uint32_t _handshake_timeout_min;
|
||||
uint32_t _handshake_timeout_max;
|
||||
bool _stopConnection;
|
||||
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
qint64 _lastConfirm;
|
||||
int _lastId;
|
||||
bool _groupStreamState;
|
||||
};
|
||||
|
||||
@@ -301,7 +301,7 @@ bool LedDeviceWled::isReadyForSegmentStreaming(semver::version& version) const
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Version provided to test for streaming readiness is not valid ");
|
||||
Error(_log, "Version provided to test for segment streaming readiness is not valid ");
|
||||
}
|
||||
return isReady;
|
||||
}
|
||||
@@ -325,7 +325,7 @@ bool LedDeviceWled::isReadyForDDPStreaming(semver::version& version) const
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Version provided to test for streaming readiness is not valid ");
|
||||
Error(_log, "Version provided to test for DDP streaming readiness is not valid ");
|
||||
}
|
||||
return isReady;
|
||||
}
|
||||
@@ -352,12 +352,12 @@ bool LedDeviceWled::powerOn()
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray();
|
||||
const QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray();
|
||||
|
||||
bool isStreamSegmentIdFound { false };
|
||||
|
||||
QJsonArray segments;
|
||||
for (const auto& segmentItem : qAsConst(propertiesSegments))
|
||||
for (const auto& segmentItem : propertiesSegments)
|
||||
{
|
||||
QJsonObject segmentObj = segmentItem.toObject();
|
||||
|
||||
@@ -505,9 +505,9 @@ bool LedDeviceWled::restoreState()
|
||||
|
||||
if (_isStreamToSegment)
|
||||
{
|
||||
QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray();
|
||||
const QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray();
|
||||
QJsonArray segments;
|
||||
for (const auto& segmentItem : qAsConst(propertiesSegments))
|
||||
for (const auto& segmentItem : propertiesSegments)
|
||||
{
|
||||
QJsonObject segmentObj = segmentItem.toObject();
|
||||
|
||||
|
||||
@@ -2,11 +2,20 @@
|
||||
#include "ProviderRestApi.h"
|
||||
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QSslSocket>
|
||||
|
||||
//std includes
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
@@ -30,12 +39,12 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _networkManager(nullptr)
|
||||
, _requestTimeout(DEFAULT_REST_TIMEOUT)
|
||||
,_isSeflSignedCertificateAccpeted(false)
|
||||
{
|
||||
_networkManager = new QNetworkAccessManager();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
_networkManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
#endif
|
||||
|
||||
_apiUrl.setScheme(scheme);
|
||||
_apiUrl.setHost(host);
|
||||
_apiUrl.setPort(port);
|
||||
@@ -46,7 +55,7 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: ProviderRestApi(scheme, host, port, "") {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath)
|
||||
: ProviderRestApi("http", host, port, basePath) {}
|
||||
: ProviderRestApi((port == 443) ? "https" : "http", host, port, basePath) {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port)
|
||||
: ProviderRestApi(host, port, "") {}
|
||||
@@ -59,18 +68,33 @@ ProviderRestApi::~ProviderRestApi()
|
||||
delete _networkManager;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setScheme(const QString& scheme)
|
||||
{
|
||||
_apiUrl.setScheme(scheme);
|
||||
}
|
||||
|
||||
void ProviderRestApi::setUrl(const QUrl& url)
|
||||
{
|
||||
_apiUrl = url;
|
||||
_basePath = url.path();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QStringList& pathElements)
|
||||
{
|
||||
setBasePath(pathElements.join(ONE_SLASH));
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QString& basePath)
|
||||
{
|
||||
_basePath.clear();
|
||||
appendPath(_basePath, basePath);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearBasePath()
|
||||
{
|
||||
_basePath.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setPath(const QStringList& pathElements)
|
||||
{
|
||||
_path.clear();
|
||||
@@ -83,6 +107,11 @@ void ProviderRestApi::setPath(const QString& path)
|
||||
appendPath(_path, path);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearPath()
|
||||
{
|
||||
_path.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::appendPath(const QString& path)
|
||||
{
|
||||
appendPath(_path, path);
|
||||
@@ -204,6 +233,7 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation
|
||||
QDateTime start = QDateTime::currentDateTime();
|
||||
QString opCode;
|
||||
QNetworkReply* reply;
|
||||
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
opCode = "GET";
|
||||
@@ -255,11 +285,11 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
HttpStatusCode httpStatusCode = static_cast<HttpStatusCode>(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
||||
response.setHttpStatusCode(httpStatusCode);
|
||||
response.setNetworkReplyError(reply->error());
|
||||
response.setHeaders(reply->rawHeaderPairs());
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
QByteArray replyData = reply->readAll();
|
||||
|
||||
if (!replyData.isEmpty())
|
||||
{
|
||||
QJsonParseError error;
|
||||
@@ -284,40 +314,40 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
else
|
||||
{
|
||||
QString errorReason;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case HttpStatusCode::BadRequest:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
break;
|
||||
case HttpStatusCode::NotFound:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
advise = httpReason;
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
if (reply->error() == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
errorReason = "Network request timeout error";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reply->error() == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
errorReason = "Network request timeout error";
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case HttpStatusCode::BadRequest:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authorization Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
break;
|
||||
case HttpStatusCode::NotFound:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
advise = httpReason;
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorReason = reply->errorString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
response.setError(true);
|
||||
response.setErrorReason(errorReason);
|
||||
}
|
||||
@@ -344,3 +374,188 @@ void ProviderRestApi::setHeader(const QByteArray &headerName, const QByteArray &
|
||||
{
|
||||
_networkRequestHeaders.setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
void httpResponse::setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs)
|
||||
{
|
||||
_responseHeaders.clear();
|
||||
for (const auto &item: pairs)
|
||||
{
|
||||
_responseHeaders[item.first] = item.second;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray httpResponse::getHeader(const QByteArray header) const
|
||||
{
|
||||
return _responseHeaders.value(header);
|
||||
}
|
||||
|
||||
bool ProviderRestApi::setCaCertificate(const QString& caFileName)
|
||||
{
|
||||
bool rc {false};
|
||||
/// Add our own CA to the default SSL configuration
|
||||
QSslConfiguration configuration = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
QFile caFile (caFileName);
|
||||
if (!caFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error(_log,"Unable to open CA-Certificate file: %s", QSTRING_CSTR(caFileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
QSslCertificate cert (&caFile);
|
||||
caFile.close();
|
||||
|
||||
QList<QSslCertificate> allowedCAs;
|
||||
allowedCAs << cert;
|
||||
configuration.setCaCertificates(allowedCAs);
|
||||
|
||||
QSslConfiguration::setDefaultConfiguration(configuration);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
if (QSslSocket::supportsSsl())
|
||||
{
|
||||
QObject::connect( _networkManager, &QNetworkAccessManager::sslErrors, this, &ProviderRestApi::onSslErrors, Qt::UniqueConnection );
|
||||
_networkManager->connectToHostEncrypted(_apiUrl.host(), _apiUrl.port(), configuration);
|
||||
rc = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ProviderRestApi::acceptSelfSignedCertificates(bool isAccepted)
|
||||
{
|
||||
_isSeflSignedCertificateAccpeted = isAccepted;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setAlternateServerIdentity(const QString& serverIdentity)
|
||||
{
|
||||
_serverIdentity = serverIdentity;
|
||||
}
|
||||
|
||||
QString ProviderRestApi::getAlternateServerIdentity() const
|
||||
{
|
||||
return _serverIdentity;
|
||||
}
|
||||
|
||||
bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) const
|
||||
{
|
||||
bool isServerIdentified {false};
|
||||
|
||||
// Perform common name validation
|
||||
QSslCertificate serverCertificate = sslConfig.peerCertificate();
|
||||
QStringList commonName = serverCertificate.subjectInfo(QSslCertificate::CommonName);
|
||||
if ( commonName.contains(getAlternateServerIdentity(), Qt::CaseInsensitive) )
|
||||
{
|
||||
isServerIdentified = true;
|
||||
}
|
||||
|
||||
return isServerIdentified;
|
||||
}
|
||||
|
||||
bool ProviderRestApi::matchesPinnedCertificate(const QSslCertificate& certificate)
|
||||
{
|
||||
bool isMatching {false};
|
||||
|
||||
QList certificateInfos = certificate.subjectInfo(QSslCertificate::CommonName);
|
||||
|
||||
if (certificateInfos.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QString identifier = certificateInfos.constFirst();
|
||||
|
||||
QString appDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QString certDir = appDataDir + "/certificates";
|
||||
QDir().mkpath(certDir);
|
||||
|
||||
QString filePath(certDir + "/" + identifier + ".pem");
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QList certificates = QSslCertificate::fromDevice(&file, QSsl::Pem);
|
||||
if (!certificates.isEmpty())
|
||||
{
|
||||
Debug (_log,"First used certificate loaded successfully");
|
||||
QSslCertificate pinnedeCertificate = certificates.constFirst();
|
||||
if (pinnedeCertificate == certificate)
|
||||
{
|
||||
isMatching = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug (_log,"Error reading first used certificate file: %s", QSTRING_CSTR(filePath));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
QByteArray pemData = certificate.toPem();
|
||||
qint64 bytesWritten = file.write(pemData);
|
||||
if (bytesWritten == pemData.size())
|
||||
{
|
||||
Debug (_log,"First used certificate saved to file: %s", QSTRING_CSTR(filePath));
|
||||
isMatching = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug (_log,"Error writing first used certificate file: %s", QSTRING_CSTR(filePath));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
return isMatching;
|
||||
}
|
||||
|
||||
void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
|
||||
{
|
||||
int ignoredErrorCount {0};
|
||||
for (const QSslError &error : errors)
|
||||
{
|
||||
bool ignoreSslError{false};
|
||||
|
||||
switch (error.error()) {
|
||||
case QSslError::HostNameMismatch :
|
||||
if (checkServerIdentity(reply->sslConfiguration()) )
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
case QSslError::SelfSignedCertificate :
|
||||
if (_isSeflSignedCertificateAccpeted)
|
||||
{
|
||||
// Get the peer certificate associated with the error
|
||||
QSslCertificate certificate = error.certificate();
|
||||
if (matchesPinnedCertificate(certificate))
|
||||
{
|
||||
Debug (_log,"'Trust on first use' - Certificate received matches pinned certificate");
|
||||
ignoreSslError = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error (_log,"'Trust on first use' - Certificate received does not match pinned certificate");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignoreSslError)
|
||||
{
|
||||
++ignoredErrorCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug (_log,"SSL Error occured: [%d] %s ",error.error(), QSTRING_CSTR(error.errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoredErrorCount == errors.size())
|
||||
{
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <QFile>
|
||||
#include <QBasicTimer>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 1000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 2000 };
|
||||
|
||||
//Set QNetworkReply timeout without external timer
|
||||
//https://stackoverflow.com/questions/37444539/how-to-set-qnetworkreply-timeout-without-external-timer
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
enum HandleMethod { Abort, Close };
|
||||
|
||||
ReplyTimeout(QNetworkReply* reply, const int timeout, HandleMethod method = Abort) :
|
||||
QObject(reply), m_method(method), m_timedout(false)
|
||||
QObject(reply), m_method(method), m_timedout(false)
|
||||
{
|
||||
Q_ASSERT(reply);
|
||||
if (reply && reply->isRunning()) {
|
||||
@@ -87,6 +88,10 @@ public:
|
||||
QJsonDocument getBody() const { return _responseBody; }
|
||||
void setBody(const QJsonDocument& body) { _responseBody = body; }
|
||||
|
||||
|
||||
QByteArray getHeader(const QByteArray header) const;
|
||||
void setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs);
|
||||
|
||||
QString getErrorReason() const { return _errorReason; }
|
||||
void setErrorReason(const QString& errorReason) { _errorReason = errorReason; }
|
||||
|
||||
@@ -99,6 +104,8 @@ public:
|
||||
private:
|
||||
|
||||
QJsonDocument _responseBody {};
|
||||
QHash<QByteArray, QByteArray> _responseHeaders;
|
||||
|
||||
bool _hasError = false;
|
||||
QString _errorReason;
|
||||
|
||||
@@ -131,6 +138,7 @@ class ProviderRestApi : public QObject
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
/// @brief Constructor of the REST-API wrapper
|
||||
///
|
||||
ProviderRestApi();
|
||||
@@ -176,6 +184,20 @@ public:
|
||||
///
|
||||
virtual ~ProviderRestApi() override;
|
||||
|
||||
///
|
||||
/// @brief Set the API's scheme
|
||||
///
|
||||
/// @param[in] scheme
|
||||
///
|
||||
void setScheme(const QString& scheme);
|
||||
|
||||
///
|
||||
/// @brief Get the API's scheme
|
||||
///
|
||||
/// return schme
|
||||
///
|
||||
QString getScheme() { return _apiUrl.scheme(); }
|
||||
|
||||
///
|
||||
/// @brief Set an API's host
|
||||
///
|
||||
@@ -190,6 +212,13 @@ public:
|
||||
///
|
||||
void setPort(const int port) { _apiUrl.setPort(port); }
|
||||
|
||||
///
|
||||
/// @brief Get the API's port
|
||||
///
|
||||
/// return port
|
||||
///
|
||||
int getPort() { return _apiUrl.port(); }
|
||||
|
||||
///
|
||||
/// @brief Set an API's url
|
||||
///
|
||||
@@ -204,6 +233,13 @@ public:
|
||||
///
|
||||
QUrl getUrl() const;
|
||||
|
||||
///
|
||||
/// @brief Set an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
/// @param[in] pathElements to form a path, e.g. (clip,v2,resource) results in "/clip/v2/resource"
|
||||
///
|
||||
void setBasePath(const QStringList& pathElements);
|
||||
|
||||
///
|
||||
/// @brief Set an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
@@ -211,6 +247,11 @@ public:
|
||||
///
|
||||
void setBasePath(const QString& basePath);
|
||||
|
||||
///
|
||||
/// @brief Clear an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
void clearBasePath();
|
||||
|
||||
///
|
||||
/// @brief Set an API's path to address resources
|
||||
///
|
||||
@@ -218,12 +259,18 @@ public:
|
||||
///
|
||||
void setPath(const QString& path);
|
||||
|
||||
///
|
||||
/// @brief Set an API's path to address resources
|
||||
///
|
||||
/// @param[in] pathElements to form a path, e.g. (lights,1,state) results in "/lights/1/state/"
|
||||
///
|
||||
void setPath(const QStringList& pathElements);
|
||||
|
||||
///
|
||||
/// @brief Clear an API's path
|
||||
///
|
||||
void clearPath();
|
||||
|
||||
///
|
||||
/// @brief Append an API's path element to path set before
|
||||
///
|
||||
@@ -252,6 +299,10 @@ public:
|
||||
///
|
||||
void setQuery(const QUrlQuery& query);
|
||||
|
||||
|
||||
QString getBasePath() {return _basePath;}
|
||||
QString getPath() {return _path;}
|
||||
|
||||
///
|
||||
/// @brief Execute GET request
|
||||
///
|
||||
@@ -359,6 +410,14 @@ public:
|
||||
/// @param[in] timeout in milliseconds.
|
||||
void setTransferTimeout(std::chrono::milliseconds timeout = DEFAULT_REST_TIMEOUT) { _requestTimeout = timeout; }
|
||||
|
||||
|
||||
bool setCaCertificate(const QString& caFileName);
|
||||
|
||||
void acceptSelfSignedCertificates(bool accept);
|
||||
|
||||
void setAlternateServerIdentity(const QString& serverIdentity);
|
||||
QString getAlternateServerIdentity() const;
|
||||
|
||||
///
|
||||
/// @brief Set the common logger for LED-devices.
|
||||
///
|
||||
@@ -366,6 +425,10 @@ public:
|
||||
///
|
||||
void setLogger(Logger* log) { _log = log; }
|
||||
|
||||
protected slots:
|
||||
/// Handle the SSLErrors
|
||||
void onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors);
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
@@ -379,9 +442,13 @@ private:
|
||||
|
||||
httpResponse executeOperation(QNetworkAccessManager::Operation op, const QUrl& url, const QByteArray& body = {});
|
||||
|
||||
bool checkServerIdentity(const QSslConfiguration& sslConfig) const;
|
||||
|
||||
bool matchesPinnedCertificate(const QSslCertificate& certificate);
|
||||
|
||||
Logger* _log;
|
||||
|
||||
// QNetworkAccessManager object for sending REST-requests.
|
||||
/// QNetworkAccessManager object for sending REST-requests.
|
||||
QNetworkAccessManager* _networkManager;
|
||||
std::chrono::milliseconds _requestTimeout;
|
||||
|
||||
@@ -394,6 +461,9 @@ private:
|
||||
QUrlQuery _query;
|
||||
|
||||
QNetworkRequest _networkRequestHeaders;
|
||||
|
||||
QString _serverIdentity;
|
||||
bool _isSeflSignedCertificateAccpeted;
|
||||
};
|
||||
|
||||
#endif // PROVIDERRESTKAPI_H
|
||||
|
||||
@@ -150,6 +150,11 @@ const int *ProviderUdpSSL::getCiphersuites() const
|
||||
return mbedtls_ssl_list_ciphersuites();
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::setPSKidentity(const QString& pskIdentity)
|
||||
{
|
||||
_psk_identity = pskIdentity;
|
||||
}
|
||||
|
||||
bool ProviderUdpSSL::initNetwork()
|
||||
{
|
||||
if ((!_isDeviceReady || _streamPaused) && _streamReady)
|
||||
@@ -334,6 +339,11 @@ void ProviderUdpSSL::freeSSLConnection()
|
||||
}
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::writeBytes(QByteArray data, bool flush)
|
||||
{
|
||||
writeBytes(static_cast<uint>(data.size()), reinterpret_cast<unsigned char*>(data.data()), flush);
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::writeBytes(unsigned int size, const uint8_t* data, bool flush)
|
||||
{
|
||||
if (!_streamReady || _streamPaused)
|
||||
|
||||
@@ -100,6 +100,14 @@ protected:
|
||||
///
|
||||
void stopConnection();
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
///
|
||||
/// @param[in] data The data
|
||||
///
|
||||
void writeBytes(QByteArray data, bool flush = false);
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
@@ -116,6 +124,8 @@ protected:
|
||||
///
|
||||
virtual const int * getCiphersuites() const;
|
||||
|
||||
void setPSKidentity(const QString& pskIdentity);
|
||||
|
||||
private:
|
||||
|
||||
bool initConnection();
|
||||
|
||||
@@ -159,12 +159,10 @@ QJsonObject LedDeviceWS281x::discover(const QJsonObject& /*params*/)
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
if (SysInfo::isUserAdmin())
|
||||
{
|
||||
//Indicate the general availability of the device, if hyperion is run under root
|
||||
deviceList << QJsonObject ({{"found",true}});
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
}
|
||||
//Indicate the general availability of the device, if hyperion is run under root
|
||||
devicesDiscovered.insert("isUserAdmin", SysInfo::isUserAdmin());
|
||||
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
|
||||
}
|
||||
else
|
||||
{
|
||||
QString errortext = QString("Invalid serial device name: %1 %2!").arg(_deviceName, _location);
|
||||
QString errortext = QString("Invalid serial device: %1 %2!").arg(_deviceName, _location);
|
||||
this->setInError( errortext );
|
||||
return false;
|
||||
}
|
||||
@@ -237,9 +237,9 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data)
|
||||
{
|
||||
if (!_rs232Port.waitForBytesWritten(WRITE_TIMEOUT.count()))
|
||||
{
|
||||
if ( _rs232Port.error() == QSerialPort::TimeoutError )
|
||||
if (_rs232Port.error() == QSerialPort::TimeoutError)
|
||||
{
|
||||
Debug(_log, "Timeout after %dms: %d frames already dropped", WRITE_TIMEOUT.count(), _frameDropCounter);
|
||||
Debug(_log, "Timeout after %dms: %d frames already dropped, Rs232 SerialPortError [%d]: %s", WRITE_TIMEOUT.count(), _frameDropCounter, _rs232Port.error(), QSTRING_CSTR(_rs232Port.errorString()));
|
||||
|
||||
++_frameDropCounter;
|
||||
|
||||
@@ -258,10 +258,16 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data)
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setInError( QString ("Rs232 SerialPortError: %1").arg(_rs232Port.errorString()) );
|
||||
this->setInError( QString ("Error writing data to %1, Error: %2").arg(_deviceName).arg(_rs232Port.error()));
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == -1)
|
||||
{
|
||||
Info(_log, "Try restarting the device %s after error occured...", QSTRING_CSTR(_activeDeviceType));
|
||||
emit enable();
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -72,41 +72,28 @@
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"panelOrderTopDown": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_order_top_down_title",
|
||||
"enum": [ 0, 1 ],
|
||||
"default": 0,
|
||||
"enum": [ "top2down", "bottom2up" ],
|
||||
"default": "top2down",
|
||||
"required": true,
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_top_down", "edt_conf_enum_bottom_up" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"panelOrderLeftRight": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_order_left_right_title",
|
||||
"enum": [ 0, 1 ],
|
||||
"default": 0,
|
||||
"enum": [ "left2right", "right2left" ],
|
||||
"default": "left2right",
|
||||
"required": true,
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_left_right", "edt_conf_enum_right_left" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"panelStartPos": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_panel_start_position",
|
||||
"step": 1,
|
||||
"minimum": 0,
|
||||
"default": 0,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 10
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
||||
@@ -35,26 +35,45 @@
|
||||
},
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"useAPIv2": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_useAPIv2_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"access": "expert",
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"useEntertainmentAPI": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_useEntertainmentAPI_title",
|
||||
"default": true,
|
||||
"propertyOrder": 5
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"switchOffOnBlack": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_switchOffOnBlack_title",
|
||||
"default": false,
|
||||
"propertyOrder": 6
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": false,
|
||||
"propertyOrder": 7
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"blackLevel": {
|
||||
"type": "number",
|
||||
@@ -64,7 +83,12 @@
|
||||
"step": 0.01,
|
||||
"minimum": 0.001,
|
||||
"maximum": 1.0,
|
||||
"propertyOrder": 8
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"onBlackTimeToPowerOff": {
|
||||
"type": "integer",
|
||||
@@ -76,7 +100,12 @@
|
||||
"maximum": 100000,
|
||||
"default": 600,
|
||||
"required": true,
|
||||
"propertyOrder": 9
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"onBlackTimeToPowerOn": {
|
||||
"type": "integer",
|
||||
@@ -88,14 +117,24 @@
|
||||
"maximum": 100000,
|
||||
"default": 300,
|
||||
"required": true,
|
||||
"propertyOrder": 9
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"candyGamma": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_candyGamma_title",
|
||||
"default": true,
|
||||
"propertyOrder": 10
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 12
|
||||
},
|
||||
"lightIds": {
|
||||
"type": "array",
|
||||
@@ -112,20 +151,23 @@
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 11
|
||||
"propertyOrder": 13
|
||||
},
|
||||
"groupId": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"step": 1,
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_groupId_title",
|
||||
"default": 0,
|
||||
"default": "",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 12
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 14
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
@@ -136,7 +178,12 @@
|
||||
"minimum": 0.5,
|
||||
"maximum": 10.0,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 13
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 15
|
||||
},
|
||||
"handshakeTimeoutMin": {
|
||||
"type": "number",
|
||||
@@ -154,7 +201,7 @@
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 14
|
||||
"propertyOrder": 16
|
||||
},
|
||||
"handshakeTimeoutMax": {
|
||||
"type": "number",
|
||||
@@ -172,7 +219,7 @@
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 15
|
||||
"propertyOrder": 17
|
||||
},
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
@@ -180,7 +227,7 @@
|
||||
"title": "edt_dev_spec_verbose_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 16
|
||||
"propertyOrder": 18
|
||||
},
|
||||
"transitiontime": {
|
||||
"type": "number",
|
||||
@@ -195,24 +242,29 @@
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 17
|
||||
"propertyOrder": 19
|
||||
},
|
||||
"blackLightsTimeout": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_blackLightsTimeout_title",
|
||||
"default": 5000,
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 18
|
||||
"propertyOrder": 20
|
||||
},
|
||||
"brightnessThreshold": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessThreshold_title",
|
||||
"default": 0.0001,
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 19
|
||||
"propertyOrder": 21
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "number",
|
||||
@@ -223,9 +275,11 @@
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 20
|
||||
"propertyOrder": 22
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "number",
|
||||
@@ -236,9 +290,11 @@
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 21
|
||||
"propertyOrder": 23
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
"rateList": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_baudrate_title",
|
||||
"enum": [ "CUSTOM","9600","14400","19200","28800","33600","38400","56000","57600","76800","115200","128000","153600","230400","256000","307200","460800","921600","1000000","1500000","2000000","3000000","4000000" ],
|
||||
"enum": [ "CUSTOM","250000","500000","1000000" ],
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_custom" ]
|
||||
},
|
||||
"default": "1000000",
|
||||
"default": "500000",
|
||||
"access": "advanced",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/mdns)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/mdns)
|
||||
|
||||
FILE ( GLOB MDNS_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
add_library(mdns ${MDNS_SOURCES})
|
||||
|
||||
include_directories(${QMDNS_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(mdns
|
||||
hyperion-utils
|
||||
${QMDNS_LIBRARIES}
|
||||
add_library(mdns
|
||||
${CMAKE_SOURCE_DIR}/include/mdns/MdnsServiceRegister.h
|
||||
${CMAKE_SOURCE_DIR}/include/mdns/MdnsBrowser.h
|
||||
${CMAKE_SOURCE_DIR}/include/mdns/MdnsProvider.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/mdns/MdnsBrowser.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/mdns/MdnsProvider.cpp
|
||||
)
|
||||
|
||||
target_include_directories(mdns PUBLIC ${QMDNS_INCLUDE_DIR})
|
||||
target_link_libraries(mdns
|
||||
hyperion
|
||||
qmdnsengine
|
||||
$<$<BOOL:${WIN32}>:bcrypt.lib>
|
||||
)
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
#include <qmdnsengine/message.h>
|
||||
#include <qmdnsengine/service.h>
|
||||
|
||||
//Qt includes
|
||||
// Qt includes
|
||||
#include <QThread>
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -19,7 +18,8 @@
|
||||
|
||||
namespace {
|
||||
const bool verboseBrowser = false;
|
||||
} //End of constants
|
||||
const int SERVICE_LOOKUP_RETRIES = 5;
|
||||
} // End of constants
|
||||
|
||||
MdnsBrowser::MdnsBrowser(QObject* parent)
|
||||
: QObject(parent)
|
||||
@@ -30,7 +30,7 @@ MdnsBrowser::MdnsBrowser(QObject* parent)
|
||||
|
||||
MdnsBrowser::~MdnsBrowser()
|
||||
{
|
||||
qDeleteAll(_browsedServiceTypes);
|
||||
_browsedServiceTypes.clear();
|
||||
}
|
||||
|
||||
void MdnsBrowser::browseForServiceType(const QByteArray& serviceType)
|
||||
@@ -38,11 +38,11 @@ void MdnsBrowser::browseForServiceType(const QByteArray& serviceType)
|
||||
if (!_browsedServiceTypes.contains(serviceType))
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Start new mDNS browser for serviceType [%s], Thread: %s", serviceType.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
QMdnsEngine::Browser* newBrowser = new QMdnsEngine::Browser(&_server, serviceType, &_cache);
|
||||
QSharedPointer<QMdnsEngine::Browser> newBrowser = QSharedPointer<QMdnsEngine::Browser>::create(&_server, serviceType, &_cache);
|
||||
|
||||
QObject::connect(newBrowser, &QMdnsEngine::Browser::serviceAdded, this, &MdnsBrowser::onServiceAdded);
|
||||
QObject::connect(newBrowser, &QMdnsEngine::Browser::serviceUpdated, this, &MdnsBrowser::onServiceUpdated);
|
||||
QObject::connect(newBrowser, &QMdnsEngine::Browser::serviceRemoved, this, &MdnsBrowser::onServiceRemoved);
|
||||
QObject::connect(newBrowser.get(), &QMdnsEngine::Browser::serviceAdded, this, &MdnsBrowser::onServiceAdded);
|
||||
QObject::connect(newBrowser.get(), &QMdnsEngine::Browser::serviceUpdated, this, &MdnsBrowser::onServiceUpdated);
|
||||
QObject::connect(newBrowser.get(), &QMdnsEngine::Browser::serviceRemoved, this, &MdnsBrowser::onServiceRemoved);
|
||||
|
||||
_browsedServiceTypes.insert(serviceType, newBrowser);
|
||||
}
|
||||
@@ -124,8 +124,8 @@ QHostAddress MdnsBrowser::getHostFirstAddress(const QByteArray& hostname)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "IP-address for hostname [%s] not yet in cache, start resolver.", toBeResolvedHostName.constData());
|
||||
qRegisterMetaType<QMdnsEngine::Message>("Message");
|
||||
auto* resolver = new QMdnsEngine::Resolver(&_server, toBeResolvedHostName, &_cache);
|
||||
connect(resolver, &QMdnsEngine::Resolver::resolved, this, &MdnsBrowser::onHostNameResolved);
|
||||
_resolver.reset(new QMdnsEngine::Resolver(&_server, toBeResolvedHostName, &_cache));
|
||||
connect(_resolver.get(), &QMdnsEngine::Resolver::resolved, this, &MdnsBrowser::onHostNameResolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ void MdnsBrowser::onHostNameResolved(const QHostAddress& address)
|
||||
|
||||
bool MdnsBrowser::resolveAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, std::chrono::milliseconds timeout)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Get address for hostname [%s], Thread: %s", QSTRING_CSTR(hostname), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
DebugIf(verboseBrowser, log, "Get address for hostname [%s], Thread: %s", QSTRING_CSTR(hostname), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
bool isHostAddressOK{ false };
|
||||
if (hostname.endsWith(".local") || hostname.endsWith(".local."))
|
||||
@@ -158,20 +158,20 @@ bool MdnsBrowser::resolveAddress(Logger* log, const QString& hostname, QHostAddr
|
||||
|
||||
if (hostAddress.isNull())
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Wait for resolver on hostname [%s]", QSTRING_CSTR(hostname));
|
||||
DebugIf(verboseBrowser, log, "Wait for resolver on hostname [%s]", QSTRING_CSTR(hostname));
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer t;
|
||||
QObject::connect(&MdnsBrowser::getInstance(), &MdnsBrowser::addressResolved, &loop, &QEventLoop::quit);
|
||||
QTimer timer;
|
||||
|
||||
QObject::connect(&MdnsBrowser::getInstance(), &MdnsBrowser::addressResolved, &loop, &QEventLoop::quit);
|
||||
weakConnect(&MdnsBrowser::getInstance(), &MdnsBrowser::addressResolved,
|
||||
[&hostAddress, hostname](const QHostAddress& resolvedAddress) {
|
||||
DebugIf(verboseBrowser, Logger::getInstance("MDNS"), "Resolver resolved hostname [%s] to address [%s], Thread: %s", QSTRING_CSTR(hostname), QSTRING_CSTR(resolvedAddress.toString()), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
[&hostAddress, hostname, log](const QHostAddress& resolvedAddress) {
|
||||
DebugIf(verboseBrowser, log, "Resolver resolved hostname [%s] to address [%s], Thread: %s", QSTRING_CSTR(hostname), QSTRING_CSTR(resolvedAddress.toString()), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
hostAddress = resolvedAddress;
|
||||
});
|
||||
|
||||
QTimer::connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
t.start(static_cast<int>(timeout.count()));
|
||||
QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
timer.start(static_cast<int>(timeout.count()));
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ bool MdnsBrowser::resolveAddress(Logger* log, const QString& hostname, QHostAddr
|
||||
}
|
||||
else
|
||||
{
|
||||
QObject::disconnect(&MdnsBrowser::getInstance(), &MdnsBrowser::addressResolved, nullptr, nullptr);
|
||||
Error(log, "Resolved mDNS hostname [%s] timed out", QSTRING_CSTR(hostname));
|
||||
}
|
||||
}
|
||||
@@ -205,8 +206,8 @@ QMdnsEngine::Record MdnsBrowser::getServiceInstanceRecord(const QByteArray& serv
|
||||
}
|
||||
|
||||
QMdnsEngine::Record srvRecord;
|
||||
bool found{ false };
|
||||
int retries = 5;
|
||||
bool found { false };
|
||||
int retries { SERVICE_LOOKUP_RETRIES };
|
||||
do
|
||||
{
|
||||
if (_cache.lookupRecord(service, QMdnsEngine::SRV, srvRecord))
|
||||
@@ -392,10 +393,10 @@ QJsonArray MdnsBrowser::getServicesDiscoveredJson(const QByteArray& serviceType,
|
||||
QMap<QByteArray, QByteArray> txtAttributes = txtRecord.attributes();
|
||||
|
||||
QVariantMap txtMap;
|
||||
QMapIterator<QByteArray, QByteArray> i(txtAttributes);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
txtMap.insert(i.key(), i.value());
|
||||
QMapIterator<QByteArray, QByteArray> iterator(txtAttributes);
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
txtMap.insert(iterator.key(), iterator.value());
|
||||
}
|
||||
obj.insert("txt", QJsonObject::fromVariantMap(txtMap));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <mdns/MdnsProvider.h>
|
||||
#include <mdns/MdnsServiceRegister.h>
|
||||
|
||||
//Qt includes
|
||||
// Qt includes
|
||||
#include <QHostInfo>
|
||||
#include <QThread>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace {
|
||||
const bool verboseProvider = false;
|
||||
} //End of constants
|
||||
} // End of constants
|
||||
|
||||
MdnsProvider::MdnsProvider(QObject* parent)
|
||||
: QObject(parent)
|
||||
@@ -24,58 +24,59 @@ MdnsProvider::MdnsProvider(QObject* parent)
|
||||
|
||||
void MdnsProvider::init()
|
||||
{
|
||||
_server = new QMdnsEngine::Server();
|
||||
_hostname = new QMdnsEngine::Hostname(_server);
|
||||
_server.reset(new QMdnsEngine::Server());
|
||||
_hostname.reset(new QMdnsEngine::Hostname(_server.data()));
|
||||
|
||||
connect(_hostname, &QMdnsEngine::Hostname::hostnameChanged, this, &MdnsProvider::onHostnameChanged);
|
||||
connect(_hostname.data(), &QMdnsEngine::Hostname::hostnameChanged, this, &MdnsProvider::onHostnameChanged);
|
||||
DebugIf(verboseProvider, _log, "Hostname [%s], isRegistered [%d]", _hostname->hostname().constData(), _hostname->isRegistered());
|
||||
}
|
||||
|
||||
MdnsProvider::~MdnsProvider()
|
||||
{
|
||||
qDeleteAll(_providedServiceTypes);
|
||||
|
||||
_hostname->deleteLater();
|
||||
_server->deleteLater();
|
||||
_providedServiceTypes.clear();
|
||||
}
|
||||
|
||||
void MdnsProvider::publishService(const QString& serviceType, quint16 servicePort, const QByteArray& serviceName)
|
||||
{
|
||||
QMdnsEngine::Provider* provider(nullptr);
|
||||
|
||||
QByteArray type = MdnsServiceRegister::getServiceType(serviceType);
|
||||
|
||||
if (!type.isEmpty())
|
||||
{
|
||||
DebugIf(verboseProvider, _log, "Publish new mDNS serviceType [%s], Thread: %s", type.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
if (!_providedServiceTypes.contains(type))
|
||||
{
|
||||
provider = new QMdnsEngine::Provider(_server, _hostname);
|
||||
_providedServiceTypes.insert(type, provider);
|
||||
QSharedPointer<QMdnsEngine::Provider> newProvider = QSharedPointer<QMdnsEngine::Provider>::create(_server.data(), _hostname.data());
|
||||
_providedServiceTypes.insert(type, newProvider);
|
||||
|
||||
}
|
||||
|
||||
QSharedPointer<QMdnsEngine::Provider> provider = _providedServiceTypes.value(type);
|
||||
if (!provider.isNull())
|
||||
{
|
||||
QMdnsEngine::Service service;
|
||||
service.setType(type);
|
||||
service.setPort(servicePort);
|
||||
|
||||
QByteArray name(QHostInfo::localHostName().toUtf8());
|
||||
if (!serviceName.isEmpty())
|
||||
{
|
||||
name.prepend(serviceName + "@");
|
||||
}
|
||||
service.setName(name);
|
||||
|
||||
QByteArray uuid = AuthManager::getInstance()->getID().toUtf8();
|
||||
const QMap<QByteArray, QByteArray> attributes = {{"id", uuid}, {"version", HYPERION_VERSION}};
|
||||
service.setAttributes(attributes);
|
||||
|
||||
DebugIf(verboseProvider, _log, "[%s], Name: [%s], Port: [%u] ", service.type().constData(), service.name().constData(), service.port());
|
||||
|
||||
provider->update(service);
|
||||
}
|
||||
else
|
||||
{
|
||||
provider = _providedServiceTypes[type];
|
||||
Error(_log, "Not able to get hold of mDNS serviceType [%s]", type.constData());
|
||||
}
|
||||
|
||||
QMdnsEngine::Service service;
|
||||
service.setType(type);
|
||||
service.setPort(servicePort);
|
||||
|
||||
QByteArray name(QHostInfo::localHostName().toUtf8());
|
||||
if (!serviceName.isEmpty())
|
||||
{
|
||||
name.prepend(serviceName + "@");
|
||||
}
|
||||
service.setName(name);
|
||||
|
||||
QByteArray id = AuthManager::getInstance()->getID().toUtf8();
|
||||
const QMap<QByteArray, QByteArray> attributes = { {"id", id}, {"version", HYPERION_VERSION} };
|
||||
service.setAttributes(attributes);
|
||||
|
||||
DebugIf(verboseProvider, _log, "[%s], Name: [%s], Port: [%u] ", service.type().constData(), service.name().constData(), service.port());
|
||||
|
||||
provider->update(service);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +1,22 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(ProtoServer_PROTOS ${CURRENT_SOURCE_DIR}/message.proto )
|
||||
|
||||
protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS ${ProtoServer_PROTOS} )
|
||||
### Split protoclient from protoserver as protoserver relates to HyperionDaemon and standalone capture binarys can't link to it
|
||||
|
||||
add_library(protoclient
|
||||
${CURRENT_SOURCE_DIR}/ProtoClientConnection.h
|
||||
${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp
|
||||
${ProtoServer_PROTO_SRCS}
|
||||
${ProtoServer_PROTO_HDRS}
|
||||
)
|
||||
|
||||
add_library(protoserver
|
||||
${CURRENT_HEADER_DIR}/ProtoServer.h
|
||||
${CURRENT_SOURCE_DIR}/ProtoServer.cpp
|
||||
)
|
||||
# set and compile proto schema
|
||||
set(ProtoServer_PROTOS ${CMAKE_SOURCE_DIR}/libsrc/protoserver/message.proto)
|
||||
protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS ${ProtoServer_PROTOS})
|
||||
|
||||
# disable warnings for auto generated proto files, we can't change the files ....
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set_source_files_properties(${ProtoServer_PROTO_SRCS} ${ProtoServer_PROTO_HDRS} ${ProtoServer_PROTOS} PROPERTIES COMPILE_FLAGS "-w -Wno-return-local-addr")
|
||||
elseif(MSVC)
|
||||
set_source_files_properties(${ProtoServer_PROTO_SRCS} ${ProtoServer_PROTO_HDRS} ${ProtoServer_PROTOS} PROPERTIES COMPILE_FLAGS "/W0")
|
||||
endif()
|
||||
|
||||
### Split protoclient from protoserver as protoserver relates to HyperionDaemon and standalone capture binarys can't link to it
|
||||
add_library(protoclient
|
||||
${CMAKE_SOURCE_DIR}/libsrc/protoserver/ProtoClientConnection.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/protoserver/ProtoClientConnection.cpp
|
||||
${ProtoServer_PROTO_SRCS}
|
||||
${ProtoServer_PROTO_HDRS}
|
||||
)
|
||||
|
||||
target_link_libraries(protoclient
|
||||
hyperion
|
||||
hyperion-utils
|
||||
@@ -39,6 +24,16 @@ target_link_libraries(protoclient
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
|
||||
target_include_directories(protoclient PUBLIC
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_library(protoserver
|
||||
${CMAKE_SOURCE_DIR}/include/protoserver/ProtoServer.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/protoserver/ProtoServer.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(protoserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user