From 7909997398ff4177f660e26db0c96677a199b69d Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:08:20 +0200 Subject: [PATCH 1/8] Update install_pr.sh Only run named 'Hyperion PR Build' have artifacts --- bin/scripts/install_pr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index ce029a08..8549f797 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -136,7 +136,7 @@ import json,sys data = json.load(sys.stdin) for i in data['workflow_runs']: - if i['head_sha'] == '"$head_sha"': + if i['head_sha'] == '"$head_sha"' and i['name'] == 'Hyperion PR Build': print(i['id']) break """ 2>/dev/null) From 48cea4ad9b1bf3ea40e2ac5c8465639f9f08565c Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sat, 30 Sep 2023 18:00:38 +0200 Subject: [PATCH 2/8] Focus on relevant PRs --- bin/scripts/install_pr.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index 8549f797..bab790a0 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -93,7 +93,7 @@ else fi # Determine if PR number exists -pulls=$(request_call "$api_url/pulls") +pulls=$(request_call "$api_url/pulls?state=open") pr_exists=$(echo "$pulls" | tr '\r\n' ' ' | ${pythonCmd} -c """ import json,sys @@ -106,7 +106,7 @@ for i in data: """ 2>/dev/null) if [ "$pr_exists" != "exists" ]; then - echo "---> Pull Request $pr_number not found -> abort" + echo "---> Pull Request $pr_number not found as open PR -> abort" exit 1 fi @@ -122,7 +122,7 @@ for i in data: """ 2>/dev/null) if [ -z "$head_sha" ]; then - echo "---> The specified PR #$pr_number has no longer any artifacts." + echo "---> The specified PR #$pr_number has no longer any artifacts or has been closed." echo "---> It may be older than 14 days. Ask the PR creator to recreate the artifacts at the following URL:" echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" exit 1 @@ -130,13 +130,13 @@ fi if [ -z "$run_id" ]; then # Determine run_id from head_sha -runs=$(request_call "$api_url/actions/runs") +runs=$(request_call "$api_url/actions/runs?head_sha=$head_sha") run_id=$(echo "$runs" | tr '\r\n' ' ' | ${pythonCmd} -c """ import json,sys data = json.load(sys.stdin) for i in data['workflow_runs']: - if i['head_sha'] == '"$head_sha"' and i['name'] == 'Hyperion PR Build': + if i['name'] == 'Hyperion PR Build': print(i['id']) break """ 2>/dev/null) From 08dc59c88561baa7dae422187060489c9b6b6f02 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sun, 1 Oct 2023 21:56:53 +0200 Subject: [PATCH 3/8] Fix #1630 - Audio Capture settings are ignored (#1640) * Fix macOS build * Update minimum cmake version * Correct compile errorswith Qt6.7 * Update minimum cmake version (2) * Use C++17 * Correct compile errors with Qt6.7 * Replace unsupported Lambda UniqueConnection * Support UTF-8 Output on console * Fix #1630 --- CMakeLists.txt | 34 +++++++------- assets/webconfig/js/content_grabber.js | 6 +-- cmake/Dependencies.cmake | 1 + dependencies/CMakeLists-qmdnsengine.txt.in | 2 +- dependencies/CMakeLists.txt | 8 ++-- include/api/JsonAPI.h | 5 ++ include/grabber/AudioGrabberWindows.h | 14 +++--- include/hyperion/ImageToLedsMap.h | 10 ++-- include/utils/global_defines.h | 2 +- libsrc/api/JsonAPI.cpp | 37 +++++++++------ libsrc/api/JsonCB.cpp | 2 +- libsrc/db/DBManager.cpp | 11 +++-- libsrc/effectengine/EffectFileHandler.cpp | 12 ++--- libsrc/forwarder/MessageForwarder.cpp | 10 ++-- libsrc/grabber/audio/AudioGrabber.cpp | 46 +++++++++++++------ libsrc/grabber/audio/AudioGrabberWindows.cpp | 8 +++- libsrc/grabber/audio/CMakeLists.txt | 2 +- libsrc/grabber/qt/QtGrabber.cpp | 2 +- libsrc/hyperion/ComponentRegister.cpp | 2 +- libsrc/hyperion/SettingsManager.cpp | 32 ++++++++----- .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 2 +- libsrc/leddevice/dev_net/LedDeviceWled.cpp | 8 ++-- libsrc/utils/Logger.cpp | 2 +- .../utils/jsonschema/QJsonSchemaChecker.cpp | 4 +- src/hyperion-aml/CMakeLists.txt | 2 +- src/hyperion-dispmanx/CMakeLists.txt | 2 +- src/hyperion-framebuffer/CMakeLists.txt | 2 +- src/hyperion-osx/CMakeLists.txt | 2 +- src/hyperion-qt/CMakeLists.txt | 2 +- src/hyperion-remote/CMakeLists.txt | 2 +- src/hyperion-remote/hyperion-remote.cpp | 4 +- src/hyperion-v4l2/CMakeLists.txt | 2 +- src/hyperion-x11/CMakeLists.txt | 2 +- src/hyperion-xcb/CMakeLists.txt | 2 +- src/hyperiond/console.h | 1 + src/hyperiond/main.cpp | 4 +- 36 files changed, 169 insertions(+), 120 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb1ef3f..813cb750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) message( STATUS "CMake Version: ${CMAKE_VERSION}" ) @@ -38,26 +38,24 @@ if ( CCACHE_FOUND ) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) -# enable C++14; MSVC doesn't have c++14 feature switch -if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - if(APPLE) - include(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("Werror=unguarded-availability" REQUIRED_UNGUARDED_AVAILABILITY) - if(REQUIRED_UNGUARDED_AVAILABILITY) - list(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "Werror=unguarded-availability") - endif() +# enable C++17 +if(APPLE) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("Werror=unguarded-availability" REQUIRED_UNGUARDED_AVAILABILITY) + if(REQUIRED_UNGUARDED_AVAILABILITY) + list(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "Werror=unguarded-availability") endif() - - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi") - endif() - - set(CMAKE_CXX_STANDARD 14) - set(CXX_STANDARD_REQUIRED ON) - set(CXX_EXTENSIONS OFF) endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi") +endif() + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + # Set build variables # Grabber SET ( DEFAULT_AMLOGIC OFF ) diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index 20da6030..6136673a 100755 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -327,7 +327,7 @@ $(document).ready(function () { var saveOptions = conf_editor_screen.getValue(); var instCaptOptions = window.serverConfig.instCapture; - instCaptOptions.systemEnable = true; + instCaptOptions.systemEnable = saveOptions.framegrabber.enable; saveOptions.instCapture = instCaptOptions; requestWriteConfig(saveOptions); @@ -679,7 +679,7 @@ $(document).ready(function () { var saveOptions = conf_editor_video.getValue(); var instCaptOptions = window.serverConfig.instCapture; - instCaptOptions.v4lEnable = true; + instCaptOptions.v4lEnable = saveOptions.grabberV4L2.enable; saveOptions.instCapture = instCaptOptions; requestWriteConfig(saveOptions); @@ -805,7 +805,7 @@ $(document).ready(function () { const saveOptions = conf_editor_audio.getValue(); const instCaptOptions = window.serverConfig.instCapture; - instCaptOptions.audioEnable = true; + instCaptOptions.audioEnable = saveOptions.grabberAudio.enable; saveOptions.instCapture = instCaptOptions; requestWriteConfig(saveOptions); diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 08120831..40ada3e1 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -48,6 +48,7 @@ macro(DeployMacOS TARGET) foreach(PLUGIN "platforms" "sqldrivers" "imageformats") if(EXISTS ${PLUGIN_DIR}/${PLUGIN}) file(GLOB files "${PLUGIN_DIR}/${PLUGIN}/*") + list(FILTER files EXCLUDE REGEX ".*libqwebp\\.dylib$") foreach(file ${files}) file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${file} diff --git a/dependencies/CMakeLists-qmdnsengine.txt.in b/dependencies/CMakeLists-qmdnsengine.txt.in index 12e45328..b3f0812a 100644 --- a/dependencies/CMakeLists-qmdnsengine.txt.in +++ b/dependencies/CMakeLists-qmdnsengine.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(qmdnsengine) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index c13e96cc..bc5044be 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -133,6 +133,9 @@ endif() if(ENABLE_PROTOBUF_SERVER) set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system") + + # defines for 3rd party sub-modules + set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") if (USE_SYSTEM_PROTO_LIBS) find_package(Protobuf REQUIRED) @@ -161,9 +164,6 @@ if(ENABLE_PROTOBUF_SERVER) # define the protobuf library set(PROTOBUF_LIBRARIES protobuf::libprotobuf) - # defines for 3rd party sub-modules - set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") - endif() # redefine at parent scope @@ -270,7 +270,7 @@ if(ENABLE_DEV_NETWORK) set(USE_SYSTEM_MBEDTLS_LIBS OFF) endif (NOT MBEDTLS_FOUND) else() - cmake_minimum_required(VERSION 3.2) + cmake_minimum_required(VERSION 3.5.1) set(CMAKE_POLICY_DEFAULT_CMP0071 NEW) diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 6346ce72..3880fc42 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -88,6 +88,11 @@ private slots: /// void handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name = QString()); + /// + /// @brief Stream a new LED Colors update + /// + void streamLedColorsUpdate(); + signals: /// /// Signal emits with the reply message provided with handleMessage() diff --git a/include/grabber/AudioGrabberWindows.h b/include/grabber/AudioGrabberWindows.h index 747212c2..9c3945b6 100644 --- a/include/grabber/AudioGrabberWindows.h +++ b/include/grabber/AudioGrabberWindows.h @@ -39,8 +39,8 @@ class AudioGrabberWindows : public AudioGrabber HANDLE notificationEvent; std::atomic isRunning{ false }; -static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR deviceDescStr, - LPCTSTR deviceModelStr, LPVOID context) +static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCWSTR deviceDescStr, + LPCWSTR deviceModelStr, LPVOID context) { // Skip undefined audio devices if (deviceIdGuid == NULL) @@ -50,12 +50,15 @@ static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR devic AudioGrabber::DeviceProperties device; + // Process Device Information + QString deviceName = QString::fromWCharArray(deviceDescStr); + // Process Device ID LPOLESTR deviceIdStr; HRESULT res = StringFromCLSID(*deviceIdGuid, &deviceIdStr); if (FAILED(res)) { - Error(Logger::getInstance("AUDIOGRABBER"), "Failed to get CLSID-string for %s with error: 0x%08x: %s", deviceDescStr, res, std::system_category().message(res).c_str()); + Error(Logger::getInstance("AUDIOGRABBER"), "Failed to get CLSID-string for %s with error: 0x%08x: %s", QSTRING_CSTR(deviceName), res, std::system_category().message(res).c_str()); return FALSE; } @@ -63,10 +66,7 @@ static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR devic CoTaskMemFree(deviceIdStr); - // Process Device Information - QString deviceName = QString::fromLocal8Bit(deviceDescStr); - - Debug(Logger::getInstance("AUDIOGRABBER"), "Found Audio Device: %s", deviceDescStr); + Debug(Logger::getInstance("AUDIOGRABBER"), "Found Audio Device: %s", QSTRING_CSTR(deviceName)); device.id = deviceId; device.name = deviceName; diff --git a/include/hyperion/ImageToLedsMap.h b/include/hyperion/ImageToLedsMap.h index 17662f28..45e7bb5a 100644 --- a/include/hyperion/ImageToLedsMap.h +++ b/include/hyperion/ImageToLedsMap.h @@ -413,9 +413,13 @@ namespace hyperion } // Compute the average of each color channel - const uint8_t avgRed = uint8_t(std::min(std::lround(sqrt(static_cast(cummRed/pixelNum))), 255L)); - const uint8_t avgGreen = uint8_t(std::min(std::lround(sqrt(static_cast(cummGreen/pixelNum))), 255L)); - const uint8_t avgBlue = uint8_t(std::min(std::lround(sqrt(static_cast(cummBlue/pixelNum))), 255L)); + + #ifdef WIN32 + #undef min + #endif + const uint8_t avgRed = static_cast(std::min(std::lround(std::sqrt(static_cast(cummRed / pixelNum))), 255L)); + const uint8_t avgGreen = static_cast(std::min(std::lround(sqrt(static_cast(cummGreen / pixelNum))), 255L)); + const uint8_t avgBlue = static_cast(std::min(std::lround(sqrt(static_cast(cummBlue / pixelNum))), 255L)); // Return the computed color return {avgRed, avgGreen, avgBlue}; diff --git a/include/utils/global_defines.h b/include/utils/global_defines.h index e5a6808e..e948fb97 100644 --- a/include/utils/global_defines.h +++ b/include/utils/global_defines.h @@ -1,6 +1,6 @@ #pragma once -#define QSTRING_CSTR(str) str.toLocal8Bit().constData() +#define QSTRING_CSTR(str) str.toUtf8().constData() typedef QList< int > QIntList; diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 212dc14b..75863ca2 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -140,6 +140,8 @@ void JsonAPI::initialize() connect(this, &JsonAPI::toggleSuspendAll, _instanceManager, &HyperionIManager::triggerToggleSuspend); connect(this, &JsonAPI::idleAll, _instanceManager, &HyperionIManager::triggerIdle); connect(this, &JsonAPI::toggleIdleAll, _instanceManager, &HyperionIManager::triggerToggleIdle); + + connect(_ledStreamTimer, &QTimer::timeout, this, &JsonAPI::streamLedColorsUpdate, Qt::UniqueConnection); } bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced) @@ -404,7 +406,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); @@ -1139,6 +1141,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 @@ -1154,21 +1161,21 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString & _streaming_leds_reply["tan"] = tan; connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector &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(); } @@ -1387,7 +1394,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; diff --git a/libsrc/api/JsonCB.cpp b/libsrc/api/JsonCB.cpp index 965abf37..e3c4b32a 100644 --- a/libsrc/api/JsonCB.cpp +++ b/libsrc/api/JsonCB.cpp @@ -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]; diff --git a/libsrc/db/DBManager.cpp b/libsrc/db/DBManager.cpp index a8711c15..f4494967 100644 --- a/libsrc/db/DBManager.cpp +++ b/libsrc/db/DBManager.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -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: diff --git a/libsrc/effectengine/EffectFileHandler.cpp b/libsrc/effectengine/EffectFileHandler.cpp index e1389e83..3d1c77eb 100644 --- a/libsrc/effectengine/EffectFileHandler.cpp +++ b/libsrc/effectengine/EffectFileHandler.cpp @@ -224,7 +224,7 @@ void EffectFileHandler::updateEffects() } QMap 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); } diff --git a/libsrc/forwarder/MessageForwarder.cpp b/libsrc/forwarder/MessageForwarder.cpp index b795cbe8..3720ad54 100644 --- a/libsrc/forwarder/MessageForwarder.cpp +++ b/libsrc/forwarder/MessageForwarder.cpp @@ -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())) diff --git a/libsrc/grabber/audio/AudioGrabber.cpp b/libsrc/grabber/audio/AudioGrabber.cpp index 1e625a1e..4f4eccbd 100644 --- a/libsrc/grabber/audio/AudioGrabber.cpp +++ b/libsrc/grabber/audio/AudioGrabber.cpp @@ -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(255), QVariant(0), QVariant(0) }))); - QJsonArray warnColorArray = config["warnColor"].toArray(QJsonArray::fromVariantList(QList({ QVariant(255), QVariant(255), QVariant(0) }))); - QJsonArray safeColorArray = config["safeColor"].toArray(QJsonArray::fromVariantList(QList({ 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() diff --git a/libsrc/grabber/audio/AudioGrabberWindows.cpp b/libsrc/grabber/audio/AudioGrabberWindows.cpp index 07837bd1..8a2228c3 100644 --- a/libsrc/grabber/audio/AudioGrabberWindows.cpp +++ b/libsrc/grabber/audio/AudioGrabberWindows.cpp @@ -1,4 +1,7 @@ #include + +#include + #include #include #include @@ -61,7 +64,10 @@ 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(1024), static_cast(audioFormat.nAvgBytesPerSec / 8)); notificationSize -= notificationSize % audioFormat.nBlockAlign; bufferCaptureSize = notificationSize * AUDIO_NOTIFICATION_COUNT; diff --git a/libsrc/grabber/audio/CMakeLists.txt b/libsrc/grabber/audio/CMakeLists.txt index 187fa9d0..714c5883 100644 --- a/libsrc/grabber/audio/CMakeLists.txt +++ b/libsrc/grabber/audio/CMakeLists.txt @@ -2,8 +2,8 @@ SET( CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber ) SET( CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio ) - if (WIN32) + add_definitions(-DUNICODE -D_UNICODE) 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") diff --git a/libsrc/grabber/qt/QtGrabber.cpp b/libsrc/grabber/qt/QtGrabber.cpp index b3f35182..cb6e0c5c 100644 --- a/libsrc/grabber/qt/QtGrabber.cpp +++ b/libsrc/grabber/qt/QtGrabber.cpp @@ -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()); diff --git a/libsrc/hyperion/ComponentRegister.cpp b/libsrc/hyperion/ComponentRegister.cpp index fd2f261d..9a040caf 100644 --- a/libsrc/hyperion/ComponentRegister.cpp +++ b/libsrc/hyperion/ComponentRegister.cpp @@ -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)); } diff --git a/libsrc/hyperion/SettingsManager.cpp b/libsrc/hyperion/SettingsManager.cpp index b9d78edc..e9fa5805 100644 --- a/libsrc/hyperion/SettingsManager.cpp +++ b/libsrc/hyperion/SettingsManager.cpp @@ -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()) { diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 8f73ae5f..ba008ab0 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -1126,7 +1126,7 @@ bool LedDevicePhilipsHue::setLights() if( !lArray.empty() ) { - for (const QJsonValue &id : qAsConst(lArray)) + for (const QJsonValue &id : std::as_const(lArray)) { int lightId = id.toString().toInt(); if( lightId > 0 ) diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp index 3ada5091..e23a8c9b 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp @@ -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(); diff --git a/libsrc/utils/Logger.cpp b/libsrc/utils/Logger.cpp index 86b4e903..b34981c9 100644 --- a/libsrc/utils/Logger.cpp +++ b/libsrc/utils/Logger.cpp @@ -65,7 +65,7 @@ void Logger::deleteInstance(const QString & name, const QString & subName) if (name.isEmpty()) { - for (auto *logger : qAsConst(LoggerMap)) { + for (auto *logger : std::as_const(LoggerMap)) { delete logger; } diff --git a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp index 685ab4c7..8cd0be75 100644 --- a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp +++ b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp @@ -59,13 +59,13 @@ QPair QJsonSchemaChecker::validate(const QJsonObject& value, bool ig QJsonObject QJsonSchemaChecker::getAutoCorrectedConfig(const QJsonObject& value, bool ignoreRequired) { _ignoreRequired = ignoreRequired; - QStringList sequence = QStringList() << "remove" << "modify" << "create"; + const QStringList sequence = QStringList() << "remove" << "modify" << "create"; _error = false; _schemaError = false; _messages.clear(); _autoCorrected = value; - for (const QString& correct : qAsConst(sequence)) + for (const QString& correct : sequence) { _correct = correct; _currentPath.clear(); diff --git a/src/hyperion-aml/CMakeLists.txt b/src/hyperion-aml/CMakeLists.txt index 37bed4af..cdea66fa 100644 --- a/src/hyperion-aml/CMakeLists.txt +++ b/src/hyperion-aml/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-aml) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-dispmanx/CMakeLists.txt b/src/hyperion-dispmanx/CMakeLists.txt index 17720483..07862bbd 100644 --- a/src/hyperion-dispmanx/CMakeLists.txt +++ b/src/hyperion-dispmanx/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-dispmanx) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-framebuffer/CMakeLists.txt b/src/hyperion-framebuffer/CMakeLists.txt index 5667203a..14ea2ce9 100644 --- a/src/hyperion-framebuffer/CMakeLists.txt +++ b/src/hyperion-framebuffer/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-framebuffer) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-osx/CMakeLists.txt b/src/hyperion-osx/CMakeLists.txt index 12f17a9b..efb15cd1 100644 --- a/src/hyperion-osx/CMakeLists.txt +++ b/src/hyperion-osx/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-osx) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) diff --git a/src/hyperion-qt/CMakeLists.txt b/src/hyperion-qt/CMakeLists.txt index 6cc4b386..f35b8534 100644 --- a/src/hyperion-qt/CMakeLists.txt +++ b/src/hyperion-qt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-qt) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index a7ce5cfe..a2805cad 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-remote) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index f47fbb14..e7fdc453 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -57,9 +57,9 @@ void showHelp(Option & option){ int getInstaneIdbyName(const QJsonObject & reply, const QString & name){ if(reply.contains("instance")){ - QJsonArray list = reply.value("instance").toArray(); + const QJsonArray list = reply.value("instance").toArray(); - for ( const auto &entry : qAsConst(list) ) { + for ( const auto &entry : list ) { const QJsonObject obj = entry.toObject(); if(obj["friendly_name"] == name && obj["running"].toBool()) { diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index 9c8783dd..1404d2ad 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-v4l2) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt index a7459ce6..88095ae3 100644 --- a/src/hyperion-x11/CMakeLists.txt +++ b/src/hyperion-x11/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-x11) find_package(X11 REQUIRED) diff --git a/src/hyperion-xcb/CMakeLists.txt b/src/hyperion-xcb/CMakeLists.txt index cd374d5a..f378aef2 100644 --- a/src/hyperion-xcb/CMakeLists.txt +++ b/src/hyperion-xcb/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-xcb) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperiond/console.h b/src/hyperiond/console.h index 98469960..e61156ae 100644 --- a/src/hyperiond/console.h +++ b/src/hyperiond/console.h @@ -15,4 +15,5 @@ void CreateConsole() freopen_s(&fDummy, "CONOUT$", "w", stderr); freopen_s(&fDummy, "CONIN$", "r", stdin); SetConsoleTitle(TEXT("Hyperion")); + SetConsoleOutputCP(CP_UTF8); } diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index 4719cd22..fa9352b9 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -268,9 +268,9 @@ int main(int argc, char** argv) if (directory.exists() && destDir.exists()) { std::cout << "Extract to folder: " << destDir.absolutePath().toStdString() << std::endl; - QStringList filenames = directory.entryList(QStringList() << "*", QDir::Files, QDir::Name | QDir::IgnoreCase); + const QStringList filenames = directory.entryList(QStringList() << "*", QDir::Files, QDir::Name | QDir::IgnoreCase); QString destFileName; - for (const QString & filename : qAsConst(filenames)) + for (const QString & filename : filenames) { destFileName = destDir.dirName()+"/"+filename; if (QFile::exists(destFileName)) From f49e1a2c0be690d45d0e169e2267318d166f80ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:03:57 +0200 Subject: [PATCH 4/8] Bump actions/checkout from 3 to 4 (#1646) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/apt.yml | 6 +++--- .github/workflows/codeql.yml | 2 +- .github/workflows/nightly.yml | 10 +++++----- .github/workflows/pull-request.yml | 6 +++--- .github/workflows/push-master.yml | 8 ++++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 66b90cb0..39f440b2 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -36,7 +36,7 @@ jobs: name: Setup APT build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set APT matrix id: apt-ppa run: | @@ -54,7 +54,7 @@ jobs: matrix: ${{ fromJson(needs.setup.outputs.apt-matrix) }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} submodules: true @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1565d699..d39190cc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7151ce2f..70d63aab 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'hyperion-project' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 @@ -48,7 +48,7 @@ jobs: if: github.repository_owner == 'hyperion-project' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check if commit has changed id: build-necessary run: | @@ -66,7 +66,7 @@ jobs: if: ${{ needs.check.outputs.build-nightly == 'true' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set nightly matrix id: nightly-ppa run: | @@ -84,7 +84,7 @@ jobs: matrix: ${{ fromJson(needs.setup.outputs.nightly-matrix) }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -135,7 +135,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Import GPG key uses: crazy-max/ghaction-import-gpg@v5.3.0 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 47769bfb..eee7c150 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -76,7 +76,7 @@ jobs: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -125,7 +125,7 @@ jobs: QT_VERSION: 5.15.2 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 8f1b44c3..b0ee7a1d 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -33,7 +33,7 @@ jobs: platform: amlogic steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -62,7 +62,7 @@ jobs: name: macOS runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -97,7 +97,7 @@ jobs: QT_VERSION: 5.15.2 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -162,7 +162,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Generate environment variables - name: Generate environment variables from .version and tag From 41bffecc0b5bc2b651f3d48516536937ad58f331 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:04:26 +0200 Subject: [PATCH 5/8] Bump peter-evans/repository-dispatch from 2.1.1 to 2.1.2 (#1645) Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/peter-evans/repository-dispatch/releases) - [Commits](https://github.com/peter-evans/repository-dispatch/compare/v2.1.1...v2.1.2) --- updated-dependencies: - dependency-name: peter-evans/repository-dispatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4c8461a..104c2c79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: steps: # Dispatch event to build new HyperBian image - name: Dispatch HyperBian build - uses: peter-evans/repository-dispatch@v2.1.1 + uses: peter-evans/repository-dispatch@v2.1.2 if: ${{ github.repository_owner == 'hyperion-project'}} with: repository: hyperion-project/HyperBian From 33722c9a09430f0b0f569d56b0a176ee9e4664ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:06:39 +0200 Subject: [PATCH 6/8] Bump crazy-max/ghaction-import-gpg from 5.3.0 to 6.0.0 (#1644) Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 5.3.0 to 6.0.0. - [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases) - [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v5.3.0...v6.0.0) --- updated-dependencies: - dependency-name: crazy-max/ghaction-import-gpg dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/apt.yml | 2 +- .github/workflows/nightly.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 39f440b2..99bce214 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -112,7 +112,7 @@ jobs: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5.3.0 + uses: crazy-max/ghaction-import-gpg@v6.0.0 with: gpg_private_key: ${{ secrets.APT_GPG }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 70d63aab..2046ba07 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -138,7 +138,7 @@ jobs: uses: actions/checkout@v4 - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5.3.0 + uses: crazy-max/ghaction-import-gpg@v6.0.0 with: gpg_private_key: ${{ secrets.APT_GPG }} From cd22d4454d99ae7bc84cabb89affcd32766c079c Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Tue, 3 Oct 2023 20:33:11 +0200 Subject: [PATCH 7/8] Philips Hue APIv2 support (#1637) * Support Philips Hue APIv2 and refactoring * Fix MDNSBrower - if timeout during host resolvment occurs * Hue API v2 - Migrate database * Fix macOS build * Handle network timeout before any other error * Address CodeQL findings * Clean-up and Fixes * Only getProperties, if username is available * Option to layout by entertainment area center * Fix Wizard --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> --- assets/webconfig/i18n/en.json | 7 +- assets/webconfig/js/content_leds.js | 16 +- assets/webconfig/js/wizard.js | 720 ++++-- libsrc/hyperion/SettingsManager.cpp | 55 + .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 2018 ++++++++++++----- .../leddevice/dev_net/LedDevicePhilipsHue.h | 203 +- libsrc/leddevice/dev_net/ProviderRestApi.cpp | 203 +- libsrc/leddevice/dev_net/ProviderRestApi.h | 74 +- libsrc/leddevice/dev_net/ProviderUdpSSL.cpp | 10 + libsrc/leddevice/dev_net/ProviderUdpSSL.h | 10 + .../leddevice/schemas/schema-philipshue.json | 108 +- libsrc/mdns/MdnsBrowser.cpp | 1 + resources/ssl/philips_hue_ca.pem | 14 + 13 files changed, 2541 insertions(+), 898 deletions(-) create mode 100644 resources/ssl/philips_hue_ca.pem diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 263c8bd6..d8518fd2 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -86,6 +86,8 @@ "conf_leds_layout_cl_bottomright": "Bottom Right (Corner)", "conf_leds_layout_cl_cornergap": "Corner Gap", "conf_leds_layout_cl_edgegap": "Edge Gap", + "conf_leds_layout_cl_entertainment": "Entertainment Area", + "conf_leds_layout_cl_entertainment_center": "Entertainment Area Center", "conf_leds_layout_cl_gaglength": "Gap length", "conf_leds_layout_cl_gappos": "gap position", "conf_leds_layout_cl_hleddepth": "Horizontal LED depth", @@ -618,7 +620,7 @@ "edt_dev_spec_gpioBcm_title": "GPIO Pin", "edt_dev_spec_gpioMap_title": "GPIO mapping", "edt_dev_spec_gpioNumber_title": "GPIO number", - "edt_dev_spec_groupId_title": "Group ID", + "edt_dev_spec_groupId_title": "Group", "edt_dev_spec_header_title": "Specific Settings", "edt_dev_spec_interpolation_title": "Interpolation", "edt_dev_spec_intervall_title": "Interval", @@ -683,6 +685,7 @@ "edt_dev_spec_transistionTime_title": "Transition time", "edt_dev_spec_uid_title": "UID", "edt_dev_spec_universe_title": "Universe", + "edt_dev_spec_useAPIv2_title": "Use API v2", "edt_dev_spec_useEntertainmentAPI_title": "Use Hue Entertainment API", "edt_dev_spec_useOrbSmoothing_title": "Use orb smoothing", "edt_dev_spec_useRgbwProtocol_title": "Use RGBW protocol", @@ -1087,7 +1090,7 @@ "wiz_cololight_noprops": "Not able to get device properties - Define Hardware LED count manually", "wiz_cololight_title": "Cololight Wizard", "wiz_guideyou": "The $1 will guide you through the settings. Just press the button!", - "wiz_hue_blinkblue": "Let ID $1 light up blue", + "wiz_hue_blinkblue": "Let it light up", "wiz_hue_clientkey": "Clientkey", "wiz_hue_create_user": "Create new User", "wiz_hue_desc1": "1. Hyperion searches automatically for a Hue-Bridge, in case it cannot find one you need to provide the hostname or IP-address and push the reload button.
2. Provide a user ID, if you do not have one create a new one.", diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 1aec94ab..2b0304c5 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -1021,11 +1021,6 @@ $(document).ready(function () { var generalOptions = window.serverSchema.properties.device; var ledType = $(this).val(); - - // philipshueentertainment backward fix - if (ledType == "philipshueentertainment") - ledType = "philipshue"; - var specificOptions = window.serverSchema.properties.alldevices[ledType]; conf_editor = createJsonEditor('editor_container_leddevice', { @@ -1060,13 +1055,10 @@ $(document).ready(function () { $('#btn_led_device_wiz').off(); if (ledType == "philipshue") { - $('#root_specificOptions_useEntertainmentAPI').on("change", function () { - var ledWizardType = (this.checked) ? "philipshueentertainment" : ledType; - var data = { type: ledWizardType }; - var hue_title = (this.checked) ? 'wiz_hue_e_title' : 'wiz_hue_title'; - changeWizard(data, hue_title, startWizardPhilipsHue); - }); - $("#root_specificOptions_useEntertainmentAPI").trigger("change"); + var ledWizardType = ledType; + var data = { type: ledWizardType }; + var hue_title = 'wiz_hue_title'; + changeWizard(data, hue_title, startWizardPhilipsHue); } else if (ledType == "atmoorb") { var ledWizardType = (this.checked) ? "atmoorb" : ledType; diff --git a/assets/webconfig/js/wizard.js b/assets/webconfig/js/wizard.js index 3f88f6db..79fc9182 100755 --- a/assets/webconfig/js/wizard.js +++ b/assets/webconfig/js/wizard.js @@ -604,7 +604,7 @@ var lightPosTopLeft112 = { hmin: 0, hmax: 0.5, vmin: 0, vmax: 0.15 }; var lightPosTopLeft121 = { hmin: 0.5, hmax: 1, vmin: 0, vmax: 0.15 }; var lightPosTopLeftNewMid = { hmin: 0.25, hmax: 0.75, vmin: 0, vmax: 0.15 }; -function assignLightPos(id, pos, name) { +function assignLightPos(pos, name) { var i = null; if (pos === "top") @@ -695,52 +695,50 @@ devicesProperties = {}; var hueIPs = []; var hueIPsinc = 0; -var hueLights = null; -var hueGroups = null; +var hueLights = []; +var hueEntertainmentConfigs = []; +var hueEntertainmentServices = []; var lightLocation = []; var groupLights = []; +var groupChannels = []; var groupLightsLocations = []; -var hueType = "philipshue"; +var isAPIv2Ready = true; +var isEntertainmentReady = true; function startWizardPhilipsHue(e) { - if (typeof e.data.type != "undefined") hueType = e.data.type; - //create html var hue_title = 'wiz_hue_title'; - var hue_intro1 = 'wiz_hue_intro1'; + var hue_intro1 = 'wiz_hue_e_intro1'; var hue_desc1 = 'wiz_hue_desc1'; var hue_create_user = 'wiz_hue_create_user'; - if (hueType == 'philipshueentertainment') { - hue_title = 'wiz_hue_e_title'; - hue_intro1 = 'wiz_hue_e_intro1'; - hue_desc1 = 'wiz_hue_e_desc1'; - hue_create_user = 'wiz_hue_e_create_user'; - } + $('#wiz_header').html('' + $.i18n(hue_title)); $('#wizp1_body').html('

' + $.i18n(hue_title) + '

' + $.i18n(hue_intro1) + '

'); $('#wizp1_footer').html(''); $('#wizp2_body').html('
'); - var hidePort = "hidden-lg"; - if (storedAccess === 'expert') { - hidePort = ""; - } - - $('#wh_topcontainer').append('

' + $.i18n(hue_desc1) + '

' + + var topContainer_html = '

' + $.i18n(hue_desc1) + '

' + '
' + '
' + '

' + $.i18n('wiz_hue_ip') + '

' + '
' + ' ' + - '
' + - '
' + - ' :' + - '
' + - '

' - ); - $('#wh_topcontainer').append(); - $('#wh_topcontainer').append(''); + ' ' + '' + + '
' + + ' ' + + '
'; + + if (storedAccess === 'expert') { + topContainer_html += '
' + + ':' + + '
'; + } + + topContainer_html += '

'; + topContainer_html += ''; + + $('#wh_topcontainer').append(topContainer_html); $('#usrcont').append('

' + $.i18n('wiz_hue_username') + '

' + '
' + @@ -751,23 +749,18 @@ function startWizardPhilipsHue(e) { '
' ); - if (hueType == 'philipshueentertainment') { - $('#usrcont').append('

' + $.i18n('wiz_hue_clientkey') + - '


'); - } + $('#usrcont').append('

' + $.i18n('wiz_hue_clientkey') + + '


'); $('#usrcont').append('

<\p>' + ''); - if (hueType == 'philipshueentertainment') { - $('#wizp2_body').append('

'); - createTable("gidsh", "gidsb", "hue_grp_ids_t"); - $('.gidsh').append(createTableRow([$.i18n('edt_dev_spec_groupId_title'), $.i18n('wiz_hue_e_use_group')], true)); - $('#wizp2_body').append(''); - } - else { - $('#wizp2_body').append(''); - } + $('#wizp2_body').append(''); + createTable("gidsh", "gidsb", "hue_grp_ids_t"); + $('.gidsh').append(createTableRow([$.i18n('edt_dev_spec_groupId_title'), ""], true)); + + $('#wizp2_body').append(''); + createTable("lidsh", "lidsb", "hue_ids_t"); $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lightid_title'), $.i18n('wiz_pos'), $.i18n('wiz_identify')], true)); $('#wizp2_footer').html(''); @@ -793,13 +786,26 @@ function startWizardPhilipsHue(e) { function checkHueBridge(cb, hueUser) { var usr = (typeof hueUser != "undefined") ? hueUser : 'config'; - if (usr == 'config') $('#wiz_hue_discovered').html(""); + if (usr === 'config') { + $('#wiz_hue_discovered').html(""); + } if (hueIPs[hueIPsinc]) { var host = hueIPs[hueIPsinc].host; var port = hueIPs[hueIPsinc].port; - getProperties_hue_bridge(cb, decodeURIComponent(host), port, usr); + if (usr != '') + { + getProperties_hue_bridge(cb, decodeURIComponent(host), port, usr); + } + else + { + cb(false, usr); + } + + if (isAPIv2Ready) { + $('#port').val(443); + } } } @@ -811,37 +817,51 @@ function checkBridgeResult(reply, usr) { $('#port').val(hueIPs[hueIPsinc].port) $('#usrcont').toggle(true); - checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper"); + + checkHueBridge(checkUserResult, $('#user').val()); } else { - //increment and check again - if (hueIPs.length - 1 > hueIPsinc) { - hueIPsinc++; - checkHueBridge(checkBridgeResult); - } - else { - $('#usrcont').toggle(false); - $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip')); - } + $('#usrcont').toggle(false); + $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip')); } }; -function checkUserResult(reply, usr) { +function checkUserResult(reply, username) { $('#usrcont').toggle(true); - if (reply) { - $('#user').val(usr); - if (hueType == 'philipshueentertainment' && $('#clientkey').val() == "") { + var hue_create_user = 'wiz_hue_e_create_user'; + if (!isEntertainmentReady) { + hue_create_user = 'wiz_hue_create_user'; + $('#hue_client_key_r').toggle(false); + } else { + $('#hue_client_key_r').toggle(true); + } + + $('#wiz_hue_create_user').text($.i18n(hue_create_user)); + $('#wiz_hue_create_user').toggle(true); + + if (reply) { + $('#user').val(username); + + if (isEntertainmentReady && $('#clientkey').val() == "") { $('#wiz_hue_usrstate').html($.i18n('wiz_hue_e_clientkey_needed')); $('#wiz_hue_create_user').toggle(true); } else { $('#wiz_hue_usrstate').html(""); $('#wiz_hue_create_user').toggle(false); - if (hueType == 'philipshue') { - get_hue_lights(); - } - if (hueType == 'philipshueentertainment') { - get_hue_groups(); + + if (isEntertainmentReady) { + $('#hue_id_headline').text($.i18n('wiz_hue_e_desc3')); + $('#hue_grp_ids_t').toggle(true); + + get_hue_groups(username); + + } else { + $('#hue_id_headline').text($.i18n('wiz_hue_desc2')); + $('#hue_grp_ids_t').toggle(false); + + get_hue_lights(username); + } } } @@ -852,22 +872,73 @@ function checkUserResult(reply, usr) { } }; -function useGroupId(id) { - $('#groupId').val(id); +function useGroupId(id, username) { + $('#groupId').val(hueEntertainmentConfigs[id].id); + if (isAPIv2Ready) { + var group = hueEntertainmentConfigs[id]; - //Ensure ligthIDs are strings - groupLights = hueGroups[id].lights.map(num => { - return String(num); - }); + groupLights = []; + for (const light of group.light_services) { + groupLights.push(light.rid); + } - groupLightsLocations = hueGroups[id].locations; - get_hue_lights(); + groupChannels = []; + for (const channel of group.channels) { + groupChannels.push(channel); + } + + groupLightsLocations = []; + for (const location of group.locations.service_locations) { + groupLightsLocations.push(location); + } + } else { + //Ensure ligthIDs are strings + groupLights = hueEntertainmentConfigs[id].lights.map(num => { + return String(num); + }); + + var lightLocations = hueEntertainmentConfigs[id].locations; + for (var locationID in lightLocations) { + var lightLocation = {}; + + let position = { + x: lightLocations[locationID][0], + y: lightLocations[locationID][1], + z: lightLocations[locationID][2] + }; + lightLocation.position = position; + + groupLightsLocations.push(lightLocation); + } + } + + get_hue_lights(username); } +function updateBridgeDetails(properties) { + var ledDeviceProperties = properties.config; + + if (!jQuery.isEmptyObject(ledDeviceProperties)) { + isEntertainmentReady = properties.isEntertainmentReady; + isAPIv2Ready = properties.isAPIv2Ready; + + if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) { + $('#wiz_hue_discovered').html( + "Bridge: " + ledDeviceProperties.name + + ", Modelid: " + ledDeviceProperties.modelid + + ", Firmware: " + ledDeviceProperties.swversion + "
" + + "API-Version: " + ledDeviceProperties.apiversion + + ", Entertainment: " + (isEntertainmentReady ? "✓" : "-") + + ", APIv2: " + (isAPIv2Ready ? "✓" : "-") + ); + } + } +} async function discover_hue_bridges() { $('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress')); - $('#wiz_hue_discovered').html("") + + // $('#wiz_hue_discovered').html("") const res = await requestLedDeviceDiscovery('philipshue'); if (res && !res.error) { const r = res.info; @@ -903,11 +974,6 @@ async function discover_hue_bridges() { port = device.port; } - //Remap https port to http port until Hue-API v2 is supported - if (port == 443) { - port = 80; - } - if (host) { if (!hueIPs.some(item => item.host === host)) { @@ -916,22 +982,39 @@ async function discover_hue_bridges() { } } } + $('#wiz_hue_ipstate').html(""); $('#host').val(hueIPs[hueIPsinc].host) $('#port').val(hueIPs[hueIPsinc].port) - var usr = $('#user').val(); - if (usr != "") { - checkHueBridge(checkUserResult, usr); - } else { - checkHueBridge(checkBridgeResult); + $('#hue_bridge_select').html(""); + + for (var key in hueIPs) { + $('#hue_bridge_select').append(createSelOpt(key, hueIPs[key].host)); } + + $('.hue_bridge_sel_watch').on("click", function () { + hueIPsinc = $(this).val(); + + var name = $("#hue_bridge_select option:selected").text(); + $('#host').val(name); + $('#port').val(hueIPs[hueIPsinc].port) + + var usr = $('#user').val(); + if (usr != "") { + checkHueBridge(checkUserResult, usr); + } else { + checkHueBridge(checkBridgeResult); + } + }); + + $('.hue_bridge_sel_watch').click(); } } } async function getProperties_hue_bridge(cb, hostAddress, port, username, resourceFilter) { - let params = { host: hostAddress, user: username, filter: resourceFilter }; + let params = { host: hostAddress, username: username, filter: resourceFilter }; if (port !== 'undefined') { params.port = parseInt(port); } @@ -945,23 +1028,27 @@ async function getProperties_hue_bridge(cb, hostAddress, port, username, resourc } // Use device's properties, if properties in chache - if (devicesProperties[ledType][key]) { + if (devicesProperties[ledType][key] && devicesProperties[ledType][key][username]) { + updateBridgeDetails(devicesProperties[ledType][key]); cb(true, username); } else { const res = await requestLedDeviceProperties(ledType, params); - - if (res && !res.error) { var ledDeviceProperties = res.info.properties; if (!jQuery.isEmptyObject(ledDeviceProperties)) { + devicesProperties[ledType][key] = {}; + devicesProperties[ledType][key][username] = ledDeviceProperties; + + isAPIv2Ready = res.info.isAPIv2Ready; + devicesProperties[ledType][key].isAPIv2Ready = isAPIv2Ready; + isEntertainmentReady = res.info.isEntertainmentReady; + devicesProperties[ledType][key].isEntertainmentReady = isEntertainmentReady; + + updateBridgeDetails(devicesProperties[ledType][key]); if (username === "config") { - if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) { - $('#wiz_hue_discovered').html("Bridge: " + ledDeviceProperties.name + ", Modelid: " + ledDeviceProperties.modelid + ", API-Version: " + ledDeviceProperties.apiversion); - cb(true); - } + cb(true); } else { - devicesProperties[ledType][key] = ledDeviceProperties; cb(true, username); } } else { @@ -973,12 +1060,12 @@ async function getProperties_hue_bridge(cb, hostAddress, port, username, resourc } } -async function identify_hue_device(hostAddress, port, username, id) { +async function identify_hue_device(hostAddress, port, username, name, id, id_v1) { var disabled = $('#btn_wiz_save').is(':disabled'); // Take care that new record cannot be save during background process $('#btn_wiz_save').prop('disabled', true); - let params = { host: decodeURIComponent(hostAddress), user: username, lightId: id }; + let params = { host: decodeURIComponent(hostAddress), username: username, lightName: decodeURIComponent(name), lightId: id, lightId_v1: id_v1 }; if (port !== 'undefined') { params.port = parseInt(port); @@ -1003,11 +1090,9 @@ function beginWizardHue() { $('#user').val(usr); } - if (hueType == 'philipshueentertainment') { - var clkey = eV("clientkey"); - if (clkey != "") { - $('#clientkey').val(clkey); - } + var clkey = eV("clientkey"); + if (clkey != "") { + $('#clientkey').val(clkey); } //check if host is empty/reachable/search for bridge @@ -1022,13 +1107,13 @@ function beginWizardHue() { $('#host').val(host); var port = eV("port"); - if (port == 0) { - $('#port').val(80); - } - else { + if (port > 0) { $('#port').val(port); } - hueIPs.unshift({ host: host, port: port }); + else { + $('#port').val(''); + } + hueIPs.push({ host: host, port: port }); if (usr != "") { checkHueBridge(checkUserResult, usr); @@ -1038,18 +1123,18 @@ function beginWizardHue() { } $('#retry_bridge').off().on('click', function () { + var host = $('#host').val(); + var port = parseInt($('#port').val()); - if ($('#host').val() != "") { + if (host != "") { - hueIPs = []; - hueIPsinc = 0; - - var port = $('#port').val(); - if (isNaN(port) || port < 1 || port > 65535) { - port = 80; - $('#port').val(80); + var idx = hueIPs.findIndex(item => item.host === host && item.port === port); + if (idx === -1) { + hueIPs.push({ host: host, port: port }); + hueIPsinc = hueIPs.length - 1; + } else { + hueIPsinc = idx; } - hueIPs.push({ host: $('#host').val(), port: port }); } else { discover_hue_bridges(); @@ -1064,29 +1149,177 @@ function beginWizardHue() { }); $('#retry_usr').off().on('click', function () { - checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper"); + checkHueBridge(checkUserResult, $('#user').val()); }); $('#wiz_hue_create_user').off().on('click', function () { - if ($('#host').val() != "") { - hueIPs.unshift({ host: $('#host').val(), port: $('#port').val() }); - } createHueUser(); }); + function assignLightEntertainmentPos(isFocusCenter, position, name, id) { + + var x = position.x; + var z = position.z; + + if (isFocusCenter) { + // Map lights as in centered range -0.5 to 0.5 + if (x < -0.5) { + x = -0.5; + } else if (x > 0.5) { + x = 0.5; + } + if (z < -0.5) { + z = -0.5; + } else if (z > 0.5) { + z = 0.5; + } + } else { + // Map lights as in full range -1 to 1 + x /= 2; + z /= 2; + } + + var h = x + 0.5; + var v = -z + 0.5; + + var hmin = h - 0.05; + var hmax = h + 0.05; + var vmin = v - 0.05; + var vmax = v + 0.05; + + let layoutObject = { + hmin: hmin < 0 ? 0 : hmin, + hmax: hmax > 1 ? 1 : hmax, + vmin: vmin < 0 ? 0 : vmin, + vmax: vmax > 1 ? 1 : vmax, + name: name + }; + + if (id) { + layoutObject.name += "_" + id; + } + return layoutObject; + } + + function assignSegmentedLightPos(segment, position, name) { + var layoutObjects = []; + + var segTotalLength = 0; + for (var key in segment) { + + segTotalLength += segment[key].length; + } + + var min; + var max; + var horizontal = true; + + var layoutObject = assignLightPos(position, name); + if (position === "left" || position === "right") { + // vertical distribution + min = layoutObject.vmin; + max = layoutObject.vmax; + horizontal = false; + + } else { + // horizontal distribution + min = layoutObject.hmin; + max = layoutObject.hmax; + } + + var step = (max - min) / segTotalLength; + var start = min; + + for (var key in segment) { + min = start; + max = round(start + segment[key].length * step); + + if (horizontal) { + layoutObject.hmin = min; + layoutObject.hmax = max; + } else { + layoutObject.vmin = min; + layoutObject.vmax = max; + } + layoutObject.name = name + "_" + key; + layoutObjects.push(JSON.parse(JSON.stringify(layoutObject))); + + start = max; + } + + return layoutObjects; + } + $('#btn_wiz_save').off().on("click", function () { var hueLedConfig = []; var finalLightIds = []; + var channelNumber = 0; //create hue led config - for (var key in hueLights) { - if (hueType == 'philipshueentertainment') { - if (groupLights.indexOf(key) == -1) continue; - } - if ($('#hue_' + key).val() != "disabled") { - finalLightIds.push(key); - var idx_content = assignLightPos(key, $('#hue_' + key).val(), hueLights[key].name); - hueLedConfig.push(JSON.parse(JSON.stringify(idx_content))); + for (var key in groupLights) { + var lightId = groupLights[key]; + + if ($('#hue_' + lightId).val() != "disabled") { + finalLightIds.push(lightId); + + var lightName; + if (isAPIv2Ready) { + var light = hueLights.find(light => light.id === lightId); + lightName = light.metadata.name; + } else { + lightName = hueLights[lightId].name; + } + + var position = $('#hue_' + lightId).val(); + var lightIdx = groupLights.indexOf(lightId); + var lightLocation = groupLightsLocations[lightIdx]; + + var serviceID; + if (isAPIv2Ready) { + serviceID = lightLocation.service.rid; + } + + if (position.startsWith("entertainment")) { + + // Layout per entertainment area definition at bridge + var isFocusCenter = false; + if (position === "entertainment_center") { + isFocusCenter = true; + } + + if (isAPIv2Ready) { + + groupChannels.forEach((channel) => { + if (channel.members[0].service.rid === serviceID) { + var layoutObject = assignLightEntertainmentPos(isFocusCenter, channel.position, lightName, channel.channel_id); + hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); + ++channelNumber; + } + }); + } else { + var layoutObject = assignLightEntertainmentPos(isFocusCenter, lightLocation.position, lightName); + hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); + } + } + else { + // Layout per manual settings + var maxSegments = 1; + + if (isAPIv2Ready) { + var service = hueEntertainmentServices.find(service => service.id === serviceID); + maxSegments = service.segments.max_segments; + } + + if (maxSegments > 1) { + var segment = service.segments.segments; + var layoutObjects = assignSegmentedLightPos(segment, position, lightName); + hueLedConfig.push(...layoutObjects); + } else { + var layoutObject = assignLightPos(position, lightName); + hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); + } + channelNumber += maxSegments; + } } } @@ -1121,7 +1354,7 @@ function beginWizardHue() { d.brightnessFactor = parseFloat(eV("brightnessFactor", 1)); d.clientkey = $('#clientkey').val(); - d.groupId = parseInt($('#groupId').val()); + d.groupId = $('#groupId').val(); d.blackLightsTimeout = parseInt(eV("blackLightsTimeout", 5000)); d.brightnessMin = parseFloat(eV("brightnessMin", 0)); d.brightnessMax = parseFloat(eV("brightnessMax", 1)); @@ -1134,8 +1367,16 @@ function beginWizardHue() { d.enableAttempts = parseInt(conf_editor.getEditor("root.generalOptions.enableAttempts").getValue()); d.enableAttemptsInterval = parseInt(conf_editor.getEditor("root.generalOptions.enableAttemptsInterval").getValue()); - if (hueType == 'philipshue') { - d.useEntertainmentAPI = false; + d.useEntertainmentAPI = isEntertainmentReady; + d.useAPIv2 = isAPIv2Ready; + + if (isEntertainmentReady) { + d.hardwareLedCount = channelNumber; + if (window.serverConfig.device.type !== d.type) { + //smoothing on, if new device + sc.smoothing = { enable: true }; + } + } else { d.hardwareLedCount = finalLightIds.length; d.verbose = false; if (window.serverConfig.device.type !== d.type) { @@ -1144,15 +1385,6 @@ function beginWizardHue() { } } - if (hueType == 'philipshueentertainment') { - d.useEntertainmentAPI = true; - d.hardwareLedCount = groupLights.length; - if (window.serverConfig.device.type !== d.type) { - //smoothing on, if new device - sc.smoothing = { enable: true }; - } - } - window.serverConfig.device = d; requestWriteConfig(sc, true); @@ -1163,7 +1395,6 @@ function beginWizardHue() { } function createHueUser() { - var host = hueIPs[hueIPsinc].host; var port = hueIPs[hueIPsinc].port; @@ -1208,7 +1439,8 @@ function createHueUser() { conf_editor.getEditor("root.specificOptions.host").setValue(host); conf_editor.getEditor("root.specificOptions.port").setValue(port); } - if (hueType == 'philipshueentertainment') { + + if (isEntertainmentReady) { var clientkey = response.clientkey; if (clientkey != 'undefined') { $('#clientkey').val(clientkey); @@ -1230,37 +1462,52 @@ function createHueUser() { }, retryInterval * 1000); } -function get_hue_groups() { - +function get_hue_groups(username) { var host = hueIPs[hueIPsinc].host; - if (devicesProperties['philipshue'][host]) { - var ledProperties = devicesProperties['philipshue'][host]; + if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) { + var ledProperties = devicesProperties['philipshue'][host][username]; - if (!jQuery.isEmptyObject(ledProperties)) { - hueGroups = ledProperties.groups; - if (Object.keys(hueGroups).length > 0) { - - $('.lidsb').html(""); - $('#wh_topcontainer').toggle(false); - $('#hue_grp_ids_t').toggle(true); - - var gC = 0; - for (var groupid in hueGroups) { - if (hueGroups[groupid].type == 'Entertainment') { - $('.gidsb').append(createTableRow([groupid + ' (' + hueGroups[groupid].name + ')', ''])); - gC++; - } - } - if (gC == 0) { - noAPISupport('wiz_hue_e_noegrpids'); + if (isAPIv2Ready) { + if (!jQuery.isEmptyObject(ledProperties.data)) { + if (Object.keys(ledProperties.data).length > 0) { + hueEntertainmentConfigs = ledProperties.data.filter(config => { + return config.type === "entertainment_configuration"; + }); + hueEntertainmentServices = ledProperties.data.filter(config => { + return (config.type === "entertainment" && config.renderer === true); + }); } } + } else { + if (!jQuery.isEmptyObject(ledProperties.groups)) { + hueEntertainmentConfigs = []; + var hueGroups = ledProperties.groups; + for (var groupid in hueGroups) { + if (hueGroups[groupid].type == 'Entertainment') { + hueGroups[groupid].id = groupid; + hueEntertainmentConfigs.push(hueGroups[groupid]); + } + } + } + } + + if (Object.keys(hueEntertainmentConfigs).length > 0) { + + $('.lidsb').html(""); + $('#wh_topcontainer').toggle(false); + $('#hue_grp_ids_t').toggle(true); + + for (var groupid in hueEntertainmentConfigs) { + $('.gidsb').append(createTableRow([groupid + ' (' + hueEntertainmentConfigs[groupid].name + ')', ''])); + } + } else { + noAPISupport('wiz_hue_e_noegrpids', username); } } } -function noAPISupport(txt) { +function noAPISupport(txt, username) { showNotification('danger', $.i18n('wiz_hue_e_title'), $.i18n('wiz_hue_e_noapisupport_hint')); conf_editor.getEditor("root.specificOptions.useEntertainmentAPI").setValue(false); $("#root_specificOptions_useEntertainmentAPI").trigger("change"); @@ -1269,51 +1516,82 @@ function noAPISupport(txt) { var txt = (txt) ? $.i18n(txt) : $.i18n('wiz_hue_e_nogrpids'); $('

' + txt + '
' + $.i18n('wiz_hue_e_noapisupport') + '

').insertBefore('#wizp2_body #hue_ids_t'); $('#hue_id_headline').html($.i18n('wiz_hue_desc2')); - hueType = 'philipshue'; - get_hue_lights(); + + get_hue_lights(username); } -function get_hue_lights() { - +function get_hue_lights(username) { var host = hueIPs[hueIPsinc].host; - if (devicesProperties['philipshue'][host]) { - var ledProperties = devicesProperties['philipshue'][host]; + if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) { + var ledProperties = devicesProperties['philipshue'][host][username]; - if (!jQuery.isEmptyObject(ledProperties.lights)) { - hueLights = ledProperties.lights; - if (Object.keys(hueLights).length > 0) { - if (hueType == 'philipshue') { - $('#wh_topcontainer').toggle(false); + if (isAPIv2Ready) { + if (!jQuery.isEmptyObject(ledProperties.data)) { + if (Object.keys(ledProperties.data).length > 0) { + hueLights = ledProperties.data.filter(config => { + return config.type === "light"; + }); } - $('#hue_ids_t, #btn_wiz_save').toggle(true); + } + } else { + if (!jQuery.isEmptyObject(ledProperties.lights)) { + hueLights = ledProperties.lights; + } + } - var lightOptions = [ - "top", "topleft", "topright", - "bottom", "bottomleft", "bottomright", - "left", "lefttop", "leftmiddle", "leftbottom", - "right", "righttop", "rightmiddle", "rightbottom", - "entire", - "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", - "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", - "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" - ]; + if (Object.keys(hueLights).length > 0) { + if (!isEntertainmentReady) { + $('#wh_topcontainer').toggle(false); + } + $('#hue_ids_t, #btn_wiz_save').toggle(true); - if (hueType == 'philipshue') { - lightOptions.unshift("disabled"); + var lightOptions = [ + "top", "topleft", "topright", + "bottom", "bottomleft", "bottomright", + "left", "lefttop", "leftmiddle", "leftbottom", + "right", "righttop", "rightmiddle", "rightbottom", + "entire", + "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", + "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", + "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" + ]; + + if (isEntertainmentReady) { + lightOptions.unshift("entertainment_center"); + lightOptions.unshift("entertainment"); + } else { + lightOptions.unshift("disabled"); + groupLights = Object.keys(hueLights); + } + + $('.lidsb').html(""); + + var pos = ""; + for (var id in groupLights) { + var lightId = groupLights[id]; + var lightId_v1 = "/lights/" + lightId; + + var lightName; + if (isAPIv2Ready) { + var light = hueLights.find(light => light.id === lightId); + lightName = light.metadata.name; + lightId_v1 = light.id_v1; + } else { + lightName = hueLights[lightId].name; } - $('.lidsb').html(""); - var pos = ""; - for (var lightid in hueLights) { - if (hueType == 'philipshueentertainment') { - if (groupLights.indexOf(lightid) == -1) continue; + if (isEntertainmentReady) { + var lightLocation = {}; + lightLocation = groupLightsLocations[id]; + if (lightLocation) { + if (isAPIv2Ready) { + pos = 0; + } else { + var x = lightLocation.position.x; + var y = lightLocation.position.y; + var z = lightLocation.position.z; - if (groupLightsLocations.hasOwnProperty(lightid)) { - lightLocation = groupLightsLocations[lightid]; - var x = lightLocation[0]; - var y = lightLocation[1]; - var z = lightLocation[2]; var xval = (x < 0) ? "left" : "right"; if (z != 1 && x >= -0.25 && x <= 0.25) xval = ""; switch (z) { @@ -1329,37 +1607,39 @@ function get_hue_lights() { } } } - var options = ""; - for (var opt in lightOptions) { - var val = lightOptions[opt]; - var txt = (val != 'entire' && val != 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_'; - options += '