mDNS Support (#1452)

* Allow build, if no grabbers are enabled

* Align available functions to right Qt version

* Update to next development version

* Align available functions to right Qt version

* fix workflows (apt/nightly)

* Disable QNetworkConfigurationManager deprecation warnings

* Initial go on Smart Pointers

* Add Deallocation

* Correct QT_WARNING_DISABLE_DEPRECATED (available since 5.9)

* Cluster Build Variables

* Hyperion Light

* Address build warnings

* Hyperion Light - UI

* Update Protobuf to latest master

* Removed compiler warnings

* Added restart ability to systray

* Correct Protobuf

* Ignore 'no-return' warning on protobuf build

* hyperion-remote: Fix auto discovery of hyperion server

* Fix Qt version override

* Update changelog

* Remove Grabber Components, if no Grabber exists

* Standalone Grabber - Fix fps default

* Remote Control - Have Source Selction accrosswhole screen

* Enable Blackborder detection only, if relevant input sources available

* Enable Blackborder detection only, if relevant input sources available

* Remote UI - rearrange containers

* Checkout

* Fix compilation on windows

* Re-added qmdnsengine template cmake

* chrono added for linux

* Removed existing AVAHI/Bonjour, allow to enable/disable mDNS

* hyperiond macos typo fix

* Fix macOS Bundle build

* Fix macOS bundle info details

* Correct CMake files

* Removed existing AVAHI/Bonjour (2)

* Share hyperion's services via mDNS

* Add mDNS Browser and mDNS for LED-Devices

* Support mDNS discovery for standalone grabbers

* Remove ZLib Dependency & Cleanup

* mDNS - hanle 2.local2 an ".local." domains equally

* Hue - Link discovery to bridge class, workaround port 443 for mDNS discovery

* Fix save button state when switching between devices

* Removed sessions (of other hyperions)

* mDNS Publisher - Simplify service naming

* mDNS refactoring & Forwarder discovery

* mDNS Updates to use device service name

* Consistency of standalone grabbers with mDNS Service Registry

* Merge branch 'hyperion-project:master' into mDNS

* Start JSON and WebServers only after Instance 0 is available

* Remove bespoke qDebug Output again

* MDNS updates and refactor Forwarder

* Minor updates

* Upgrade to CMake 3.1

* typo

* macOS fix

* Correct merge

* - Remove dynamic linker flag from standalone dispmanX Grabber
- Added ability to use system qmdns libs

* Cec handler library will load at runtime

* typo fix

* protobuf changes

* mDNS changes for Windows/macOS

* test window build qmdnsengine

* absolute path to protobuf cmake dir

* Rework Hue Wizard supporting mDNS

* LED-Devices - Retry support + Refactoring (excl. Hue)

* LED-Devices - Refactoring/Retry support Hue + additional alignments

* Address LGTM findings

* Fix CI-Build, revert test changes

* Build Windows in Release mode to avoid python problem

* Correct that WebServerObject is available earlier

* Ensure that instance name in logs for one instance are presented

* Update content LEDs

* Rework mDNS Address lookup

* Fix LED UI

* Fix for non mDNS Services (ignore default port)

* Disbale device when now input is available

* Revert back some updates, ensure last color is updated when switched on

* Handle reopening case and changed IP, port for API-calls

* Add UPD-DDP Device

* WLED support for DDP

* Fix printout

* LEDDevice - Allow more retries, udapte defaults

* LED-Net Devices - Select Custom device, if configured

Co-authored-by: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com>
Co-authored-by: Paulchen Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
LordGrey 2022-05-01 19:42:47 +02:00 committed by GitHub
parent 3ef4ebc1a4
commit e9936e131b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
148 changed files with 5885 additions and 4459 deletions

View File

@ -3,6 +3,7 @@
# detect CI # detect CI
if [ "$HOME" != "" ]; then if [ "$HOME" != "" ]; then
# GitHub Actions # GitHub Actions
echo "Github Actions detected"
CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$GITHUB_WORKSPACE" CI_BUILD_DIR="$GITHUB_WORKSPACE"
else else
@ -20,8 +21,11 @@ else
PLATFORM=${PLATFORM}-dev PLATFORM=${PLATFORM}-dev
fi fi
echo "Platform: ${PLATFORM}, build type: ${BUILD_TYPE}, CI_NAME: $CI_NAME, docker image: ${DOCKER_IMAGE}, docker type: ${DOCKER_TAG}"
# Build the package on osx or linux # Build the package on osx or linux
if [[ "$CI_NAME" == 'osx' || "$CI_NAME" == 'darwin' ]]; then if [[ "$CI_NAME" == 'osx' || "$CI_NAME" == 'darwin' ]]; then
echo "Compile Hyperion on OSX or Darwin"
# compile prepare # compile prepare
mkdir build || exit 1 mkdir build || exit 1
cd build cd build
@ -31,12 +35,13 @@ if [[ "$CI_NAME" == 'osx' || "$CI_NAME" == 'darwin' ]]; then
exit 0; exit 0;
exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; }
elif [[ $CI_NAME == *"mingw64_nt"* || "$CI_NAME" == 'windows_nt' ]]; then elif [[ $CI_NAME == *"mingw64_nt"* || "$CI_NAME" == 'windows_nt' ]]; then
echo "Compile Hyperion on Windows"
# compile prepare # compile prepare
echo "Number of Cores $NUMBER_OF_PROCESSORS" echo "Number of Cores $NUMBER_OF_PROCESSORS"
mkdir build || exit 1 mkdir build || exit 1
cd build cd build
cmake -G "Visual Studio 17 2022" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2 cmake -G "Visual Studio 17 2022" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE="Release" ../ || exit 2
cmake --build . --target package --config Release -- -nologo -v:m -maxcpucount || exit 3 cmake --build . --target package --config "Release" -- -nologo -v:m -maxcpucount || exit 3
exit 0; exit 0;
exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; }
elif [[ "$CI_NAME" == 'linux' ]]; then elif [[ "$CI_NAME" == 'linux' ]]; then

3
.gitmodules vendored
View File

@ -9,3 +9,6 @@
[submodule "dependencies/external/protobuf"] [submodule "dependencies/external/protobuf"]
path = dependencies/external/protobuf path = dependencies/external/protobuf
url = https://github.com/protocolbuffers/protobuf url = https://github.com/protocolbuffers/protobuf
[submodule "dependencies/external/qmdnsengine"]
path = dependencies/external/qmdnsengine
url = https://github.com/nitroshare/qmdnsengine.git

View File

@ -16,7 +16,6 @@
"ms-vscode.cmake-tools", "ms-vscode.cmake-tools",
"spmeesseman.vscode-taskexplorer", "spmeesseman.vscode-taskexplorer",
"yzhang.markdown-all-in-one", "yzhang.markdown-all-in-one",
"CoenraadS.bracket-pair-colorizer",
"vscode-icons-team.vscode-icons", "vscode-icons-team.vscode-icons",
"editorconfig.editorconfig" "editorconfig.editorconfig"
] ]

View File

@ -12,10 +12,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow to build a "light" version of Hyperion, i.e. no grabbers, or services like flat-/proto buffers, boblight, CEC - Allow to build a "light" version of Hyperion, i.e. no grabbers, or services like flat-/proto buffers, boblight, CEC
- Allow to restart Hyperion via Systray - Allow to restart Hyperion via Systray
- LED-Device: Support retry attempts enabling devices, e.g. to open devices after network or a device itself got available (#1302)
(Fixes that devices got "stuck", if initial open failed e.g. for WLED, Hue)
- LED-Devices: New UDP-DDP (Distributed Display Protocol) device to overcome the 490 LEDs limitation of UDP-RAW
- LED Matrix Layout - Support vertical cabling direction (#1420) - LED Matrix Layout - Support vertical cabling direction (#1420)
- Support additional Yeelight models - Support additional Yeelight models
- LED-Devices: Show warning, if get properties failed (Network devices: indication that network device is not reachable) - LED-Devices: Show warning, if get properties failed (Network devices: indication that network device is not reachable)
- hyperion-remote: Show image filename in UI for images sent - hyperion-remote: Show image filename in UI for images sent
- mDNS support for all platforms inkl. Windows (#740)
- LED-Devices mDNS discovery support and ease of configuration (Cololight, Nanoleaf, Philips-Hue, WLED, Yeelight); removes the need to configure IP-Address, as address is resolved automatically.
- Forwarder: mDNS discovery support and ease of configuration of other Hyperion instances
- Grabber: mDNS discovery for standalone grabbers
- New language: Japanese - New language: Japanese
### Changed ### Changed
@ -24,6 +31,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Boblight: Support multiple Boblight clients with different priorities - Boblight: Support multiple Boblight clients with different priorities
- UI: Allow configuration of a Boblight server per LED-instance - UI: Allow configuration of a Boblight server per LED-instance
- UI: LED Layout - Removed limitations on indention - UI: LED Layout - Removed limitations on indention
- mDNS Publisher :Aligned Hyperion mDNS names to general conventions and simplified namings
- Refactored Philips Hue wizard and LED-Device
- LED-Devices: WLED's default streaming protocol is now UDP-DDP. More than 490 LEDs are supported now (requires minimum WLED 0.11.0). UDP-RAW is still supported in parallel (via expert settings).
### Fixed ### Fixed
@ -31,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Effects: Fix that start effect is stuck on UI - Effects: Fix that start effect is stuck on UI
- Fixes that the Led-Device output flow was interrupted, by an enabling API request on an already enabled device (#967 - Fixes that the Led-Device output flow was interrupted, by an enabling API request on an already enabled device (#967
- Yeelight - Workaround: Ignore error when setting music mode = off, but the music-mode is already off (#1372) - Yeelight - Workaround: Ignore error when setting music mode = off, but the music-mode is already off (#1372)
- Fixed: Hue Entertainment mode does not resume after no signal (#930)
- Standalone grabbers: Improved fps help/error text, fixed default address and port, fixed auto discovery of Hyperion server in hyperion-remote - Standalone grabbers: Improved fps help/error text, fixed default address and port, fixed auto discovery of Hyperion server in hyperion-remote
- Fixed Qt version override, e.g. set via QTDIR - Fixed Qt version override, e.g. set via QTDIR
- Remote control UI: Treat duration=0 as endless - Remote control UI: Treat duration=0 as endless
@ -40,8 +51,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed: Signal detection does not switch off all instances (#1281) - Fixed: Signal detection does not switch off all instances (#1281)
- Reworked PriorityMuxer and Sub-scriptions - Reworked PriorityMuxer and Sub-scriptions
- Do not kill application on SIGILL-signal (#1435) - Do not kill application on SIGILL-signal (#1435)
- Start JSON and WebServer only, if Hyperion's instance 0 is available
## Removed ## Removed
- UI Removed sessions (of other Hyperions)
- Replaced existing AVAHI/Bonjour code by QMdnsEngine
## [2.0.12](https://github.com/hyperion-project/hyperion.ng/releases/tag/2.0.12) - 2021-11-20 ## [2.0.12](https://github.com/hyperion-project/hyperion.ng/releases/tag/2.0.12) - 2021-11-20
Hyperion's November release brings you some new features, removed IPv6 address related limitations, as well as fixing a couple of issues. Hyperion's November release brings you some new features, removed IPv6 address related limitations, as well as fixing a couple of issues.

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0.0) cmake_minimum_required(VERSION 3.1.0)
message( STATUS "CMake Version: ${CMAKE_VERSION}" ) message( STATUS "CMake Version: ${CMAKE_VERSION}" )
@ -38,6 +38,26 @@ if ( CCACHE_FOUND )
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND) 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()
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()
# Set build variables # Set build variables
# Grabber # Grabber
SET ( DEFAULT_AMLOGIC OFF ) SET ( DEFAULT_AMLOGIC OFF )
@ -69,18 +89,18 @@ SET ( DEFAULT_DEV_USB_HID OFF )
SET ( DEFAULT_DEV_WS281XPWM OFF ) SET ( DEFAULT_DEV_WS281XPWM OFF )
# Services # Services
SET ( DEFAULT_AVAHI ON )
SET ( DEFAULT_EFFECTENGINE ON ) SET ( DEFAULT_EFFECTENGINE ON )
SET ( DEFAULT_EXPERIMENTAL OFF ) SET ( DEFAULT_EXPERIMENTAL OFF )
SET ( DEFAULT_MDNS ON )
SET ( DEFAULT_REMOTE_CTL ON ) SET ( DEFAULT_REMOTE_CTL ON )
# Build # Build
SET ( DEFAULT_JSONCHECKS ON ) SET ( DEFAULT_JSONCHECKS ON )
SET ( DEFAULT_DEPLOY_DEPENDENCIES ON ) SET ( DEFAULT_DEPLOY_DEPENDENCIES ON )
SET ( DEFAULT_USE_SHARED_AVAHI_LIBS ON )
SET ( DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF ) SET ( DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF )
SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF ) SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF )
SET ( DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF ) SET ( DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF )
SET ( DEFAULT_USE_SYSTEM_QMDNS_LIBS OFF )
SET ( DEFAULT_TESTS OFF ) SET ( DEFAULT_TESTS OFF )
# Build Hyperion with a reduced set of functionality, overwrites other default values # Build Hyperion with a reduced set of functionality, overwrites other default values
@ -175,8 +195,6 @@ elseif ( "${PLATFORM}" MATCHES "x11" )
endif() endif()
elseif ( "${PLATFORM}" STREQUAL "imx6" ) elseif ( "${PLATFORM}" STREQUAL "imx6" )
SET ( DEFAULT_FB ON ) SET ( DEFAULT_FB ON )
elseif (WIN32)
SET ( DEFAULT_AVAHI OFF)
endif() endif()
# enable tests for -dev builds # enable tests for -dev builds
@ -319,15 +337,15 @@ removeIndent()
message(STATUS "Services options:") message(STATUS "Services options:")
addIndent(" - ") addIndent(" - ")
option(ENABLE_AVAHI "Enable Zeroconf" ${DEFAULT_AVAHI})
message(STATUS "ENABLE_AVAHI = " ${ENABLE_AVAHI})
option(ENABLE_EFFECTENGINE "Enable Effect-Engine" ${DEFAULT_EFFECTENGINE}) option(ENABLE_EFFECTENGINE "Enable Effect-Engine" ${DEFAULT_EFFECTENGINE})
message(STATUS "ENABLE_EFFECTENGINE = " ${ENABLE_EFFECTENGINE}) message(STATUS "ENABLE_EFFECTENGINE = " ${ENABLE_EFFECTENGINE})
option(ENABLE_EXPERIMENTAL "Compile experimental features" ${DEFAULT_EXPERIMENTAL}) option(ENABLE_EXPERIMENTAL "Compile experimental features" ${DEFAULT_EXPERIMENTAL})
message(STATUS "ENABLE_EXPERIMENTAL = ${ENABLE_EXPERIMENTAL}") message(STATUS "ENABLE_EXPERIMENTAL = ${ENABLE_EXPERIMENTAL}")
option(ENABLE_MDNS "Enable mDNS (aka Zeroconf)" ${DEFAULT_MDNS})
message(STATUS "ENABLE_MDNS = " ${ENABLE_MDNS})
option(ENABLE_REMOTE_CTL "Enable Hyperion remote control" ${DEFAULT_REMOTE_CTL}) option(ENABLE_REMOTE_CTL "Enable Hyperion remote control" ${DEFAULT_REMOTE_CTL})
message(STATUS "ENABLE_REMOTE_CTL = " ${ENABLE_REMOTE_CTL}) message(STATUS "ENABLE_REMOTE_CTL = " ${ENABLE_REMOTE_CTL})
@ -342,19 +360,27 @@ message(STATUS "ENABLE_JSONCHECKS = ${ENABLE_JSONCHECKS}")
option(ENABLE_DEPLOY_DEPENDENCIES "Deploy with dependencies" ${DEFAULT_DEPLOY_DEPENDENCIES}) option(ENABLE_DEPLOY_DEPENDENCIES "Deploy with dependencies" ${DEFAULT_DEPLOY_DEPENDENCIES})
message(STATUS "ENABLE_DEPLOY_DEPENDENCIES = ${ENABLE_DEPLOY_DEPENDENCIES}") message(STATUS "ENABLE_DEPLOY_DEPENDENCIES = ${ENABLE_DEPLOY_DEPENDENCIES}")
if(ENABLE_FLATBUF_SERVER OR ENABLE_FLATBUF_CONNECT)
message(STATUS "DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS = ${DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS}")
endif()
if(ENABLE_PROTOBUF_SERVER)
message(STATUS "DEFAULT_USE_SYSTEM_PROTO_LIBS = ${DEFAULT_USE_SYSTEM_PROTO_LIBS}")
endif()
message(STATUS "DEFAULT_USE_SYSTEM_MBEDTLS_LIBS = ${DEFAULT_USE_SYSTEM_MBEDTLS_LIBS}")
if (ENABLE_MDNS)
message(STATUS "DEFAULT_USE_SYSTEM_QMDNS_LIBS = ${DEFAULT_USE_SYSTEM_QMDNS_LIBS}")
endif()
option(ENABLE_PROFILER "enable profiler capabilities - not for release code" OFF) option(ENABLE_PROFILER "enable profiler capabilities - not for release code" OFF)
message(STATUS "ENABLE_PROFILER = ${ENABLE_PROFILER}") message(STATUS "ENABLE_PROFILER = ${ENABLE_PROFILER}")
option(ENABLE_TESTS "Compile additional test applications" ${DEFAULT_TESTS}) option(ENABLE_TESTS "Compile additional test applications" ${DEFAULT_TESTS})
message(STATUS "ENABLE_TESTS = ${ENABLE_TESTS}") message(STATUS "ENABLE_TESTS = ${ENABLE_TESTS}")
if (ENABLE_AVAHI)
message(STATUS "DEFAULT_USE_SHARED_AVAHI_LIBS = ${DEFAULT_USE_SHARED_AVAHI_LIBS}")
endif()
message(STATUS "DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS = ${DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS}")
message(STATUS "DEFAULT_USE_SYSTEM_MBEDTLS_LIBS = ${DEFAULT_USE_SYSTEM_MBEDTLS_LIBS}")
message(STATUS "DEFAULT_USE_SYSTEM_PROTO_LIBS = ${DEFAULT_USE_SYSTEM_PROTO_LIBS}")
removeIndent() removeIndent()
SET ( FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf ) SET ( FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf )
@ -504,21 +530,23 @@ if (DEFINED QTDIR)
list(PREPEND CMAKE_PREFIX_PATH ${QTDIR} "${QTDIR}/lib") list(PREPEND CMAKE_PREFIX_PATH ${QTDIR} "${QTDIR}/lib")
endif() endif()
message( STATUS "CMAKE_PREFIX_PATH used: ${CMAKE_PREFIX_PATH}" ) if (CMAKE_PREFIX_PATH)
message( STATUS "CMAKE_PREFIX_PATH used: ${CMAKE_PREFIX_PATH}" )
endif()
# find QT libs # find QT libs
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql Widgets REQUIRED)
message( STATUS "Found Qt Version: ${QT_VERSION}" ) message( STATUS "Found Qt Version: ${QT_VERSION}" )
if (${QT_VERSION_MAJOR} GREATER_EQUAL 6 ) if (${QT_VERSION_MAJOR} GREATER_EQUAL 6 )
SET(QT_MIN_VERSION "6.2.0") SET(QT_MIN_VERSION "6.2.2")
ELSE() else()
SET(QT_MIN_VERSION "5.5.0") SET(QT_MIN_VERSION "5.5.0")
ENDIF() endif()
IF ( "${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" ) if ( "${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" )
message( FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}" ) message( FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}" )
ENDIF() endif()
find_package(Qt${QT_VERSION_MAJOR} ${QT_VERSION} COMPONENTS Core Gui Network Sql Widgets REQUIRED) find_package(Qt${QT_VERSION_MAJOR} ${QT_VERSION} COMPONENTS Core Gui Network Sql Widgets REQUIRED)
@ -526,7 +554,7 @@ message( STATUS "Qt version used: ${QT_VERSION}" )
if (APPLE AND (${QT_VERSION_MAJOR} GREATER_EQUAL 6) ) if (APPLE AND (${QT_VERSION_MAJOR} GREATER_EQUAL 6) )
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) set(OPENSSL_ROOT_DIR /usr/local/opt/openssl)
ENDIF() endif()
# Add libusb and pthreads # Add libusb and pthreads
find_package(libusb-1.0 REQUIRED) find_package(libusb-1.0 REQUIRED)
@ -557,33 +585,6 @@ endif ()
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${GENERATED_QRC}" ) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${GENERATED_QRC}" )
# enable C++11; MSVC doesn't have c++11 feature switch
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(APPLE)
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()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
if (CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi")
endif()
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(STATUS "No support for C++11 detected. Compilation will most likely fail on your compiler")
endif()
endif()
# uninstall target # uninstall target
configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)

View File

@ -17,7 +17,7 @@
], ],
"environments": [ "environments": [
// Replace path with your installation path // Replace path with your installation path
//{ "QTDIR": "C:/Qt/6.2.0/msvc2019_64/" }, //{ "QTDIR": "C:/Qt/6.2.2/msvc2019_64/" },
//{ "VULKAN_SDK": "C:/VulkanSDK/1.2.182.0" } //{ "VULKAN_SDK": "C:/VulkanSDK/1.2.182.0" }
] ]
}, },
@ -38,7 +38,7 @@
], ],
"environments": [ "environments": [
// Replace path with your installation path // Replace path with your installation path
//{ "QTDIR": "C:/Qt/6.2.0/msvc2019_64/" }, //{ "QTDIR": "C:/Qt/6.2.2/msvc2019_64/" },
//{ "VULKAN_SDK": "C:/VulkanSDK/1.2.182.0" } //{ "VULKAN_SDK": "C:/VulkanSDK/1.2.182.0" }
] ]
} }

View File

@ -69,8 +69,8 @@
// Define to enable the WS281x-PWM-via-DMA-device using jgarff's library // Define to enable the WS281x-PWM-via-DMA-device using jgarff's library
#cmakedefine ENABLE_DEV_WS281XPWM #cmakedefine ENABLE_DEV_WS281XPWM
// Define to enable AVAHI // Define to enable MDNS
#cmakedefine ENABLE_AVAHI #cmakedefine ENABLE_MDNS
// Define to enable EFFECTENGINE // Define to enable EFFECTENGINE
#cmakedefine ENABLE_EFFECTENGINE #cmakedefine ENABLE_EFFECTENGINE

View File

@ -88,6 +88,16 @@
"conf_leds_layout_cl_leftbottom": "Left 50% - 100% Bottom", "conf_leds_layout_cl_leftbottom": "Left 50% - 100% Bottom",
"conf_leds_layout_cl_leftmiddle": "Left 25% - 75% Middle", "conf_leds_layout_cl_leftmiddle": "Left 25% - 75% Middle",
"conf_leds_layout_cl_lefttop": "Left 0% - 50% Top", "conf_leds_layout_cl_lefttop": "Left 0% - 50% Top",
"conf_leds_layout_cl_lightPosBottomLeft14": "Bottom: 0 - 25% from Left",
"conf_leds_layout_cl_lightPosBottomLeft12": "Bottom: 25 - 50% from Left",
"conf_leds_layout_cl_lightPosBottomLeft34": "Bottom: 50 - 75% from Left",
"conf_leds_layout_cl_lightPosBottomLeft11": "Bottom: 75 - 100% from Left",
"conf_leds_layout_cl_lightPosBottomLeft112": "Bottom: 0 - 50% from Left",
"conf_leds_layout_cl_lightPosBottomLeft121": "Bottom: 50 - 100% from Left",
"conf_leds_layout_cl_lightPosBottomLeftNewMid": "Bottom: 25 - 75% from Left",
"conf_leds_layout_cl_lightPosTopLeft112": "Top: 0 - 50% from Left",
"conf_leds_layout_cl_lightPosTopLeft121": "Top: 50 - 100% from Left",
"conf_leds_layout_cl_lightPosTopLeftNewMid": "Top: 25 - 75% from Left",
"conf_leds_layout_cl_overlap": "Overlap", "conf_leds_layout_cl_overlap": "Overlap",
"conf_leds_layout_cl_reversdir": "Reverse direction", "conf_leds_layout_cl_reversdir": "Reverse direction",
"conf_leds_layout_cl_right": "Right", "conf_leds_layout_cl_right": "Right",
@ -329,6 +339,8 @@
"edt_conf_enum_top_down": "Top down", "edt_conf_enum_top_down": "Top down",
"edt_conf_enum_transeffect_smooth": "Smooth", "edt_conf_enum_transeffect_smooth": "Smooth",
"edt_conf_enum_transeffect_sudden": "Sudden", "edt_conf_enum_transeffect_sudden": "Sudden",
"edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW",
"edt_conf_enum_unicolor_mean": "Unicolor", "edt_conf_enum_unicolor_mean": "Unicolor",
"edt_conf_fbs_heading_title": "Flatbuffers Server", "edt_conf_fbs_heading_title": "Flatbuffers Server",
"edt_conf_fbs_timeout_expl": "If no data is received for the given period, the component will be (soft) disabled.", "edt_conf_fbs_timeout_expl": "If no data is received for the given period, the component will be (soft) disabled.",
@ -355,13 +367,20 @@
"edt_conf_fge_heading_title": "Boot Effect/Color", "edt_conf_fge_heading_title": "Boot Effect/Color",
"edt_conf_fge_type_expl": "Choose between a color or effect.", "edt_conf_fge_type_expl": "Choose between a color or effect.",
"edt_conf_fge_type_title": "Type", "edt_conf_fge_type_title": "Type",
"edt_conf_fw_flat_expl": "One flatbuffer target per line. Contains IP:PORT (Example: 127.0.0.1:19401)", "edt_conf_fw_flat_expl": "One flatbuffer target per configuration item",
"edt_conf_fw_flat_itemtitle": "flatbuffer target", "edt_conf_fw_flat_itemtitle": "flatbuffer target",
"edt_conf_fw_flat_services_discovered_expl": "Hyperion servers discovered providing flatbuffer services",
"edt_conf_fw_flat_services_discovered_title": "Flatbuffer targets discoverded",
"edt_conf_fw_flat_title": "List of flatbuffer targets", "edt_conf_fw_flat_title": "List of flatbuffer targets",
"edt_conf_fw_heading_title": "Forwarder", "edt_conf_fw_heading_title": "Forwarder",
"edt_conf_fw_json_expl": "One json target per line. Contains IP:PORT (Example: 127.0.0.1:19446)", "edt_conf_fw_json_expl": "One JSON target per configuration item",
"edt_conf_fw_json_itemtitle": "Json target", "edt_conf_fw_json_itemtitle": "JSON target",
"edt_conf_fw_json_title": "List of json targets", "edt_conf_fw_json_services_discovered_expl": "Hyperion servers discovered providing JSON-API services",
"edt_conf_fw_json_services_discovered_title": "JSON targets discoverded",
"edt_conf_fw_json_title": "List of JSON targets",
"edt_conf_fw_remote_service_discovered_none": "No remote services discovered",
"edt_conf_fw_service_name_expl": "Name of the service provider",
"edt_conf_fw_service_name_title": "Service name",
"edt_conf_gen_configVersion_title": "Configuration version", "edt_conf_gen_configVersion_title": "Configuration version",
"edt_conf_gen_heading_title": "General Settings", "edt_conf_gen_heading_title": "General Settings",
"edt_conf_gen_name_expl": "A user defined name which is used to detect Hyperion. (Helpful with more than one Hyperion instance)", "edt_conf_gen_name_expl": "A user defined name which is used to detect Hyperion. (Helpful with more than one Hyperion instance)",
@ -510,6 +529,10 @@
"edt_dev_general_autostart_title_info": "The LED device is switched-on during startup or not", "edt_dev_general_autostart_title_info": "The LED device is switched-on during startup or not",
"edt_dev_general_colorOrder_title": "RGB byte order", "edt_dev_general_colorOrder_title": "RGB byte order",
"edt_dev_general_colorOrder_title_info": "The device's color order", "edt_dev_general_colorOrder_title_info": "The device's color order",
"edt_dev_general_enableAttempts_title": "Connection attempts",
"edt_dev_general_enableAttempts_title_info": "Number of attempts connecting a device before it goes into an error state.",
"edt_dev_general_enableAttemptsInterval_title": "Retry interval",
"edt_dev_general_enableAttemptsInterval_title_info": "Intervall between two connection attempts.",
"edt_dev_general_hardwareLedCount_title": "Hardware LED count", "edt_dev_general_hardwareLedCount_title": "Hardware LED count",
"edt_dev_general_hardwareLedCount_title_info": "The number of physical LEDs availabe for the given device", "edt_dev_general_hardwareLedCount_title_info": "The number of physical LEDs availabe for the given device",
"edt_dev_general_heading_title": "General Settings", "edt_dev_general_heading_title": "General Settings",
@ -523,17 +546,15 @@
"edt_dev_spec_baudrate_title": "Baudrate", "edt_dev_spec_baudrate_title": "Baudrate",
"edt_dev_spec_blackLightsTimeout_title": "Signal detection timeout on black", "edt_dev_spec_blackLightsTimeout_title": "Signal detection timeout on black",
"edt_dev_spec_brightnessFactor_title": "Brightness factor", "edt_dev_spec_brightnessFactor_title": "Brightness factor",
"edt_dev_spec_brightnessMax_title": "Brightness maximum",
"edt_dev_spec_brightnessMin_title": "Brightness minimum",
"edt_dev_spec_brightnessOverwrite_title": "Overwrite brightness", "edt_dev_spec_brightnessOverwrite_title": "Overwrite brightness",
"edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum", "edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum",
"edt_dev_spec_brightness_title": "Brightness", "edt_dev_spec_brightness_title": "Brightness",
"edt_dev_spec_candyGamma_title" : "'Candy' mode (double gamma correction)",
"edt_dev_spec_chanperfixture_title": "Channels per Fixture", "edt_dev_spec_chanperfixture_title": "Channels per Fixture",
"edt_dev_spec_cid_title": "CID", "edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Clientkey", "edt_dev_spec_clientKey_title": "Clientkey",
"edt_dev_spec_colorComponent_title": "Colour component", "edt_dev_spec_colorComponent_title": "Colour component",
"edt_dev_spec_debugLevel_title": "Debug Level", "edt_dev_spec_debugLevel_title": "Debug Level",
"edt_dev_spec_debugStreamer_title": "Streamer Debug",
"edt_dev_spec_delayAfterConnect_title": "Delay after connect", "edt_dev_spec_delayAfterConnect_title": "Delay after connect",
"edt_dev_spec_devices_discovered_none": "No Devices Discovered", "edt_dev_spec_devices_discovered_none": "No Devices Discovered",
"edt_dev_spec_devices_discovered_title": "Devices Discovered", "edt_dev_spec_devices_discovered_title": "Devices Discovered",
@ -568,6 +589,8 @@
"edt_dev_spec_networkDeviceName_title": "Network devicename", "edt_dev_spec_networkDeviceName_title": "Network devicename",
"edt_dev_spec_networkDevicePort_title": "Port", "edt_dev_spec_networkDevicePort_title": "Port",
"edt_dev_spec_numberOfLeds_title": "Number of LEDs", "edt_dev_spec_numberOfLeds_title": "Number of LEDs",
"edt_dev_spec_onBlackTimeToPowerOff": "Time to power off the lamp if the black level is triggered",
"edt_dev_spec_onBlackTimeToPowerOn": "Time to power on the lamp if the signal is restored",
"edt_dev_spec_orbIds_title": "Orb ID(s)", "edt_dev_spec_orbIds_title": "Orb ID(s)",
"edt_dev_spec_order_left_right_title": "2.", "edt_dev_spec_order_left_right_title": "2.",
"edt_dev_spec_order_top_down_title": "1.", "edt_dev_spec_order_top_down_title": "1.",
@ -575,8 +598,10 @@
"edt_dev_spec_panel_start_position": "Start panel [0-max panels]", "edt_dev_spec_panel_start_position": "Start panel [0-max panels]",
"edt_dev_spec_panelorganisation_title": "Panel numbering sequence", "edt_dev_spec_panelorganisation_title": "Panel numbering sequence",
"edt_dev_spec_pid_title": "PID", "edt_dev_spec_pid_title": "PID",
"edt_dev_spec_port_expl": "Service Port [1-65535]",
"edt_dev_spec_port_title": "Port", "edt_dev_spec_port_title": "Port",
"edt_dev_spec_printTimeStamp_title": "Add timestamp", "edt_dev_spec_printTimeStamp_title": "Add timestamp",
"edt_dev_spec_stream_protocol_title": "Streaming protocol",
"edt_dev_spec_pwmChannel_title": "PWM channel", "edt_dev_spec_pwmChannel_title": "PWM channel",
"edt_dev_spec_razer_device_title": "Razer Chroma Device", "edt_dev_spec_razer_device_title": "Razer Chroma Device",
"edt_dev_spec_restoreOriginalState_title": "Restore lights' state", "edt_dev_spec_restoreOriginalState_title": "Restore lights' state",
@ -585,10 +610,10 @@
"edt_dev_spec_spipath_title": "SPI Device", "edt_dev_spec_spipath_title": "SPI Device",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum", "edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum",
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer handshake timeout minimum", "edt_dev_spec_sslHSTimeoutMin_title": "Streamer handshake timeout minimum",
"edt_dev_spec_sslReadTimeout_title": "Streamer read timeout",
"edt_dev_spec_switchOffOnBlack_title": "Switch off on black", "edt_dev_spec_switchOffOnBlack_title": "Switch off on black",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Switch-off, below minimum", "edt_dev_spec_switchOffOnbelowMinBrightness_title": "Switch-off, below minimum",
"edt_dev_spec_syncOverwrite_title": "Disable synchronisation", "edt_dev_spec_syncOverwrite_title": "Disable synchronisation",
"edt_dev_spec_targetIpHost_expl": "Hostname (DNS/mDNS) or IP-address (IPv4 orIPv6)",
"edt_dev_spec_targetIpHost_title": "Hostname/IP-address", "edt_dev_spec_targetIpHost_title": "Hostname/IP-address",
"edt_dev_spec_targetIpHost_title_info": "The device's hostname or IP-address", "edt_dev_spec_targetIpHost_title_info": "The device's hostname or IP-address",
"edt_dev_spec_targetIp_title": "IP-address", "edt_dev_spec_targetIp_title": "IP-address",

View File

@ -1,5 +1,5 @@
$(document).ready(function () { $(document).ready(function () {
var darkModeOverwrite = getStorage("darkModeOverwrite", true); var darkModeOverwrite = getStorage("darkModeOverwrite");
if (darkModeOverwrite == "false" || darkModeOverwrite == null) { if (darkModeOverwrite == "false" || darkModeOverwrite == null) {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
@ -7,11 +7,11 @@ $(document).ready(function () {
} }
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
setStorage("darkMode", "off", false); setStorage("darkMode", "off");
} }
} }
if (getStorage("darkMode", false) == "on") { if (getStorage("darkMode") == "on") {
handleDarkMode(); handleDarkMode();
} }
@ -42,7 +42,6 @@ $(document).ready(function () {
$('#btn_hypinstanceswitch').toggle(true) $('#btn_hypinstanceswitch').toggle(true)
else else
$('#btn_hypinstanceswitch').toggle(false) $('#btn_hypinstanceswitch').toggle(false)
updateSessions();
}); // end cmd-serverinfo }); // end cmd-serverinfo
// Update language selection // Update language selection
@ -73,11 +72,6 @@ $(document).ready(function () {
//End language selection //End language selection
$(window.hyperion).on("cmd-sessions-update", function (event) {
window.serverInfo.sessions = event.response.data;
updateSessions();
});
$(window.hyperion).on("cmd-authorize-tokenRequest cmd-authorize-getPendingTokenRequests", function (event) { $(window.hyperion).on("cmd-authorize-tokenRequest cmd-authorize-getPendingTokenRequests", function (event) {
var val = event.response.info; var val = event.response.info;
if (Array.isArray(event.response.info)) { if (Array.isArray(event.response.info)) {
@ -121,7 +115,7 @@ $(document).ready(function () {
requestGetPendingTokenRequests(); requestGetPendingTokenRequests();
//Switch to last selected instance and load related config //Switch to last selected instance and load related config
var lastSelectedInstance = getStorage('lastSelectedInstance', false); var lastSelectedInstance = getStorage('lastSelectedInstance');
if (lastSelectedInstance == null || window.serverInfo.instance && !window.serverInfo.instance[lastSelectedInstance]) { if (lastSelectedInstance == null || window.serverInfo.instance && !window.serverInfo.instance[lastSelectedInstance]) {
lastSelectedInstance = 0; lastSelectedInstance = 0;
} }
@ -157,7 +151,7 @@ $(document).ready(function () {
$("#btn_lock_ui").removeAttr('style') $("#btn_lock_ui").removeAttr('style')
if (event.response.hasOwnProperty('info')) if (event.response.hasOwnProperty('info'))
setStorage("loginToken", event.response.info.token, true); setStorage("loginToken", event.response.info.token);
requestServerConfigSchema(); requestServerConfigSchema();
}); });
@ -171,7 +165,7 @@ $(document).ready(function () {
}); });
$(window.hyperion).on("cmd-authorize-newPasswordRequired", function (event) { $(window.hyperion).on("cmd-authorize-newPasswordRequired", function (event) {
var loginToken = getStorage("loginToken", true) var loginToken = getStorage("loginToken")
if (event.response.info.newPasswordRequired == true) { if (event.response.info.newPasswordRequired == true) {
window.defaultPasswordIsSet = true; window.defaultPasswordIsSet = true;
@ -204,8 +198,8 @@ $(document).ready(function () {
$(window.hyperion).on("error", function (event) { $(window.hyperion).on("error", function (event) {
//If we are getting an error "No Authorization" back with a set loginToken we will forward to new Login (Token is expired. //If we are getting an error "No Authorization" back with a set loginToken we will forward to new Login (Token is expired.
//e.g.: hyperiond was started new in the meantime) //e.g.: hyperiond was started new in the meantime)
if (event.reason == "No Authorization" && getStorage("loginToken", true)) { if (event.reason == "No Authorization" && getStorage("loginToken")) {
removeStorage("loginToken", true); removeStorage("loginToken");
requestRequiresAdminAuth(); requestRequiresAdminAuth();
} }
else if (event.reason == "Selected Hyperion instance isn't running") { else if (event.reason == "Selected Hyperion instance isn't running") {
@ -281,8 +275,8 @@ $(document).ready(function () {
if (!isInData) { if (!isInData) {
//Delete Storage information about the last used but now stopped instance //Delete Storage information about the last used but now stopped instance
if (getStorage('lastSelectedInstance', false)) if (getStorage('lastSelectedInstance'))
removeStorage('lastSelectedInstance', false) removeStorage('lastSelectedInstance')
currentHyperionInstance = 0; currentHyperionInstance = 0;
currentHyperionInstanceName = getInstanceNameByIndex(0); currentHyperionInstanceName = getInstanceNameByIndex(0);
@ -341,7 +335,7 @@ function suppressDefaultPwWarning() {
$(function () { $(function () {
var sidebar = $('#side-menu'); // cache sidebar to a variable for performance var sidebar = $('#side-menu'); // cache sidebar to a variable for performance
sidebar.on("click", 'a.inactive' , function () { sidebar.on("click", 'a.inactive', function () {
sidebar.find('.active').toggleClass('active inactive'); sidebar.find('.active').toggleClass('active inactive');
$(this).toggleClass('active inactive'); $(this).toggleClass('active inactive');
}); });
@ -354,13 +348,13 @@ $(document.body).on('hide.bs.modal,hidden.bs.modal', function () {
//Dark Mode //Dark Mode
$("#btn_darkmode").off().on("click", function (e) { $("#btn_darkmode").off().on("click", function (e) {
if (getStorage("darkMode", false) != "on") { if (getStorage("darkMode") != "on") {
handleDarkMode(); handleDarkMode();
setStorage("darkModeOverwrite", true, true); setStorage("darkModeOverwrite", true);
} }
else { else {
setStorage("darkMode", "off", false); setStorage("darkMode", "off",);
setStorage("darkModeOverwrite", true, true); setStorage("darkModeOverwrite", true);
location.reload(); location.reload();
} }
}); });
@ -392,3 +386,4 @@ function SwitchToMenuItem(target, item) {
} }
}; };

View File

@ -21,7 +21,7 @@ var toggleKeystoneCorrectionArea = false;
var devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi']; var devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi'];
var devRPiPWM = ['ws281x']; var devRPiPWM = ['ws281x'];
var devRPiGPIO = ['piblaster']; var devRPiGPIO = ['piblaster'];
var devNET = ['atmoorb', 'cololight', 'fadecandy', 'philipshue', 'nanoleaf', 'razer', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw', 'wled', 'yeelight']; var devNET = ['atmoorb', 'cololight', 'fadecandy', 'philipshue', 'nanoleaf', 'razer', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udpddp', 'udph801', 'udpraw', 'wled', 'yeelight'];
var devSerial = ['adalight', 'dmx', 'atmo', 'sedu', 'tpm2', 'karate']; var devSerial = ['adalight', 'dmx', 'atmo', 'sedu', 'tpm2', 'karate'];
var devHID = ['hyperionusbasp', 'lightpack', 'paintpack', 'rawhid']; var devHID = ['hyperionusbasp', 'lightpack', 'paintpack', 'rawhid'];
@ -1075,6 +1075,7 @@ $(document).ready(function () {
conf_editor.on('ready', function () { conf_editor.on('ready', function () {
var hwLedCountDefault = 1; var hwLedCountDefault = 1;
var colorOrderDefault = "rgb"; var colorOrderDefault = "rgb";
var filter = {};
$('#btn_test_controller').hide(); $('#btn_test_controller').hide();
@ -1083,12 +1084,6 @@ $(document).ready(function () {
case "wled": case "wled":
case "nanoleaf": case "nanoleaf":
showAllDeviceInputOptions("hostList", false); showAllDeviceInputOptions("hostList", false);
case "adalight":
case "atmo":
case "dmx":
case "karate":
case "sedu":
case "tpm2":
case "apa102": case "apa102":
case "apa104": case "apa104":
case "ws2801": case "ws2801":
@ -1101,7 +1096,18 @@ $(document).ready(function () {
case "ws2812spi": case "ws2812spi":
case "piblaster": case "piblaster":
case "ws281x": case "ws281x":
discover_device(ledType);
//Serial devices
case "adalight":
case "atmo":
case "dmx":
case "karate":
case "sedu":
case "tpm2":
if (storedAccess === 'expert') {
filter.discoverAll = true;
}
discover_device(ledType, filter);
hwLedCountDefault = 1; hwLedCountDefault = 1;
colorOrderDefault = "rgb"; colorOrderDefault = "rgb";
break; break;
@ -1172,6 +1178,7 @@ $(document).ready(function () {
case "tpm2net": case "tpm2net":
case "udpe131": case "udpe131":
case "udpartnet": case "udpartnet":
case "udpddp":
case "udph801": case "udph801":
case "udpraw": case "udpraw":
var host = conf_editor.getEditor("root.specificOptions.host").getValue(); var host = conf_editor.getEditor("root.specificOptions.host").getValue();
@ -1255,33 +1262,43 @@ $(document).ready(function () {
var hostList = conf_editor.getEditor("root.specificOptions.hostList"); var hostList = conf_editor.getEditor("root.specificOptions.hostList");
if (hostList) { if (hostList) {
var val = hostList.getValue(); var val = hostList.getValue();
var host = conf_editor.getEditor("root.specificOptions.host");
var showOptions = true; var showOptions = true;
switch (val) { switch (val) {
case 'CUSTOM': case 'CUSTOM':
case '': case '':
conf_editor.getEditor(specOptPath + "host").enable(); host.enable();
conf_editor.getEditor(specOptPath + "host").setValue(""); //Populate existing host for current custom config
if (ledType === window.serverConfig.device.type) {
host.setValue(window.serverConfig.device.host);
} else {
host.setValue("");
}
break; break;
case 'NONE': case 'NONE':
conf_editor.getEditor(specOptPath + "host").enable(); host.enable();
//Trigger getProperties via host value //Trigger getProperties via host value
conf_editor.notifyWatchers(specOptPath + "host"); conf_editor.notifyWatchers(specOptPath + "host");
break; break;
case 'SELECT': case 'SELECT':
conf_editor.getEditor(specOptPath + "host").setValue(""); host.setValue("");
conf_editor.getEditor(specOptPath + "host").disable(); host.disable();
showOptions = false; showOptions = false;
break; break;
default: default:
conf_editor.getEditor(specOptPath + "host").disable(); host.disable();
conf_editor.getEditor(specOptPath + "host").setValue(val); host.setValue(val);
//Trigger getProperties via host value //Trigger getProperties via host value
conf_editor.notifyWatchers(specOptPath + "host"); conf_editor.notifyWatchers(specOptPath + "host");
break; break;
} }
showAllDeviceInputOptions("hostList", showOptions); showAllDeviceInputOptions("hostList", showOptions);
if (!host.isEnabled() && host.getValue().endsWith("._tcp.local")) {
showInputOptionForItem(conf_editor, 'specificOptions', 'host', false);
}
} }
}); });
@ -1749,44 +1766,34 @@ var updateSelectList = function (ledType, discoveryInfo) {
var name; var name;
var host; var host;
switch (ledType) {
case "nanoleaf":
if (discoveryMethod === "ssdp") {
name = device.other["nl-devicename"];
}
else {
name = device.name;
}
break;
case "cololight":
if (discoveryMethod === "ssdp") {
name = device.hostname;
}
else {
name = device.name;
}
break;
case "wled":
name = device.name;
break;
default:
name = device.name;
}
if (discoveryMethod === "ssdp") { if (discoveryMethod === "ssdp") {
host = device.ip; host = device.ip;
} }
else { else {
host = device.name; host = device.service;
}
switch (ledType) {
case "nanoleaf":
if (discoveryMethod === "ssdp") {
name = device.other["nl-devicename"] + " (" + host + ")";
}
else {
name = device.name;
}
break;
default:
if (discoveryMethod === "ssdp") {
name = device.hostname + " (" + host + ")";
}
else {
name = device.name;
}
break;
} }
enumVals.push(host); enumVals.push(host);
if (host !== name) { enumTitelVals.push(name);
enumTitelVals.push(name + " (" + host + ")");
}
else {
enumTitelVals.push(host);
}
} }
//Always allow to add custom configuration //Always allow to add custom configuration
@ -1794,8 +1801,14 @@ var updateSelectList = function (ledType, discoveryInfo) {
// Select configured device // Select configured device
var configuredDeviceType = window.serverConfig.device.type; var configuredDeviceType = window.serverConfig.device.type;
var configuredHost = window.serverConfig.device.hostList; var configuredHost = window.serverConfig.device.hostList;
if (ledType === configuredDeviceType && $.inArray(configuredHost, enumVals) != -1) { if (ledType === configuredDeviceType) {
if ($.inArray(configuredHost, enumVals) != -1) {
enumDefaultVal = configuredHost; enumDefaultVal = configuredHost;
} else if (configuredHost === "CUSTOM") {
enumDefaultVal = "CUSTOM";
} else {
addSelect = true;
}
} }
else { else {
addSelect = true; addSelect = true;
@ -1928,6 +1941,7 @@ async function discover_device(ledType, params) {
} }
async function getProperties_device(ledType, key, params) { async function getProperties_device(ledType, key, params) {
var disabled = $('#btn_submit_controller').is(':disabled');
// Take care that connfig cannot be saved during background processing // Take care that connfig cannot be saved during background processing
$('#btn_submit_controller').prop('disabled', true); $('#btn_submit_controller').prop('disabled', true);
@ -1946,7 +1960,7 @@ async function getProperties_device(ledType, key, params) {
devicesProperties[ledType][key] = ledDeviceProperties; devicesProperties[ledType][key] = ledDeviceProperties;
if (!window.readOnlyMode) { if (!window.readOnlyMode) {
$('#btn_submit_controller').prop('disabled', false); $('#btn_submit_controller').prop('disabled', disabled);
} }
} }
else { else {
@ -1961,6 +1975,7 @@ async function getProperties_device(ledType, key, params) {
} }
async function identify_device(type, params) { async function identify_device(type, params) {
var disabled = $('#btn_submit_controller').is(':disabled');
// Take care that connfig cannot be saved and identification cannot be retriggerred during background processing // Take care that connfig cannot be saved and identification cannot be retriggerred during background processing
$('#btn_submit_controller').prop('disabled', true); $('#btn_submit_controller').prop('disabled', true);
$('#btn_test_controller').prop('disabled', true); $('#btn_test_controller').prop('disabled', true);
@ -1969,7 +1984,7 @@ async function identify_device(type, params) {
$('#btn_test_controller').prop('disabled', false); $('#btn_test_controller').prop('disabled', false);
if (!window.readOnlyMode) { if (!window.readOnlyMode) {
$('#btn_submit_controller').prop('disabled', false); $('#btn_submit_controller').prop('disabled', disabled);
} }
} }
@ -1988,12 +2003,18 @@ function updateElements(ledType, key) {
case "wled": case "wled":
var ledProperties = devicesProperties[ledType][key]; var ledProperties = devicesProperties[ledType][key];
if (ledProperties && ledProperties.leds && ledProperties.maxLedCount) { if (ledProperties && ledProperties.leds) {
hardwareLedCount = ledProperties.leds.count; hardwareLedCount = ledProperties.leds.count;
if (ledProperties.maxLedCount) {
var maxLedCount = ledProperties.maxLedCount; var maxLedCount = ledProperties.maxLedCount;
if (hardwareLedCount > maxLedCount) { if (hardwareLedCount > maxLedCount) {
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount)); showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
hardwareLedCount = maxLedCount; hardwareLedCount = maxLedCount;
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
//Workaround, as value seems to getting updated property when a 'getEditor("root.specificOptions").getValue()' is done during save
var editor = conf_editor.getEditor("root.specificOptions");
editor.value["streamProtocol"] = "RAW";
}
} }
} }
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount); conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
@ -2060,6 +2081,15 @@ function updateElements(ledType, key) {
default: default:
} }
} }
if (!conf_editor.validate().length) {
if (!window.readOnlyMode) {
$('#btn_submit_controller').attr('disabled', false);
}
}
else {
$('#btn_submit_controller').attr('disabled', true);
}
} }
function showAllDeviceInputOptions(showForKey, state) { function showAllDeviceInputOptions(showForKey, state) {

View File

@ -137,7 +137,7 @@ $(document).ready(function () {
var date = new Date(parseInt(utime)); var date = new Date(parseInt(utime));
var subComponent = ""; var subComponent = "";
if (window.serverInfo.instance.length > 1) { if (window.serverInfo.instance.length >= 1) {
if (logger_subname.startsWith("I")) { if (logger_subname.startsWith("I")) {
var instanceNum = logger_subname.substring(1); var instanceNum = logger_subname.substring(1);
if (window.serverInfo.instance[instanceNum]) { if (window.serverInfo.instance[instanceNum]) {

View File

@ -11,6 +11,9 @@ $(document).ready(function () {
var conf_editor_fbs = null; var conf_editor_fbs = null;
var conf_editor_forw = null; var conf_editor_forw = null;
// Service properties , 2-dimensional array of [servicetype][id]
discoveredRemoteServices = {};
addJsonEditorHostValidation(); addJsonEditorHostValidation();
if (window.showOptHelp) { if (window.showOptHelp) {
@ -142,10 +145,21 @@ $(document).ready(function () {
forwarder: window.schema.forwarder forwarder: window.schema.forwarder
}, true, true); }, true, true);
conf_editor_forw.on('ready', function () {
updateServiceCacheForwarderConfiguredItems("jsonapi");
updateServiceCacheForwarderConfiguredItems("flatbuffer");
var forwarderEnable = conf_editor_forw.getEditor("root.forwarder.enable").getValue();
if (forwarderEnable) {
discoverRemoteHyperionServices("jsonapi");
discoverRemoteHyperionServices("flatbuffer");
}
});
conf_editor_forw.on('change', function () { conf_editor_forw.on('change', function () {
var forwarderEnable = conf_editor_forw.getEditor("root.forwarder.enable").getValue(); var forwarderEnable = conf_editor_forw.getEditor("root.forwarder.enable").getValue();
if (forwarderEnable) { if (forwarderEnable) {
showInputOptionsForKey(conf_editor_forw, "forwarder", "enable", true);
$('#forwarderHelpPanelId').show(); $('#forwarderHelpPanelId').show();
} else { } else {
showInputOptionsForKey(conf_editor_forw, "forwarder", "enable", false); showInputOptionsForKey(conf_editor_forw, "forwarder", "enable", false);
@ -154,6 +168,23 @@ $(document).ready(function () {
conf_editor_forw.validate().length || window.readOnlyMode ? $('#btn_submit_forwarder').prop('disabled', true) : $('#btn_submit_forwarder').prop('disabled', false); conf_editor_forw.validate().length || window.readOnlyMode ? $('#btn_submit_forwarder').prop('disabled', true) : $('#btn_submit_forwarder').prop('disabled', false);
}); });
conf_editor_forw.watch('root.forwarder.jsonapiselect', () => {
updateForwarderServiceSections("jsonapi");
});
conf_editor_forw.watch('root.forwarder.flatbufferselect', () => {
updateForwarderServiceSections("flatbuffer");
});
conf_editor_forw.watch('root.forwarder.enable', () => {
var forwarderEnable = conf_editor_forw.getEditor("root.forwarder.enable").getValue();
if (forwarderEnable) {
discoverRemoteHyperionServices("jsonapi");
discoverRemoteHyperionServices("flatbuffer");
}
});
$('#btn_submit_forwarder').off().on('click', function () { $('#btn_submit_forwarder').off().on('click', function () {
requestWriteConfig(conf_editor_forw.getValue()); requestWriteConfig(conf_editor_forw.getValue());
}); });
@ -238,5 +269,143 @@ $(document).ready(function () {
checkApiTokenState(window.serverConfig.network.apiAuth); checkApiTokenState(window.serverConfig.network.apiAuth);
removeOverlay(); removeOverlay();
function updateForwarderServiceSections(type) {
var editorPath = "root.forwarder." + type
var selectedServices = conf_editor_forw.getEditor(editorPath + "select").getValue();
if (jQuery.isEmptyObject(selectedServices) || selectedServices[0] === "NONE") {
conf_editor_forw.getEditor(editorPath).setValue([]);
showInputOptionForItem(conf_editor_forw, "forwarder", type, false);
} else {
var newServices = [];
for (var i = 0; i < selectedServices.length; ++i) {
var service = discoveredRemoteServices[type][selectedServices[i]];
var newrecord = {};
newrecord.name = service.name;
newrecord.host = service.host;
newrecord.port = service.port;
newServices.push(newrecord);
}
conf_editor_forw.getEditor(editorPath).setValue(newServices);
showInputOptionForItem(conf_editor_forw, "forwarder", type, true);
conf_editor_forw.getEditor(editorPath).disable();
}
}
function updateForwarderSelectList(type) {
var selectionElement = type + "select"
var enumVals = [];
var enumTitelVals = [];
var enumDefaultVals = [];
for (var key in discoveredRemoteServices[type]) {
var service = discoveredRemoteServices[type][key];
enumVals.push(service.host);
enumTitelVals.push(service.name);
if (service.inConfig == true) {
enumDefaultVals.push(service.host);
}
}
let addSchemaElements = {
"uniqueItems": true
};
if (jQuery.isEmptyObject(enumVals)) {
enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_conf_fw_remote_service_discovered_none'));
}
updateJsonEditorMultiSelection(conf_editor_forw, 'root.forwarder', selectionElement, addSchemaElements, enumVals, enumTitelVals, enumDefaultVals);
};
function updateServiceCacheForwarderConfiguredItems(serviceType) {
var editor = conf_editor_forw.getEditor("root.forwarder." + serviceType);
if (editor) {
if (!discoveredRemoteServices[serviceType]) {
discoveredRemoteServices[serviceType] = {};
}
var configuredServices = JSON.parse(JSON.stringify(editor.getValue('items')));
for (const service of configuredServices) {
//Handle not named sceanrios
if (!service.name) {
service.name = service.host;
}
service.inConfig = true;
discoveredRemoteServices[serviceType][service.host] = service;
}
}
}
function updateRemoteServiceCache(discoveryInfo) {
for (var serviceType in discoveryInfo) {
if (!discoveredRemoteServices[serviceType]) {
discoveredRemoteServices[serviceType] = {};
}
var discoveredServices = discoveryInfo[serviceType];
for (const service of discoveredServices) {
if (!service.sameHost)
{
//Handle non mDNS sceanrios
if (!service.name) {
service.name = service.host;
} else {
service.host = service.service;
}
if (discoveredRemoteServices[serviceType][service.host]) {
service.inConfig = true;
}
discoveredRemoteServices[serviceType][service.host] = service;
}
}
}
};
async function discoverRemoteHyperionServices(type, params) {
const result = await requestServiceDiscovery(type, params);
var discoveryResult;
if (result && !result.error) {
discoveryResult = result.info;
}
else {
discoveryResult = {
"services": []
};
}
switch (type) {
case "jsonapi":
case "flatbuffer":
updateRemoteServiceCache(discoveryResult.services);
updateForwarderSelectList(type);
break;
}
};
}); });

View File

@ -26,7 +26,6 @@ window.loggingStreamActive = false;
window.loggingHandlerInstalled = false; window.loggingHandlerInstalled = false;
window.watchdog = 0; window.watchdog = 0;
window.debugMessagesActive = true; window.debugMessagesActive = true;
window.wSess = [];
window.currentHyperionInstance = 0; window.currentHyperionInstance = 0;
window.currentHyperionInstanceName = "?"; window.currentHyperionInstanceName = "?";
window.comps = []; window.comps = [];
@ -136,7 +135,11 @@ function initWebSocket()
// skip tan -1 error handling // skip tan -1 error handling
if(tan != -1){ if(tan != -1){
var error = response.hasOwnProperty("error")? response.error : "unknown"; var error = response.hasOwnProperty("error")? response.error : "unknown";
if (error == "Service Unavailable") {
window.location.reload();
} else {
$(window.hyperion).trigger({type:"error",reason:error}); $(window.hyperion).trigger({type:"error",reason:error});
}
console.log("[window.websocket::onmessage] ",error) console.log("[window.websocket::onmessage] ",error)
} }
} }
@ -307,7 +310,7 @@ function requestInstanceSwitch(inst)
function requestServerInfo() function requestServerInfo()
{ {
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update", "instance-update"]'); sendToHyperion("serverinfo","",'"subscribe":["components-update", "priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update", "instance-update"]');
} }
function requestSysInfo() function requestSysInfo()
@ -485,9 +488,21 @@ function requestLedDeviceIdentification(type, params)
return sendAsyncToHyperion("leddevice", "identify", data, Math.floor(Math.random() * 1000)); return sendAsyncToHyperion("leddevice", "identify", data, Math.floor(Math.random() * 1000));
} }
async function requestLedDeviceAddAuthorization(type, params) {
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "addAuthorization", data, Math.floor(Math.random() * 1000));
}
async function requestInputSourcesDiscovery(type, params) { async function requestInputSourcesDiscovery(type, params) {
let data = { sourceType: type, params: params }; let data = { sourceType: type, params: params };
return sendAsyncToHyperion("inputsource", "discover", data, Math.floor(Math.random() * 1000)); return sendAsyncToHyperion("inputsource", "discover", data, Math.floor(Math.random() * 1000));
} }
async function requestServiceDiscovery(type, params) {
let data = { serviceType: type, params: params };
return sendAsyncToHyperion("service", "discover", data, Math.floor(Math.random() * 1000));
}

View File

@ -2,14 +2,14 @@ var availAccess = ['default', 'advanced', 'expert'];
var storedAccess; var storedAccess;
//Change Password //Change Password
function changePassword(){ function changePassword() {
showInfoDialog('changePassword', $.i18n('InfoDialog_changePassword_title')); showInfoDialog('changePassword', $.i18n('InfoDialog_changePassword_title'));
// fill default pw if default is set // fill default pw if default is set
if(window.defaultPasswordIsSet) if (window.defaultPasswordIsSet)
$('#current-password').val('hyperion') $('#current-password').val('hyperion')
$('#id_btn_ok').off().on('click',function() { $('#id_btn_ok').off().on('click', function () {
var oldPw = $('#current-password').val(); var oldPw = $('#current-password').val();
var newPw = $('#new-password').val(); var newPw = $('#new-password').val();
@ -17,7 +17,7 @@ function changePassword(){
history.pushState({}, "New password"); history.pushState({}, "New password");
}); });
$('#new-password, #current-password').off().on('input',function(e) { $('#new-password, #current-password').off().on('input', function (e) {
($('#current-password').val().length >= 8 && $('#new-password').val().length >= 8) && !window.readOnlyMode ? $('#id_btn_ok').prop('disabled', false) : $('#id_btn_ok').prop('disabled', true); ($('#current-password').val().length >= 8 && $('#new-password').val().length >= 8) && !window.readOnlyMode ? $('#id_btn_ok').prop('disabled', false) : $('#id_btn_ok').prop('disabled', true);
}); });
} }
@ -44,18 +44,17 @@ $(document).ready(function () {
$('#btn_setlang').prop("disabled", true); $('#btn_setlang').prop("disabled", true);
} }
$('#btn_setaccess').off().on('click',function() { $('#btn_setaccess').off().on('click', function () {
var newAccess; var newAccess;
showInfoDialog('select', $.i18n('InfoDialog_access_title'), $.i18n('InfoDialog_access_text')); showInfoDialog('select', $.i18n('InfoDialog_access_title'), $.i18n('InfoDialog_access_text'));
for (var lcx = 0; lcx<availAccess.length; lcx++) for (var lcx = 0; lcx < availAccess.length; lcx++) {
{ $('#id_select').append(createSelOpt(availAccess[lcx], $.i18n('general_access_' + availAccess[lcx])));
$('#id_select').append(createSelOpt(availAccess[lcx], $.i18n('general_access_'+availAccess[lcx])));
} }
$('#id_select').val(storedAccess); $('#id_select').val(storedAccess);
$('#id_select').off().on('change',function() { $('#id_select').off().on('change', function () {
newAccess = $('#id_select').val(); newAccess = $('#id_select').val();
if (newAccess == storedAccess) if (newAccess == storedAccess)
$('#id_btn_saveset').prop('disabled', true); $('#id_btn_saveset').prop('disabled', true);
@ -63,7 +62,7 @@ $(document).ready(function () {
$('#id_btn_saveset').prop('disabled', false); $('#id_btn_saveset').prop('disabled', false);
}); });
$('#id_btn_saveset').off().on('click',function() { $('#id_btn_saveset').off().on('click', function () {
setStorage("accesslevel", newAccess); setStorage("accesslevel", newAccess);
reload(); reload();
}); });
@ -72,13 +71,13 @@ $(document).ready(function () {
}); });
// change pw btn // change pw btn
$('#btn_changePassword').off().on('click',function() { $('#btn_changePassword').off().on('click', function () {
changePassword(); changePassword();
}); });
//Lock Ui //Lock Ui
$('#btn_lock_ui').off().on('click',function() { $('#btn_lock_ui').off().on('click', function () {
removeStorage('loginToken', true); removeStorage('loginToken');
location.replace('/'); location.replace('/');
}); });
@ -86,27 +85,5 @@ $(document).ready(function () {
if (storedAccess != 'expert') if (storedAccess != 'expert')
$('#load_webconfig').toggle(false); $('#load_webconfig').toggle(false);
// instance switcher
$('#btn_instanceswitch').off().on('click',function() {
var lsys = window.sysInfo.system.hostName+':'+window.serverConfig.webConfig.port;
showInfoDialog('iswitch', $.i18n('InfoDialog_iswitch_title'), $.i18n('InfoDialog_iswitch_text'));
for (var i = 0; i<window.wSess.length; i++)
{
if(lsys != window.wSess[i].host+':'+window.wSess[i].port)
{
var hyperionAddress = window.wSess[i].address;
if(hyperionAddress.indexOf(':') > -1 && hyperionAddress.length == 36) hyperionAddress = '['+hyperionAddress+']';
hyperionAddress = 'http://'+hyperionAddress+':'+window.wSess[i].port;
$('#id_select').append(createSelOpt(hyperionAddress, window.wSess[i].name));
}
}
$('#id_btn_saveset').off().on('click',function() {
$("#loading_overlay").addClass("overlay");
window.location.href = $('#id_select').val();
});
});
}); });

View File

@ -14,30 +14,21 @@ function storageComp() {
return false; return false;
} }
function getStorage(item, session) { function getStorage(item) {
if (storageComp()) { if (storageComp()) {
if (session === true)
return sessionStorage.getItem(item);
else
return localStorage.getItem(item); return localStorage.getItem(item);
} }
return null; return null;
} }
function setStorage(item, value, session) { function setStorage(item, value) {
if (storageComp()) { if (storageComp()) {
if (session === true)
sessionStorage.setItem(item, value);
else
localStorage.setItem(item, value); localStorage.setItem(item, value);
} }
} }
function removeStorage(item, session) { function removeStorage(item) {
if (storageComp()) { if (storageComp()) {
if (session === true)
sessionStorage.removeItem(item);
else
localStorage.removeItem(item); localStorage.removeItem(item);
} }
} }
@ -48,23 +39,6 @@ function debugMessage(msg) {
} }
} }
function updateSessions() {
var sess = window.serverInfo.sessions;
if (sess && sess.length) {
window.wSess = [];
for (var i = 0; i < sess.length; i++) {
if (sess[i].type == "_http._tcp." || sess[i].type == "_https._tcp." || sess[i].type == "_hyperiond-http._tcp.") {
window.wSess.push(sess[i]);
}
}
if (window.wSess.length > 1)
$('#btn_instanceswitch').toggle(true);
else
$('#btn_instanceswitch').toggle(false);
}
}
function validateDuration(d) { function validateDuration(d) {
if (typeof d === "undefined" || d < 0) if (typeof d === "undefined" || d < 0)
return ENDLESS; return ENDLESS;
@ -73,8 +47,8 @@ function validateDuration(d) {
} }
function getHashtag() { function getHashtag() {
if (getStorage('lasthashtag', true) != null) if (getStorage('lasthashtag') != null)
return getStorage('lasthashtag', true); return getStorage('lasthashtag');
else { else {
var tag = document.URL; var tag = document.URL;
tag = tag.substr(tag.indexOf("#") + 1); tag = tag.substr(tag.indexOf("#") + 1);
@ -87,20 +61,20 @@ function getHashtag() {
function loadContent(event, forceRefresh) { function loadContent(event, forceRefresh) {
var tag; var tag;
var lastSelectedInstance = getStorage('lastSelectedInstance', false); var lastSelectedInstance = getStorage('lastSelectedInstance');
if (lastSelectedInstance && (lastSelectedInstance != window.currentHyperionInstance)) { if (lastSelectedInstance && (lastSelectedInstance != window.currentHyperionInstance)) {
if (window.serverInfo.instance[lastSelectedInstance] && window.serverInfo.instance[lastSelectedInstance].running) { if (window.serverInfo.instance[lastSelectedInstance] && window.serverInfo.instance[lastSelectedInstance].running) {
instanceSwitch(lastSelectedInstance); instanceSwitch(lastSelectedInstance);
} else { } else {
removeStorage('lastSelectedInstance', false); removeStorage('lastSelectedInstance');
} }
} }
if (typeof event != "undefined") { if (typeof event != "undefined") {
tag = event.currentTarget.hash; tag = event.currentTarget.hash;
tag = tag.substr(tag.indexOf("#") + 1); tag = tag.substr(tag.indexOf("#") + 1);
setStorage('lasthashtag', tag, true); setStorage('lasthashtag', tag);
} }
else else
tag = getHashtag(); tag = getHashtag();
@ -112,7 +86,7 @@ function loadContent(event, forceRefresh) {
if (status == "error") { if (status == "error") {
tag = 'dashboard'; tag = 'dashboard';
console.log("Could not find page:", prevTag, ", Redirecting to:", tag); console.log("Could not find page:", prevTag, ", Redirecting to:", tag);
setStorage('lasthashtag', tag, true); setStorage('lasthashtag', tag);
$("#page-content").load("/content/" + tag + ".html", function (response, status, xhr) { $("#page-content").load("/content/" + tag + ".html", function (response, status, xhr) {
if (status == "error") { if (status == "error") {
@ -215,7 +189,7 @@ function instanceSwitch(inst) {
requestInstanceSwitch(inst) requestInstanceSwitch(inst)
window.currentHyperionInstance = inst; window.currentHyperionInstance = inst;
window.currentHyperionInstanceName = getInstanceNameByIndex(inst); window.currentHyperionInstanceName = getInstanceNameByIndex(inst);
setStorage('lastSelectedInstance', inst, false) setStorage('lastSelectedInstance', inst)
updateHyperionInstanceListing() updateHyperionInstanceListing()
} }
@ -332,7 +306,7 @@ function showInfoDialog(type, header, message) {
if (type == "select" || type == "iswitch") if (type == "select" || type == "iswitch")
$('#id_body').append('<select id="id_select" class="form-control" style="margin-top:10px;width:auto;"></select>'); $('#id_body').append('<select id="id_select" class="form-control" style="margin-top:10px;width:auto;"></select>');
if (getStorage("darkMode", false) == "on") if (getStorage("darkMode") == "on")
$('#id_logo').attr("src", 'img/hyperion/logo_negativ.png'); $('#id_logo').attr("src", 'img/hyperion/logo_negativ.png');
$(type == "renInst" || type == "changePassword" ? "#modal_dialog_rename" : "#modal_dialog").modal({ $(type == "renInst" || type == "changePassword" ? "#modal_dialog_rename" : "#modal_dialog").modal({
@ -1246,7 +1220,7 @@ function handleDarkMode() {
href: "../css/darkMode.css" href: "../css/darkMode.css"
}).appendTo("head"); }).appendTo("head");
setStorage("darkMode", "on", false); setStorage("darkMode", "on");
$('#btn_darkmode_icon').removeClass('fa fa-moon-o'); $('#btn_darkmode_icon').removeClass('fa fa-moon-o');
$('#btn_darkmode_icon').addClass('mdi mdi-white-balance-sunny'); $('#btn_darkmode_icon').addClass('mdi mdi-white-balance-sunny');
$('#navbar_brand_logo').attr("src", 'img/hyperion/logo_negativ.png'); $('#navbar_brand_logo').attr("src", 'img/hyperion/logo_negativ.png');
@ -1337,7 +1311,16 @@ function isValidIPv6(value) {
function isValidHostname(value) { function isValidHostname(value) {
if (value.match( if (value.match(
'(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9].)+[a-zA-Z]{2,63}$)' '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$'
))
return true;
else
return false;
}
function isValidServicename(value) {
if (value.match(
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9 \-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$'
)) ))
return true; return true;
else else
@ -1349,6 +1332,5 @@ function isValidHostnameOrIP4(value) {
} }
function isValidHostnameOrIP(value) { function isValidHostnameOrIP(value) {
return (isValidHostnameOrIP4(value) || isValidIPv6(value)); return (isValidHostnameOrIP4(value) || isValidIPv6(value) || isValidServicename(value));
} }

View File

@ -57,7 +57,7 @@ function startWizardRGB() {
$('#wizp2_body').append('<table class="table borderless" style="width:200px"><tbody><tr><td class="ltd"><label>' + $.i18n('wiz_rgb_qrend') + '</label></td><td class="itd"><select id="wiz_r_select" class="form-control wselect"></select></td></tr><tr><td class="ltd"><label>' + $.i18n('wiz_rgb_qgend') + '</label></td><td class="itd"><select id="wiz_g_select" class="form-control wselect"></select></td></tr></tbody></table>'); $('#wizp2_body').append('<table class="table borderless" style="width:200px"><tbody><tr><td class="ltd"><label>' + $.i18n('wiz_rgb_qrend') + '</label></td><td class="itd"><select id="wiz_r_select" class="form-control wselect"></select></td></tr><tr><td class="ltd"><label>' + $.i18n('wiz_rgb_qgend') + '</label></td><td class="itd"><select id="wiz_g_select" class="form-control wselect"></select></td></tr></tbody></table>');
$('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button><button type="button" class="btn btn-primary" id="btn_wiz_checkok" style="display:none" data-dismiss="modal"><i class="fa fa-fw fa-check"></i>' + $.i18n('general_btn_ok') + '</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button><button type="button" class="btn btn-primary" id="btn_wiz_checkok" style="display:none" data-dismiss="modal"><i class="fa fa-fw fa-check"></i>' + $.i18n('general_btn_ok') + '</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>');
if (getStorage("darkMode", false) == "on") if (getStorage("darkMode") == "on")
$('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png');
//open modal //open modal
@ -454,7 +454,7 @@ function startWizardCC() {
'<button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>' '<button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'
); );
if (getStorage("darkMode", false) == "on") if (getStorage("darkMode") == "on")
$('#wizard_logo').prop("src", 'img/hyperion/logo_negativ.png'); $('#wizard_logo').prop("src", 'img/hyperion/logo_negativ.png');
//open modal //open modal
@ -591,6 +591,19 @@ var lightPosRightMiddle = { hmin: 0.85, hmax: 1.0, vmin: 0.25, vmax: 0.75 };
var lightPosRightBottom = { hmin: 0.85, hmax: 1.0, vmin: 0.5, vmax: 1.0 }; var lightPosRightBottom = { hmin: 0.85, hmax: 1.0, vmin: 0.5, vmax: 1.0 };
var lightPosEntire = { hmin: 0.0, hmax: 1.0, vmin: 0.0, vmax: 1.0 }; var lightPosEntire = { hmin: 0.0, hmax: 1.0, vmin: 0.0, vmax: 1.0 };
var lightPosBottomLeft14 = { hmin: 0, hmax: 0.25, vmin: 0.85, vmax: 1.0 };
var lightPosBottomLeft12 = { hmin: 0.25, hmax: 0.5, vmin: 0.85, vmax: 1.0 };
var lightPosBottomLeft34 = { hmin: 0.5, hmax: 0.75, vmin: 0.85, vmax: 1.0 };
var lightPosBottomLeft11 = { hmin: 0.75, hmax: 1, vmin: 0.85, vmax: 1.0 };
var lightPosBottomLeft112 = { hmin: 0, hmax: 0.5, vmin: 0.85, vmax: 1.0 };
var lightPosBottomLeft121 = { hmin: 0.5, hmax: 1, vmin: 0.85, vmax: 1.0 };
var lightPosBottomLeftNewMid = { hmin: 0.25, hmax: 0.75, vmin: 0.85, vmax: 1.0 };
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(id, pos, name) {
var i = null; var i = null;
@ -622,6 +635,26 @@ function assignLightPos(id, pos, name) {
i = lightPosRightMiddle; i = lightPosRightMiddle;
else if (pos === "rightbottom") else if (pos === "rightbottom")
i = lightPosRightBottom; i = lightPosRightBottom;
else if (pos === "lightPosBottomLeft14")
i = lightPosBottomLeft14;
else if (pos === "lightPosBottomLeft12")
i = lightPosBottomLeft12;
else if (pos === "lightPosBottomLeft34")
i = lightPosBottomLeft34;
else if (pos === "lightPosBottomLeft11")
i = lightPosBottomLeft11;
else if (pos === "lightPosBottomLeft112")
i = lightPosBottomLeft112;
else if (pos === "lightPosBottomLeft121")
i = lightPosBottomLeft121;
else if (pos === "lightPosBottomLeftNewMid")
i = lightPosBottomLeftNewMid;
else if (pos === "lightPosTopLeft112")
i = lightPosTopLeft112;
else if (pos === "lightPosTopLeft121")
i = lightPosTopLeft121;
else if (pos === "lightPosTopLeftNewMid")
i = lightPosTopLeftNewMid;
else else
i = lightPosEntire; i = lightPosEntire;
@ -653,21 +686,22 @@ function getIdInLights(id) {
); );
} }
// External properties properties, 2-dimensional arry of [ledType][key]
devicesProperties = {};
//**************************** //****************************
// Wizard Philips Hue // Wizard Philips Hue
//**************************** //****************************
var hueIPs = []; var hueIPs = [];
var hueIPsinc = 0; var hueIPsinc = 0;
var lightIDs = null; var hueLights = null;
var groupIDs = null; var hueGroups = null;
var lightLocation = []; var lightLocation = [];
var groupLights = []; var groupLights = [];
var groupLightsLocations = []; var groupLightsLocations = [];
var hueType = "philipshue"; var hueType = "philipshue";
let hueUrl = new URL('http://dummy');
function startWizardPhilipsHue(e) { function startWizardPhilipsHue(e) {
if (typeof e.data.type != "undefined") hueType = e.data.type; if (typeof e.data.type != "undefined") hueType = e.data.type;
@ -739,7 +773,7 @@ function startWizardPhilipsHue(e) {
$('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>');
$('#wizp3_body').html('<span>' + $.i18n('wiz_hue_press_link') + '</span> <br /><br /><center><span id="connectionTime"></span><br /><i class="fa fa-cog fa-spin" style="font-size:100px"></i></center>'); $('#wizp3_body').html('<span>' + $.i18n('wiz_hue_press_link') + '</span> <br /><br /><center><span id="connectionTime"></span><br /><i class="fa fa-cog fa-spin" style="font-size:100px"></i></center>');
if (getStorage("darkMode", false) == "on") if (getStorage("darkMode") == "on")
$('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png');
//open modal //open modal
@ -762,43 +796,10 @@ function checkHueBridge(cb, hueUser) {
if (usr == 'config') $('#wiz_hue_discovered').html(""); if (usr == 'config') $('#wiz_hue_discovered').html("");
if (hueIPs[hueIPsinc]) { if (hueIPs[hueIPsinc]) {
hueUrl.hostname = "dummy";
var host = hueIPs[hueIPsinc].host; var host = hueIPs[hueIPsinc].host;
if (isValidIPv6(host)) {
hueUrl.hostname = "[" + host + "]";
} else {
hueUrl.hostname = host;
}
var port = hueIPs[hueIPsinc].port; var port = hueIPs[hueIPsinc].port;
if (port > 0) {
hueUrl.port = port;
}
hueUrl.pathname = '/api/' + usr; getProperties_hue_bridge(cb, decodeURIComponent(host), port, usr);
$.ajax({
url: hueUrl,
type: "GET",
dataType: "json",
timeout: 2500
})
.done(function (json) {
if (json.config) {
cb(true, usr);
} else {
if (json.name && json.bridgeid && json.modelid) {
$('#wiz_hue_discovered').html("Bridge: " + json.name + ", Modelid: " + json.modelid + ", API-Version: " + json.apiversion);
cb(true);
} else {
cb(false);
}
}
})
.fail(function () {
cb(false);
});
} }
} }
@ -809,7 +810,6 @@ function checkBridgeResult(reply, usr) {
$('#host').val(hueIPs[hueIPsinc].host) $('#host').val(hueIPs[hueIPsinc].host)
$('#port').val(hueIPs[hueIPsinc].port) $('#port').val(hueIPs[hueIPsinc].port)
//now check hue user on this bridge
$('#usrcont').toggle(true); $('#usrcont').toggle(true);
checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper"); checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper");
} }
@ -852,28 +852,10 @@ function checkUserResult(reply, usr) {
} }
}; };
function identHueId(id, off, oState) {
if (off !== true) {
setTimeout(identHueId, 1500, id, true, oState);
var put_data = '{"on":true,"bri":254,"hue":47000,"sat":254}';
}
else {
var put_data = '{"on":' + oState.on + ',"bri":' + oState.bri + ',"hue":' + oState.hue + ',"sat":' + oState.sat + '}';
}
hueUrl.pathname = '/api/' + $('#user').val() + '/lights/' + id + '/state',
$.ajax({
url: hueUrl,
type: 'PUT',
timeout: 2000,
data: put_data
})
}
function useGroupId(id) { function useGroupId(id) {
$('#groupId').val(id); $('#groupId').val(id);
groupLights = groupIDs[id].lights; groupLights = hueGroups[id].lights;
groupLightsLocations = groupIDs[id].locations; groupLightsLocations = hueGroups[id].locations;
get_hue_lights(); get_hue_lights();
} }
@ -881,9 +863,6 @@ async function discover_hue_bridges() {
$('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress')); $('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress'));
$('#wiz_hue_discovered').html("") $('#wiz_hue_discovered').html("")
const res = await requestLedDeviceDiscovery('philipshue'); const res = await requestLedDeviceDiscovery('philipshue');
// TODO: error case unhandled
// res can be: false (timeout) or res.error (not found)
if (res && !res.error) { if (res && !res.error) {
const r = res.info; const r = res.info;
@ -896,10 +875,16 @@ async function discover_hue_bridges() {
hueIPs = []; hueIPs = [];
hueIPsinc = 0; hueIPsinc = 0;
var discoveryMethod = "ssdp";
if (res.info.discoveryMethod) {
discoveryMethod = res.info.discoveryMethod;
}
for (const device of r.devices) { for (const device of r.devices) {
if (device && device.ip && device.port) { if (device) {
var host; var host;
var port; var port;
if (discoveryMethod === "ssdp") {
if (device.hostname && device.domain) { if (device.hostname && device.domain) {
host = device.hostname + "." + device.domain; host = device.hostname + "." + device.domain;
port = device.port; port = device.port;
@ -907,6 +892,15 @@ async function discover_hue_bridges() {
host = device.ip; host = device.ip;
port = device.port; port = device.port;
} }
} else {
host = device.service;
port = device.port;
}
//Remap https port to http port until Hue-API v2 is supported
if (port == 443) {
port = 80;
}
if (host) { if (host) {
@ -930,65 +924,71 @@ async function discover_hue_bridges() {
} }
} }
async function getProperties_hue_bridge(hostAddress, port, username, resourceFilter) { async function getProperties_hue_bridge(cb, hostAddress, port, username, resourceFilter) {
let params = { host: hostAddress, user: username, filter: resourceFilter }; let params = { host: hostAddress, user: username, filter: resourceFilter };
if (port !== 'undefined') { if (port !== 'undefined') {
params.port = port; params.port = parseInt(port);
} }
const res = await requestLedDeviceProperties('philipshue', params); var ledType = 'philipshue';
var key = hostAddress;
//Create ledType cache entry
if (!devicesProperties[ledType]) {
devicesProperties[ledType] = {};
}
// Use device's properties, if properties in chache
if (devicesProperties[ledType][key]) {
cb(true, username);
} else {
const res = await requestLedDeviceProperties(ledType, params);
// TODO: error case unhandled
// res can be: false (timeout) or res.error (not found)
if (res && !res.error) { if (res && !res.error) {
const r = res.info var ledDeviceProperties = res.info.properties;
if (!jQuery.isEmptyObject(ledDeviceProperties)) {
// Process properties returned if (username === "config") {
console.log(r); if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) {
$('#wiz_hue_discovered').html("Bridge: " + ledDeviceProperties.name + ", Modelid: " + ledDeviceProperties.modelid + ", API-Version: " + ledDeviceProperties.apiversion);
cb(true);
}
} else {
devicesProperties[ledType][key] = ledDeviceProperties;
cb(true, username);
}
} else {
cb(false, username);
}
} else {
cb(false, username);
}
} }
} }
async function identify_hue_device(hostAddress, port, username, id) { async function identify_hue_device(hostAddress, port, username, id) {
var disabled = $('#btn_wiz_save').is(':disabled');
// Take care that new record cannot be save during background process // Take care that new record cannot be save during background process
$('#btn_wiz_save').prop('disabled', true); $('#btn_wiz_save').prop('disabled', true);
let params = { host: hostAddress, user: username, lightId: id }; let params = { host: decodeURIComponent(hostAddress), user: username, lightId: id };
if (port !== 'undefined') { if (port !== 'undefined') {
params.port = port; params.port = parseInt(port);
} }
await requestLedDeviceIdentification('philipshue', params); await requestLedDeviceIdentification('philipshue', params);
if (!window.readOnlyMode) { if (!window.readOnlyMode) {
$('#btn_wiz_save').prop('disabled', false); $('#btn_wiz_save').prop('disabled', disabled);
} }
} }
function getHueIPs() {
$('#wiz_hue_ipstate').html($.i18n('wiz_hue_searchb'));
$.ajax({
url: 'https://discovery.meethue.com',
crossDomain: true,
type: 'GET',
timeout: 3000
})
.done(function (data, textStatus, jqXHR) {
if (data.length == 0) {
$('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
} else {
hueIPs = data;
checkHueBridge(checkBridgeResult);
}
})
.fail(function (jqXHR, textStatus) {
$('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
});
};
//return editor Value //return editor Value
function eV(vn) { function eV(vn, defaultVal = "") {
return (vn) ? conf_editor.getEditor("root.specificOptions." + vn).getValue() : ""; var editor = (vn) ? conf_editor.getEditor("root.specificOptions." + vn) : null;
return (editor == null) ? defaultVal : ((defaultVal != "" && !isNaN(defaultVal) && isNaN(editor.getValue())) ? defaultVal : editor.getValue());
} }
function beginWizardHue() { function beginWizardHue() {
@ -1009,7 +1009,6 @@ function beginWizardHue() {
hueIPs = []; hueIPs = [];
hueIPsinc = 0; hueIPsinc = 0;
//getHueIPs();
discover_hue_bridges(); discover_hue_bridges();
} }
else { else {
@ -1068,13 +1067,13 @@ function beginWizardHue() {
var finalLightIds = []; var finalLightIds = [];
//create hue led config //create hue led config
for (var key in lightIDs) { for (var key in hueLights) {
if (hueType == 'philipshueentertainment') { if (hueType == 'philipshueentertainment') {
if (groupLights.indexOf(key) == -1) continue; if (groupLights.indexOf(key) == -1) continue;
} }
if ($('#hue_' + key).val() != "disabled") { if ($('#hue_' + key).val() != "disabled") {
finalLightIds.push(key); finalLightIds.push(key);
var idx_content = assignLightPos(key, $('#hue_' + key).val(), lightIDs[key].name); var idx_content = assignLightPos(key, $('#hue_' + key).val(), hueLights[key].name);
hueLedConfig.push(JSON.parse(JSON.stringify(idx_content))); hueLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
} }
} }
@ -1100,23 +1099,28 @@ function beginWizardHue() {
d.type = 'philipshue'; d.type = 'philipshue';
d.colorOrder = 'rgb'; d.colorOrder = 'rgb';
d.lightIds = finalLightIds; d.lightIds = finalLightIds;
d.transitiontime = parseInt(eV("transitiontime")); d.transitiontime = parseInt(eV("transitiontime", 1));
d.restoreOriginalState = (eV("restoreOriginalState") == true); d.restoreOriginalState = (eV("restoreOriginalState", false) == true);
d.switchOffOnBlack = (eV("switchOffOnBlack") == true); d.switchOffOnBlack = (eV("switchOffOnBlack", false) == true);
d.brightnessFactor = parseFloat(eV("brightnessFactor"));
d.blackLevel = parseFloat(eV("blackLevel", 0.009));
d.onBlackTimeToPowerOff = parseInt(eV("onBlackTimeToPowerOff", 600));
d.onBlackTimeToPowerOn = parseInt(eV("onBlackTimeToPowerOn", 300));
d.brightnessFactor = parseFloat(eV("brightnessFactor", 1));
d.clientkey = $('#clientkey').val(); d.clientkey = $('#clientkey').val();
d.groupId = parseInt($('#groupId').val()); d.groupId = parseInt($('#groupId').val());
d.blackLightsTimeout = parseInt(eV("blackLightsTimeout")); d.blackLightsTimeout = parseInt(eV("blackLightsTimeout", 5000));
d.brightnessMin = parseFloat(eV("brightnessMin")); d.brightnessMin = parseFloat(eV("brightnessMin", 0));
d.brightnessMax = parseFloat(eV("brightnessMax")); d.brightnessMax = parseFloat(eV("brightnessMax", 1));
d.brightnessThreshold = parseFloat(eV("brightnessThreshold")); d.brightnessThreshold = parseFloat(eV("brightnessThreshold", 0.0001));
d.sslReadTimeout = parseInt(eV("sslReadTimeout")); d.handshakeTimeoutMin = parseInt(eV("handshakeTimeoutMin", 300));
d.sslHSTimeoutMin = parseInt(eV("sslHSTimeoutMin")); d.handshakeTimeoutMax = parseInt(eV("handshakeTimeoutMax", 1000));
d.sslHSTimeoutMax = parseInt(eV("sslHSTimeoutMax"));
d.verbose = (eV("verbose") == true); d.verbose = (eV("verbose") == true);
d.debugStreamer = (eV("debugStreamer") == true);
d.debugLevel = (eV("debugLevel")); d.autoStart = conf_editor.getEditor("root.generalOptions.autoStart").getValue();
d.enableAttempts = parseInt(conf_editor.getEditor("root.generalOptions.enableAttempts").getValue());
d.enableAttemptsInterval = parseInt(conf_editor.getEditor("root.generalOptions.enableAttemptsInterval").getValue());
if (hueType == 'philipshue') { if (hueType == 'philipshue') {
d.useEntertainmentAPI = false; d.useEntertainmentAPI = false;
@ -1129,7 +1133,6 @@ function beginWizardHue() {
if (hueType == 'philipshueentertainment') { if (hueType == 'philipshueentertainment') {
d.useEntertainmentAPI = true; d.useEntertainmentAPI = true;
d.hardwareLedCount = groupLights.length; d.hardwareLedCount = groupLights.length;
d.rewriteTime = 20;
//smoothing on //smoothing on
sc.smoothing.enable = true; sc.smoothing.enable = true;
} }
@ -1144,83 +1147,92 @@ function beginWizardHue() {
} }
function createHueUser() { function createHueUser() {
var connectionRetries = 30;
var data = { "devicetype": "hyperion#" + Date.now() } var host = hueIPs[hueIPsinc].host;
if (hueType == 'philipshueentertainment') { var port = hueIPs[hueIPsinc].port;
data = { "devicetype": "hyperion#" + Date.now(), "generateclientkey": true }
let params = { host: host };
if (port !== 'undefined') {
params.port = parseInt(port);
} }
var retryTime = 30;
var retryInterval = 2;
var UserInterval = setInterval(function () { var UserInterval = setInterval(function () {
hueUrl.pathname = '/api/';
$.ajax({
type: "POST",
url: hueUrl,
processData: false,
timeout: 1000,
contentType: 'application/json',
data: JSON.stringify(data)
})
.done(function (r) {
$('#wizp1').toggle(false); $('#wizp1').toggle(false);
$('#wizp2').toggle(false); $('#wizp2').toggle(false);
$('#wizp3').toggle(true); $('#wizp3').toggle(true);
connectionRetries--; (async () => {
$("#connectionTime").html(connectionRetries);
if (connectionRetries == 0) { retryTime -= retryInterval;
$("#connectionTime").html(retryTime);
if (retryTime <= 0) {
abortConnection(UserInterval); abortConnection(UserInterval);
clearInterval(UserInterval);
} }
else { else {
if (typeof r[0].error != 'undefined') { const res = await requestLedDeviceAddAuthorization('philipshue', params);
console.log(connectionRetries + ": link not pressed"); if (res && !res.error) {
} var response = res.info;
if (typeof r[0].success != 'undefined') {
if (jQuery.isEmptyObject(response)) {
debugMessage(retryTime + ": link button not pressed or device not reachable");
} else {
$('#wizp1').toggle(false); $('#wizp1').toggle(false);
$('#wizp2').toggle(true); $('#wizp2').toggle(true);
$('#wizp3').toggle(false); $('#wizp3').toggle(false);
if (r[0].success.username != 'undefined') {
$('#user').val(r[0].success.username); var username = response.username;
conf_editor.getEditor("root.specificOptions.username").setValue(r[0].success.username); if (username != 'undefined') {
$('#user').val(username);
conf_editor.getEditor("root.specificOptions.username").setValue(username);
conf_editor.getEditor("root.specificOptions.host").setValue(host);
conf_editor.getEditor("root.specificOptions.port").setValue(port);
} }
if (hueType == 'philipshueentertainment') { if (hueType == 'philipshueentertainment') {
if (r[0].success.clientkey != 'undefined') { var clientkey = response.clientkey;
$('#clientkey').val(r[0].success.clientkey); if (clientkey != 'undefined') {
conf_editor.getEditor("root.specificOptions.clientkey").setValue(r[0].success.clientkey); $('#clientkey').val(clientkey);
conf_editor.getEditor("root.specificOptions.clientkey").setValue(clientkey);
} }
} }
checkHueBridge(checkUserResult, r[0].success.username); checkHueBridge(checkUserResult, username);
clearInterval(UserInterval); clearInterval(UserInterval);
} }
} } else {
})
.fail(function (XMLHttpRequest, textStatus, errorThrown) {
$('#wizp1').toggle(false); $('#wizp1').toggle(false);
$('#wizp2').toggle(true); $('#wizp2').toggle(true);
$('#wizp3').toggle(false); $('#wizp3').toggle(false);
clearInterval(UserInterval); clearInterval(UserInterval);
}) }
}, 1000); }
})();
}, retryInterval * 1000);
} }
function get_hue_groups() { function get_hue_groups() {
hueUrl.pathname = '/api/' + $("#user").val() + '/groups';
$.ajax({ var host = hueIPs[hueIPsinc].host;
type: "GET",
url: hueUrl, if (devicesProperties['philipshue'][host]) {
processData: false, var ledProperties = devicesProperties['philipshue'][host];
contentType: 'application/json'
}) if (!jQuery.isEmptyObject(ledProperties)) {
.done(function (r) { hueGroups = ledProperties.groups;
if (Object.keys(r).length > 0) { if (Object.keys(hueGroups).length > 0) {
$('.lidsb').html("");
$('#wh_topcontainer').toggle(false); $('#wh_topcontainer').toggle(false);
$('#hue_grp_ids_t').toggle(true); $('#hue_grp_ids_t').toggle(true);
groupIDs = r;
var gC = 0; var gC = 0;
for (var groupid in r) { for (var groupid in hueGroups) {
if (r[groupid].type == 'Entertainment') { if (hueGroups[groupid].type == 'Entertainment') {
$('.gidsb').append(createTableRow([groupid + ' (' + r[groupid].name + ')', '<button class="btn btn-sm btn-primary" onClick=useGroupId(' + groupid + ')>' + $.i18n('wiz_hue_e_use_groupid', groupid) + '</button>'])); $('.gidsb').append(createTableRow([groupid + ' (' + hueGroups[groupid].name + ')', '<button class="btn btn-sm btn-primary" onClick=useGroupId(' + groupid + ')>' + $.i18n('wiz_hue_e_use_groupid', groupid) + '</button>']));
gC++; gC++;
} }
} }
@ -1228,10 +1240,8 @@ function get_hue_groups() {
noAPISupport('wiz_hue_e_noegrpids'); noAPISupport('wiz_hue_e_noegrpids');
} }
} }
else {
noAPISupport('wiz_hue_e_nogrpids');
} }
}) }
} }
function noAPISupport(txt) { function noAPISupport(txt) {
@ -1247,42 +1257,30 @@ function noAPISupport(txt) {
get_hue_lights(); get_hue_lights();
} }
function get_light_state(id) {
hueUrl.pathname = '/api/' + $("#user").val() + '/lights/' + id;
$.ajax({
type: "GET",
url: hueUrl,
processData: false,
contentType: 'application/json'
})
.done(function (r) {
if (Object.keys(r).length > 0) {
identHueId(id, false, r['state']);
}
})
}
function get_hue_lights() { function get_hue_lights() {
hueUrl.pathname = '/api/' + $("#user").val() + '/lights';
$.ajax({ var host = hueIPs[hueIPsinc].host;
type: "GET",
url: hueUrl, if (devicesProperties['philipshue'][host]) {
processData: false, var ledProperties = devicesProperties['philipshue'][host];
contentType: 'application/json'
}) if (!jQuery.isEmptyObject(ledProperties.lights)) {
.done(function (r) { hueLights = ledProperties.lights;
if (Object.keys(r).length > 0) { if (Object.keys(hueLights).length > 0) {
if (hueType == 'philipshue') { if (hueType == 'philipshue') {
$('#wh_topcontainer').toggle(false); $('#wh_topcontainer').toggle(false);
} }
$('#hue_ids_t, #btn_wiz_save').toggle(true); $('#hue_ids_t, #btn_wiz_save').toggle(true);
lightIDs = r;
var lightOptions = [ var lightOptions = [
"top", "topleft", "topright", "top", "topleft", "topright",
"bottom", "bottomleft", "bottomright", "bottom", "bottomleft", "bottomright",
"left", "lefttop", "leftmiddle", "leftbottom", "left", "lefttop", "leftmiddle", "leftbottom",
"right", "righttop", "rightmiddle", "rightbottom", "right", "righttop", "rightmiddle", "rightbottom",
"entire" "entire",
"lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121",
"lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11",
"lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121"
]; ];
if (hueType == 'philipshue') { if (hueType == 'philipshue') {
@ -1291,7 +1289,7 @@ function get_hue_lights() {
$('.lidsb').html(""); $('.lidsb').html("");
var pos = ""; var pos = "";
for (var lightid in r) { for (var lightid in hueLights) {
if (hueType == 'philipshueentertainment') { if (hueType == 'philipshueentertainment') {
if (groupLights.indexOf(lightid) == -1) continue; if (groupLights.indexOf(lightid) == -1) continue;
@ -1323,15 +1321,15 @@ function get_hue_lights() {
if (pos == val) options += ' selected="selected"'; if (pos == val) options += ' selected="selected"';
options += '>' + $.i18n(txt + val) + '</option>'; options += '>' + $.i18n(txt + val) + '</option>';
} }
$('.lidsb').append(createTableRow([lightid + ' (' + r[lightid].name + ')', '<select id="hue_' + lightid + '" class="hue_sel_watch form-control">' $('.lidsb').append(createTableRow([lightid + ' (' + hueLights[lightid].name + ')', '<select id="hue_' + lightid + '" class="hue_sel_watch form-control">'
+ options + options
+ '</select>', '<button class="btn btn-sm btn-primary" onClick=identify_hue_device("' + $("#host").val() + '","' + $("#port").val() + '","' + $("#user").val() + '",' + lightid + ')>' + $.i18n('wiz_hue_blinkblue', lightid) + '</button>'])); + '</select>', '<button class="btn btn-sm btn-primary" onClick=identify_hue_device("' + encodeURIComponent($("#host").val()) + '","' + $('#port').val() + '","' + $("#user").val() + '",' + lightid + ')>' + $.i18n('wiz_hue_blinkblue', lightid) + '</button>']));
} }
if (hueType != 'philipshueentertainment') { if (hueType != 'philipshueentertainment') {
$('.hue_sel_watch').on("change", function () { $('.hue_sel_watch').on("change", function () {
var cC = 0; var cC = 0;
for (var key in lightIDs) { for (var key in hueLights) {
if ($('#hue_' + key).val() != "disabled") { if ($('#hue_' + key).val() != "disabled") {
cC++; cC++;
} }
@ -1346,7 +1344,8 @@ function get_hue_lights() {
var txt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_hue_noids') + '</p>'; var txt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_hue_noids') + '</p>';
$('#wizp2_body').append(txt); $('#wizp2_body').append(txt);
} }
}) }
}
} }
function abortConnection(UserInterval) { function abortConnection(UserInterval) {
@ -1386,7 +1385,7 @@ function startWizardYeelight(e) {
+ $.i18n('general_btn_save') + '</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_save') + '</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>'
+ $.i18n('general_btn_cancel') + '</button>'); + $.i18n('general_btn_cancel') + '</button>');
if (getStorage("darkMode", false) == "on") if (getStorage("darkMode") == "on")
$('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png');
//open modal //open modal
@ -1413,19 +1412,15 @@ function beginWizardYeelight() {
//create yeelight led config //create yeelight led config
for (var key in lights) { for (var key in lights) {
if ($('#yee_' + key).val() !== "disabled") { if ($('#yee_' + key).val() !== "disabled") {
//delete lights[key].model;
var name = lights[key].name;
// Set Name to layout-position, if empty // Set Name to layout-position, if empty
if (lights[key].name === "") { if (name === "") {
lights[key].name = $.i18n('conf_leds_layout_cl_' + $('#yee_' + key).val()); name = lights[key].host;
} }
finalLights.push(lights[key]); finalLights.push(lights[key]);
var name = lights[key].host;
if (lights[key].name !== "")
name += '_' + lights[key].name;
var idx_content = assignLightPos(key, $('#yee_' + key).val(), name); var idx_content = assignLightPos(key, $('#yee_' + key).val(), name);
yeelightLedConfig.push(JSON.parse(JSON.stringify(idx_content))); yeelightLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
} }
@ -1460,6 +1455,7 @@ function beginWizardYeelight() {
window.serverConfig.device = d; window.serverConfig.device = d;
//smoothing off //smoothing off
if (!(window.serverConfig.smoothing == null))
window.serverConfig.smoothing.enable = false; window.serverConfig.smoothing.enable = false;
requestWriteConfig(window.serverConfig, true); requestWriteConfig(window.serverConfig, true);
@ -1479,24 +1475,31 @@ async function discover_yeelight_lights() {
if (res && !res.error) { if (res && !res.error) {
const r = res.info; const r = res.info;
var discoveryMethod = "ssdp";
if (res.info.discoveryMethod) {
discoveryMethod = res.info.discoveryMethod;
}
// Process devices returned by discovery // Process devices returned by discovery
for (const device of r.devices) { for (const device of r.devices) {
if (device.hostname !== "") { if (device.hostname !== "") {
if (getHostInLights(device.hostname).length === 0) { if (getHostInLights(device.hostname).length === 0) {
var light = {}; var light = {};
light.host = device.hostname;
if (discoveryMethod === "ssdp") {
//Create a valid hostname //Create a valid hostname
if (device.domain) if (device.domain) {
{
light.host += '.' + device.domain; light.host += '.' + device.domain;
} }
} else {
light.host = device.service;
light.name = device.name;
}
light.port = device.port; light.port = device.port;
if (device.txt) { if (device.txt) {
light.name = device.name;
light.model = device.txt.md; light.model = device.txt.md;
//Yeelight does not provide correct API port with mDNS response, use default one //Yeelight does not provide correct API port with mDNS response, use default one
light.port = 55443; light.port = 55443;
@ -1547,7 +1550,10 @@ function assign_yeelight_lights() {
"bottom", "bottomleft", "bottomright", "bottom", "bottomleft", "bottomright",
"left", "lefttop", "leftmiddle", "leftbottom", "left", "lefttop", "leftmiddle", "leftbottom",
"right", "righttop", "rightmiddle", "rightbottom", "right", "righttop", "rightmiddle", "rightbottom",
"entire" "entire",
"lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121",
"lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11",
"lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121"
]; ];
lightOptions.unshift("disabled"); lightOptions.unshift("disabled");
@ -1561,7 +1567,7 @@ function assign_yeelight_lights() {
var lightName = lights[lightid].name; var lightName = lights[lightid].name;
if (lightName === "") if (lightName === "")
lightName = $.i18n('edt_dev_spec_lights_itemtitle'); lightName = $.i18n('edt_dev_spec_lights_itemtitle') + '(' + lightHostname + ')';
var options = ""; var options = "";
for (var opt in lightOptions) { for (var opt in lightOptions) {
@ -1578,10 +1584,10 @@ function assign_yeelight_lights() {
options = '<option value=disabled>' + $.i18n('wiz_yeelight_unsupported') + '</option>'; options = '<option value=disabled>' + $.i18n('wiz_yeelight_unsupported') + '</option>';
} }
$('.lidsb').append(createTableRow([(parseInt(lightid, 10) + 1) + '. ' + lightName + '<br>(' + lightHostname + ')', '<select id="yee_' + lightid + '" ' + enabled + ' class="yee_sel_watch form-control">' $('.lidsb').append(createTableRow([(parseInt(lightid, 10) + 1) + '. ' + lightName, '<select id="yee_' + lightid + '" ' + enabled + ' class="yee_sel_watch form-control">'
+ options + options
+ '</select>', '<button class="btn btn-sm btn-primary" onClick=identify_yeelight_device("' + lightHostname + '",' + lightPort + ')>' + '</select>', '<button class="btn btn-sm btn-primary" onClick=identify_yeelight_device("' + lightHostname + '",' + lightPort + ')>'
+ $.i18n('wiz_identify_light', lightName) + '</button>'])); + $.i18n('wiz_identify') + '</button>']));
} }
$('.yee_sel_watch').on("change", function () { $('.yee_sel_watch').on("change", function () {
@ -1605,8 +1611,8 @@ function assign_yeelight_lights() {
} }
} }
async function getProperties_yeelight(hostname, port) { async function getProperties_yeelight(host, port) {
let params = { hostname: hostname, port: port }; let params = { host: host, port: port };
const res = await requestLedDeviceProperties('yeelight', params); const res = await requestLedDeviceProperties('yeelight', params);
@ -1614,21 +1620,22 @@ async function getProperties_yeelight(hostname, port) {
// res can be: false (timeout) or res.error (not found) // res can be: false (timeout) or res.error (not found)
if (res && !res.error) { if (res && !res.error) {
const r = res.info const r = res.info
console.log("Yeelight properties: ", r);
// Process properties returned
console.log(r);
} }
} }
async function identify_yeelight_device(hostname, port) { async function identify_yeelight_device(host, port) {
var disabled = $('#btn_wiz_save').is(':disabled');
// Take care that new record cannot be save during background process // Take care that new record cannot be save during background process
$('#btn_wiz_save').prop('disabled', true); $('#btn_wiz_save').prop('disabled', true);
let params = { hostname: hostname, port: port }; let params = { host: host, port: port };
await requestLedDeviceIdentification("yeelight", params); await requestLedDeviceIdentification("yeelight", params);
if (!window.readOnlyMode) { if (!window.readOnlyMode) {
$('#btn_wiz_save').prop('disabled', false); $('#btn_wiz_save').prop('disabled', disabled);
} }
} }
@ -1661,7 +1668,7 @@ function startWizardAtmoOrb(e) {
+ $.i18n('general_btn_save') + '</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_save') + '</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>'
+ $.i18n('general_btn_cancel') + '</button>'); + $.i18n('general_btn_cancel') + '</button>');
if (getStorage("darkMode", false) == "on") if (getStorage("darkMode") == "on")
$('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png');
//open modal //open modal
@ -1802,7 +1809,10 @@ function assign_atmoorb_lights() {
"bottom", "bottomleft", "bottomright", "bottom", "bottomleft", "bottomright",
"left", "lefttop", "leftmiddle", "leftbottom", "left", "lefttop", "leftmiddle", "leftbottom",
"right", "righttop", "rightmiddle", "rightbottom", "right", "righttop", "rightmiddle", "rightbottom",
"entire" "entire",
"lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121",
"lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11",
"lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121"
]; ];
lightOptions.unshift("disabled"); lightOptions.unshift("disabled");
@ -1865,6 +1875,8 @@ function assign_atmoorb_lights() {
} }
async function identify_atmoorb_device(orbId) { async function identify_atmoorb_device(orbId) {
var disabled = $('#btn_wiz_save').is(':disabled');
// Take care that new record cannot be save during background process // Take care that new record cannot be save during background process
$('#btn_wiz_save').prop('disabled', true); $('#btn_wiz_save').prop('disabled', true);
@ -1872,6 +1884,7 @@ async function identify_atmoorb_device(orbId) {
await requestLedDeviceIdentification("atmoorb", params); await requestLedDeviceIdentification("atmoorb", params);
if (!window.readOnlyMode) { if (!window.readOnlyMode) {
$('#btn_wiz_save').prop('disabled', false); $('#btn_wiz_save').prop('disabled', disabled);
} }
} }

View File

@ -9,7 +9,7 @@ macro(DeployMacOS TARGET)
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
) )
install(CODE "set(TARGET_FILE \"${TARGET_FILE}\") \n set(TARGET_BUNDLE_NAME \"${TARGET}.app\") \n set(PLUGIN_DIR \"${QT_PLUGIN_DIR}\")" COMPONENT "Hyperion") install(CODE "set(TARGET_FILE \"${TARGET_FILE}\") \n set(TARGET_BUNDLE_NAME \"${TARGET}.app\") \n set(PLUGIN_DIR \"${QT_PLUGIN_DIR}\") \n set(BUILD_DIR \"${CMAKE_BINARY_DIR}\")" COMPONENT "Hyperion")
install(CODE [[ install(CODE [[
file(GET_RUNTIME_DEPENDENCIES file(GET_RUNTIME_DEPENDENCIES
EXECUTABLES ${TARGET_FILE} EXECUTABLES ${TARGET_FILE}

View File

@ -0,0 +1,22 @@
# QMDNSENGINE_FOUND
# QMDNS_INCLUDE_DIR
# QMDNS_LIBRARIES
find_path(QMDNS_INCLUDE_DIR
NAMES qmdnsengine/mdns.h
PATH_SUFFIXES include
)
find_library(QMDNS_LIBRARIES
NAMES libqmdnsengine.a
PATHS /usr/local /usr
PATH_SUFFIXES lib64 lib
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(qmdnsengine
FOUND_VAR QMDNSENGINE_FOUND
REQUIRED_VARS QMDNS_INCLUDE_DIR QMDNS_LIBRARIES
)
mark_as_advanced(QMDNS_INCLUDE_DIR QMDNS_LIBRARIES)

View File

@ -20,7 +20,9 @@
"output" : "/dev/null", "output" : "/dev/null",
"colorOrder" : "rgb", "colorOrder" : "rgb",
"latchTime" : 0, "latchTime" : 0,
"rewriteTime": 0 "rewriteTime": 0,
"enableAttempts": 6,
"enableAttemptsInterval": 15
}, },
"color" : "color" :
@ -140,8 +142,8 @@
"forwarder" : "forwarder" :
{ {
"enable" : false, "enable" : false,
"json" : [], "jsonapi" : [],
"flat" : [] "flatbuffer" : []
}, },
"jsonServer" : "jsonServer" :

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.2)
project(qmdnsengine)
set(WORK_DIR "@QMDNS_WORK_DIR@")
set(SOURCE_DIR "@QMDNS_SOURCE_DIR@")
set(INSTALL_DIR "@QMDNS_INSTALL_DIR@")
set(CMAKE_ARGS "@QMDNS_CMAKE_ARGS@")
set(QMDNS_LOGGING "@QMDNS_LOGGING@")
include(ExternalProject)
ExternalProject_Add(qmdnsengine
PREFIX ${WORK_DIR}
BUILD_ALWAYS OFF
DOWNLOAD_COMMAND ""
SOURCE_DIR ${SOURCE_DIR}
INSTALL_DIR ${INSTALL_DIR}
CMAKE_ARGS ${CMAKE_ARGS}
LOG_DOWNLOAD ${QMDNS_LOGGING}
LOG_UPDATE ${QMDNS_LOGGING}
LOG_CONFIGURE ${QMDNS_LOGGING}
LOG_BUILD ${QMDNS_LOGGING}
LOG_INSTALL ${QMDNS_LOGGING}
LOG_TEST ${QMDNS_LOGGING}
)

View File

@ -14,6 +14,58 @@ if(ENABLE_DEV_WS281XPWM)
external/rpi_ws281x/rpihw.c) external/rpi_ws281x/rpihw.c)
endif() endif()
#=============================================================================
# QMdnsEngine
#=============================================================================
if (ENABLE_MDNS)
set(USE_SYSTEM_QMDNS_LIBS ${DEFAULT_USE_SYSTEM_QMDNS_LIBS} CACHE BOOL "use qmdnsengine library from system")
if (USE_SYSTEM_QMDNS_LIBS)
find_package(qmdnsengine REQUIRED)
else ()
if (NOT DEFINED BUILD_QMDNS_ONCE)
set(BUILD_QMDNS_ONCE CACHE INTERNAL "Done")
set(QMDNS_WORK_DIR "${CMAKE_BINARY_DIR}/dependencies/external/qmdnsengine")
set(QMDNS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/qmdnsengine")
set(QMDNS_INSTALL_DIR ${CMAKE_BINARY_DIR})
set(QMDNS_CMAKE_ARGS
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}
-DBIN_INSTALL_DIR:STRING=lib
-DLIB_INSTALL_DIR:STRING=lib
-DINCLUDE_INSTALL_DIR:STRING=include
-DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH}
-Wno-dev
)
if(${CMAKE_BUILD_TYPE} AND ${CMAKE_BUILD_TYPE} EQUAL "Debug")
set(QMDNS_LOGGING 1)
else ()
set(QMDNS_LOGGING 0)
endif ()
configure_file(${CMAKE_SOURCE_DIR}/dependencies/CMakeLists-qmdnsengine.txt.in ${QMDNS_WORK_DIR}/CMakeLists.txt @ONLY)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${QMDNS_WORK_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . --config "${CMAKE_BUILD_TYPE}" WORKING_DIRECTORY ${QMDNS_WORK_DIR})
endif()
set(QMDNS_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include")
if(WIN32)
set(QMDNS_LIBRARIES ${CMAKE_BINARY_DIR}/lib/qmdnsengine${CMAKE_STATIC_LIBRARY_SUFFIX})
else()
set(QMDNS_LIBRARIES ${CMAKE_BINARY_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}qmdnsengine${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
mark_as_advanced (QMDNS_INCLUDE_DIR QMDNS_LIBRARIES)
endif ()
set(QMDNS_INCLUDE_DIR ${QMDNS_INCLUDE_DIR} PARENT_SCOPE)
set(QMDNS_LIBRARIES ${QMDNS_LIBRARIES} PARENT_SCOPE)
include_directories(${QMDNS_INCLUDE_DIR})
endif()
#============================================================================= #=============================================================================
# FLATBUFFER # FLATBUFFER
#============================================================================= #=============================================================================
@ -50,12 +102,16 @@ if(ENABLE_FLATBUF_SERVER OR ENABLE_FLATBUF_CONNECT)
set ( FLATBUFFERS_FLATC_EXECUTABLE "${CMAKE_BINARY_DIR}/../build-x86x64/bin/flatc") set ( FLATBUFFERS_FLATC_EXECUTABLE "${CMAKE_BINARY_DIR}/../build-x86x64/bin/flatc")
endif() endif()
endif() endif()
set(FLATBUFFERS_FLATC_EXECUTABLE ${FLATBUFFERS_FLATC_EXECUTABLE} PARENT_SCOPE) set(FLATBUFFERS_FLATC_EXECUTABLE ${FLATBUFFERS_FLATC_EXECUTABLE} PARENT_SCOPE)
set(FLATBUFFERS_INCLUDE_DIRS ${FLATBUFFERS_INCLUDE_DIRS} PARENT_SCOPE) set(FLATBUFFERS_INCLUDE_DIRS ${FLATBUFFERS_INCLUDE_DIRS} PARENT_SCOPE)
include_directories(${FLATBUFFERS_INCLUDE_DIRS}) include_directories(${FLATBUFFERS_INCLUDE_DIRS})
if (FLATBUFFERS_INCLUDE_DIRS AND EXISTS "${FLATBUFFERS_INCLUDE_DIRS}/../package.json")
# message(STATUS "Using flatbuffers compiler: " ${FLATBUFFERS_FLATC_EXECUTABLE}) file(STRINGS "${FLATBUFFERS_INCLUDE_DIRS}/../package.json" _FLATBUFFERS_VERSION_STRING REGEX "^[ \t\r\n]+\"version\":[ \t\r\n]+\"[0-9]+.[0-9]+.[0-9]+\",")
string(REGEX REPLACE "^[ \t\r\n]+\"version\":[ \t\r\n]+\"([0-9]+.[0-9]+.[0-9]+)\"," "\\1" FLATBUFFERS_PARSE_VERSION "${_FLATBUFFERS_VERSION_STRING}")
message(STATUS "Flatbuffers version used: ${FLATBUFFERS_PARSE_VERSION}")
endif ()
function(compile_flattbuffer_schema SRC_FBS OUTPUT_DIR) function(compile_flattbuffer_schema SRC_FBS OUTPUT_DIR)
string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS}) string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS})
@ -76,11 +132,15 @@ endif()
#============================================================================= #=============================================================================
if(ENABLE_PROTOBUF_SERVER) if(ENABLE_PROTOBUF_SERVER)
set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system") set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system")
if (USE_SYSTEM_PROTO_LIBS) if (USE_SYSTEM_PROTO_LIBS)
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
if(CMAKE_VERSION VERSION_GREATER 3.5.2)
set(PROTOBUF_INCLUDE_DIRS ${Protobuf_INCLUDE_DIRS})
set(PROTOBUF_PROTOC_EXECUTABLE ${Protobuf_PROTOC_EXECUTABLE})
set(PROTOBUF_LIBRARIES ${Protobuf_LIBRARIES})
endif()
else () else ()
set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf with tests") set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf with tests")
set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "Build protobuf shared") set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "Build protobuf shared")
@ -90,26 +150,24 @@ if(ENABLE_PROTOBUF_SERVER)
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Build protobuf static") set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Build protobuf static")
endif() endif()
add_subdirectory(external/protobuf/cmake) add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/cmake")
if(CMAKE_CROSSCOMPILING) # define the include for the protobuf library
# when crosscompiling import the protoc executable targets from a file generated by a native build
option(IMPORT_PROTOC "Protoc export file (protoc_export.cmake) from a native build" "IMPORT_PROTOC-FILE_NOT_FOUND")
include(${IMPORT_PROTOC})
else()
# export the protoc compiler so it can be used when cross compiling
export(TARGETS protoc FILE "${CMAKE_BINARY_DIR}/protoc_export.cmake")
endif()
# define the include for the protobuf library at the parent scope
set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src") set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src")
# define the protoc executable at the parent scope # define the protoc executable
set(PROTOBUF_PROTOC_EXECUTABLE "$<TARGET_FILE:protoc>") set(PROTOBUF_PROTOC_EXECUTABLE protobuf::protoc)
# define the protobuf library
set(PROTOBUF_LIBRARIES protobuf::libprotobuf)
endif() endif()
set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE} PARENT_SCOPE) # redefine at parent scope
set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE) set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE)
set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE} PARENT_SCOPE)
set(PROTOBUF_LIBRARIES ${PROTOBUF_LIBRARIES} PARENT_SCOPE)
# include headers
include_directories(${PROTOBUF_INCLUDE_DIRS}) include_directories(${PROTOBUF_INCLUDE_DIRS})
# message(STATUS "Using protobuf compiler: " ${PROTOBUF_PROTOC_EXECUTABLE}) # message(STATUS "Using protobuf compiler: " ${PROTOBUF_PROTOC_EXECUTABLE})
@ -210,6 +268,7 @@ if(ENABLE_DEV_NETWORK)
endif (USE_SYSTEM_MBEDTLS_LIBS) endif (USE_SYSTEM_MBEDTLS_LIBS)
if (NOT USE_SYSTEM_MBEDTLS_LIBS) if (NOT USE_SYSTEM_MBEDTLS_LIBS)
cmake_minimum_required(VERSION 3.2)
set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF CACHE BOOL "system mbedtls libraries not found, disable use system mbedtls libraries") set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF CACHE BOOL "system mbedtls libraries not found, disable use system mbedtls libraries")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared mbedtls libraries") set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared mbedtls libraries")
@ -219,7 +278,7 @@ if(ENABLE_DEV_NETWORK)
set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Enable mbedTLS static libraries") set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Enable mbedTLS static libraries")
set(MBEDTLS_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/dependencies/external/mbedtls/download") set(MBEDTLS_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/dependencies/external/mbedtls/download")
set(MBEDTLS_SOURCE_DIR "${CMAKE_BINARY_DIR}/dependencies/external/mbedtls/src") set(MBEDTLS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/dependencies/external/mbedtls")
set(MBEDTLS_BINARY_DIR "${CMAKE_BINARY_DIR}/dependencies/external/mbedtls/build") set(MBEDTLS_BINARY_DIR "${CMAKE_BINARY_DIR}/dependencies/external/mbedtls/build")
set(MBEDTLS_INSTALL_DIR "${CMAKE_BINARY_DIR}") set(MBEDTLS_INSTALL_DIR "${CMAKE_BINARY_DIR}")
if(${CMAKE_BUILD_TYPE} AND ${CMAKE_BUILD_TYPE} EQUAL "Debug") if(${CMAKE_BUILD_TYPE} AND ${CMAKE_BUILD_TYPE} EQUAL "Debug")
@ -330,4 +389,3 @@ if(ENABLE_DEV_NETWORK)
endif (NOT USE_SYSTEM_MBEDTLS_LIBS) endif (NOT USE_SYSTEM_MBEDTLS_LIBS)
endif(ENABLE_DEV_NETWORK) endif(ENABLE_DEV_NETWORK)

1
dependencies/external/qmdnsengine vendored Submodule

@ -0,0 +1 @@
Subproject commit 0ca80117e853671d909b3cec9e2bdcac85a13b9f

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 412 KiB

View File

@ -402,13 +402,6 @@ signals:
/// ///
void onStartInstanceResponse(const int &tan); void onStartInstanceResponse(const int &tan);
private slots:
///
/// @brief Is called whenever a Hyperion instance wants the current register list
/// @param callerInstance The instance should be returned in the answer call
///
void requestActiveRegister(QObject *callerInstance);
private: private:
void stopDataConnectionss(); void stopDataConnectionss();

View File

@ -292,6 +292,12 @@ private:
/// ///
void handleInputSourceCommand(const QJsonObject& message, const QString& command, int tan); void handleInputSourceCommand(const QJsonObject& message, const QString& command, int tan);
/// Handle an incoming JSON message to request remote hyperion servers providing a given hyperion service
///
/// @param message the incoming message
///
void handleServiceCommand(const QJsonObject &message, const QString &command, int tan);
/// ///
/// Handle an incoming JSON message of unknown type /// Handle an incoming JSON message of unknown type
/// ///

View File

@ -6,10 +6,7 @@
// components def // components def
#include <utils/Components.h> #include <utils/Components.h>
// bonjour
#ifdef ENABLE_AVAHI
#include <bonjour/bonjourrecord.h>
#endif
// videModes // videModes
#include <utils/VideoMode.h> #include <utils/VideoMode.h>
// settings // settings
@ -21,7 +18,6 @@
class Hyperion; class Hyperion;
class ComponentRegister; class ComponentRegister;
class BonjourBrowserWrapper;
class PriorityMuxer; class PriorityMuxer;
class JsonCB : public QObject class JsonCB : public QObject
@ -73,13 +69,6 @@ private slots:
/// @brief handle component state changes /// @brief handle component state changes
/// ///
void handleComponentState(hyperion::Components comp, bool state); void handleComponentState(hyperion::Components comp, bool state);
#ifdef ENABLE_AVAHI
///
/// @brief handle emits from bonjour wrapper
/// @param bRegisters The full register map
///
void handleBonjourChange(const QMap<QString,BonjourRecord>& bRegisters);
#endif
/// ///
/// @brief handle emits from PriorityMuxer /// @brief handle emits from PriorityMuxer
@ -140,10 +129,7 @@ private:
Hyperion* _hyperion; Hyperion* _hyperion;
/// pointer of comp register /// pointer of comp register
ComponentRegister* _componentRegister; ComponentRegister* _componentRegister;
#ifdef ENABLE_AVAHI
/// Bonjour instance
BonjourBrowserWrapper* _bonjour;
#endif
/// priority muxer instance /// priority muxer instance
PriorityMuxer* _prioMuxer; PriorityMuxer* _prioMuxer;
/// contains all available commands /// contains all available commands

View File

@ -1,68 +0,0 @@
#pragma once
// qt incl
#include <QObject>
#include <QMap>
#include <QHostInfo>
#include <bonjour/bonjourrecord.h>
class BonjourServiceBrowser;
class BonjourServiceResolver;
class QTimer;
class BonjourBrowserWrapper : public QObject
{
Q_OBJECT
private:
friend class HyperionDaemon;
///
/// @brief Browse for hyperion services in bonjour, constructed from HyperionDaemon
/// Searching for hyperion http service by default
///
BonjourBrowserWrapper(QObject * parent = nullptr);
public:
///
/// @brief Browse for a service
///
bool browseForServiceType(const QString &serviceType);
///
/// @brief Get all available sessions
///
QMap<QString, BonjourRecord> getAllServices() { return _hyperionSessions; }
static BonjourBrowserWrapper* instance;
static BonjourBrowserWrapper *getInstance() { return instance; }
signals:
///
/// @brief Emits whenever a change happend
///
void browserChange( const QMap<QString, BonjourRecord> &bRegisters );
private:
/// map of service names and browsers
QMap<QString, BonjourServiceBrowser *> _browsedServices;
/// Resolver
BonjourServiceResolver *_bonjourResolver;
// contains all current active service sessions
QMap<QString, BonjourRecord> _hyperionSessions;
QString _bonjourCurrentServiceToResolve;
/// timer to resolve changes
QTimer *_timerBonjourResolver;
private slots:
///
/// @brief is called whenever a BonjourServiceBrowser emits change
void currentBonjourRecordsChanged( const QList<BonjourRecord> &list );
/// @brief new record resolved
void bonjourRecordResolved( const QHostInfo &hostInfo, int port );
///
/// @brief timer slot which updates regularly entries
///
void bonjourResolve();
};

View File

@ -1,71 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BONJOURRECORD_H
#define BONJOURRECORD_H
#include <QtCore/QMetaType>
#include <QtCore/QString>
class BonjourRecord
{
public:
BonjourRecord() : port(-1) {}
BonjourRecord(const QString &name, const QString &regType, const QString &domain)
: serviceName(name)
, registeredType(regType)
, replyDomain(domain)
, port(-1)
{}
BonjourRecord(const char *name, const char *regType, const char *domain)
: serviceName(QString::fromUtf8(name))
, registeredType(QString::fromUtf8(regType))
, replyDomain(QString::fromUtf8(domain))
, port(-1)
{
}
QString serviceName;
QString registeredType;
QString replyDomain;
QString hostName;
QString address;
int port;
bool operator==(const BonjourRecord &other) const
{
return serviceName == other.serviceName
&& registeredType == other.registeredType
&& replyDomain == other.replyDomain;
}
};
Q_DECLARE_METATYPE(BonjourRecord)
#endif // BONJOURRECORD_H

View File

@ -1,69 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BONJOURSERVICEBROWSER_H
#define BONJOURSERVICEBROWSER_H
#include <QtCore/QObject>
#ifndef PLATFORM_AMLOGIC
#include <dns_sd.h>
#else
#include <avahi-compat-libdns_sd/dns_sd.h>
#endif
#include "bonjour/bonjourrecord.h"
class QSocketNotifier;
class BonjourServiceBrowser : public QObject
{
Q_OBJECT
public:
BonjourServiceBrowser(QObject *parent = 0);
~BonjourServiceBrowser() override;
void browseForServiceType(const QString &serviceType);
inline QList<BonjourRecord> currentRecords() const { return bonjourRecords; }
inline QString serviceType() const { return browsingType; }
signals:
void currentBonjourRecordsChanged(const QList<BonjourRecord> &list);
void error(DNSServiceErrorType err);
private slots:
void bonjourSocketReadyRead();
private:
static void DNSSD_API bonjourBrowseReply(DNSServiceRef , DNSServiceFlags flags, quint32,
DNSServiceErrorType errorCode, const char *serviceName,
const char *regType, const char *replyDomain, void *context);
DNSServiceRef dnssref;
QSocketNotifier *bonjourSocket;
QList<BonjourRecord> bonjourRecords;
QString browsingType;
};
#endif // BONJOURSERVICEBROWSER_H

View File

@ -1,76 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BONJOURSERVICEREGISTER_H
#define BONJOURSERVICEREGISTER_H
#include <QtCore/QObject>
#include "bonjourrecord.h"
class QSocketNotifier;
#ifndef PLATFORM_AMLOGIC
#include <dns_sd.h>
#else
#include <avahi-compat-libdns_sd/dns_sd.h>
#endif
class BonjourServiceRegister : public QObject
{
Q_OBJECT
public:
BonjourServiceRegister(QObject *parent = 0);
~BonjourServiceRegister() override;
void registerService(const QString& service, int port);
void registerService(const BonjourRecord &record, quint16 servicePort, const std::vector<std::pair<std::string, std::string>>& txt = {});
inline BonjourRecord registeredRecord() const { return finalRecord; }
quint16 getPort() const { return _port; }
signals:
void error(DNSServiceErrorType error);
void serviceRegistered(const BonjourRecord &record);
private slots:
void bonjourSocketReadyRead();
private:
static void DNSSD_API bonjourRegisterService(DNSServiceRef sdRef, DNSServiceFlags,
DNSServiceErrorType errorCode, const char *name,
const char *regtype, const char *domain,
void *context);
DNSServiceRef dnssref;
QSocketNotifier *bonjourSocket;
BonjourRecord finalRecord;
// current port
quint16 _port = 0;
};
#endif // BONJOURSERVICEREGISTER_H

View File

@ -1,71 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BONJOURSERVICERESOLVER_H
#define BONJOURSERVICERESOLVER_H
#include <QtCore/QObject>
#ifndef PLATFORM_AMLOGIC
#include <dns_sd.h>
#else
#include <avahi-compat-libdns_sd/dns_sd.h>
#endif
class QSocketNotifier;
class QHostInfo;
class BonjourRecord;
class BonjourServiceResolver : public QObject
{
Q_OBJECT
public:
BonjourServiceResolver(QObject *parent);
~BonjourServiceResolver() override;
bool resolveBonjourRecord(const BonjourRecord &record);
signals:
void bonjourRecordResolved(const QHostInfo &hostInfo, int port);
void error(DNSServiceErrorType error);
private slots:
void bonjourSocketReadyRead();
void cleanupResolve();
void finishConnect(const QHostInfo &hostInfo);
private:
static void DNSSD_API bonjourResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags,
quint32 interfaceIndex, DNSServiceErrorType errorCode,
const char *fullName, const char *hosttarget, quint16 port,
quint16 txtLen, const char *txtRecord, void *context);
DNSServiceRef dnssref;
QSocketNotifier *bonjourSocket;
int bonjourPort;
};
#endif // BONJOURSERVICERESOLVER_H

View File

@ -7,7 +7,6 @@
// qt // qt
#include <QVector> #include <QVector>
class BonjourServiceRegister;
class QTcpServer; class QTcpServer;
class FlatBufferClient; class FlatBufferClient;
class NetOrigin; class NetOrigin;
@ -20,6 +19,7 @@ class NetOrigin;
class FlatBufferServer : public QObject class FlatBufferServer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
FlatBufferServer(const QJsonDocument& config, QObject* parent = nullptr); FlatBufferServer(const QJsonDocument& config, QObject* parent = nullptr);
~FlatBufferServer() override; ~FlatBufferServer() override;
@ -34,6 +34,12 @@ public slots:
void initServer(); void initServer();
signals:
///
/// @emits whenever the server would like to announce its service details
///
void publishService(const QString& serviceType, quint16 servicePort, const QByteArray& serviceName = "");
private slots: private slots:
/// ///
/// @brief Is called whenever a new socket wants to connect /// @brief Is called whenever a new socket wants to connect
@ -64,7 +70,6 @@ private:
int _timeout; int _timeout;
quint16 _port; quint16 _port;
const QJsonDocument _config; const QJsonDocument _config;
BonjourServiceRegister * _serviceRegister = nullptr;
QVector<FlatBufferClient*> _openConnections; QVector<FlatBufferClient*> _openConnections;
}; };

View File

@ -28,6 +28,17 @@
class Hyperion; class Hyperion;
class QTcpSocket; class QTcpSocket;
class FlatBufferConnection; class FlatBufferConnection;
class MessageForwarderFlatbufferClientsHelper;
struct TargetHost {
QHostAddress host;
quint16 port;
bool operator == (TargetHost const& a) const
{
return ((host == a.host) && (port == a.port));
}
};
class MessageForwarder : public QObject class MessageForwarder : public QObject
{ {
@ -39,14 +50,15 @@ public:
void addJsonTarget(const QJsonObject& targetConfig); void addJsonTarget(const QJsonObject& targetConfig);
void addFlatbufferTarget(const QJsonObject& targetConfig); void addFlatbufferTarget(const QJsonObject& targetConfig);
private slots: public slots:
/// ///
/// @brief Handle settings update from Hyperion Settingsmanager emit or this constructor /// @brief Handle settings update from Hyperion Settingsmanager emit or this constructor
/// @param type settingyType from enum /// @param type settingsType from enum
/// @param config configuration object /// @param config configuration object
/// ///
void handleSettingsUpdate(settings::type type, const QJsonDocument &config); void handleSettingsUpdate(settings::type type, const QJsonDocument &config);
private slots:
/// ///
/// @brief Handle component state change MessageForwarder /// @brief Handle component state change MessageForwarder
/// @param component The component from enum /// @param component The component from enum
@ -81,15 +93,13 @@ private slots:
private: private:
struct TargetHost { void enableTargets(bool enable, const QJsonObject& config);
QHostAddress host;
quint16 port;
bool operator == (TargetHost const& a) const int startJsonTargets(const QJsonObject& config);
{ void stopJsonTargets();
return ((host == a.host) && (port == a.port));
} int startFlatbufferTargets(const QJsonObject& config);
}; void stopFlatbufferTargets();
/// Hyperion instance /// Hyperion instance
Hyperion *_hyperion; Hyperion *_hyperion;
@ -105,10 +115,37 @@ private:
/// Flatbuffer connection for forwarding /// Flatbuffer connection for forwarding
QList<TargetHost> _flatbufferTargets; QList<TargetHost> _flatbufferTargets;
QList<FlatBufferConnection*> _forwardClients;
/// Flag if forwarder is enabled /// Flag if forwarder is enabled
bool _forwarder_enabled = true; bool _forwarder_enabled = true;
const int _priority; const int _priority;
MessageForwarderFlatbufferClientsHelper* _messageForwarderFlatBufHelper;
};
class MessageForwarderFlatbufferClientsHelper : public QObject
{
Q_OBJECT
public:
MessageForwarderFlatbufferClientsHelper();
~MessageForwarderFlatbufferClientsHelper();
signals:
void addClient(const QString& origin, const TargetHost& targetHost, int priority, bool skipReply);
void clearClients();
public slots:
bool isFree() const;
void forwardImage(const Image<ColorRgb>& image);
void addClientHandler(const QString& origin, const TargetHost& targetHost, int priority, bool skipReply);
void clearClientsHandler();
private:
QList<FlatBufferConnection*> _forwardClients;
bool _free;
}; };

View File

@ -22,6 +22,10 @@
#include <sys/ipc.h> #include <sys/ipc.h>
#include <sys/shm.h> #include <sys/shm.h>
#ifdef Bool
#undef Bool
#endif
class X11Grabber : public Grabber , public QAbstractNativeEventFilter class X11Grabber : public Grabber , public QAbstractNativeEventFilter
{ {
public: public:

View File

@ -11,7 +11,6 @@
class QTcpServer; class QTcpServer;
class QTcpSocket; class QTcpSocket;
class JsonClientConnection; class JsonClientConnection;
class BonjourServiceRegister;
class NetOrigin; class NetOrigin;
/// ///
@ -31,11 +30,18 @@ public:
JsonServer(const QJsonDocument& config); JsonServer(const QJsonDocument& config);
~JsonServer() override; ~JsonServer() override;
void initServer();
/// ///
/// @return the port number on which this TCP listens for incoming connections /// @return the port number on which this TCP listens for incoming connections
/// ///
uint16_t getPort() const; uint16_t getPort() const;
signals:
///
/// @emits whenever the server would like to announce its service details
///
void publishService(const QString& serviceType, quint16 servicePort, const QByteArray& serviceName = "");
private slots: private slots:
/// ///
@ -71,7 +77,7 @@ private:
/// port /// port
uint16_t _port = 0; uint16_t _port = 0;
BonjourServiceRegister * _serviceRegister = nullptr; const QJsonDocument _config;
void start(); void start();
void stop(); void stop();

View File

@ -1,4 +1,4 @@
#ifndef LEDEVICE_H #ifndef LEDEVICE_H
#define LEDEVICE_H #define LEDEVICE_H
// qt includes // qt includes
@ -14,6 +14,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <algorithm> #include <algorithm>
#include <chrono>
// Utility includes // Utility includes
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
@ -50,6 +51,13 @@ public:
/// ///
~LedDevice() override; ~LedDevice() override;
///
/// @brief Set the common logger for LED-devices.
///
/// @param[in] log The logger to be used
///
void setLogger(Logger* log);
/// ///
/// @brief Set the current active LED-device type. /// @brief Set the current active LED-device type.
/// ///
@ -64,6 +72,8 @@ public:
/// ///
void setLedCount(int ledCount); void setLedCount(int ledCount);
void setColorOrder(const QString& colorOrder);
/// ///
/// @brief Set a device's latch time. /// @brief Set a device's latch time.
/// ///
@ -83,6 +93,19 @@ public:
/// ///
void setRewriteTime(int rewriteTime_ms); void setRewriteTime(int rewriteTime_ms);
/// @brief Set a device's enablement cycle's parameters.
///
/// @param[in] maxEnableRetries Maximum number of attempts to enable a device, if reached retries will be stopped
/// @param[in] enableAttemptsTimerInterval Interval in seconds between two enablement attempts
///
void setEnableAttempts(int maxEnablAttempts, std::chrono::seconds enableAttemptsTimerInterval);
/// @brief Enable a device automatically after Hyperion startup or not
///
/// @param[in] isAutoStart
///
void setAutoStart(bool isAutoStart);
/// ///
/// @brief Discover devices of this type available (for configuration). /// @brief Discover devices of this type available (for configuration).
/// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways. /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
@ -120,6 +143,15 @@ public:
/// ///
virtual void identify(const QJsonObject& /*params*/) {} virtual void identify(const QJsonObject& /*params*/) {}
///
/// @brief Add an authorization/client-key or token to the device
///
/// Used in context of a set of devices of the same type.
///
/// @param[in] params Parameters to address device
/// @return A JSON structure holding the authorization key/token
virtual QJsonObject addAuthorization(const QJsonObject& /*params*/) { return QJsonObject(); }
/// ///
/// @brief Check, if device is properly initialised /// @brief Check, if device is properly initialised
/// ///
@ -127,7 +159,7 @@ public:
/// ///
/// @return True, if device is initialised /// @return True, if device is initialised
/// ///
bool isInitialised() const { return _isDeviceInitialised; } bool isInitialised() const;
/// ///
/// @brief Check, if device is ready to be used. /// @brief Check, if device is ready to be used.
@ -136,14 +168,14 @@ public:
/// ///
/// @return True, if device is ready /// @return True, if device is ready
/// ///
bool isReady() const { return _isDeviceReady; } bool isReady() const;
/// ///
/// @brief Check, if device is in error state. /// @brief Check, if device is in error state.
/// ///
/// @return True, if device is in error /// @return True, if device is in error
/// ///
bool isInError() const { return _isDeviceInError; } bool isInError() const;
/// ///
/// @brief Prints the color values to stdout. /// @brief Prints the color values to stdout.
@ -152,13 +184,6 @@ public:
/// ///
static void printLedValues(const std::vector<ColorRgb>& ledValues); static void printLedValues(const std::vector<ColorRgb>& ledValues);
///
/// @brief Set the common logger for LED-devices.
///
/// @param[in] log The logger to be used
///
void setLogger(Logger* log) { _log = log; }
public slots: public slots:
/// ///
@ -181,47 +206,47 @@ public slots:
/// @param[in] ledValues The color per LED /// @param[in] ledValues The color per LED
/// @return Zero on success else negative (i.e. device is not ready) /// @return Zero on success else negative (i.e. device is not ready)
/// ///
virtual int updateLeds(const std::vector<ColorRgb>& ledValues); virtual int updateLeds(std::vector<ColorRgb> ledValues);
/// ///
/// @brief Get the currently defined LatchTime. /// @brief Get the currently defined LatchTime.
/// ///
/// @return Latch time in milliseconds /// @return Latch time in milliseconds
/// ///
int getLatchTime() const { return _latchTime_ms; } int getLatchTime() const;
/// ///
/// @brief Get the currently defined RewriteTime. /// @brief Get the currently defined RewriteTime.
/// ///
/// @return Rewrite time in milliseconds /// @return Rewrite time in milliseconds
/// ///
int getRewriteTime() const { return _refreshTimerInterval_ms; } int getRewriteTime() const;
/// ///
/// @brief Get the number of LEDs supported by the device. /// @brief Get the number of LEDs supported by the device.
/// ///
/// @return Number of device's LEDs, 0 = unknown number /// @return Number of device's LEDs, 0 = unknown number
/// ///
int getLedCount() const { return _ledCount; } int getLedCount() const;
/// ///
/// @brief Get the current active LED-device type. /// @brief Get the current active LED-device type.
/// ///
QString getActiveDeviceType() const { return _activeDeviceType; } QString getActiveDeviceType() const;
/// ///
/// @brief Get color order of device. /// @brief Get color order of device.
/// ///
/// @return The color order /// @return The color order
/// ///
QString getColorOrder() const { return _colorOrder; } QString getColorOrder() const;
/// ///
/// @brief Get the LED-Device component's state. /// @brief Get the LED-Device component's state.
/// ///
/// @return True, if enabled /// @return True, if enabled
/// ///
inline bool componentState() const { return _isEnabled; } bool componentState() const;
/// ///
/// @brief Enables the device for output. /// @brief Enables the device for output.
@ -257,11 +282,6 @@ public slots:
/// ///
virtual bool switchOff(); virtual bool switchOff();
bool switchOnOff(bool onState)
{
return onState == true ? switchOn() : switchOff();
}
signals: signals:
/// ///
/// @brief Emits whenever the LED-Device switches between on/off. /// @brief Emits whenever the LED-Device switches between on/off.
@ -361,6 +381,16 @@ protected:
/// ///
virtual bool restoreState(); virtual bool restoreState();
///
/// @brief Start a new enable cycle
///
void startEnableAttemptsTimer();
///
/// @brief Stop a new enable cycle
///
void stopEnableAttemptsTimer();
/// ///
/// @brief Converts an uint8_t array to hex string. /// @brief Converts an uint8_t array to hex string.
/// ///
@ -368,7 +398,7 @@ protected:
/// @param size of the array /// @param size of the array
/// @param number Number of array items to be converted. /// @param number Number of array items to be converted.
/// @return array as string of hex values /// @return array as string of hex values
QString uint8_t_to_hex_string(const uint8_t * data, const int size, int number = -1) const; static QString uint8_t_to_hex_string(const uint8_t * data, const int size, int number = -1) ;
/// ///
/// @brief Converts a ByteArray to hex string. /// @brief Converts a ByteArray to hex string.
@ -376,7 +406,7 @@ protected:
/// @param data ByteArray /// @param data ByteArray
/// @param number Number of array items to be converted. /// @param number Number of array items to be converted.
/// @return array as string of hex values /// @return array as string of hex values
QString toHex(const QByteArray& data, int number = -1) const; static QString toHex(const QByteArray& data, int number = -1) ;
/// Current device's type /// Current device's type
QString _activeDeviceType; QString _activeDeviceType;
@ -414,6 +444,7 @@ protected:
QJsonObject _orignalStateValues; QJsonObject _orignalStateValues;
// Device states // Device states
/// Is the device enabled? /// Is the device enabled?
bool _isEnabled; bool _isEnabled;
@ -429,9 +460,6 @@ protected:
/// Is the device in error state and stopped? /// Is the device in error state and stopped?
bool _isDeviceInError; bool _isDeviceInError;
/// Is the device in the switchOff process?
bool _isInSwitchOff;
/// Timestamp of last write /// Timestamp of last write
QDateTime _lastWriteTime; QDateTime _lastWriteTime;
@ -459,6 +487,15 @@ private:
/// @brief Stop refresh cycle /// @brief Stop refresh cycle
void stopRefreshTimer(); void stopRefreshTimer();
/// Timer that enables a device (used to retry enablement, if enabled failed before)
QTimer* _enableAttemptsTimer;
// Device configuration parameters
std::chrono::seconds _enableAttemptTimerInterval;
int _enableAttempts;
int _maxEnableAttempts;
/// Is last write refreshing enabled? /// Is last write refreshing enabled?
bool _isRefreshEnabled; bool _isRefreshEnabled;

View File

@ -94,16 +94,6 @@ signals:
/// ///
int updateLeds(const std::vector<ColorRgb>& ledValues); int updateLeds(const std::vector<ColorRgb>& ledValues);
///
/// @brief Enables the LED-Device.
///
void enable();
///
/// @brief Disables the LED-Device.
///
void disable();
/// ///
/// @brief Switch the LEDs on. /// @brief Switch the LEDs on.
/// ///

170
include/mdns/MdnsBrowser.h Normal file
View File

@ -0,0 +1,170 @@
#ifndef MDNS_BROWSER_H
#define MDNS_BROWSER_H
#include <chrono>
#include <type_traits>
#include <qmdnsengine/server.h>
#include <qmdnsengine/service.h>
#include <qmdnsengine/browser.h>
#include <qmdnsengine/cache.h>
#include <qmdnsengine/resolver.h>
#include <qmdnsengine/dns.h>
#include <qmdnsengine/record.h>
// Qt includes
#include <QObject>
#include <QByteArray>
// Utility includes
#include <utils/Logger.h>
namespace {
constexpr std::chrono::milliseconds DEFAULT_DISCOVER_TIMEOUT{ 500 };
constexpr std::chrono::milliseconds DEFAULT_ADDRESS_RESOLVE_TIMEOUT{ 1000 };
} //End of constants
class MdnsBrowser : public QObject
{
Q_OBJECT
// Run MdnsBrowser as singleton
private:
///
/// @brief Browse for hyperion services in bonjour, constructed from HyperionDaemon
/// Searching for hyperion http service by default
///
// Run MdnsBrowser as singleton
MdnsBrowser(QObject* parent = nullptr);
~MdnsBrowser() override;
public:
static MdnsBrowser& getInstance()
{
static MdnsBrowser* instance = new MdnsBrowser();
return *instance;
}
MdnsBrowser(const MdnsBrowser&) = delete;
MdnsBrowser(MdnsBrowser&&) = delete;
MdnsBrowser& operator=(const MdnsBrowser&) = delete;
MdnsBrowser& operator=(MdnsBrowser&&) = delete;
QMdnsEngine::Service getFirstService(const QByteArray& serviceType, const QString& filter = ".*", const std::chrono::milliseconds waitTime = DEFAULT_DISCOVER_TIMEOUT) const;
QJsonArray getServicesDiscoveredJson(const QByteArray& serviceType, const QString& filter = ".*", const std::chrono::milliseconds waitTime = std::chrono::milliseconds{ 0 }) const;
void printCache(const QByteArray& name = QByteArray(), quint16 type = QMdnsEngine::ANY) const;
public slots:
///
/// @brief Browse for a service of type
///
void browseForServiceType(const QByteArray& serviceType);
QHostAddress getHostFirstAddress(const QByteArray& hostname);
void onHostNameResolved(const QHostAddress& address);
QMdnsEngine::Record getServiceInstanceRecord(const QByteArray& serviceInstance, const std::chrono::milliseconds waitTime = DEFAULT_DISCOVER_TIMEOUT) const;
bool resolveAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, std::chrono::milliseconds timeout = DEFAULT_ADDRESS_RESOLVE_TIMEOUT);
Q_SIGNALS:
/**
* @brief Indicate that the specified service was updated
*
* This signal is emitted when the SRV record for a service (identified by
* its name and type) or a TXT record has changed.
*/
void serviceFound(const QMdnsEngine::Service& service);
/**
* @brief Indicate that the specified service was removed
*
* This signal is emitted when an essential record (PTR or SRV) is
* expiring from the cache. This will also occur when an updated PTR or
* SRV record is received with a TTL of 0.
*/
void serviceRemoved(const QMdnsEngine::Service& service);
void addressResolved(const QHostAddress address);
private slots:
void onServiceAdded(const QMdnsEngine::Service& service);
void onServiceUpdated(const QMdnsEngine::Service& service);
void onServiceRemoved(const QMdnsEngine::Service& service);
private:
// template <typename Func1, typename Func2, typename std::enable_if_t<std::is_member_pointer<Func2>::value, int> = 0>
// static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
// Func1 signal,
// typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
// Func2 slot)
// {
// QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
// QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
// *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
// QObject::disconnect(conn_normal);
// QObject::disconnect(*conn_delete);
// delete conn_delete;
// });
// return conn_normal;
// }
template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
Func1 signal,
Func2 slot)
{
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
QObject::disconnect(conn_normal);
QObject::disconnect(*conn_delete);
delete conn_delete;
});
return conn_normal;
}
// template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
// static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
// Func1 signal,
// typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
// Func2 slot)
// {
// Q_UNUSED(receiver);
// QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
// QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
// *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
// QObject::disconnect(conn_normal);
// QObject::disconnect(*conn_delete);
// delete conn_delete;
// });
// return conn_normal;
// }
/// The logger instance for mDNS-Service
Logger* _log;
QMdnsEngine::Server _server;
QMdnsEngine::Cache _cache;
QMap<QByteArray, QMdnsEngine::Browser*> _browsedServiceTypes;
};
#endif // MDNSBROWSER_H

View File

@ -0,0 +1,51 @@
#ifndef MDNSPROVIDER_H
#define MDNSPROVIDER_H
#include <qmdnsengine/server.h>
#include <qmdnsengine/hostname.h>
#include <qmdnsengine/provider.h>
#include <qmdnsengine/service.h>
// Qt includes
#include <QObject>
#include <QByteArray>
// Utility includes
#include <utils/Logger.h>
class MdnsProvider : public QObject
{
public:
MdnsProvider(QObject* parent = nullptr);
~MdnsProvider() override;
QList<QByteArray> getServiceTypesProvided() const { return _providedServiceTypes.keys(); }
public slots:
///
/// @brief Init MdnsProvider after thread start
///
void init();
void publishService (const QString& serviceType, quint16 servicePort, const QByteArray& serviceName = "");
private slots:
void onHostnameChanged(const QByteArray& hostname);
private:
/// The logger instance for mDNS-Service
Logger* _log;
QMdnsEngine::Server* _server;
QMdnsEngine::Hostname* _hostname;
/// map of services provided
QMap<QByteArray, QMdnsEngine::Provider*> _providedServiceTypes;
};
#endif // MDNSPROVIDER_H

View File

@ -0,0 +1,38 @@
#ifndef MDNSSERVICEREGISTER_H
#define MDNSSERVICEREGISTER_H
#include <QByteArray>
#include <QMap>
struct mdnsConfig
{
QByteArray serviceType;
QString serviceNameFilter;
};
typedef QMap<QString, mdnsConfig> MdnsServiceMap;
const MdnsServiceMap mDnsServiceMap = {
//Hyperion
{"jsonapi" , {"_hyperiond-json._tcp.local.", ".*"}},
{"flatbuffer" , {"_hyperiond-flatbuf._tcp.local.", ".*"}},
{"protobuffer" , {"_hyperiond-protobuf._tcp.local.", ".*"}},
{"http" , {"_http._tcp.local.", ".*"}},
{"https" , {"_https._tcp.local.", ".*"}},
//LED Devices
{"cololight" , {"_hap._tcp.local.", "ColoLight.*"}},
{"nanoleaf" , {"_nanoleafapi._tcp.local.", ".*"}},
{"philipshue" , {"_hue._tcp.local.", ".*"}},
{"wled" , {"_wled._tcp.local.", ".*"}},
{"yeelight" , {"_hap._tcp.local.", "Yeelight.*|YLBulb.*"}},
};
class MdnsServiceRegister {
public:
static QByteArray getServiceType(const QString &serviceType) { return mDnsServiceMap[serviceType].serviceType; }
static QString getServiceNameFilter(const QString &serviceType) { return mDnsServiceMap[serviceType].serviceNameFilter; }
static MdnsServiceMap getAllConfigs () { return mDnsServiceMap; }
};
#endif // MDNSSERVICEREGISTER_H

View File

@ -24,6 +24,12 @@ public:
ProtoServer(const QJsonDocument& config, QObject* parent = nullptr); ProtoServer(const QJsonDocument& config, QObject* parent = nullptr);
~ProtoServer() override; ~ProtoServer() override;
signals:
///
/// @emits whenever the server would like to announce its service details
///
void publishService(const QString& serviceType, quint16 servicePort, const QByteArray& serviceName = "");
public slots: public slots:
/// ///
/// @brief Handle settings update /// @brief Handle settings update

View File

@ -31,6 +31,30 @@ struct ColorRgb
static const ColorRgb YELLOW; static const ColorRgb YELLOW;
/// 'White' RgbColor (255, 255, 255) /// 'White' RgbColor (255, 255, 255)
static const ColorRgb WHITE; static const ColorRgb WHITE;
ColorRgb() = default;
ColorRgb(uint8_t _red, uint8_t _green,uint8_t _blue):
red(_red),
green(_green),
blue(_blue)
{
}
ColorRgb operator-(const ColorRgb& b) const
{
ColorRgb a(*this);
a.red -= b.red;
a.green -= b.green;
a.blue -= b.blue;
return a;
}
QString toQString() const
{
return QString("(%1,%2,%3)").arg(red).arg(green).arg(blue);
}
}; };
/// Assert to ensure that the size of the structure is 'only' 3 bytes /// Assert to ensure that the size of the structure is 'only' 3 bytes

View File

@ -1,12 +1,17 @@
#pragma once #pragma once
#include <utils/Logger.h>
#include <QTcpServer> #include <QTcpServer>
#include <QUrl> #include <QUrl>
#include <QHostAddress> #include <QHostAddress>
#include <QHostInfo> #include <QHostInfo>
#include <HyperionConfig.h>
#include <utils/Logger.h>
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#endif
namespace NetUtils { namespace NetUtils {
const int MAX_PORT = 65535; const int MAX_PORT = 65535;
@ -38,15 +43,15 @@ namespace NetUtils {
/// ///
/// @brief Check if the port is in the valid range /// @brief Check if the port is in the valid range
/// @param log The logger of the caller to print/// /// @param log The logger of the caller to print///
/// @param[in] port The port to be tested /// @param[in] port The port to be tested (port = -1 is ignored for testing)
/// @param[in] host A hostname/IP-address to make reference to during logging /// @param[in] host A hostname/IP-address to make reference to during logging
/// @return True on success else false /// @return True on success else false
/// ///
inline bool isValidPort(Logger* log, int port, const QString& host) inline bool isValidPort(Logger* log, int port, const QString& host)
{ {
if (port <= 0 || port > MAX_PORT) if ((port <= 0 || port > MAX_PORT) && port != -1)
{ {
Error(log, "Invalid port [%d] for host: %s!", port, QSTRING_CSTR(host)); Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [0 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
return false; return false;
} }
return true; return true;
@ -59,7 +64,7 @@ namespace NetUtils {
/// @param[in/out] port The resolved port, if available. /// @param[in/out] port The resolved port, if available.
/// @return True on success else false /// @return True on success else false
/// ///
inline bool resolveHostPort(const QString& address, QString& host, quint16& port) inline bool resolveHostPort(const QString& address, QString& host, int& port)
{ {
if (address.isEmpty()) if (address.isEmpty())
{ {
@ -91,38 +96,109 @@ namespace NetUtils {
} }
/// ///
/// @brief Check if the port is in the valid range /// @brief Resolve a hostname (DNS/mDNS) into an IP-address. A given IP address will be passed through
/// @param log The logger of the caller to print /// @param[in/out] log The logger of the caller to print
/// @param[in] address The port to be tested /// @param[in] hostname The hostname to be resolved
/// @param[out] hostAddress A hostname to make reference to during logging /// @param[out] hostAddress The resolved IP-Address
/// @return True on success else false /// @return True on success else false
/// ///
inline bool resolveMdDnsHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress)
inline bool resolveHostAddress(Logger* log, const QString& address, QHostAddress& hostAddress)
{ {
bool isHostAddressOK{ false }; bool isHostAddressOK{ false };
if (!hostname.isEmpty())
if (hostAddress.setAddress(address))
{ {
Debug(log, "Successfully parsed %s as an IP-address.", QSTRING_CSTR(hostAddress.toString())); #ifdef ENABLE_MDNS
if (hostname.endsWith(".local") || hostname.endsWith(".local."))
{
QHostAddress resolvedAddress;
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "resolveAddress",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, isHostAddressOK),
Q_ARG(Logger*, log), Q_ARG(QString, hostname), Q_ARG(QHostAddress&, resolvedAddress));
hostAddress = resolvedAddress;
}
else
#endif
{
if (hostAddress.setAddress(hostname))
{
//Debug(log, "IP-address (%s) not required to be resolved.", QSTRING_CSTR(hostAddress.toString()));
isHostAddressOK = true; isHostAddressOK = true;
} }
else else
{ {
QHostInfo hostInfo = QHostInfo::fromName(address); QHostInfo hostInfo = QHostInfo::fromName(hostname);
if (hostInfo.error() == QHostInfo::NoError) if (hostInfo.error() == QHostInfo::NoError)
{ {
hostAddress = hostInfo.addresses().first(); hostAddress = hostInfo.addresses().at(0);
Debug(log, "Successfully resolved IP-address (%s) for hostname (%s).", QSTRING_CSTR(hostAddress.toString()), QSTRING_CSTR(address)); Debug(log, "Successfully resolved hostname (%s) to IP-address (%s)", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
isHostAddressOK = true; isHostAddressOK = true;
} }
else else
{ {
QString errortext = QString("Failed resolving IP-address for [%1], (%2) %3").arg(address).arg(hostInfo.error()).arg(hostInfo.errorString()); QString errortext = QString("Failed resolving hostname (%1) to IP-address. Error: (%2) %3").arg(hostname).arg(hostInfo.error()).arg(hostInfo.errorString());
Error(log, "%s", QSTRING_CSTR(errortext)); Error(log, "%s", QSTRING_CSTR(errortext));
isHostAddressOK = false; isHostAddressOK = false;
} }
} }
}
}
return isHostAddressOK; return isHostAddressOK;
} }
}
///
/// @brief Resolve a hostname(DNS) or mDNS service name into an IP-address. A given IP address will be passed through
/// @param[in/out] log The logger of the caller to print
/// @param[in] hostname The hostname/mDNS service name to be resolved
/// @param[out] hostAddress The resolved IP-Address
/// @param[in/out] port The port provided by the mDNS service, if not mDNS the input port is returned
/// @return True on success else false
///
inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, int& port)
{
bool areHostAddressPartOK{ false };
QString target {hostname};
#ifdef ENABLE_MDNS
if (hostname.endsWith("._tcp.local"))
{
//Treat hostname as service instance name that requires to be resolved into an mDNS-Hostname first
QMdnsEngine::Record service = MdnsBrowser::getInstance().getServiceInstanceRecord(hostname.toUtf8());
if (!service.target().isEmpty())
{
Info(log, "Resolved service [%s] to mDNS hostname [%s], service port [%d]", QSTRING_CSTR(hostname), service.target().constData(), service.port());
target = service.target();
port = service.port();
}
else
{
Error(log, "Cannot resolve mDNS hostname for given service [%s]!", QSTRING_CSTR(hostname));
return areHostAddressPartOK;
}
}
#endif
QHostAddress resolvedAddress;
if (NetUtils::resolveMdDnsHostToAddress(log, target, resolvedAddress))
{
hostAddress = resolvedAddress;
if (hostname != hostAddress.toString())
{
Info(log, "Resolved hostname (%s) to IP-address (%s)", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
}
if (NetUtils::isValidPort(log, port, hostAddress.toString()))
{
areHostAddressPartOK = true;
}
}
return areHostAddressPartOK;
}
inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress)
{
int ignoredPort {MAX_PORT};
return resolveHostToAddress(log, hostname, hostAddress, ignoredPort);
}
}

View File

@ -11,7 +11,6 @@
// settings // settings
#include <utils/settings.h> #include <utils/settings.h>
class BonjourServiceRegister;
class StaticFileServing; class StaticFileServing;
class QtHttpServer; class QtHttpServer;
@ -52,6 +51,11 @@ signals:
/// ///
void portChanged(quint16 port); void portChanged(quint16 port);
///
/// @emits whenever the server would like to announce its service details
///
void publishService(const QString& serviceType, quint16 servicePort, const QByteArray& serviceName = "");
public slots: public slots:
/// ///
/// @brief Init server after thread start /// @brief Init server after thread start
@ -93,8 +97,6 @@ private:
const QString WEBSERVER_DEFAULT_CRT_PATH = ":/hyperion.crt"; const QString WEBSERVER_DEFAULT_CRT_PATH = ":/hyperion.crt";
const QString WEBSERVER_DEFAULT_KEY_PATH = ":/hyperion.key"; const QString WEBSERVER_DEFAULT_KEY_PATH = ":/hyperion.key";
quint16 WEBSERVER_DEFAULT_PORT = 8090; quint16 WEBSERVER_DEFAULT_PORT = 8090;
BonjourServiceRegister * _serviceRegister = nullptr;
}; };
#endif // WEBSERVER_H #endif // WEBSERVER_H

View File

@ -32,8 +32,8 @@ add_subdirectory(db)
add_subdirectory(api) add_subdirectory(api)
add_subdirectory(ssdp) add_subdirectory(ssdp)
if(ENABLE_AVAHI) if(ENABLE_MDNS)
add_subdirectory(bonjour) add_subdirectory(mdns)
endif() endif()
if(ENABLE_EFFECTENGINE) if(ENABLE_EFFECTENGINE)

View File

@ -14,6 +14,7 @@
#include <QBuffer> #include <QBuffer>
#include <QByteArray> #include <QByteArray>
#include <QTimer> #include <QTimer>
#include <QThread>
// hyperion includes // hyperion includes
#include <utils/jsonschema/QJsonFactory.h> #include <utils/jsonschema/QJsonFactory.h>
@ -23,9 +24,6 @@
#include <utils/ColorSys.h> #include <utils/ColorSys.h>
#include <utils/Process.h> #include <utils/Process.h>
// bonjour wrapper
#include <bonjour/bonjourbrowserwrapper.h>
// ledmapping int <> string transform methods // ledmapping int <> string transform methods
#include <hyperion/ImageProcessor.h> #include <hyperion/ImageProcessor.h>
@ -50,11 +48,7 @@ API::API(Logger *log, bool localConnection, QObject *parent)
_authorized = false; _authorized = false;
_adminAuthorized = false; _adminAuthorized = false;
_hyperion = _instanceManager->getHyperionInstance(0);
_currInstanceIndex = 0; _currInstanceIndex = 0;
// TODO FIXME
// report back current registers when a Hyperion instance request it
//connect(ApiSync::getInstance(), &ApiSync::requestActiveRegister, this, &API::requestActiveRegister, Qt::QueuedConnection);
// connect to possible token responses that has been requested // connect to possible token responses that has been requested
connect(_authManager, &AuthManager::tokenResponse, [=] (bool success, QObject *caller, const QString &token, const QString &comment, const QString &id, const int &tan) connect(_authManager, &AuthManager::tokenResponse, [=] (bool success, QObject *caller, const QString &token, const QString &comment, const QString &id, const int &tan)
@ -73,7 +67,7 @@ API::API(Logger *log, bool localConnection, QObject *parent)
void API::init() void API::init()
{ {
assert(_hyperion); _hyperion = _instanceManager->getHyperionInstance(0);
bool apiAuthRequired = _authManager->isAuthRequired(); bool apiAuthRequired = _authManager->isAuthRequired();
@ -336,13 +330,6 @@ void API::stopInstance(quint8 index)
QMetaObject::invokeMethod(_instanceManager, "stopInstance", Qt::QueuedConnection, Q_ARG(quint8, index)); QMetaObject::invokeMethod(_instanceManager, "stopInstance", Qt::QueuedConnection, Q_ARG(quint8, index));
} }
void API::requestActiveRegister(QObject *callerInstance)
{
// TODO FIXME
//if (_activeRegisters.size())
// QMetaObject::invokeMethod(ApiSync::getInstance(), "answerActiveRegister", Qt::QueuedConnection, Q_ARG(QObject *, callerInstance), Q_ARG(MapRegister, _activeRegisters));
}
bool API::deleteInstance(quint8 index, QString &replyMsg) bool API::deleteInstance(quint8 index, QString &replyMsg)
{ {
if (_adminAuthorized) if (_adminAuthorized)

View File

@ -13,7 +13,7 @@
"subcommand": { "subcommand": {
"type" : "string", "type" : "string",
"required" : true, "required" : true,
"enum" : ["discover","getProperties","identify"] "enum": [ "discover", "getProperties", "identify", "addAuthorization" ]
}, },
"ledDeviceType": { "ledDeviceType": {
"type" : "string", "type" : "string",

View File

@ -0,0 +1,28 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["service"]
},
"tan" : {
"type" : "integer"
},
"subcommand": {
"type" : "string",
"required" : true,
"enum" : ["discover"]
},
"serviceType": {
"type" : "string",
"required" : true
},
"params": {
"type" : "object",
"required" : false
}
},
"additionalProperties": false
}

View File

@ -5,7 +5,7 @@
"command": { "command": {
"type" : "string", "type" : "string",
"required" : true, "required" : true,
"enum": [ "color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "instance", "leddevice", "inputsource", "transform", "correction", "temperature" ] "enum": [ "color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "instance", "leddevice", "inputsource", "service", "transform", "correction", "temperature" ]
} }
} }
} }

View File

@ -22,6 +22,7 @@
<file alias="schema-instance">JSONRPC_schema/schema-instance.json</file> <file alias="schema-instance">JSONRPC_schema/schema-instance.json</file>
<file alias="schema-leddevice">JSONRPC_schema/schema-leddevice.json</file> <file alias="schema-leddevice">JSONRPC_schema/schema-leddevice.json</file>
<file alias="schema-inputsource">JSONRPC_schema/schema-inputsource.json</file> <file alias="schema-inputsource">JSONRPC_schema/schema-inputsource.json</file>
<file alias="schema-service">JSONRPC_schema/schema-service.json</file>
<!-- The following schemas are derecated but used to ensure backward compatibility with hyperion Classic remote control--> <!-- The following schemas are derecated but used to ensure backward compatibility with hyperion Classic remote control-->
<file alias="schema-transform">JSONRPC_schema/schema-hyperion-classic.json</file> <file alias="schema-transform">JSONRPC_schema/schema-hyperion-classic.json</file>
<file alias="schema-correction">JSONRPC_schema/schema-hyperion-classic.json</file> <file alias="schema-correction">JSONRPC_schema/schema-hyperion-classic.json</file>

View File

@ -63,11 +63,6 @@
#include <utils/Process.h> #include <utils/Process.h>
#include <utils/JsonUtils.h> #include <utils/JsonUtils.h>
// bonjour wrapper
#ifdef ENABLE_AVAHI
#include <bonjour/bonjourbrowserwrapper.h>
#endif
// ledmapping int <> string transform methods // ledmapping int <> string transform methods
#include <hyperion/ImageProcessor.h> #include <hyperion/ImageProcessor.h>
@ -77,6 +72,15 @@
// auth manager // auth manager
#include <hyperion/AuthManager.h> #include <hyperion/AuthManager.h>
#ifdef ENABLE_MDNS
// mDNS discover
#include <mdns/MdnsBrowser.h>
#include <mdns/MdnsServiceRegister.h>
#else
// ssdp discover
#include <ssdp/SSDPDiscover.h>
#endif
using namespace hyperion; using namespace hyperion;
// Constants // Constants
@ -98,8 +102,6 @@ void JsonAPI::initialize()
{ {
// init API, REQUIRED! // init API, REQUIRED!
API::init(); API::init();
// Initialise jsonCB with current instance
_jsonCB->setSubscriptionsTo(_hyperion);
// setup auth interface // setup auth interface
connect(this, &API::onPendingTokenRequest, this, &JsonAPI::newPendingTokenRequest); connect(this, &API::onPendingTokenRequest, this, &JsonAPI::newPendingTokenRequest);
@ -112,7 +114,12 @@ void JsonAPI::initialize()
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage); connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
// notify hyperion about a jsonMessageForward // notify hyperion about a jsonMessageForward
if (_hyperion != nullptr)
{
// Initialise jsonCB with current instance
_jsonCB->setSubscriptionsTo(_hyperion);
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage); connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
}
} }
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced) bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
@ -180,6 +187,12 @@ void JsonAPI::handleMessage(const QString &messageString, const QString &httpAut
return; return;
} }
proceed: proceed:
if (_hyperion == nullptr)
{
sendErrorReply("Service Unavailable", command, tan);
return;
}
// switch over all possible commands and handle them // switch over all possible commands and handle them
if (command == "color") if (command == "color")
handleColorCommand(message, command, tan); handleColorCommand(message, command, tan);
@ -221,6 +234,8 @@ proceed:
handleLedDeviceCommand(message, command, tan); handleLedDeviceCommand(message, command, tan);
else if (command == "inputsource") else if (command == "inputsource")
handleInputSourceCommand(message, command, tan); handleInputSourceCommand(message, command, tan);
else if (command == "service")
handleServiceCommand(message, command, tan);
// BEGIN | The following commands are deprecated but used to ensure backward compatibility with hyperion Classic remote control // BEGIN | The following commands are deprecated but used to ensure backward compatibility with hyperion Classic remote control
else if (command == "clearall") else if (command == "clearall")
@ -627,6 +642,11 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
services.append("protobuffer"); services.append("protobuffer");
#endif #endif
#if defined(ENABLE_MDNS)
services.append("mDNS");
#endif
services.append("SSDP");
if (!availableScreenGrabbers.isEmpty() || !availableVideoGrabbers.isEmpty() || services.contains("flatbuffer") || services.contains("protobuffer")) if (!availableScreenGrabbers.isEmpty() || !availableVideoGrabbers.isEmpty() || services.contains("flatbuffer") || services.contains("protobuffer"))
{ {
services.append("borderdetection"); services.append("borderdetection");
@ -649,24 +669,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
info["components"] = component; info["components"] = component;
info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType()); info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType());
// add sessions
QJsonArray sessions;
#ifdef ENABLE_AVAHI
for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices())
{
if (session.port < 0)
continue;
QJsonObject item;
item["name"] = session.serviceName;
item["type"] = session.registeredType;
item["domain"] = session.replyDomain;
item["host"] = session.hostName;
item["address"] = session.address;
item["port"] = session.port;
sessions.append(item);
}
info["sessions"] = sessions;
#endif
// add instance info // add instance info
QJsonArray instanceInfo; QJsonArray instanceInfo;
for (const auto &entry : API::getAllInstanceData()) for (const auto &entry : API::getAllInstanceData())
@ -1571,6 +1573,16 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &
sendSuccessReply(full_command, tan); sendSuccessReply(full_command, tan);
} }
else if (subc == "addAuthorization")
{
ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject();
const QJsonObject response = ledDevice->addAuthorization(params);
Debug(_log, "response: [%s]", QString(QJsonDocument(response).toJson(QJsonDocument::Compact)).toUtf8().constData());
sendSuccessDataReply(QJsonDocument(response), full_command, tan);
}
else else
{ {
sendErrorReply("Unknown or missing subcommand", full_command, tan); sendErrorReply("Unknown or missing subcommand", full_command, tan);
@ -1725,6 +1737,55 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
} }
} }
void JsonAPI::handleServiceCommand(const QJsonObject &message, const QString &command, int tan)
{
DebugIf(verbose, _log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData());
const QString &subc = message["subcommand"].toString().trimmed();
const QString type = message["serviceType"].toString().trimmed();
QString full_command = command + "-" + subc;
if (subc == "discover")
{
QByteArray serviceType;
QJsonObject servicesDiscovered;
QJsonObject servicesOfType;
QJsonArray serviceList;
#ifdef ENABLE_MDNS
QString discoveryMethod("mDNS");
serviceType = MdnsServiceRegister::getServiceType(type);
#else
QString discoveryMethod("ssdp");
#endif
if (!serviceType.isEmpty())
{
#ifdef ENABLE_MDNS
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, serviceType));
serviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(serviceType, MdnsServiceRegister::getServiceNameFilter(type), DEFAULT_DISCOVER_TIMEOUT);
#endif
servicesOfType.insert(type, serviceList);
servicesDiscovered.insert("discoveryMethod", discoveryMethod);
servicesDiscovered.insert("services", servicesOfType);
sendSuccessDataReply(QJsonDocument(servicesDiscovered), full_command, tan);
}
else
{
sendErrorReply(QString("Discovery of service type [%1] via %2 not supported").arg(type, discoveryMethod), full_command, tan);
}
}
else
{
sendErrorReply("Unknown or missing subcommand", full_command, tan);
}
}
void JsonAPI::handleNotImplemented(const QString &command, int tan) void JsonAPI::handleNotImplemented(const QString &command, int tan)
{ {
sendErrorReply("Command not implemented", command, tan); sendErrorReply("Command not implemented", command, tan);

View File

@ -9,10 +9,6 @@
// components // components
#include <hyperion/ComponentRegister.h> #include <hyperion/ComponentRegister.h>
// bonjour wrapper
#ifdef ENABLE_AVAHI
#include <bonjour/bonjourbrowserwrapper.h>
#endif
// priorityMuxer // priorityMuxer
#include <hyperion/PriorityMuxer.h> #include <hyperion/PriorityMuxer.h>
@ -33,12 +29,9 @@ JsonCB::JsonCB(QObject* parent)
: QObject(parent) : QObject(parent)
, _hyperion(nullptr) , _hyperion(nullptr)
, _componentRegister(nullptr) , _componentRegister(nullptr)
#ifdef ENABLE_AVAHI
, _bonjour(BonjourBrowserWrapper::getInstance())
#endif
, _prioMuxer(nullptr) , _prioMuxer(nullptr)
{ {
_availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update" _availableCommands << "components-update" << "priorities-update" << "imageToLedMapping-update"
<< "adjustment-update" << "videomode-update" << "settings-update" << "leds-update" << "instance-update" << "token-update"; << "adjustment-update" << "videomode-update" << "settings-update" << "leds-update" << "instance-update" << "token-update";
#if defined(ENABLE_EFFECTENGINE) #if defined(ENABLE_EFFECTENGINE)
@ -66,16 +59,6 @@ bool JsonCB::subscribeFor(const QString& type, bool unsubscribe)
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection); connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection);
} }
if(type == "sessions-update")
{
#ifdef ENABLE_AVAHI
if(unsubscribe)
disconnect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange);
else
connect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange, Qt::UniqueConnection);
#endif
}
if(type == "priorities-update") if(type == "priorities-update")
{ {
if (unsubscribe) if (unsubscribe)
@ -208,26 +191,6 @@ void JsonCB::handleComponentState(hyperion::Components comp, bool state)
doCallback("components-update", QVariant(data)); doCallback("components-update", QVariant(data));
} }
#ifdef ENABLE_AVAHI
void JsonCB::handleBonjourChange(const QMap<QString,BonjourRecord>& bRegisters)
{
QJsonArray data;
for (const auto & session: bRegisters)
{
if (session.port<0) continue;
QJsonObject item;
item["name"] = session.serviceName;
item["type"] = session.registeredType;
item["domain"] = session.replyDomain;
item["host"] = session.hostName;
item["address"]= session.address;
item["port"] = session.port;
data.append(item);
}
doCallback("sessions-update", QVariant(data));
}
#endif
void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs) void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs)
{ {

View File

@ -1,33 +0,0 @@
# Define the current source locations
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/bonjour)
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/bonjour)
FILE ( GLOB Bonjour_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
add_library(bonjour ${Bonjour_SOURCES} )
target_link_libraries(bonjour
hyperion
hyperion-utils
Qt${QT_VERSION_MAJOR}::Network
)
IF (NOT APPLE)
set(USE_SHARED_AVAHI_LIBS ${DEFAULT_USE_SHARED_AVAHI_LIBS} CACHE BOOL "use avahi libraries from system")
if (USE_SHARED_AVAHI_LIBS)
target_link_libraries(bonjour
dns_sd
avahi-client
avahi-common
avahi-core)
else()
target_link_libraries(bonjour
libdns_sd.a
libavahi-client.a
libavahi-common.a
libavahi-core.a)
endif()
target_link_libraries(bonjour dbus-1)
ENDIF()

View File

@ -1,84 +0,0 @@
#include <bonjour/bonjourbrowserwrapper.h>
//qt incl
#include <QTimer>
// bonjour
#include <bonjour/bonjourservicebrowser.h>
#include <bonjour/bonjourserviceresolver.h>
BonjourBrowserWrapper* BonjourBrowserWrapper::instance = nullptr;
BonjourBrowserWrapper::BonjourBrowserWrapper(QObject * parent)
: QObject(parent)
, _bonjourResolver(new BonjourServiceResolver(this))
, _timerBonjourResolver(new QTimer(this))
{
// register meta
qRegisterMetaType<QMap<QString,BonjourRecord>>("QMap<QString,BonjourRecord>");
BonjourBrowserWrapper::instance = this;
connect(_bonjourResolver, &BonjourServiceResolver::bonjourRecordResolved, this, &BonjourBrowserWrapper::bonjourRecordResolved);
connect(_timerBonjourResolver, &QTimer::timeout, this, &BonjourBrowserWrapper::bonjourResolve);
_timerBonjourResolver->setInterval(1000);
_timerBonjourResolver->start();
// browse for _hyperiond-http._tcp
browseForServiceType(QLatin1String("_hyperiond-http._tcp"));
}
bool BonjourBrowserWrapper::browseForServiceType(const QString &serviceType)
{
if(!_browsedServices.contains(serviceType))
{
BonjourServiceBrowser* newBrowser = new BonjourServiceBrowser(this);
connect(newBrowser, &BonjourServiceBrowser::currentBonjourRecordsChanged, this, &BonjourBrowserWrapper::currentBonjourRecordsChanged);
newBrowser->browseForServiceType(serviceType);
_browsedServices.insert(serviceType, newBrowser);
return true;
}
return false;
}
void BonjourBrowserWrapper::currentBonjourRecordsChanged(const QList<BonjourRecord> &list)
{
_hyperionSessions.clear();
for ( auto rec : list )
{
_hyperionSessions.insert(rec.serviceName, rec);
}
}
void BonjourBrowserWrapper::bonjourRecordResolved(const QHostInfo &hostInfo, int port)
{
if ( _hyperionSessions.contains(_bonjourCurrentServiceToResolve))
{
QString host = hostInfo.hostName();
QString domain = _hyperionSessions[_bonjourCurrentServiceToResolve].replyDomain;
if (host.endsWith("."+domain))
{
host.remove(host.length()-domain.length()-1,domain.length()+1);
}
_hyperionSessions[_bonjourCurrentServiceToResolve].hostName = host;
_hyperionSessions[_bonjourCurrentServiceToResolve].port = port;
_hyperionSessions[_bonjourCurrentServiceToResolve].address = hostInfo.addresses().isEmpty() ? "" : hostInfo.addresses().first().toString();
//Debug(_log, "found hyperion session: %s:%d",QSTRING_CSTR(hostInfo.hostName()), port);
//emit change
emit browserChange(_hyperionSessions);
}
}
void BonjourBrowserWrapper::bonjourResolve()
{
for(auto key : _hyperionSessions.keys())
{
if (_hyperionSessions[key].port < 0)
{
_bonjourCurrentServiceToResolve = key;
_bonjourResolver->resolveBonjourRecord(_hyperionSessions[key]);
break;
}
}
}

View File

@ -1,109 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "bonjour/bonjourservicebrowser.h"
#include <QtCore/QSocketNotifier>
BonjourServiceBrowser::BonjourServiceBrowser(QObject *parent)
: QObject(parent)
, dnssref(0)
, bonjourSocket(0)
{
}
BonjourServiceBrowser::~BonjourServiceBrowser()
{
if (dnssref)
{
DNSServiceRefDeallocate(dnssref);
dnssref = 0;
}
}
void BonjourServiceBrowser::browseForServiceType(const QString &serviceType)
{
DNSServiceErrorType err = DNSServiceBrowse(&dnssref, 0, 0, serviceType.toUtf8().constData(), 0, bonjourBrowseReply, this);
if (err != kDNSServiceErr_NoError)
{
emit error(err);
}
else
{
int sockfd = DNSServiceRefSockFD(dnssref);
if (sockfd == -1)
{
emit error(kDNSServiceErr_Invalid);
}
else
{
bonjourSocket = new QSocketNotifier(sockfd, QSocketNotifier::Read, this);
connect(bonjourSocket, &QSocketNotifier::activated, this, &BonjourServiceBrowser::bonjourSocketReadyRead);
}
}
}
void BonjourServiceBrowser::bonjourSocketReadyRead()
{
DNSServiceErrorType err = DNSServiceProcessResult(dnssref);
if (err != kDNSServiceErr_NoError)
{
emit error(err);
}
}
void BonjourServiceBrowser::bonjourBrowseReply(DNSServiceRef , DNSServiceFlags flags,
quint32 , DNSServiceErrorType errorCode,
const char *serviceName, const char *regType,
const char *replyDomain, void *context)
{
BonjourServiceBrowser *serviceBrowser = static_cast<BonjourServiceBrowser *>(context);
if (errorCode != kDNSServiceErr_NoError)
{
emit serviceBrowser->error(errorCode);
}
else
{
BonjourRecord bonjourRecord(serviceName, regType, replyDomain);
if ((flags & kDNSServiceFlagsAdd) != 0)
{
if (!serviceBrowser->bonjourRecords.contains(bonjourRecord))
{
serviceBrowser->bonjourRecords.append(bonjourRecord);
}
}
else
{
serviceBrowser->bonjourRecords.removeAll(bonjourRecord);
}
if (!(flags & kDNSServiceFlagsMoreComing))
{
emit serviceBrowser->currentBonjourRecordsChanged(serviceBrowser->bonjourRecords);
}
}
}

View File

@ -1,151 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <bonjour/bonjourserviceregister.h>
#include <stdlib.h>
#include <QtCore/QSocketNotifier>
#include <QHostInfo>
#include <utils/Logger.h>
#include <HyperionConfig.h>
#include <hyperion/AuthManager.h>
BonjourServiceRegister::BonjourServiceRegister(QObject *parent)
: QObject(parent), dnssref(0), bonjourSocket(0)
{
setenv("AVAHI_COMPAT_NOWARN", "1", 1);
}
BonjourServiceRegister::~BonjourServiceRegister()
{
if (dnssref)
{
DNSServiceRefDeallocate(dnssref);
dnssref = 0;
}
}
void BonjourServiceRegister::registerService(const QString& service, int port)
{
_port = port;
// zeroconf $configname@$hostname:port
// TODO add name of the main instance
registerService(
BonjourRecord(QHostInfo::localHostName()+ ":" + QString::number(port),
service,
QString()
),
port
);
}
void BonjourServiceRegister::registerService(const BonjourRecord &record, quint16 servicePort, const std::vector<std::pair<std::string, std::string>>& txt)
{
if (dnssref)
{
Warning(Logger::getInstance("BonJour"), "Already registered a service for this object, aborting new register");
return;
}
quint16 bigEndianPort = servicePort;
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
{
bigEndianPort = 0 | ((servicePort & 0x00ff) << 8) | ((servicePort & 0xff00) >> 8);
}
#endif
// base txtRec
std::vector<std::pair<std::string, std::string> > txtBase = {{"id",AuthManager::getInstance()->getID().toStdString()},{"version",HYPERION_VERSION}};
// create txt record
TXTRecordRef txtRec;
TXTRecordCreate(&txtRec,0,NULL);
if(!txt.empty())
{
txtBase.insert(txtBase.end(), txt.begin(), txt.end());
}
// add txt records
for(std::vector<std::pair<std::string, std::string> >::const_iterator it = txtBase.begin(); it != txtBase.end(); ++it)
{
//Debug(Logger::getInstance("BonJour"), "TXTRecord: key:%s, value:%s",it->first.c_str(),it->second.c_str());
uint8_t txtLen = (uint8_t)strlen(it->second.c_str());
TXTRecordSetValue(&txtRec, it->first.c_str(), txtLen, it->second.c_str());
}
DNSServiceErrorType err = DNSServiceRegister(&dnssref, 0, 0, record.serviceName.toUtf8().constData(),
record.registeredType.toUtf8().constData(),
(record.replyDomain.isEmpty() ? 0 : record.replyDomain.toUtf8().constData()),
0, bigEndianPort, TXTRecordGetLength(&txtRec), TXTRecordGetBytesPtr(&txtRec), bonjourRegisterService, this);
if (err != kDNSServiceErr_NoError)
{
emit error(err);
}
else
{
int sockfd = DNSServiceRefSockFD(dnssref);
if (sockfd == -1)
{
emit error(kDNSServiceErr_Invalid);
}
else
{
bonjourSocket = new QSocketNotifier(sockfd, QSocketNotifier::Read, this);
connect(bonjourSocket, &QSocketNotifier::activated, this, &BonjourServiceRegister::bonjourSocketReadyRead);
}
}
TXTRecordDeallocate(&txtRec);
}
void BonjourServiceRegister::bonjourSocketReadyRead()
{
DNSServiceErrorType err = DNSServiceProcessResult(dnssref);
if (err != kDNSServiceErr_NoError)
emit error(err);
}
void BonjourServiceRegister::bonjourRegisterService(DNSServiceRef, DNSServiceFlags,
DNSServiceErrorType errorCode, const char *name,
const char *regtype, const char *domain,
void *data)
{
BonjourServiceRegister *serviceRegister = static_cast<BonjourServiceRegister *>(data);
if (errorCode != kDNSServiceErr_NoError)
{
emit serviceRegister->error(errorCode);
}
else
{
serviceRegister->finalRecord = BonjourRecord(QString::fromUtf8(name),
QString::fromUtf8(regtype),
QString::fromUtf8(domain));
emit serviceRegister->serviceRegistered(serviceRegister->finalRecord);
}
}

View File

@ -1,122 +0,0 @@
/*
Copyright (c) 2007, Trenton Schulz
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <QtCore/QSocketNotifier>
#include <QtNetwork/QHostInfo>
#include "bonjour/bonjourrecord.h"
#include "bonjour/bonjourserviceresolver.h"
BonjourServiceResolver::BonjourServiceResolver(QObject *parent)
: QObject(parent)
, dnssref(0)
, bonjourSocket(0)
, bonjourPort(-1)
{
}
BonjourServiceResolver::~BonjourServiceResolver()
{
cleanupResolve();
}
void BonjourServiceResolver::cleanupResolve()
{
if (dnssref)
{
DNSServiceRefDeallocate(dnssref);
dnssref = 0;
delete bonjourSocket;
bonjourPort = -1;
}
}
bool BonjourServiceResolver::resolveBonjourRecord(const BonjourRecord &record)
{
if (dnssref)
{
//qWarning("resolve in process, aborting");
return false;
}
DNSServiceErrorType err = DNSServiceResolve(&dnssref, 0, 0,
record.serviceName.toUtf8().constData(),
record.registeredType.toUtf8().constData(),
record.replyDomain.toUtf8().constData(),
(DNSServiceResolveReply)bonjourResolveReply, this);
if (err != kDNSServiceErr_NoError)
{
emit error(err);
}
else
{
int sockfd = DNSServiceRefSockFD(dnssref);
if (sockfd == -1)
{
emit error(kDNSServiceErr_Invalid);
}
else
{
bonjourSocket = new QSocketNotifier(sockfd, QSocketNotifier::Read, this);
connect(bonjourSocket, &QSocketNotifier::activated, this, &BonjourServiceResolver::bonjourSocketReadyRead);
}
}
return true;
}
void BonjourServiceResolver::bonjourSocketReadyRead()
{
DNSServiceErrorType err = DNSServiceProcessResult(dnssref);
if (err != kDNSServiceErr_NoError)
emit error(err);
}
void BonjourServiceResolver::bonjourResolveReply(DNSServiceRef sdRef, DNSServiceFlags ,
quint32 , DNSServiceErrorType errorCode,
const char *, const char *hosttarget, quint16 port,
quint16 , const char *, void *context)
{
BonjourServiceResolver *serviceResolver = static_cast<BonjourServiceResolver *>(context);
if (errorCode != kDNSServiceErr_NoError) {
emit serviceResolver->error(errorCode);
return;
}
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
{
port = 0 | ((port & 0x00ff) << 8) | ((port & 0xff00) >> 8);
}
#endif
serviceResolver->bonjourPort = port;
QHostInfo::lookupHost(QString::fromUtf8(hosttarget), serviceResolver, SLOT(finishConnect(const QHostInfo &)));
}
void BonjourServiceResolver::finishConnect(const QHostInfo &hostInfo)
{
emit bonjourRecordResolved(hostInfo, bonjourPort);
QMetaObject::invokeMethod(this, "cleanupResolve", Qt::QueuedConnection);
}

View File

@ -8,6 +8,8 @@
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QDebug>
#include <QFile>
/* Enable to turn on detailed CEC logs */ /* Enable to turn on detailed CEC logs */
// #define VERBOSE_CEC // #define VERBOSE_CEC
@ -34,15 +36,16 @@ bool CECHandler::start()
if (_cecAdapter) if (_cecAdapter)
return true; return true;
Info(_logger, "Starting CEC handler"); std::string library = std::string("" CEC_LIBRARY);
_cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() : nullptr);
_cecAdapter = LibCecInitialise(&_cecConfig);
if(!_cecAdapter) if(!_cecAdapter)
{ {
Error(_logger, "Failed loading libcec.so"); Error(_logger, "Failed to loading libcec.so");
return false; return false;
} }
Info(_logger, "CEC handler started");
auto adapters = getAdapters(); auto adapters = getAdapters();
if (adapters.isEmpty()) if (adapters.isEmpty())
{ {

View File

@ -5,13 +5,14 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/cec)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/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") FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp")
add_library(cechandler ${CEC_SOURCES}) add_library(cechandler ${CEC_SOURCES})
add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}")
include_directories(${CEC_INCLUDE_DIRS}) include_directories(${CEC_INCLUDE_DIRS})
target_link_libraries(cechandler target_link_libraries(cechandler
dl
${CEC_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Core
${CMAKE_DL_LIBS}
) )

View File

@ -60,5 +60,10 @@ flatbuffers
Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Core
) )
if(ENABLE_MDNS)
target_link_libraries(flatbufserver mdns)
endif()
endif() endif()

View File

@ -37,6 +37,7 @@ FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString&
FlatBufferConnection::~FlatBufferConnection() FlatBufferConnection::~FlatBufferConnection()
{ {
Debug(_log, "Closing connection to: %s:%u", QSTRING_CSTR(_host), _port);
_timer.stop(); _timer.stop();
_socket.close(); _socket.close();
} }

View File

@ -6,16 +6,18 @@
#include <utils/NetOrigin.h> #include <utils/NetOrigin.h>
#include <utils/GlobalSignals.h> #include <utils/GlobalSignals.h>
// bonjour
#ifdef ENABLE_AVAHI
#include <bonjour/bonjourserviceregister.h>
#endif
// qt // qt
#include <QJsonObject> #include <QJsonObject>
#include <QTcpServer> #include <QTcpServer>
#include <QTcpSocket> #include <QTcpSocket>
// Constants
namespace {
const char SERVICE_TYPE[] = "flatbuffer";
} //End of constants
FlatBufferServer::FlatBufferServer(const QJsonDocument& config, QObject* parent) FlatBufferServer::FlatBufferServer(const QJsonDocument& config, QObject* parent)
: QObject(parent) : QObject(parent)
, _server(new QTcpServer(this)) , _server(new QTcpServer(this))
@ -106,19 +108,8 @@ void FlatBufferServer::startServer()
else else
{ {
Info(_log,"Started on port %d", _port); Info(_log,"Started on port %d", _port);
#ifdef ENABLE_AVAHI
if(_serviceRegister == nullptr) emit publishService(SERVICE_TYPE, _port);
{
_serviceRegister = new BonjourServiceRegister(this);
_serviceRegister->registerService("_hyperiond-flatbuf._tcp", _port);
}
else if(_serviceRegister->getPort() != _port)
{
delete _serviceRegister;
_serviceRegister = new BonjourServiceRegister(this);
_serviceRegister->registerService("_hyperiond-flatbuf._tcp", _port);
}
#endif
} }
} }
} }

View File

@ -1,5 +1,5 @@
// STL includes // STL includes
#include <stdexcept> #include <chrono>
// project includes // project includes
#include <forwarder/MessageForwarder.h> #include <forwarder/MessageForwarder.h>
@ -12,23 +12,46 @@
#include <utils/NetUtils.h> #include <utils/NetUtils.h>
// qt includes // qt includes
#include <QTcpServer>
#include <QTcpSocket> #include <QTcpSocket>
#include <QHostInfo> #include <QHostInfo>
#include <QNetworkInterface> #include <QNetworkInterface>
#include <QThread>
#include <flatbufserver/FlatBufferConnection.h> #include <flatbufserver/FlatBufferConnection.h>
// mDNS discover
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#include <mdns/MdnsServiceRegister.h>
#endif
// Constants
namespace {
const int DEFAULT_FORWARDER_FLATBUFFFER_PRIORITY = 140;
constexpr std::chrono::milliseconds CONNECT_TIMEOUT{500}; // JSON-socket connect timeout in ms
} //End of constants
MessageForwarder::MessageForwarder(Hyperion* hyperion) MessageForwarder::MessageForwarder(Hyperion* hyperion)
: _hyperion(hyperion) : _hyperion(hyperion)
, _log(nullptr) , _log(nullptr)
, _muxer(_hyperion->getMuxerInstance()) , _muxer(_hyperion->getMuxerInstance())
, _forwarder_enabled(true) , _forwarder_enabled(false)
, _priority(140) , _priority(DEFAULT_FORWARDER_FLATBUFFFER_PRIORITY)
, _messageForwarderFlatBufHelper(nullptr)
{ {
QString subComponent = hyperion->property("instance").toString(); QString subComponent = hyperion->property("instance").toString();
_log= Logger::getInstance("NETFORWARDER", subComponent); _log= Logger::getInstance("NETFORWARDER", subComponent);
qRegisterMetaType<TargetHost>("TargetHost");
#ifdef ENABLE_MDNS
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType("jsonapi")));
#endif
// get settings updates // get settings updates
connect(_hyperion, &Hyperion::settingsChanged, this, &MessageForwarder::handleSettingsUpdate); connect(_hyperion, &Hyperion::settingsChanged, this, &MessageForwarder::handleSettingsUpdate);
@ -37,82 +60,23 @@ MessageForwarder::MessageForwarder(Hyperion* hyperion)
// connect with Muxer visible priority changes // connect with Muxer visible priority changes
connect(_muxer, &PriorityMuxer::visiblePriorityChanged, this, &MessageForwarder::handlePriorityChanges); connect(_muxer, &PriorityMuxer::visiblePriorityChanged, this, &MessageForwarder::handlePriorityChanges);
// init
handleSettingsUpdate(settings::NETFORWARD, _hyperion->getSetting(settings::NETFORWARD));
} }
MessageForwarder::~MessageForwarder() MessageForwarder::~MessageForwarder()
{ {
while (!_forwardClients.isEmpty()) stopJsonTargets();
{ stopFlatbufferTargets();
delete _forwardClients.takeFirst();
}
} }
void MessageForwarder::handleSettingsUpdate(settings::type type, const QJsonDocument& config) void MessageForwarder::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
{ {
if (type == settings::NETFORWARD) if (type == settings::NETFORWARD)
{ {
// clear the current targets
_jsonTargets.clear();
_flatbufferTargets.clear();
while (!_forwardClients.isEmpty())
{
delete _forwardClients.takeFirst();
}
// build new one
const QJsonObject& obj = config.object(); const QJsonObject& obj = config.object();
if (!obj["json"].isNull())
{
const QJsonArray& addr = obj["json"].toArray();
for (const auto& entry : addr)
{
addJsonTarget(entry.toObject());
}
}
if (!obj["flat"].isNull())
{
const QJsonArray& addr = obj["flat"].toArray();
for (const auto& entry : addr)
{
addFlatbufferTarget(entry.toObject());
}
}
bool isForwarderEnabledinSettings = obj["enable"].toBool(false); bool isForwarderEnabledinSettings = obj["enable"].toBool(false);
enableTargets(isForwarderEnabledinSettings, obj);
if (!_jsonTargets.isEmpty() && isForwarderEnabledinSettings && _forwarder_enabled)
{
for (const auto& targetHost : qAsConst(_jsonTargets))
{
InfoIf(isForwarderEnabledinSettings, _log, "Forwarding now to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
}
connect(_hyperion, &Hyperion::forwardJsonMessage, this, &MessageForwarder::forwardJsonMessage, Qt::UniqueConnection);
}
else if (_jsonTargets.isEmpty() || !isForwarderEnabledinSettings || !_forwarder_enabled)
{
disconnect(_hyperion, &Hyperion::forwardJsonMessage, nullptr, nullptr);
}
if (!_flatbufferTargets.isEmpty() && isForwarderEnabledinSettings && _forwarder_enabled)
{
for (const auto& targetHost : qAsConst(_flatbufferTargets))
{
InfoIf(isForwarderEnabledinSettings, _log, "Forwarding now to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
}
}
else if (_flatbufferTargets.isEmpty() || !isForwarderEnabledinSettings || !_forwarder_enabled)
{
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
}
// update comp state
_hyperion->setNewComponentState(hyperion::COMP_FORWARDER, isForwarderEnabledinSettings);
} }
} }
@ -120,30 +84,59 @@ void MessageForwarder::handleCompStateChangeRequest(hyperion::Components compone
{ {
if (component == hyperion::COMP_FORWARDER && _forwarder_enabled != enable) if (component == hyperion::COMP_FORWARDER && _forwarder_enabled != enable)
{ {
_forwarder_enabled = enable; Info(_log, "Forwarder is %s", (enable ? "enabled" : "disabled"));
handleSettingsUpdate(settings::NETFORWARD, _hyperion->getSetting(settings::NETFORWARD)); QJsonDocument config {_hyperion->getSetting(settings::type::NETFORWARD)};
Info(_log, "Forwarder change state to %s", (_forwarder_enabled ? "enabled" : "disabled")); enableTargets(enable, config.object());
_hyperion->setNewComponentState(component, _forwarder_enabled);
} }
} }
void MessageForwarder::enableTargets(bool enable, const QJsonObject& config)
{
if (!enable)
{
_forwarder_enabled = false;
stopJsonTargets();
stopFlatbufferTargets();
}
else
{
int jsonTargetNum = startJsonTargets(config);
int flatbufTargetNum = startFlatbufferTargets(config);
if (flatbufTargetNum > 0)
{
hyperion::Components activeCompId = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority()).componentId;
if (activeCompId == hyperion::COMP_GRABBER)
{
connect(_hyperion, &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
}
else if (activeCompId == hyperion::COMP_V4L)
{
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
}
}
if (jsonTargetNum > 0 || flatbufTargetNum > 0)
{
_forwarder_enabled = true;
}
else
{
_forwarder_enabled = false;
Warning(_log,"No JSON- nor Flatbuffer-Forwarder configured -> Forwarding disabled", _forwarder_enabled);
}
}
_hyperion->setNewComponentState(hyperion::COMP_FORWARDER, _forwarder_enabled);
}
void MessageForwarder::handlePriorityChanges(int priority) void MessageForwarder::handlePriorityChanges(int priority)
{ {
const QJsonObject obj = _hyperion->getSetting(settings::NETFORWARD).object(); if (priority != 0 && _forwarder_enabled)
if (priority != 0 && _forwarder_enabled && obj["enable"].toBool())
{ {
hyperion::Components activeCompId = _hyperion->getPriorityInfo(priority).componentId; hyperion::Components activeCompId = _hyperion->getPriorityInfo(priority).componentId;
if (activeCompId == hyperion::COMP_GRABBER || activeCompId == hyperion::COMP_V4L) if (activeCompId == hyperion::COMP_GRABBER || activeCompId == hyperion::COMP_V4L)
{ {
if (!obj["flat"].isNull())
{
const QJsonArray& addr = obj["flat"].toArray();
for (const auto& entry : addr)
{
addFlatbufferTarget(entry.toObject());
}
}
switch (activeCompId) switch (activeCompId)
{ {
case hyperion::COMP_GRABBER: case hyperion::COMP_GRABBER:
@ -177,37 +170,90 @@ void MessageForwarder::addJsonTarget(const QJsonObject& targetConfig)
{ {
TargetHost targetHost; TargetHost targetHost;
QString config_host = targetConfig["host"].toString(); QString hostName = targetConfig["host"].toString();
if (NetUtils::resolveHostAddress(_log, config_host, targetHost.host)) int port = targetConfig["port"].toInt();
if (!hostName.isEmpty())
{ {
int config_port = targetConfig["port"].toInt(); if (NetUtils::resolveHostToAddress(_log, hostName, targetHost.host, port))
if (NetUtils::isValidPort(_log, config_port, config_host))
{ {
targetHost.port = static_cast<quint16>(config_port); QString address = targetHost.host.toString();
if (hostName != address)
{
Info(_log, "Resolved hostname [%s] to address [%s]", QSTRING_CSTR(hostName), QSTRING_CSTR(address));
}
if (NetUtils::isValidPort(_log, port, targetHost.host.toString()))
{
targetHost.port = static_cast<quint16>(port);
// verify loop with JSON-server // verify loop with JSON-server
const QJsonObject& obj = _hyperion->getSetting(settings::JSONSERVER).object(); const QJsonObject& obj = _hyperion->getSetting(settings::JSONSERVER).object();
if ((QNetworkInterface::allAddresses().indexOf(targetHost.host) != -1) && targetHost.port == static_cast<quint16>(obj["port"].toInt())) if ((QNetworkInterface::allAddresses().indexOf(targetHost.host) != -1) && targetHost.port == static_cast<quint16>(obj["port"].toInt()))
{ {
Error(_log, "Loop between JSON-Server and Forwarder! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(config_host), config_port); Error(_log, "Loop between JSON-Server and Forwarder! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(targetHost.host.toString()), port);
} }
else else
{
if (_forwarder_enabled)
{ {
if (_jsonTargets.indexOf(targetHost) == -1) if (_jsonTargets.indexOf(targetHost) == -1)
{ {
Info(_log, "JSON-Forwarder settings: Adding target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); Debug(_log, "JSON-Forwarder settings: Adding target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
_jsonTargets << targetHost; _jsonTargets << targetHost;
} }
else else
{ {
Warning(_log, "JSON Forwarder settings: Duplicate target host configuration! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); Warning(_log, "JSON-Forwarder settings: Duplicate target host configuration! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
} }
} }
}
}
}
}
int MessageForwarder::startJsonTargets(const QJsonObject& config)
{
if (!config["jsonapi"].isNull())
{
_jsonTargets.clear();
const QJsonArray& addr = config["jsonapi"].toArray();
#ifdef ENABLE_MDNS
if (!addr.isEmpty())
{
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType("jsonapi")));
}
#endif
for (const auto& entry : addr)
{
addJsonTarget(entry.toObject());
}
if (!_jsonTargets.isEmpty())
{
for (const auto& targetHost : qAsConst(_jsonTargets))
{
Info(_log, "Forwarding now to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
}
connect(_hyperion, &Hyperion::forwardJsonMessage, this, &MessageForwarder::forwardJsonMessage, Qt::UniqueConnection);
} }
} }
return _jsonTargets.size();
}
void MessageForwarder::stopJsonTargets()
{
if (!_jsonTargets.isEmpty())
{
disconnect(_hyperion, &Hyperion::forwardJsonMessage, nullptr, nullptr);
for (const auto& targetHost : qAsConst(_jsonTargets))
{
Info(_log, "Stopped forwarding to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
}
_jsonTargets.clear();
} }
} }
@ -215,31 +261,40 @@ void MessageForwarder::addFlatbufferTarget(const QJsonObject& targetConfig)
{ {
TargetHost targetHost; TargetHost targetHost;
QString config_host = targetConfig["host"].toString(); QString hostName = targetConfig["host"].toString();
if (NetUtils::resolveHostAddress(_log, config_host, targetHost.host)) int port = targetConfig["port"].toInt();
if (!hostName.isEmpty())
{ {
int config_port = targetConfig["port"].toInt(); if (NetUtils::resolveHostToAddress(_log, hostName, targetHost.host, port))
if (NetUtils::isValidPort(_log, config_port, config_host))
{ {
targetHost.port = static_cast<quint16>(config_port); QString address = targetHost.host.toString();
if (hostName != address)
{
Info(_log, "Resolved hostname [%s] to address [%s]", QSTRING_CSTR(hostName), QSTRING_CSTR(address));
}
if (NetUtils::isValidPort(_log, port, targetHost.host.toString()))
{
targetHost.port = static_cast<quint16>(port);
// verify loop with Flatbuffer-server // verify loop with Flatbuffer-server
const QJsonObject& obj = _hyperion->getSetting(settings::FLATBUFSERVER).object(); const QJsonObject& obj = _hyperion->getSetting(settings::FLATBUFSERVER).object();
if ((QNetworkInterface::allAddresses().indexOf(targetHost.host) != -1) && targetHost.port == static_cast<quint16>(obj["port"].toInt())) if ((QNetworkInterface::allAddresses().indexOf(targetHost.host) != -1) && targetHost.port == static_cast<quint16>(obj["port"].toInt()))
{ {
Error(_log, "Loop between Flatbuffer-Server and Forwarder! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(config_host), config_port); Error(_log, "Loop between Flatbuffer-Server and Forwarder! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(targetHost.host.toString()), port);
} }
else else
{
if (_forwarder_enabled)
{ {
if (_flatbufferTargets.indexOf(targetHost) == -1) if (_flatbufferTargets.indexOf(targetHost) == -1)
{ {
Info(_log, "Flatbuffer-Forwarder settings: Adding target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); Debug(_log, "Flatbuffer-Forwarder settings: Adding target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
_flatbufferTargets << targetHost; _flatbufferTargets << targetHost;
FlatBufferConnection* flatbuf = new FlatBufferConnection("Forwarder", targetHost.host.toString(), _priority, false, targetHost.port); if (_messageForwarderFlatBufHelper != nullptr)
_forwardClients << flatbuf; {
emit _messageForwarderFlatBufHelper->addClient("Forwarder", targetHost, _priority, false);
}
} }
else else
{ {
@ -251,6 +306,66 @@ void MessageForwarder::addFlatbufferTarget(const QJsonObject& targetConfig)
} }
} }
int MessageForwarder::startFlatbufferTargets(const QJsonObject& config)
{
if (!config["flatbuffer"].isNull())
{
if (_messageForwarderFlatBufHelper == nullptr)
{
_messageForwarderFlatBufHelper = new MessageForwarderFlatbufferClientsHelper();
}
else
{
emit _messageForwarderFlatBufHelper->clearClients();
}
_flatbufferTargets.clear();
const QJsonArray& addr = config["flatbuffer"].toArray();
#ifdef ENABLE_MDNS
if (!addr.isEmpty())
{
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType("flatbuffer")));
}
#endif
for (const auto& entry : addr)
{
addFlatbufferTarget(entry.toObject());
}
if (!_flatbufferTargets.isEmpty())
{
for (const auto& targetHost : qAsConst(_flatbufferTargets))
{
Info(_log, "Forwarding now to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
}
}
}
return _flatbufferTargets.size();
}
void MessageForwarder::stopFlatbufferTargets()
{
if (!_flatbufferTargets.isEmpty())
{
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
if (_messageForwarderFlatBufHelper != nullptr)
{
delete _messageForwarderFlatBufHelper;
_messageForwarderFlatBufHelper = nullptr;
}
for (const auto& targetHost : qAsConst(_flatbufferTargets))
{
Info(_log, "Stopped forwarding to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port);
}
_flatbufferTargets.clear();
}
}
void MessageForwarder::forwardJsonMessage(const QJsonObject& message) void MessageForwarder::forwardJsonMessage(const QJsonObject& message)
{ {
if (_forwarder_enabled) if (_forwarder_enabled)
@ -259,7 +374,7 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject& message)
for (const auto& targetHost : qAsConst(_jsonTargets)) for (const auto& targetHost : qAsConst(_jsonTargets))
{ {
client.connectToHost(targetHost.host, targetHost.port); client.connectToHost(targetHost.host, targetHost.port);
if (client.waitForConnected(500)) if (client.waitForConnected(CONNECT_TIMEOUT.count()))
{ {
sendJsonMessage(message, &client); sendJsonMessage(message, &client);
client.close(); client.close();
@ -270,11 +385,13 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject& message)
void MessageForwarder::forwardFlatbufferMessage(const QString& /*name*/, const Image<ColorRgb>& image) void MessageForwarder::forwardFlatbufferMessage(const QString& /*name*/, const Image<ColorRgb>& image)
{ {
if (_forwarder_enabled) if (_messageForwarderFlatBufHelper != nullptr)
{ {
for (int i = 0; i < _forwardClients.size(); i++) bool isfree = _messageForwarderFlatBufHelper->isFree();
if (isfree && _forwarder_enabled)
{ {
_forwardClients.at(i)->setImage(image); QMetaObject::invokeMethod(_messageForwarderFlatBufHelper, "forwardImage", Qt::QueuedConnection, Q_ARG(Image<ColorRgb>, image));
} }
} }
} }
@ -316,12 +433,72 @@ void MessageForwarder::sendJsonMessage(const QJsonObject& message, QTcpSocket* s
// parse reply data // parse reply data
QJsonParseError error; QJsonParseError error;
QJsonDocument::fromJson(serializedReply, &error); /* QJsonDocument reply = */ QJsonDocument::fromJson(serializedReply, &error);
if (error.error != QJsonParseError::NoError) if (error.error != QJsonParseError::NoError)
{ {
Error(_log, "Error while parsing reply: invalid json"); Error(_log, "Error while parsing reply: invalid JSON");
return; return;
} }
} }
MessageForwarderFlatbufferClientsHelper::MessageForwarderFlatbufferClientsHelper()
{
QThread* mainThread = new QThread();
mainThread->setObjectName("ForwarderHelperThread");
this->moveToThread(mainThread);
mainThread->start();
_free = true;
connect(this, &MessageForwarderFlatbufferClientsHelper::addClient, this, &MessageForwarderFlatbufferClientsHelper::addClientHandler);
connect(this, &MessageForwarderFlatbufferClientsHelper::clearClients, this, &MessageForwarderFlatbufferClientsHelper::clearClientsHandler);
}
MessageForwarderFlatbufferClientsHelper::~MessageForwarderFlatbufferClientsHelper()
{
_free=false;
while (!_forwardClients.isEmpty())
{
_forwardClients.takeFirst()->deleteLater();
}
QThread* oldThread = this->thread();
disconnect(oldThread, nullptr, nullptr, nullptr);
oldThread->quit();
oldThread->wait();
delete oldThread;
}
void MessageForwarderFlatbufferClientsHelper::addClientHandler(const QString& origin, const TargetHost& targetHost, int priority, bool skipReply)
{
FlatBufferConnection* flatbuf = new FlatBufferConnection(origin, targetHost.host.toString(), priority, skipReply, targetHost.port);
_forwardClients << flatbuf;
_free = true;
}
void MessageForwarderFlatbufferClientsHelper::clearClientsHandler()
{
while (!_forwardClients.isEmpty())
{
delete _forwardClients.takeFirst();
}
_free = false;
}
bool MessageForwarderFlatbufferClientsHelper::isFree() const
{
return _free;
}
void MessageForwarderFlatbufferClientsHelper::forwardImage(const Image<ColorRgb>& image)
{
_free = false;
for (int i = 0; i < _forwardClients.size(); i++)
{
_forwardClients.at(i)->setImage(image);
}
_free = true;
}

View File

@ -41,7 +41,3 @@ endif()
if(ENABLE_FORWARDER) if(ENABLE_FORWARDER)
target_link_libraries(hyperion forwarder) target_link_libraries(hyperion forwarder)
endif() endif()
if (ENABLE_AVAHI)
target_link_libraries(hyperion bonjour)
endif ()

View File

@ -149,6 +149,7 @@ void Hyperion::start()
if (_instIndex == 0) if (_instIndex == 0)
{ {
_messageForwarder = new MessageForwarder(this); _messageForwarder = new MessageForwarder(this);
_messageForwarder->handleSettingsUpdate(settings::NETFORWARD, getSetting(settings::NETFORWARD));
} }
#endif #endif
@ -698,7 +699,6 @@ void Hyperion::update()
// Smoothing is disabled // Smoothing is disabled
if (! _deviceSmooth->enabled()) if (! _deviceSmooth->enabled())
{ {
//std::cout << "Hyperion::update()> Non-Smoothing - "; LedDevice::printLedValues ( _ledBuffer);
emit ledDeviceData(_ledBuffer); emit ledDeviceData(_ledBuffer);
} }
else else
@ -710,11 +710,4 @@ void Hyperion::update()
} }
} }
} }
#if 0
else
{
//LEDDevice is disabled
Debug(_log, "LEDDevice is disabled - no update required");
}
#endif
} }

View File

@ -22,11 +22,16 @@ HyperionIManager::HyperionIManager(const QString& rootPath, QObject* parent, boo
Hyperion* HyperionIManager::getHyperionInstance(quint8 instance) Hyperion* HyperionIManager::getHyperionInstance(quint8 instance)
{ {
Hyperion* pInstance {nullptr};
if(_runningInstances.contains(instance)) if(_runningInstances.contains(instance))
return _runningInstances.value(instance); return _runningInstances.value(instance);
if (!_runningInstances.isEmpty())
{
Warning(_log,"The requested instance index '%d' with name '%s' isn't running, return main instance", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); Warning(_log,"The requested instance index '%d' with name '%s' isn't running, return main instance", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)));
return _runningInstances.value(0); pInstance = _runningInstances.value(0);
}
return pInstance;
} }
QVector<QVariantMap> HyperionIManager::getInstanceData() const QVector<QVariantMap> HyperionIManager::getInstanceData() const

View File

@ -41,6 +41,33 @@
}, },
"access": "advanced", "access": "advanced",
"propertyOrder": 4 "propertyOrder": 4
},
"enableAttempts": {
"type": "integer",
"title": "edt_dev_general_enableAttempts_title",
"minimum": 0,
"maximum": 120,
"default": 12,
"required": true,
"options": {
"infoText": "edt_dev_general_enableAttempts_title_info"
},
"access": "advanced",
"propertyOrder": 5
},
"enableAttemptsInterval": {
"type": "integer",
"title": "edt_dev_general_enableAttemptsInterval_title",
"minimum": 5,
"maximum": 120,
"default": 15,
"required": true,
"append": "edt_append_s",
"options": {
"infoText": "edt_dev_general_enableAttemptsInterval_title_info"
},
"access": "advanced",
"propertyOrder": 6
} }
}, },
"dependencies": { "dependencies": {

View File

@ -1,7 +1,7 @@
{ {
"type" : "object", "type": "object",
"title" : "edt_conf_fw_heading_title", "title": "edt_conf_fw_heading_title",
"required" : true, "required": true,
"properties": { "properties": {
"enable": { "enable": {
"type": "boolean", "type": "boolean",
@ -10,54 +10,81 @@
"default": false, "default": false,
"propertyOrder": 1 "propertyOrder": 1
}, },
"json": { "jsonapiselect": {
"type": "array",
"uniqueItems": true,
"format": "select",
"title": "edt_conf_fw_json_services_discovered_title",
"propertyOrder": 2
},
"jsonapi": {
"type": "array", "type": "array",
"title": "edt_conf_fw_json_title", "title": "edt_conf_fw_json_title",
"propertyOrder": 2,
"uniqueItems": true, "uniqueItems": true,
"access": "expert",
"items": { "items": {
"type": "object", "type": "object",
"title": "edt_conf_fw_json_itemtitle", "title": "edt_conf_fw_json_itemtitle",
"required": true,
"properties": { "properties": {
"name": {
"type": "string",
"title": "edt_conf_fw_service_name_title",
"required": true,
"access": "expert",
"propertyOrder": 1
},
"host": { "host": {
"type": "string", "type": "string",
"format": "hostname_or_ip", "format": "hostname_or_ip",
"minLength": 7, "minLength": 7,
"title": "edt_dev_spec_targetIpHost_title", "title": "edt_dev_spec_targetIpHost_title",
"required": true, "required": true,
"propertyOrder": 1 "access": "expert",
"propertyOrder": 2
}, },
"port": { "port": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"maximum": 65535, "maximum": 65535,
"default": 19444,
"title": "edt_dev_spec_port_title", "title": "edt_dev_spec_port_title",
"required": true, "required": true,
"access": "expert", "access": "expert",
"propertyOrder": 2 "propertyOrder": 3
}
} }
} }
}, },
"flat": { "propertyOrder": 3
},
"flatbufferselect": {
"type": "array",
"uniqueItems": true,
"format": "select",
"title": "edt_conf_fw_flat_services_discovered_title",
"propertyOrder": 4
},
"flatbuffer": {
"type": "array", "type": "array",
"title": "edt_conf_fw_flat_title", "title": "edt_conf_fw_flat_title",
"propertyOrder": 3,
"uniqueItems": true, "uniqueItems": true,
"access": "expert",
"items": { "items": {
"type": "object", "type": "object",
"title": "edt_conf_fw_flat_itemtitle", "title": "edt_conf_fw_flat_itemtitle",
"required": true,
"properties": { "properties": {
"name": {
"type": "string",
"title": "edt_conf_fw_service_name_title",
"access": "expert",
"propertyOrder": 1
},
"host": { "host": {
"type": "string", "type": "string",
"format": "hostname_or_ip", "format": "hostname_or_ip",
"minLength": 7, "minLength": 7,
"title": "edt_dev_spec_targetIpHost_title", "title": "edt_dev_spec_targetIpHost_title",
"required": true, "required": true,
"propertyOrder": 1 "access": "expert",
"propertyOrder": 2
}, },
"port": { "port": {
"type": "integer", "type": "integer",
@ -67,11 +94,13 @@
"title": "edt_dev_spec_port_title", "title": "edt_dev_spec_port_title",
"required": true, "required": true,
"access": "expert", "access": "expert",
"propertyOrder": 2 "propertyOrder": 3
}
}
} }
} }
}, },
"additionalProperties": false "propertyOrder": 5
} }
},
"additionalProperties": false
}

View File

@ -13,3 +13,8 @@ target_link_libraries(jsonserver
Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Gui
) )
if(ENABLE_MDNS)
target_link_libraries(jsonserver mdns)
endif()

View File

@ -1,37 +1,35 @@
// system includes // system includes
#include <stdexcept> #include <stdexcept>
// project includes
#include "HyperionConfig.h"
#include <jsonserver/JsonServer.h>
#include "JsonClientConnection.h"
// bonjour include
#ifdef ENABLE_AVAHI
#include <bonjour/bonjourserviceregister.h>
#endif
#include <utils/NetOrigin.h>
// qt includes // qt includes
#include <QTcpServer> #include <QTcpServer>
#include <QTcpSocket> #include <QTcpSocket>
#include <QJsonDocument> #include <QJsonDocument>
#include <QByteArray> #include <QByteArray>
// project includes
#include "HyperionConfig.h"
#include <jsonserver/JsonServer.h>
#include "JsonClientConnection.h"
#include <utils/NetOrigin.h>
// Constants
namespace {
const char SERVICE_TYPE[] = "jsonapi";
} //End of constants
JsonServer::JsonServer(const QJsonDocument& config) JsonServer::JsonServer(const QJsonDocument& config)
: QObject() : QObject()
, _server(new QTcpServer(this)) , _server(new QTcpServer(this))
, _openConnections() , _openConnections()
, _log(Logger::getInstance("JSONSERVER")) , _log(Logger::getInstance("JSONSERVER"))
, _netOrigin(NetOrigin::getInstance()) , _netOrigin(NetOrigin::getInstance())
, _config(config)
{ {
Debug(_log, "Created instance"); Debug(_log, "Created instance");
// Set trigger for incoming connections
connect(_server, &QTcpServer::newConnection, this, &JsonServer::newConnection);
// init
handleSettingsUpdate(settings::JSONSERVER, config);
} }
JsonServer::~JsonServer() JsonServer::~JsonServer()
@ -39,31 +37,29 @@ JsonServer::~JsonServer()
qDeleteAll(_openConnections); qDeleteAll(_openConnections);
} }
void JsonServer::initServer()
{
// Set trigger for incoming connections
connect(_server, &QTcpServer::newConnection, this, &JsonServer::newConnection);
// init
handleSettingsUpdate(settings::JSONSERVER, _config);
}
void JsonServer::start() void JsonServer::start()
{ {
if(_server->isListening()) if(!_server->isListening())
return; {
if (!_server->listen(QHostAddress::Any, _port)) if (!_server->listen(QHostAddress::Any, _port))
{ {
Error(_log,"Could not bind to port '%d', please use an available port", _port); Error(_log,"Could not bind to port '%d', please use an available port", _port);
return;
} }
else
{
Info(_log, "Started on port %d", _port); Info(_log, "Started on port %d", _port);
emit publishService(SERVICE_TYPE, _port);
#ifdef ENABLE_AVAHI
if(_serviceRegister == nullptr)
{
_serviceRegister = new BonjourServiceRegister(this);
_serviceRegister->registerService("_hyperiond-json._tcp", _port);
} }
else if( _serviceRegister->getPort() != _port)
{
delete _serviceRegister;
_serviceRegister = new BonjourServiceRegister(this);
_serviceRegister->registerService("_hyperiond-json._tcp", _port);
} }
#endif
} }
void JsonServer::stop() void JsonServer::stop()

View File

@ -141,3 +141,6 @@ if (ENABLE_DEV_USB_HID)
endif() endif()
endif() endif()
if(ENABLE_MDNS)
target_link_libraries(leddevice mdns)
endif()

View File

@ -15,20 +15,27 @@
//std includes //std includes
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <chrono>
// Constants // Constants
namespace { namespace {
// Configuration settings // Configuration settings
const char CONFIG_CURRENT_LED_COUNT[] = "currentLedCount"; const char CONFIG_CURRENT_LED_COUNT[] = "currentLedCount";
const char CONFIG_COLOR_ORDER[] = "colorOrder"; const char CONFIG_COLOR_ORDER[] = "colorOrder";
const char CONFIG_AUTOSTART[] = "autoStart"; const char CONFIG_AUTOSTART[] = "autoStart";
const char CONFIG_LATCH_TIME[] = "latchTime"; const char CONFIG_LATCH_TIME[] = "latchTime";
const char CONFIG_REWRITE_TIME[] = "rewriteTime"; const char CONFIG_REWRITE_TIME[] = "rewriteTime";
int DEFAULT_LED_COUNT = 1; int DEFAULT_LED_COUNT{ 1 };
const char DEFAULT_COLOR_ORDER[] = "RGB"; const char DEFAULT_COLOR_ORDER[]{ "RGB" };
const bool DEFAULT_IS_AUTOSTART = true; const bool DEFAULT_IS_AUTOSTART{ true };
const char CONFIG_ENABLE_ATTEMPTS[] = "enableAttempts";
const char CONFIG_ENABLE_ATTEMPTS_INTERVALL[] = "enableAttemptsInterval";
const int DEFAULT_MAX_ENABLE_ATTEMPTS{ 5 };
constexpr std::chrono::seconds DEFAULT_ENABLE_ATTEMPTS_INTERVAL{ 5 };
} //End of constants } //End of constants
@ -38,7 +45,11 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
, _log(Logger::getInstance("LEDDEVICE")) , _log(Logger::getInstance("LEDDEVICE"))
, _ledBuffer(0) , _ledBuffer(0)
, _refreshTimer(nullptr) , _refreshTimer(nullptr)
, _enableAttemptsTimer(nullptr)
, _refreshTimerInterval_ms(0) , _refreshTimerInterval_ms(0)
, _enableAttemptTimerInterval(DEFAULT_ENABLE_ATTEMPTS_INTERVAL)
, _enableAttempts(0)
, _maxEnableAttempts(DEFAULT_MAX_ENABLE_ATTEMPTS)
, _latchTime_ms(0) , _latchTime_ms(0)
, _ledCount(0) , _ledCount(0)
, _isRestoreOrigState(false) , _isRestoreOrigState(false)
@ -47,9 +58,8 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
, _isDeviceReady(false) , _isDeviceReady(false)
, _isOn(false) , _isOn(false)
, _isDeviceInError(false) , _isDeviceInError(false)
, _isInSwitchOff (false)
, _lastWriteTime(QDateTime::currentDateTime()) , _lastWriteTime(QDateTime::currentDateTime())
, _isRefreshEnabled (false) , _isRefreshEnabled(false)
, _isAutoStart(true) , _isAutoStart(true)
{ {
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower(); _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
@ -57,42 +67,39 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
LedDevice::~LedDevice() LedDevice::~LedDevice()
{ {
delete _refreshTimer; this->stopEnableAttemptsTimer();
this->stopRefreshTimer();
} }
void LedDevice::start() void LedDevice::start()
{ {
Info(_log, "Start LedDevice '%s'.", QSTRING_CSTR(_activeDeviceType)); Info(_log, "Start LedDevice '%s'.", QSTRING_CSTR(_activeDeviceType));
// setup refreshTimer
if ( _refreshTimer == nullptr )
{
_refreshTimer = new QTimer(this);
_refreshTimer->setTimerType(Qt::PreciseTimer);
_refreshTimer->setInterval( _refreshTimerInterval_ms );
connect(_refreshTimer, &QTimer::timeout, this, &LedDevice::rewriteLEDs );
}
close(); close();
_isDeviceInitialised = false; _isDeviceInitialised = false;
// General initialisation and configuration of LedDevice
if ( init(_devConfig) ) if (init(_devConfig))
{ {
// Everything is OK -> enable device // Everything is OK -> enable device
_isDeviceInitialised = true; _isDeviceInitialised = true;
if (_isAutoStart) if (_isAutoStart)
{ {
this->enable(); if (!_isEnabled)
{
Debug(_log, "Not enabled -> enable device");
enable();
}
} }
} }
} }
void LedDevice::stop() void LedDevice::stop()
{ {
Debug(_log, "Stop device");
this->disable(); this->disable();
this->stopRefreshTimer(); this->stopRefreshTimer();
Info(_log, " Stopped LedDevice '%s'", QSTRING_CSTR(_activeDeviceType) ); Info(_log, " Stopped LedDevice '%s'", QSTRING_CSTR(_activeDeviceType));
} }
int LedDevice::open() int LedDevice::open()
@ -113,6 +120,7 @@ int LedDevice::close()
void LedDevice::setInError(const QString& errorMsg) void LedDevice::setInError(const QString& errorMsg)
{ {
_isOn = false;
_isDeviceInError = true; _isDeviceInError = true;
_isDeviceReady = false; _isDeviceReady = false;
_isEnabled = false; _isEnabled = false;
@ -124,21 +132,53 @@ void LedDevice::setInError(const QString& errorMsg)
void LedDevice::enable() void LedDevice::enable()
{ {
if ( !_isEnabled ) Debug(_log, "Enable device %s'", QSTRING_CSTR(_activeDeviceType));
if (!_isEnabled)
{ {
if (_enableAttemptsTimer != nullptr && _enableAttemptsTimer->isActive())
{
_enableAttemptsTimer->stop();
}
_isDeviceInError = false; _isDeviceInError = false;
if ( ! _isDeviceReady ) if (!_isDeviceInitialised)
{
_isDeviceInitialised = init(_devConfig);
}
if (!_isDeviceReady)
{ {
open(); open();
} }
if ( _isDeviceReady ) bool isEnableFailed(true);
if (_isDeviceReady)
{ {
if (switchOn())
{
stopEnableAttemptsTimer();
_isEnabled = true; _isEnabled = true;
if ( switchOn() ) isEnableFailed = false;
{
emit enableStateChanged(_isEnabled); emit enableStateChanged(_isEnabled);
Info(_log, "LedDevice '%s' enabled", QSTRING_CSTR(_activeDeviceType));
}
}
if (isEnableFailed)
{
emit enableStateChanged(false);
if (_maxEnableAttempts > 0)
{
Debug(_log, "Device's enablement failed - Start retry timer. Retried already done [%d], isEnabled: [%d]", _enableAttempts, _isEnabled);
startEnableAttemptsTimer();
}
else
{
Debug(_log, "Device's enablement failed");
} }
} }
} }
@ -146,9 +186,11 @@ void LedDevice::enable()
void LedDevice::disable() void LedDevice::disable()
{ {
if ( _isEnabled ) Debug(_log, "Disable device %s'", QSTRING_CSTR(_activeDeviceType));
if (_isEnabled)
{ {
_isEnabled = false; _isEnabled = false;
this->stopEnableAttemptsTimer();
this->stopRefreshTimer(); this->stopRefreshTimer();
switchOff(); switchOff();
@ -163,47 +205,110 @@ void LedDevice::setActiveDeviceType(const QString& deviceType)
_activeDeviceType = deviceType; _activeDeviceType = deviceType;
} }
bool LedDevice::init(const QJsonObject &deviceConfig) bool LedDevice::init(const QJsonObject& deviceConfig)
{ {
Debug(_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() ); Debug(_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData());
_colorOrder = deviceConfig[CONFIG_COLOR_ORDER].toString(DEFAULT_COLOR_ORDER); setLedCount(deviceConfig[CONFIG_CURRENT_LED_COUNT].toInt(DEFAULT_LED_COUNT)); // property injected to reflect real led count
_isAutoStart = deviceConfig[CONFIG_AUTOSTART].toBool(DEFAULT_IS_AUTOSTART); setColorOrder(deviceConfig[CONFIG_COLOR_ORDER].toString(DEFAULT_COLOR_ORDER));
setLatchTime(deviceConfig[CONFIG_LATCH_TIME].toInt(_latchTime_ms));
setLedCount( deviceConfig[CONFIG_CURRENT_LED_COUNT].toInt(DEFAULT_LED_COUNT) ); // property injected to reflect real led count setRewriteTime(deviceConfig[CONFIG_REWRITE_TIME].toInt(_refreshTimerInterval_ms));
setLatchTime( deviceConfig[CONFIG_LATCH_TIME].toInt( _latchTime_ms ) ); setAutoStart(deviceConfig[CONFIG_AUTOSTART].toBool(DEFAULT_IS_AUTOSTART));
setRewriteTime ( deviceConfig[CONFIG_REWRITE_TIME].toInt( _refreshTimerInterval_ms) ); setEnableAttempts(deviceConfig[CONFIG_ENABLE_ATTEMPTS].toInt(DEFAULT_MAX_ENABLE_ATTEMPTS),
std::chrono::seconds(deviceConfig[CONFIG_ENABLE_ATTEMPTS_INTERVALL].toInt(DEFAULT_ENABLE_ATTEMPTS_INTERVAL.count()))
);
return true; return true;
} }
void LedDevice::startRefreshTimer() void LedDevice::startRefreshTimer()
{ {
if ( _isDeviceReady && _isEnabled ) if (_refreshTimerInterval_ms > 0)
{ {
if (_isDeviceReady && _isOn)
{
// setup refreshTimer
if (_refreshTimer == nullptr)
{
_refreshTimer = new QTimer(this);
_refreshTimer->setTimerType(Qt::PreciseTimer);
connect(_refreshTimer, &QTimer::timeout, this, &LedDevice::rewriteLEDs);
}
_refreshTimer->setInterval(_refreshTimerInterval_ms);
//Debug(_log, "Start refresh timer with interval = %ims", _refreshTimer->interval());
_refreshTimer->start(); _refreshTimer->start();
} }
else
{
Debug(_log, "Device is not ready to start a refresh timer");
}
}
} }
void LedDevice::stopRefreshTimer() void LedDevice::stopRefreshTimer()
{ {
if ( _refreshTimer != nullptr ) if (_refreshTimer != nullptr)
{ {
//Debug(_log, "Stopping refresh timer");
_refreshTimer->stop(); _refreshTimer->stop();
delete _refreshTimer;
_refreshTimer = nullptr;
} }
} }
int LedDevice::updateLeds(const std::vector<ColorRgb>& ledValues) void LedDevice::startEnableAttemptsTimer()
{
++_enableAttempts;
if (_enableAttempts <= _maxEnableAttempts)
{
if (_enableAttemptTimerInterval.count() > 0)
{
// setup enable retry timer
if (_enableAttemptsTimer == nullptr)
{
_enableAttemptsTimer = new QTimer(this);
_enableAttemptsTimer->setTimerType(Qt::PreciseTimer);
connect(_enableAttemptsTimer, &QTimer::timeout, this, &LedDevice::enable);
}
_enableAttemptsTimer->setInterval(static_cast<int>(_enableAttemptTimerInterval.count() * 1000)); //NOLINT
Info(_log, "Start %d. attempt of %d to enable the device in %d seconds", _enableAttempts, _maxEnableAttempts, _enableAttemptTimerInterval.count());
_enableAttemptsTimer->start();
}
}
else
{
Error(_log, "Device disabled. Maximum number of %d attempts enabling the device reached. Tried for %d seconds.", _maxEnableAttempts, _enableAttempts * _enableAttemptTimerInterval.count());
_enableAttempts = 0;
}
}
void LedDevice::stopEnableAttemptsTimer()
{
if (_enableAttemptsTimer != nullptr)
{
Debug(_log, "Stopping enable retry timer");
_enableAttemptsTimer->stop();
delete _enableAttemptsTimer;
_enableAttemptsTimer = nullptr;
_enableAttempts = 0;
}
}
int LedDevice::updateLeds(std::vector<ColorRgb> ledValues)
{ {
int retval = 0; int retval = 0;
if ( !_isEnabled || !_isOn || !_isDeviceReady || _isDeviceInError ) if (!_isEnabled || !_isOn || !_isDeviceReady || _isDeviceInError)
{ {
//std::cout << "LedDevice::updateLeds(), LedDevice NOT ready! "; //std::cout << "LedDevice::updateLeds(), LedDevice NOT ready! ";
retval = -1; retval = -1;
} }
else else
{ {
qint64 elapsedTimeMs = _lastWriteTime.msecsTo( QDateTime::currentDateTime() ); qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
if (_latchTime_ms == 0 || elapsedTimeMs >= _latchTime_ms) if (_latchTime_ms == 0 || elapsedTimeMs >= _latchTime_ms)
{ {
//std::cout << "LedDevice::updateLeds(), Elapsed time since last write (" << elapsedTimeMs << ") ms > _latchTime_ms (" << _latchTime_ms << ") ms" << std::endl; //std::cout << "LedDevice::updateLeds(), Elapsed time since last write (" << elapsedTimeMs << ") ms > _latchTime_ms (" << _latchTime_ms << ") ms" << std::endl;
@ -211,16 +316,16 @@ int LedDevice::updateLeds(const std::vector<ColorRgb>& ledValues)
_lastWriteTime = QDateTime::currentDateTime(); _lastWriteTime = QDateTime::currentDateTime();
// if device requires refreshing, save Led-Values and restart the timer // if device requires refreshing, save Led-Values and restart the timer
if ( _isRefreshEnabled && _isEnabled ) if (_isRefreshEnabled && _isEnabled)
{ {
this->startRefreshTimer();
_lastLedValues = ledValues; _lastLedValues = ledValues;
this->startRefreshTimer();
} }
} }
else else
{ {
//std::cout << "LedDevice::updateLeds(), Skip write. elapsedTime (" << elapsedTimeMs << ") ms < _latchTime_ms (" << _latchTime_ms << ") ms" << std::endl; //std::cout << "LedDevice::updateLeds(), Skip write. elapsedTime (" << elapsedTimeMs << ") ms < _latchTime_ms (" << _latchTime_ms << ") ms" << std::endl;
if ( _isRefreshEnabled ) if (_isRefreshEnabled)
{ {
//Stop timer to allow for next non-refresh update //Stop timer to allow for next non-refresh update
this->stopRefreshTimer(); this->stopRefreshTimer();
@ -234,19 +339,22 @@ int LedDevice::rewriteLEDs()
{ {
int retval = -1; int retval = -1;
if ( _isDeviceReady && _isEnabled ) if (_isEnabled && _isOn && _isDeviceReady && !_isDeviceInError)
{ {
// qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime()); // qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
// std::cout << "LedDevice::rewriteLEDs(): Rewrite LEDs now, elapsedTime [" << elapsedTimeMs << "] ms" << std::endl; // std::cout << "LedDevice::rewriteLEDs(): Rewrite LEDs now, elapsedTime [" << elapsedTimeMs << "] ms" << std::endl;
// //:TESTING: Inject "white" output records to differentiate from normal writes // //:TESTING: Inject "white" output records to differentiate from normal writes
// _lastLedValues.clear(); // _lastLedValues.clear();
// _lastLedValues.resize(static_cast<unsigned long>(_ledCount), ColorRgb::WHITE); // _lastLedValues.resize(static_cast<unsigned long>(_ledCount), ColorRgb::WHITE);
// printLedValues(_lastLedValues); // printLedValues(_lastLedValues);
// //:TESTING: // //:TESTING:
if (!_lastLedValues.empty())
{
retval = write(_lastLedValues); retval = write(_lastLedValues);
_lastWriteTime = QDateTime::currentDateTime(); _lastWriteTime = QDateTime::currentDateTime();
} }
}
else else
{ {
// If Device is not ready stop timer // If Device is not ready stop timer
@ -257,6 +365,7 @@ int LedDevice::rewriteLEDs()
int LedDevice::writeBlack(int numberOfWrites) int LedDevice::writeBlack(int numberOfWrites)
{ {
Debug(_log, "Set LED strip to black to switch of LEDs");
return writeColor(ColorRgb::BLACK, numberOfWrites); return writeColor(ColorRgb::BLACK, numberOfWrites);
} }
@ -273,7 +382,7 @@ int LedDevice::writeColor(const ColorRgb& color, int numberOfWrites)
QTimer::singleShot(_latchTime_ms, &loop, &QEventLoop::quit); QTimer::singleShot(_latchTime_ms, &loop, &QEventLoop::quit);
loop.exec(); loop.exec();
} }
_lastLedValues = std::vector<ColorRgb>(static_cast<unsigned long>(_ledCount),color); _lastLedValues = std::vector<ColorRgb>(static_cast<unsigned long>(_ledCount), color);
rc = write(_lastLedValues); rc = write(_lastLedValues);
} }
return rc; return rc;
@ -281,24 +390,31 @@ int LedDevice::writeColor(const ColorRgb& color, int numberOfWrites)
bool LedDevice::switchOn() bool LedDevice::switchOn()
{ {
bool rc = false; bool rc{ false };
if ( _isOn ) if (_isOn)
{ {
Debug(_log, "Device %s is already on. Skipping.", QSTRING_CSTR(_activeDeviceType));
rc = true; rc = true;
} }
else else
{ {
if ( _isEnabled &&_isDeviceInitialised ) if (_isDeviceReady)
{ {
if ( storeState() ) Info(_log, "Switching device %s ON", QSTRING_CSTR(_activeDeviceType));
if (storeState())
{ {
if ( powerOn() ) if (powerOn())
{ {
Info(_log, "Device %s is ON", QSTRING_CSTR(_activeDeviceType));
_isOn = true; _isOn = true;
_isInSwitchOff = false; emit enableStateChanged(_isEnabled);
rc = true; rc = true;
} }
else
{
Warning(_log, "Failed switching device %s ON", QSTRING_CSTR(_activeDeviceType));
}
} }
} }
} }
@ -307,32 +423,40 @@ bool LedDevice::switchOn()
bool LedDevice::switchOff() bool LedDevice::switchOff()
{ {
bool rc = false; bool rc{ false };
if ( !_isOn ) if (!_isOn)
{ {
rc = true; rc = true;
} }
else else
{ {
if ( _isDeviceInitialised ) if (_isDeviceInitialised)
{ {
// Disable device to ensure no standard Led updates are written/processed Info(_log, "Switching device %s OFF", QSTRING_CSTR(_activeDeviceType));
// Disable device to ensure no standard LED updates are written/processed
_isOn = false; _isOn = false;
_isInSwitchOff = true;
rc = true; rc = true;
if ( _isDeviceReady ) if (_isDeviceReady)
{ {
if ( _isRestoreOrigState ) if (_isRestoreOrigState)
{ {
//Restore devices state //Restore devices state
restoreState(); restoreState();
} }
else else
{ {
powerOff(); if (powerOff())
{
Info(_log, "Device %s is OFF", QSTRING_CSTR(_activeDeviceType));
}
else
{
Warning(_log, "Failed switching device %s OFF", QSTRING_CSTR(_activeDeviceType));
}
} }
} }
} }
@ -342,10 +466,12 @@ bool LedDevice::switchOff()
bool LedDevice::powerOff() bool LedDevice::powerOff()
{ {
bool rc = false; bool rc{ false };
Debug(_log, "Power Off: %s", QSTRING_CSTR(_activeDeviceType));
// Simulate power-off by writing a final "Black" to have a defined outcome // Simulate power-off by writing a final "Black" to have a defined outcome
if ( writeBlack() >= 0 ) if (writeBlack() >= 0)
{ {
rc = true; rc = true;
} }
@ -354,15 +480,18 @@ bool LedDevice::powerOff()
bool LedDevice::powerOn() bool LedDevice::powerOn()
{ {
bool rc = true; bool rc{ true };
Debug(_log, "Power On: %s", QSTRING_CSTR(_activeDeviceType));
return rc; return rc;
} }
bool LedDevice::storeState() bool LedDevice::storeState()
{ {
bool rc = true; bool rc{ true };
if ( _isRestoreOrigState ) if (_isRestoreOrigState)
{ {
// Save device's original state // Save device's original state
// _originalStateValues = get device's state; // _originalStateValues = get device's state;
@ -373,9 +502,9 @@ bool LedDevice::storeState()
bool LedDevice::restoreState() bool LedDevice::restoreState()
{ {
bool rc = true; bool rc{ true };
if ( _isRestoreOrigState ) if (_isRestoreOrigState)
{ {
// Restore device's original state // Restore device's original state
// update device using _originalStateValues // update device using _originalStateValues
@ -393,7 +522,7 @@ QJsonObject LedDevice::discover(const QJsonObject& /*params*/)
QJsonArray deviceList; QJsonArray deviceList;
devicesDiscovered.insert("devices", deviceList); devicesDiscovered.insert("devices", deviceList);
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() ); Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return devicesDiscovered; return devicesDiscovered;
} }
@ -401,75 +530,98 @@ QString LedDevice::discoverFirst()
{ {
QString deviceDiscovered; QString deviceDiscovered;
Debug(_log, "deviceDiscovered: [%s]", QSTRING_CSTR(deviceDiscovered) ); Debug(_log, "deviceDiscovered: [%s]", QSTRING_CSTR(deviceDiscovered));
return deviceDiscovered; return deviceDiscovered;
} }
QJsonObject LedDevice::getProperties(const QJsonObject& params) QJsonObject LedDevice::getProperties(const QJsonObject& params)
{ {
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() ); Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject properties; QJsonObject properties;
QJsonObject deviceProperties; QJsonObject deviceProperties;
properties.insert("properties", deviceProperties); properties.insert("properties", deviceProperties);
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() ); Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
return properties; return properties;
} }
void LedDevice::setLogger(Logger* log)
{
_log = log;
}
void LedDevice::setLedCount(int ledCount) void LedDevice::setLedCount(int ledCount)
{ {
assert(ledCount >= 0); assert(ledCount >= 0);
_ledCount = ledCount; _ledCount = static_cast<uint>(ledCount);
_ledRGBCount = _ledCount * sizeof(ColorRgb); _ledRGBCount = _ledCount * sizeof(ColorRgb);
_ledRGBWCount = _ledCount * sizeof(ColorRgbw); _ledRGBWCount = _ledCount * sizeof(ColorRgbw);
Debug(_log, "LedCount set to %d", _ledCount);
} }
void LedDevice::setLatchTime( int latchTime_ms ) void LedDevice::setColorOrder(const QString& colorOrder)
{
_colorOrder = colorOrder;
Debug(_log, "ColorOrder set to %s", QSTRING_CSTR(_colorOrder.toUpper()));
}
void LedDevice::setLatchTime(int latchTime_ms)
{ {
assert(latchTime_ms >= 0); assert(latchTime_ms >= 0);
_latchTime_ms = latchTime_ms; _latchTime_ms = latchTime_ms;
Debug(_log, "LatchTime updated to %dms", _latchTime_ms); Debug(_log, "LatchTime set to %dms", _latchTime_ms);
} }
void LedDevice::setRewriteTime( int rewriteTime_ms ) void LedDevice::setAutoStart(bool isAutoStart)
{ {
assert(rewriteTime_ms >= 0); _isAutoStart = isAutoStart;
Debug(_log, "AutoStart %s", (_isAutoStart ? "enabled" : "disabled"));
}
//Check, if refresh timer was not initialised due to getProperties/identify sceanrios void LedDevice::setRewriteTime(int rewriteTime_ms)
if (_refreshTimer != nullptr) {
{ _refreshTimerInterval_ms = qMax(rewriteTime_ms, 0);
_refreshTimerInterval_ms = rewriteTime_ms;
if (_refreshTimerInterval_ms > 0) if (_refreshTimerInterval_ms > 0)
{ {
_isRefreshEnabled = true; _isRefreshEnabled = true;
if (_refreshTimerInterval_ms <= _latchTime_ms) if (_refreshTimerInterval_ms <= _latchTime_ms)
{ {
int new_refresh_timer_interval = _latchTime_ms + 10; int new_refresh_timer_interval = _latchTime_ms + 10; //NOLINT
Warning(_log, "latchTime(%d) is bigger/equal rewriteTime(%d), set rewriteTime to %dms", _latchTime_ms, _refreshTimerInterval_ms, new_refresh_timer_interval); Warning(_log, "latchTime(%d) is bigger/equal rewriteTime(%d), set rewriteTime to %dms", _latchTime_ms, _refreshTimerInterval_ms, new_refresh_timer_interval);
_refreshTimerInterval_ms = new_refresh_timer_interval; _refreshTimerInterval_ms = new_refresh_timer_interval;
_refreshTimer->setInterval(_refreshTimerInterval_ms);
} }
Debug(_log, "Refresh interval = %dms", _refreshTimerInterval_ms); Debug(_log, "Refresh interval = %dms", _refreshTimerInterval_ms);
_refreshTimer->setInterval(_refreshTimerInterval_ms); startRefreshTimer();
_lastWriteTime = QDateTime::currentDateTime();
} }
else
Debug(_log, "RewriteTime updated to %dms", _refreshTimerInterval_ms); {
_isRefreshEnabled = false;
stopRefreshTimer();
} }
} }
void LedDevice::setEnableAttempts(int maxEnableRetries, std::chrono::seconds enableRetryTimerInterval)
{
stopEnableAttemptsTimer();
maxEnableRetries = qMax(maxEnableRetries, 0);
_enableAttempts = 0;
_maxEnableAttempts = maxEnableRetries;
_enableAttemptTimerInterval = enableRetryTimerInterval;
Debug(_log, "Max enable retries: %d, enable retry interval = %llds", _maxEnableAttempts, static_cast<int>(_enableAttemptTimerInterval.count()));
}
void LedDevice::printLedValues(const std::vector<ColorRgb>& ledValues) void LedDevice::printLedValues(const std::vector<ColorRgb>& ledValues)
{ {
std::cout << "LedValues [" << ledValues.size() <<"] ["; std::cout << "LedValues [" << ledValues.size() << "] [";
for (const ColorRgb& color : ledValues) for (const ColorRgb& color : ledValues)
{ {
std::cout << color; std::cout << color;
@ -477,24 +629,24 @@ void LedDevice::printLedValues(const std::vector<ColorRgb>& ledValues)
std::cout << "]" << std::endl; std::cout << "]" << std::endl;
} }
QString LedDevice::uint8_t_to_hex_string(const uint8_t * data, const int size, int number) const QString LedDevice::uint8_t_to_hex_string(const uint8_t* data, const int size, int number)
{ {
if ( number <= 0 || number > size) if (number <= 0 || number > size)
{ {
number = size; number = size;
} }
QByteArray bytes (reinterpret_cast<const char*>(data), number); QByteArray bytes(reinterpret_cast<const char*>(data), number);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return bytes.toHex(':'); return bytes.toHex(':');
#else #else
return bytes.toHex(); return bytes.toHex();
#endif #endif
} }
QString LedDevice::toHex(const QByteArray& data, int number) const QString LedDevice::toHex(const QByteArray& data, int number)
{ {
if ( number <= 0 || number > data.size()) if (number <= 0 || number > data.size())
{ {
number = data.size(); number = data.size();
} }
@ -505,3 +657,47 @@ QString LedDevice::toHex(const QByteArray& data, int number) const
return data.left(number).toHex(); return data.left(number).toHex();
#endif #endif
} }
bool LedDevice::isInitialised() const
{
return _isDeviceInitialised;
}
bool LedDevice::isReady() const
{
return _isDeviceReady;
}
bool LedDevice::isInError() const
{
return _isDeviceInError;
}
int LedDevice::getLatchTime() const
{
return _latchTime_ms;
}
int LedDevice::getRewriteTime() const
{
return _refreshTimerInterval_ms;
}
int LedDevice::getLedCount() const
{
return static_cast<int>(_ledCount);
}
QString LedDevice::getActiveDeviceType() const
{
return _activeDeviceType;
}
QString LedDevice::getColorOrder() const
{
return _colorOrder;
}
bool LedDevice::componentState() const {
return _isEnabled;
}

View File

@ -27,6 +27,7 @@
<file alias="schema-udpartnet">schemas/schema-artnet.json</file> <file alias="schema-udpartnet">schemas/schema-artnet.json</file>
<file alias="schema-udph801">schemas/schema-h801.json</file> <file alias="schema-udph801">schemas/schema-h801.json</file>
<file alias="schema-udpraw">schemas/schema-udpraw.json</file> <file alias="schema-udpraw">schemas/schema-udpraw.json</file>
<file alias="schema-udpddp">schemas/schema-udpddp.json</file>
<file alias="schema-ws2801">schemas/schema-ws2801.json</file> <file alias="schema-ws2801">schemas/schema-ws2801.json</file>
<file alias="schema-ws2812spi">schemas/schema-ws2812spi.json</file> <file alias="schema-ws2812spi">schemas/schema-ws2812spi.json</file>
<file alias="schema-apa104">schemas/schema-apa104.json</file> <file alias="schema-apa104">schemas/schema-apa104.json</file>

View File

@ -67,9 +67,6 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config)
// further signals // further signals
connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds, Qt::QueuedConnection); connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds, Qt::QueuedConnection);
connect(this, &LedDeviceWrapper::enable, _ledDevice, &LedDevice::enable);
connect(this, &LedDeviceWrapper::disable, _ledDevice, &LedDevice::disable);
connect(this, &LedDeviceWrapper::switchOn, _ledDevice, &LedDevice::switchOn); connect(this, &LedDeviceWrapper::switchOn, _ledDevice, &LedDevice::switchOn);
connect(this, &LedDeviceWrapper::switchOff, _ledDevice, &LedDevice::switchOff); connect(this, &LedDeviceWrapper::switchOff, _ledDevice, &LedDevice::switchOff);
@ -81,6 +78,100 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config)
thread->start(); thread->start();
} }
void LedDeviceWrapper::handleComponentState(hyperion::Components component, bool state)
{
if (component == hyperion::COMP_LEDDEVICE)
{
if (state)
{
QMetaObject::invokeMethod(_ledDevice, "enable", Qt::BlockingQueuedConnection);
}
else
{
QMetaObject::invokeMethod(_ledDevice, "disable", Qt::BlockingQueuedConnection);
}
QMetaObject::invokeMethod(_ledDevice, "componentState", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, _enabled));
}
}
void LedDeviceWrapper::handleInternalEnableState(bool newState)
{
_hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, newState);
_enabled = newState;
if (_enabled)
{
_hyperion->update();
}
}
void LedDeviceWrapper::stopDeviceThread()
{
// turns the LEDs off & stop refresh timers
emit stopLedDevice();
// get current thread
QThread* oldThread = _ledDevice->thread();
disconnect(oldThread, nullptr, nullptr, nullptr);
oldThread->quit();
oldThread->wait();
delete oldThread;
disconnect(_ledDevice, nullptr, nullptr, nullptr);
delete _ledDevice;
_ledDevice = nullptr;
}
QString LedDeviceWrapper::getActiveDeviceType() const
{
QString value = 0;
QMetaObject::invokeMethod(_ledDevice, "getActiveDeviceType", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value));
return value;
}
unsigned int LedDeviceWrapper::getLedCount() const
{
int value = 0;
QMetaObject::invokeMethod(_ledDevice, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value));
return value;
}
QString LedDeviceWrapper::getColorOrder() const
{
QString value;
QMetaObject::invokeMethod(_ledDevice, "getColorOrder", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value));
return value;
}
int LedDeviceWrapper::getLatchTime() const
{
int value = 0;
QMetaObject::invokeMethod(_ledDevice, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value));
return value;
}
bool LedDeviceWrapper::enabled() const
{
return _enabled;
}
int LedDeviceWrapper::addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr)
{
QMutexLocker lock(&_ledDeviceMapLock);
_ledDeviceMap.emplace(name,funcPtr);
return 0;
}
const LedDeviceRegistry& LedDeviceWrapper::getDeviceMap()
{
QMutexLocker lock(&_ledDeviceMapLock);
return _ledDeviceMap;
}
QJsonObject LedDeviceWrapper::getLedDeviceSchemas() QJsonObject LedDeviceWrapper::getLedDeviceSchemas()
{ {
// make sure the resources are loaded (they may be left out after static linking) // make sure the resources are loaded (they may be left out after static linking)
@ -115,101 +206,3 @@ QJsonObject LedDeviceWrapper::getLedDeviceSchemas()
return result; return result;
} }
int LedDeviceWrapper::addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr)
{
QMutexLocker lock(&_ledDeviceMapLock);
_ledDeviceMap.emplace(name,funcPtr);
return 0;
}
const LedDeviceRegistry& LedDeviceWrapper::getDeviceMap()
{
QMutexLocker lock(&_ledDeviceMapLock);
return _ledDeviceMap;
}
int LedDeviceWrapper::getLatchTime() const
{
int value = 0;
QMetaObject::invokeMethod(_ledDevice, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value));
return value;
}
QString LedDeviceWrapper::getActiveDeviceType() const
{
QString value = 0;
QMetaObject::invokeMethod(_ledDevice, "getActiveDeviceType", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value));
return value;
}
QString LedDeviceWrapper::getColorOrder() const
{
QString value;
QMetaObject::invokeMethod(_ledDevice, "getColorOrder", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value));
return value;
}
unsigned int LedDeviceWrapper::getLedCount() const
{
int value = 0;
QMetaObject::invokeMethod(_ledDevice, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value));
return value;
}
bool LedDeviceWrapper::enabled() const
{
return _enabled;
}
void LedDeviceWrapper::handleComponentState(hyperion::Components component, bool state)
{
if(component == hyperion::COMP_LEDDEVICE)
{
if ( state )
{
emit enable();
}
else
{
emit disable();
}
//Get device's state, considering situations where it is not ready
bool deviceState = false;
QMetaObject::invokeMethod(_ledDevice, "componentState", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, deviceState));
_hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, deviceState);
_enabled = deviceState;
}
}
void LedDeviceWrapper::handleInternalEnableState(bool newState)
{
_hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, newState);
_enabled = newState;
if (_enabled)
{
_hyperion->update();
}
}
void LedDeviceWrapper::stopDeviceThread()
{
// turns the LEDs off & stop refresh timers
emit stopLedDevice();
// get current thread
QThread* oldThread = _ledDevice->thread();
disconnect(oldThread, nullptr, nullptr, nullptr);
oldThread->quit();
oldThread->wait();
delete oldThread;
disconnect(_ledDevice, nullptr, nullptr, nullptr);
delete _ledDevice;
_ledDevice = nullptr;
}

View File

@ -33,7 +33,7 @@ public:
/// ///
/// Sets configuration /// Sets configuration
/// ///
/// @para#endif // LEDEVICETEMPLATE_Hm deviceConfig the json device config /// @param deviceConfig the json device config
/// @return true if success /// @return true if success
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;

View File

@ -45,23 +45,16 @@ LedDeviceAtmoOrb::~LedDeviceAtmoOrb()
bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig) bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
if ( LedDevice::init(deviceConfig) ) if ( LedDevice::init(deviceConfig) )
{ {
_multicastGroup = deviceConfig["host"].toString(MULTICAST_GROUP_DEFAULT_ADDRESS); _multicastGroup = deviceConfig["host"].toString(MULTICAST_GROUP_DEFAULT_ADDRESS);
_multiCastGroupPort = static_cast<quint16>(deviceConfig["port"].toInt(MULTICAST_GROUP_DEFAULT_PORT)); _multiCastGroupPort = static_cast<quint16>(deviceConfig["port"].toInt(MULTICAST_GROUP_DEFAULT_PORT));
_useOrbSmoothing = deviceConfig["useOrbSmoothing"].toBool(false); _useOrbSmoothing = deviceConfig["useOrbSmoothing"].toBool(false);
_skipSmoothingDiff = deviceConfig["skipSmoothingDiff"].toInt(0); _skipSmoothingDiff = deviceConfig["skipSmoothingDiff"].toInt(0);
QStringList orbIds = QStringUtils::split(deviceConfig["orbIds"].toString().simplified().remove(" "),",", QStringUtils::SplitBehavior::SkipEmptyParts); QStringList orbIds = QStringUtils::split(deviceConfig["orbIds"].toString().simplified().remove(" "),",", QStringUtils::SplitBehavior::SkipEmptyParts);
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
Debug(_log, "LedCount : %d", this->getLedCount());
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
Debug(_log, "LatchTime : %d", this->getLatchTime());
Debug(_log, "MulticastGroup : %s", QSTRING_CSTR(_multicastGroup)); Debug(_log, "MulticastGroup : %s", QSTRING_CSTR(_multicastGroup));
Debug(_log, "MulticastGroupPort: %d", _multiCastGroupPort); Debug(_log, "MulticastGroupPort: %d", _multiCastGroupPort);
Debug(_log, "Orb ID list : %s", QSTRING_CSTR(deviceConfig["orbIds"].toString())); Debug(_log, "Orb ID list : %s", QSTRING_CSTR(deviceConfig["orbIds"].toString()));

View File

@ -8,21 +8,27 @@
#include <chrono> #include <chrono>
// mDNS discover
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#include <mdns/MdnsServiceRegister.h>
#endif
#include <utils/NetUtils.h>
// Constants // Constants
namespace { namespace {
const bool verbose = false; const bool verbose = false;
const bool verbose3 = false; const bool verbose3 = false;
// Configuration settings // Configuration settings
const char CONFIG_HOST[] = "host";
const char CONFIG_HW_LED_COUNT[] = "hardwareLedCount"; const char CONFIG_HW_LED_COUNT[] = "hardwareLedCount";
const int COLOLIGHT_BEADS_PER_MODULE = 19; const int COLOLIGHT_BEADS_PER_MODULE = 19;
const int STREAM_DEFAULT_PORT = 8900;
// Cololight discovery service // Cololight discovery service
const int API_DEFAULT_PORT = 8900;
const char DISCOVERY_ADDRESS[] = "255.255.255.255"; const char DISCOVERY_ADDRESS[] = "255.255.255.255";
const quint16 DISCOVERY_PORT = 12345; const quint16 DISCOVERY_PORT = 12345;
const char DISCOVERY_MESSAGE[] = "Z-SEARCH * \r\n"; const char DISCOVERY_MESSAGE[] = "Z-SEARCH * \r\n";
@ -46,6 +52,11 @@ LedDeviceCololight::LedDeviceCololight(const QJsonObject& deviceConfig)
, _distance(0) , _distance(0)
, _sequenceNumber(1) , _sequenceNumber(1)
{ {
#ifdef ENABLE_MDNS
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
#endif
_packetFixPart.append(reinterpret_cast<const char*>(PACKET_HEADER), sizeof(PACKET_HEADER)); _packetFixPart.append(reinterpret_cast<const char*>(PACKET_HEADER), sizeof(PACKET_HEADER));
_packetFixPart.append(reinterpret_cast<const char*>(PACKET_SECU), sizeof(PACKET_SECU)); _packetFixPart.append(reinterpret_cast<const char*>(PACKET_SECU), sizeof(PACKET_SECU));
} }
@ -57,23 +68,14 @@ LedDevice* LedDeviceCololight::construct(const QJsonObject& deviceConfig)
bool LedDeviceCololight::init(const QJsonObject& deviceConfig) bool LedDeviceCololight::init(const QJsonObject& deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
_port = API_DEFAULT_PORT; if ( ProviderUdp::init(deviceConfig) )
if (ProviderUdp::init(deviceConfig))
{ {
// Initialise LedDevice configuration and execution environment _hostName = _devConfig[ CONFIG_HOST ].toString();
Debug(_log, "DeviceType : %s", QSTRING_CSTR(this->getActiveDeviceType())); _port = STREAM_DEFAULT_PORT;
Debug(_log, "ColorOrder : %s", QSTRING_CSTR(this->getColorOrder()));
Debug(_log, "LatchTime : %d", this->getLatchTime());
if (initLedsConfiguration())
{
initDirectColorCmdTemplate();
isInitOK = true; isInitOK = true;
} }
}
return isInitOK; return isInitOK;
} }
@ -161,6 +163,27 @@ void LedDeviceCololight::initDirectColorCmdTemplate()
} }
} }
int LedDeviceCololight::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
if (initLedsConfiguration())
{
initDirectColorCmdTemplate();
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
}
return retval;
}
bool LedDeviceCololight::getInfo() bool LedDeviceCololight::getInfo()
{ {
bool isCmdOK = false; bool isCmdOK = false;
@ -652,10 +675,19 @@ QJsonObject LedDeviceCololight::discover(const QJsonObject& /*params*/)
QJsonObject devicesDiscovered; QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType); devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
QString discoveryMethod("ssdp");
QJsonArray deviceList; QJsonArray deviceList;
#ifdef ENABLE_MDNS
QString discoveryMethod("mDNS");
deviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(
MdnsServiceRegister::getServiceType(_activeDeviceType),
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
DEFAULT_DISCOVER_TIMEOUT
);
#else
QString discoveryMethod("ssdp");
deviceList = discover(); deviceList = discover();
#endif
devicesDiscovered.insert("discoveryMethod", discoveryMethod); devicesDiscovered.insert("discoveryMethod", discoveryMethod);
devicesDiscovered.insert("devices", deviceList); devicesDiscovered.insert("devices", deviceList);
@ -669,19 +701,16 @@ QJsonObject LedDeviceCololight::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; QJsonObject properties;
QString hostName = params["host"].toString("");
quint16 apiPort = static_cast<quint16>(params["port"].toInt(API_DEFAULT_PORT));
QJsonObject propertiesDetails; QJsonObject propertiesDetails;
if (!hostName.isEmpty())
_hostName = params[CONFIG_HOST].toString("");
_port = STREAM_DEFAULT_PORT;
Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{ {
QJsonObject deviceConfig; if (ProviderUdp::open() == 0)
deviceConfig.insert("host", hostName);
deviceConfig.insert("port", apiPort);
if (ProviderUdp::init(deviceConfig))
{ {
if (getInfo()) if (getInfo())
{ {
@ -717,16 +746,14 @@ void LedDeviceCololight::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());
QString hostName = params["host"].toString(""); _hostName = params[CONFIG_HOST].toString("");
quint16 apiPort = static_cast<quint16>(params["port"].toInt(API_DEFAULT_PORT)); _port = STREAM_DEFAULT_PORT;
if (!hostName.isEmpty()) Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{ {
QJsonObject deviceConfig; if (ProviderUdp::open() == 0)
deviceConfig.insert("host", hostName);
deviceConfig.insert("port", apiPort);
if (ProviderUdp::init(deviceConfig))
{ {
if (setStateDirect(false) && setState(true)) if (setStateDirect(false) && setState(true))
{ {

View File

@ -161,6 +161,13 @@ protected:
/// ///
bool init(const QJsonObject& deviceConfig) override; bool init(const QJsonObject& deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///

View File

@ -18,15 +18,20 @@ const int MAX_NUM_LEDS = 10000; // OPC can handle 21845 LEDs - in theory, fadeca
const int OPC_SET_PIXELS = 0; // OPC command codes const int OPC_SET_PIXELS = 0; // OPC command codes
const int OPC_SYS_EX = 255; // OPC command codes const int OPC_SYS_EX = 255; // OPC command codes
const int OPC_HEADER_SIZE = 4; // OPC header size const int OPC_HEADER_SIZE = 4; // OPC header size
} //End of constants
// TCP elements // TCP elements
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const char DEFAULT_HOST[] = "127.0.0.1";
const int STREAM_DEFAULT_PORT = 7890; const int STREAM_DEFAULT_PORT = 7890;
} //End of constants
LedDeviceFadeCandy::LedDeviceFadeCandy(const QJsonObject& deviceConfig) LedDeviceFadeCandy::LedDeviceFadeCandy(const QJsonObject& deviceConfig)
: LedDevice(deviceConfig) : LedDevice(deviceConfig)
, _client(nullptr) , _client(nullptr)
, _host() , _hostName()
, _port(STREAM_DEFAULT_PORT) , _port(STREAM_DEFAULT_PORT)
{ {
} }
@ -43,7 +48,7 @@ LedDevice* LedDeviceFadeCandy::construct(const QJsonObject& deviceConfig)
bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig) bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
if (LedDevice::init(deviceConfig)) if (LedDevice::init(deviceConfig))
{ {
@ -55,11 +60,11 @@ bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
} }
else else
{ {
_host = deviceConfig["host"].toString("127.0.0.1"); _hostName = _devConfig[ CONFIG_HOST ].toString(DEFAULT_HOST);
_port = deviceConfig["port"].toInt(STREAM_DEFAULT_PORT); _port = deviceConfig[CONFIG_PORT].toInt(STREAM_DEFAULT_PORT);
//If host not configured the init fails //If host not configured the init fails
if (_host.isEmpty()) if (_hostName.isEmpty())
{ {
this->setInError("No target hostname nor IP defined"); this->setInError("No target hostname nor IP defined");
} }
@ -90,24 +95,20 @@ bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
_opc_data[1] = OPC_SET_PIXELS; _opc_data[1] = OPC_SET_PIXELS;
qToBigEndian<quint16>(static_cast<quint16>(_ledRGBCount), _opc_data.data() + 2); qToBigEndian<quint16>(static_cast<quint16>(_ledRGBCount), _opc_data.data() + 2);
if (initNetwork())
{
isInitOK = true; isInitOK = true;
} }
} }
} }
}
return isInitOK; return isInitOK;
} }
bool LedDeviceFadeCandy::initNetwork() bool LedDeviceFadeCandy::initNetwork()
{ {
bool isInitOK = false; bool isInitOK = true;
if (_client == nullptr) if (_client == nullptr)
{ {
_client = new QTcpSocket(this); _client = new QTcpSocket(this);
isInitOK = true;
} }
return isInitOK; return isInitOK;
} }
@ -118,6 +119,8 @@ int LedDeviceFadeCandy::open()
QString errortext; QString errortext;
_isDeviceReady = false; _isDeviceReady = false;
if (initNetwork())
{
// Try to open the LedDevice // Try to open the LedDevice
if (!tryConnect()) if (!tryConnect())
{ {
@ -130,6 +133,7 @@ int LedDeviceFadeCandy::open()
_isDeviceReady = true; _isDeviceReady = true;
retval = 0; retval = 0;
} }
}
return retval; return retval;
} }
@ -162,10 +166,10 @@ bool LedDeviceFadeCandy::tryConnect()
if (_client != nullptr) if (_client != nullptr)
{ {
if (_client->state() == QAbstractSocket::UnconnectedState) { if (_client->state() == QAbstractSocket::UnconnectedState) {
_client->connectToHost(_host, static_cast<quint16>(_port)); _client->connectToHost(_hostName, static_cast<quint16>(_port));
if (_client->waitForConnected(CONNECT_TIMEOUT.count())) if (_client->waitForConnected(CONNECT_TIMEOUT.count()))
{ {
Info(_log, "fadecandy/opc: connected to %s:%d on channel %d", QSTRING_CSTR(_host), _port, _channel); Info(_log, "fadecandy/opc: connected to %s:%d on channel %d", QSTRING_CSTR(_hostName), _port, _channel);
if (_setFcConfig) if (_setFcConfig)
{ {
sendFadeCandyConfiguration(); sendFadeCandyConfiguration();

View File

@ -130,7 +130,7 @@ private:
void sendFadeCandyConfiguration(); void sendFadeCandyConfiguration();
QTcpSocket* _client; QTcpSocket* _client;
QString _host; QString _hostName;
int _port; int _port;
int _channel; int _channel;
QByteArray _opc_data; QByteArray _opc_data;

View File

@ -1,16 +1,23 @@
// Local-Hyperion includes // Local-Hyperion includes
#include "LedDeviceNanoleaf.h" #include "LedDeviceNanoleaf.h"
#include <ssdp/SSDPDiscover.h> //std includes
#include <utils/QStringUtils.h> #include <sstream>
#include <iomanip>
// Qt includes // Qt includes
#include <QNetworkReply> #include <QNetworkReply>
#include <QtEndian> #include <QtEndian>
//std includes #include <ssdp/SSDPDiscover.h>
#include <sstream> #include <utils/QStringUtils.h>
#include <iomanip>
// mDNS discover
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#include <mdns/MdnsServiceRegister.h>
#endif
#include <utils/NetUtils.h>
// Constants // Constants
namespace { namespace {
@ -18,7 +25,7 @@ const bool verbose = false;
const bool verbose3 = false; const bool verbose3 = false;
// Configuration settings // Configuration settings
const char CONFIG_ADDRESS[] = "host"; const char CONFIG_HOST[] = "host";
const char CONFIG_AUTH_TOKEN[] = "token"; const char CONFIG_AUTH_TOKEN[] = "token";
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState"; const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
const char CONFIG_BRIGHTNESS[] = "brightness"; const char CONFIG_BRIGHTNESS[] = "brightness";
@ -115,6 +122,10 @@ LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject& deviceConfig)
, _extControlVersion(EXTCTRLVER_V2) , _extControlVersion(EXTCTRLVER_V2)
, _panelLedCount(0) , _panelLedCount(0)
{ {
#ifdef ENABLE_MDNS
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
#endif
} }
LedDevice* LedDeviceNanoleaf::construct(const QJsonObject& deviceConfig) LedDevice* LedDeviceNanoleaf::construct(const QJsonObject& deviceConfig)
@ -130,6 +141,8 @@ LedDeviceNanoleaf::~LedDeviceNanoleaf()
bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig) bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
{ {
bool isInitOK {false};
// Overwrite non supported/required features // Overwrite non supported/required features
setLatchTime(0); setLatchTime(0);
setRewriteTime(0); setRewriteTime(0);
@ -141,21 +154,19 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
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());
bool isInitOK = false; if ( ProviderUdp::init(deviceConfig) )
if (LedDevice::init(deviceConfig))
{ {
int configuredLedCount = this->getLedCount(); //Set hostname as per configuration and default port
Debug(_log, "DeviceType : %s", QSTRING_CSTR(this->getActiveDeviceType())); _hostName = deviceConfig[CONFIG_HOST].toString();
Debug(_log, "LedCount : %d", configuredLedCount); _port = STREAM_CONTROL_DEFAULT_PORT;
Debug(_log, "ColorOrder : %s", QSTRING_CSTR(this->getColorOrder())); _apiPort = API_DEFAULT_PORT;
Debug(_log, "RewriteTime : %d", this->getRewriteTime()); _authToken = deviceConfig[CONFIG_AUTH_TOKEN].toString();
Debug(_log, "LatchTime : %d", this->getLatchTime());
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE); _isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE);
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE); _isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
_brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX); _brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX);
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName) );
Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState); Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite); Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
Debug(_log, "Set Brightness to : %d", _brightness); Debug(_log, "Set Brightness to : %d", _brightness);
@ -178,37 +189,9 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
{ {
_leftRight = deviceConfig[CONFIG_PANEL_ORDER_LEFT_RIGHT].toInt() == 0; _leftRight = deviceConfig[CONFIG_PANEL_ORDER_LEFT_RIGHT].toInt() == 0;
} }
_startPos = deviceConfig[CONFIG_PANEL_START_POS].toInt(0); _startPos = deviceConfig[CONFIG_PANEL_START_POS].toInt(0);
//Set hostname as per configuration and_defaultHost default port isInitOK = true;
_hostName = deviceConfig[CONFIG_ADDRESS].toString();
_apiPort = API_DEFAULT_PORT;
_authToken = deviceConfig[CONFIG_AUTH_TOKEN].toString();
//If host not configured the init failed
if (_hostName.isEmpty())
{
this->setInError("No target hostname nor IP defined");
isInitOK = false;
}
else
{
if (initRestAPI(_hostName, _apiPort, _authToken))
{
// Read LedDevice configuration and validate against device configuration
if (initLedsConfiguration())
{
// Set UDP streaming host and port
_devConfig["host"] = _hostName;
_devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
isInitOK = ProviderUdp::init(_devConfig);
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName));
Debug(_log, "Port : %d", _port);
}
}
}
} }
return isInitOK; return isInitOK;
} }
@ -358,18 +341,17 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
return isInitOK; return isInitOK;
} }
bool LedDeviceNanoleaf::initRestAPI(const QString& hostname, int port, const QString& token) bool LedDeviceNanoleaf::openRestAPI()
{ {
bool isInitOK = false; bool isInitOK {true};
if (_restApi == nullptr) if (_restApi == nullptr)
{ {
_restApi = new ProviderRestApi(hostname, port); _restApi = new ProviderRestApi(_address.toString(), _apiPort);
_restApi->setLogger(_log);
//Base-path is api-path + authentication token //Base-path is api-path + authentication token
_restApi->setBasePath(QString(API_BASE_PATH).arg(token)); _restApi->setBasePath(QString(API_BASE_PATH).arg(_authToken));
isInitOK = true;
} }
return isInitOK; return isInitOK;
} }
@ -379,13 +361,27 @@ int LedDeviceNanoleaf::open()
int retval = -1; int retval = -1;
_isDeviceReady = false; _isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
{
if ( openRestAPI() )
{
// Read LedDevice configuration and validate against device configuration
if (initLedsConfiguration())
{
if (ProviderUdp::open() == 0) if (ProviderUdp::open() == 0)
{ {
// Everything is OK, device is ready // Everything is OK, device is ready
_isDeviceReady = true; _isDeviceReady = true;
retval = 0; retval = 0;
} }
}
}
else
{
_restApi->setHost(_address.toString());
_restApi->setPort(_apiPort);
}
}
return retval; return retval;
} }
@ -414,13 +410,23 @@ QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
QJsonObject devicesDiscovered; QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType); devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
QString discoveryMethod("ssdp");
QJsonArray deviceList; QJsonArray deviceList;
#ifdef ENABLE_MDNS
QString discoveryMethod("mDNS");
deviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(
MdnsServiceRegister::getServiceType(_activeDeviceType),
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
DEFAULT_DISCOVER_TIMEOUT
);
#else
QString discoveryMethod("ssdp");
deviceList = discover(); deviceList = discover();
#endif
devicesDiscovered.insert("discoveryMethod", discoveryMethod); devicesDiscovered.insert("discoveryMethod", discoveryMethod);
devicesDiscovered.insert("devices", deviceList); 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; return devicesDiscovered;
@ -431,15 +437,17 @@ 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; QJsonObject properties;
// Get Nanoleaf device properties _hostName = params[CONFIG_HOST].toString("");
QString hostName = params["host"].toString(""); _apiPort = API_DEFAULT_PORT;
_authToken = params["token"].toString("");
if (!hostName.isEmpty()) Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
{
if ( openRestAPI() )
{ {
QString authToken = params["token"].toString("");
QString filter = params["filter"].toString(""); QString filter = params["filter"].toString("");
initRestAPI(hostName, API_DEFAULT_PORT, authToken);
_restApi->setPath(filter); _restApi->setPath(filter);
// Perform request // Perform request
@ -448,10 +456,10 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
{ {
Warning(_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason())); Warning(_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
} }
properties.insert("properties", response.getBody().object()); properties.insert("properties", response.getBody().object());
}
DebugIf(verbose,_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData()); DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
} }
return properties; return properties;
} }
@ -460,12 +468,16 @@ 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());
QString hostName = params["host"].toString(""); _hostName = params[CONFIG_HOST].toString("");
if (!hostName.isEmpty()) _apiPort = API_DEFAULT_PORT;if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{ _authToken = params["token"].toString("");
QString authToken = params["token"].toString("");
initRestAPI(hostName, API_DEFAULT_PORT, authToken); Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
{
if ( openRestAPI() )
{
_restApi->setPath("identify"); _restApi->setPath("identify");
// Perform request // Perform request
@ -475,6 +487,7 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
Warning(_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason())); Warning(_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
} }
} }
}
} }
bool LedDeviceNanoleaf::powerOn() bool LedDeviceNanoleaf::powerOn()
@ -662,7 +675,7 @@ bool LedDeviceNanoleaf::restoreState()
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 { } else {
Warning (_log, "%s restoring effect failed with error: Cannot restore dynamic or solid effect. Turning device off", QSTRING_CSTR(_activeDeviceType)); Warning (_log, "%s restoring effect failed with error: Cannot restore dynamic or solid effect. Device is switched off", QSTRING_CSTR(_activeDeviceType));
_originalIsOn = false; _originalIsOn = false;
} }
break; break;

View File

@ -150,13 +150,9 @@ private:
/// ///
/// @brief Initialise the access to the REST-API wrapper /// @brief Initialise the access to the REST-API wrapper
/// ///
/// @param[in] host
/// @param[in] port
/// @param[in] authentication token
///
/// @return True, if success /// @return True, if success
/// ///
bool initRestAPI(const QString& hostname, int port, const QString& token); bool openRestAPI();
/// ///
/// @brief Get Nanoleaf device details and configuration /// @brief Get Nanoleaf device details and configuration
@ -188,8 +184,6 @@ private:
///REST-API wrapper ///REST-API wrapper
ProviderRestApi* _restApi; ProviderRestApi* _restApi;
QString _hostName;
int _apiPort; int _apiPort;
QString _authToken; QString _authToken;

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,6 @@
// Qt includes // Qt includes
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QEventLoop>
#include <QNetworkReply> #include <QNetworkReply>
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#include <QStringList> #include <QStringList>
@ -85,7 +84,7 @@ struct CiColor
/// ///
/// @return color point /// @return color point
/// ///
static CiColor rgbToCiColor(double red, double green, double blue, const CiColorTriangle &colorSpace); static CiColor rgbToCiColor(double red, double green, double blue, const CiColorTriangle& colorSpace, bool candyGamma);
/// ///
/// @param p the color point to check /// @param p the color point to check
@ -149,8 +148,9 @@ public:
/// @param bridge the bridge /// @param bridge the bridge
/// @param id the light id /// @param id the light id
/// ///
PhilipsHueLight(Logger* log, unsigned int id, QJsonObject values, unsigned int ledidx); PhilipsHueLight(Logger* log, int id, QJsonObject values, int ledidx,
~PhilipsHueLight(); int onBlackTimeToPowerOff,
int onBlackTimeToPowerOn);
/// ///
/// @param on /// @param on
@ -167,11 +167,12 @@ public:
/// ///
void setColor(const CiColor& color); void setColor(const CiColor& color);
unsigned int getId() const; int getId() const;
bool getOnOffState() const; bool getOnOffState() const;
int getTransitionTime() const; int getTransitionTime() const;
CiColor getColor() const; CiColor getColor() const;
bool hasColor() const;
/// ///
/// @return the color space of the light determined by the model id reported by the bridge. /// @return the color space of the light determined by the model id reported by the bridge.
@ -180,15 +181,21 @@ public:
void saveOriginalState(const QJsonObject& values); void saveOriginalState(const QJsonObject& values);
QString getOriginalState() const; QString getOriginalState() const;
bool isBusy();
bool isBlack(bool isBlack);
bool isWhite(bool isWhite);
void setBlack();
void blackScreenTriggered();
private: private:
Logger* _log; Logger* _log;
/// light id /// light id
unsigned int _id; int _id;
unsigned int _ledidx; int _ledidx;
bool _on; bool _on;
int _transitionTime; int _transitionTime;
CiColor _color; CiColor _color;
bool _hasColor;
/// darkes blue color in hue lamp GAMUT = black /// darkes blue color in hue lamp GAMUT = black
CiColor _colorBlack; CiColor _colorBlack;
/// The model id of the hue lamp which is used to determine the color space. /// The model id of the hue lamp which is used to determine the color space.
@ -201,6 +208,12 @@ private:
QString _originalState; QString _originalState;
CiColor _originalColor; CiColor _originalColor;
qint64 _lastSendColorTime;
qint64 _lastBlackTime;
qint64 _lastWhiteTime;
bool _blackScreenTriggered;
qint64 _onBlackTimeToPowerOff;
qint64 _onBlackTimeToPowerOn;
}; };
class LedDevicePhilipsHueBridge : public ProviderUdpSSL class LedDevicePhilipsHueBridge : public ProviderUdpSSL
@ -215,13 +228,9 @@ public:
/// ///
/// @brief Initialise the access to the REST-API wrapper /// @brief Initialise the access to the REST-API wrapper
/// ///
/// @param[in] host
/// @param[in] port
/// @param[in] authentication token
///
/// @return True, if success /// @return True, if success
/// ///
bool initRestAPI(const QString &hostname, int port, const QString &token ); bool openRestAPI();
/// ///
/// @brief Perform a REST-API GET /// @brief Perform a REST-API GET
@ -238,20 +247,18 @@ public:
/// @param route the route of the POST request. /// @param route the route of the POST request.
/// @param content the content of the POST request. /// @param content the content of the POST request.
/// ///
QJsonDocument post(const QString& route, const QString& content); QJsonDocument put(const QString& route, const QString& content, bool supressError = false);
QJsonDocument getLightState(unsigned int lightId); QJsonDocument getLightState( int lightId);
void setLightState(unsigned int lightId = 0, const QString &state = ""); void setLightState( int lightId = 0, const QString &state = "");
QMap<quint16,QJsonObject> getLightMap() const; QMap<int,QJsonObject> getLightMap() const;
QMap<quint16,QJsonObject> getGroupMap() const; QMap<int,QJsonObject> getGroupMap() const;
QString getGroupName(quint16 groupId = 0) const;
QJsonArray getGroupLights(quint16 groupId = 0) const;
QString getGroupName(int groupId = 0) const;
QJsonArray getGroupLights(int groupId = 0) const;
protected: protected:
@ -281,23 +288,66 @@ protected:
/// @brief Check, if Hue API response indicate error /// @brief Check, if Hue API response indicate error
/// ///
/// @param[in] response from Hue-Bridge in JSON-format /// @param[in] response from Hue-Bridge in JSON-format
/// @param[in] suppressError Treat an error as a warning
///
/// return True, Hue Bridge reports error /// return True, Hue Bridge reports error
/// ///
bool checkApiError(const QJsonDocument &response ); bool checkApiError(const QJsonDocument& response, bool supressError = false);
///
/// @brief Discover devices of this type available (for configuration).
/// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params) override;
///
/// @brief Get the Hue Bridge device's resource properties
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP",
/// "port" : port
/// "user" : "username",
/// "filter": "resource to query", root "/" is used, if empty
/// }
///@endcode
///
/// @param[in] params Parameters to query device
/// @return A JSON structure holding the device's properties
///
QJsonObject getProperties(const QJsonObject& params) override;
///
/// @brief Add an authorization/client-key to the Hue Bridge device
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP",
/// "port" : port
/// }
///@endcode
///
/// @param[in] params Parameters to query device
/// @return A JSON structure holding the authorization keys
///
QJsonObject addAuthorization(const QJsonObject& params) override;
///REST-API wrapper ///REST-API wrapper
ProviderRestApi* _restApi; ProviderRestApi* _restApi;
/// Ip address of the bridge
QString _hostname;
int _apiPort; int _apiPort;
/// User name for the API ("newdeveloper") /// User name for the API ("newdeveloper")
QString _username; QString _authToken;
bool _useHueEntertainmentAPI; bool _useHueEntertainmentAPI;
QJsonDocument getGroupState( unsigned int groupId ); QJsonDocument getGroupState( int groupId );
QJsonDocument setGroupState( unsigned int groupId, bool state); QJsonDocument setGroupState( int groupId, bool state);
bool isStreamOwner(const QString &streamOwner) const; bool isStreamOwner(const QString &streamOwner) const;
bool initMaps(); bool initMaps();
@ -308,6 +358,14 @@ protected:
private: private:
///
/// @brief Discover Philips-Hue devices available (for configuration).
/// Philips-Hue specific ssdp discovery
///
/// @return A JSON structure holding a list of devices found
///
QJsonArray discover();
QJsonDocument getAllBridgeInfos(); QJsonDocument getAllBridgeInfos();
void setBridgeConfig( const QJsonDocument &doc ); void setBridgeConfig( const QJsonDocument &doc );
void setLightsMap( const QJsonDocument &doc ); void setLightsMap( const QJsonDocument &doc );
@ -324,8 +382,8 @@ private:
bool _isHueEntertainmentReady; bool _isHueEntertainmentReady;
QMap<quint16,QJsonObject> _lightsMap; QMap<int,QJsonObject> _lightsMap;
QMap<quint16,QJsonObject> _groupsMap; QMap<int,QJsonObject> _groupsMap;
}; };
/** /**
@ -360,34 +418,6 @@ public:
/// @return LedDevice constructed /// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig); static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// @brief Discover devices of this type available (for configuration).
/// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params) override;
///
/// @brief Get the Hue Bridge device's resource properties
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP
/// "port" : port
/// "user" : "username",
/// "filter": "resource to query", root "/" is used, if empty
/// }
///@endcode
///
/// @param[in] params Parameters to query device
/// @return A JSON structure holding the device's properties
///
QJsonObject getProperties(const QJsonObject& params) override;
/// ///
/// @brief Send an update to the device to identify it. /// @brief Send an update to the device to identify it.
/// ///
@ -412,7 +442,7 @@ public:
/// ///
unsigned int getLightsCount() const { return _lightsCount; } unsigned int getLightsCount() const { return _lightsCount; }
void setOnOffState(PhilipsHueLight& light, bool on); void setOnOffState(PhilipsHueLight& light, bool on, bool force = false);
void setTransitionTime(PhilipsHueLight& light); void setTransitionTime(PhilipsHueLight& light);
void setColor(PhilipsHueLight& light, CiColor& color); void setColor(PhilipsHueLight& light, CiColor& color);
void setState(PhilipsHueLight& light, bool on, const CiColor& color); void setState(PhilipsHueLight& light, bool on, const CiColor& color);
@ -443,13 +473,6 @@ protected:
/// ///
int open() override; int open() override;
///
/// @brief Closes the output device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
int close() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///
@ -465,7 +488,7 @@ protected:
/// Depending on the configuration, the device may store its current state for later restore. /// Depending on the configuration, the device may store its current state for later restore.
/// @see powerOn, storeState /// @see powerOn, storeState
/// ///
/// @return True if success /// @return True, if success
/// ///
bool switchOn() override; bool switchOn() override;
@ -518,28 +541,17 @@ protected:
/// ///
bool restoreState() override; bool restoreState() override;
private slots:
void noSignalTimeout();
private: private:
bool initLeds(); bool initLeds();
///
/// @brief Creates new PhilipsHueLight(s) based on user lightid with bridge feedback
///
/// @param map Map of lightid/value pairs of bridge
///
void newLights(QMap<quint16, QJsonObject> map);
bool setLights(); 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<quint16, QJsonObject> &map); bool updateLights(const QMap<int, QJsonObject> &map);
/// ///
/// @brief Set the number of LEDs supported by the device. /// @brief Set the number of LEDs supported by the device.
@ -554,13 +566,9 @@ private:
bool startStream(); bool startStream();
bool stopStream(); bool stopStream();
void writeStream(); void writeStream(bool flush = false);
int writeSingleLights(const std::vector<ColorRgb>& ledValues); int writeSingleLights(const std::vector<ColorRgb>& ledValues);
bool noSignalDetection();
void stopBlackTimeoutTimer();
QByteArray prepareStreamData() const; QByteArray prepareStreamData() const;
/// ///
@ -574,32 +582,28 @@ private:
bool _isInitLeds; bool _isInitLeds;
/// Array of the light ids. /// Array of the light ids.
std::vector<quint16> _lightIds; std::vector<int> _lightIds;
/// Array to save the lamps. /// Array to save the lamps.
std::vector<PhilipsHueLight> _lights; std::vector<PhilipsHueLight> _lights;
unsigned int _lightsCount; int _lightsCount;
quint16 _groupId; int _groupId;
double _brightnessMin;
double _brightnessMax;
bool _allLightsBlack;
QTimer* _blackLightsTimer;
int _blackLightsTimeout; int _blackLightsTimeout;
double _brightnessThreshold; double _blackLevel;
int _onBlackTimeToPowerOff;
int _handshake_timeout_min; int _onBlackTimeToPowerOn;
int _handshake_timeout_max; bool _candyGamma;
int _ssl_read_timeout;
// TODO: Check what is the correct class
uint32_t _handshake_timeout_min;
uint32_t _handshake_timeout_max;
bool _stopConnection; bool _stopConnection;
QString _groupName; QString _groupName;
QString _streamOwner; QString _streamOwner;
int start_retry_left; qint64 _lastConfirm;
int stop_retry_left; int _lastId;
bool _groupStreamState;
}; };

View File

@ -65,13 +65,6 @@ bool LedDeviceRazer::init(const QJsonObject& deviceConfig)
// Initialise sub-class // Initialise sub-class
if (LedDevice::init(deviceConfig)) if (LedDevice::init(deviceConfig))
{ {
// Initialise LedDevice configuration and execution environment
uint configuredLedCount = this->getLedCount();
Debug(_log, "DeviceType : %s", QSTRING_CSTR(this->getActiveDeviceType()));
Debug(_log, "LedCount : %u", configuredLedCount);
Debug(_log, "ColorOrder : %s", QSTRING_CSTR(this->getColorOrder()));
Debug(_log, "LatchTime : %d", this->getLatchTime());
Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
//Razer Chroma SDK allows localhost connection only //Razer Chroma SDK allows localhost connection only
_hostname = API_DEFAULT_HOST; _hostname = API_DEFAULT_HOST;
@ -86,6 +79,7 @@ bool LedDeviceRazer::init(const QJsonObject& deviceConfig)
Debug(_log, "Razer Device : %s", QSTRING_CSTR(_razerDeviceType)); Debug(_log, "Razer Device : %s", QSTRING_CSTR(_razerDeviceType));
Debug(_log, "Single Color : %d", _isSingleColor); Debug(_log, "Single Color : %d", _isSingleColor);
int configuredLedCount = this->getLedCount();
if (resolveDeviceProperties(_razerDeviceType)) if (resolveDeviceProperties(_razerDeviceType))
{ {
if (_isSingleColor && configuredLedCount > 1) if (_isSingleColor && configuredLedCount > 1)
@ -125,6 +119,8 @@ bool LedDeviceRazer::initRestAPI(const QString& hostname, int port)
if (_restApi == nullptr) if (_restApi == nullptr)
{ {
_restApi = new ProviderRestApi(hostname, port); _restApi = new ProviderRestApi(hostname, port);
_restApi->setLogger(_log);
_restApi->setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); _restApi->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
isInitOK = true; isInitOK = true;

View File

@ -1,6 +1,13 @@
#include "LedDeviceTpm2net.h" #include "LedDeviceTpm2net.h"
#include <utils/NetUtils.h>
// Constants
namespace {
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const ushort TPM2_DEFAULT_PORT = 65506; const ushort TPM2_DEFAULT_PORT = 65506;
}
LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig) LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig)
: ProviderUdp(deviceConfig) : ProviderUdp(deviceConfig)
@ -20,13 +27,14 @@ LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig) bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
_port = TPM2_DEFAULT_PORT;
// Initialise sub-class // Initialise sub-class
if ( ProviderUdp::init(deviceConfig) ) if ( ProviderUdp::init(deviceConfig) )
{ {
_hostName = _devConfig[ CONFIG_HOST ].toString();
_port = deviceConfig[CONFIG_PORT].toInt(TPM2_DEFAULT_PORT);
_tpm2_max = deviceConfig["max-packet"].toInt(170); _tpm2_max = deviceConfig["max-packet"].toInt(170);
_tpm2ByteCount = 3 * _ledCount; _tpm2ByteCount = 3 * _ledCount;
_tpm2TotalPackets = (_tpm2ByteCount / _tpm2_max) + ((_tpm2ByteCount % _tpm2_max) != 0); _tpm2TotalPackets = (_tpm2ByteCount / _tpm2_max) + ((_tpm2ByteCount % _tpm2_max) != 0);
@ -38,6 +46,23 @@ bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
return isInitOK; return isInitOK;
} }
int LedDeviceTpm2net::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
int LedDeviceTpm2net::write(const std::vector<ColorRgb> &ledValues) int LedDeviceTpm2net::write(const std::vector<ColorRgb> &ledValues)
{ {
int retVal = 0; int retVal = 0;

View File

@ -41,6 +41,13 @@ private:
/// ///
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///

View File

@ -9,7 +9,16 @@
#include <QHostInfo> #include <QHostInfo>
#include <utils/NetUtils.h>
// Constants
namespace {
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const ushort ARTNET_DEFAULT_PORT = 6454; const ushort ARTNET_DEFAULT_PORT = 6454;
}
LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject &deviceConfig) LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject &deviceConfig)
: ProviderUdp(deviceConfig) : ProviderUdp(deviceConfig)
@ -23,13 +32,14 @@ LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig) bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
_port = ARTNET_DEFAULT_PORT;
// Initialise sub-class // Initialise sub-class
if ( ProviderUdp::init(deviceConfig) ) if ( ProviderUdp::init(deviceConfig) )
{ {
_hostName = _devConfig[ CONFIG_HOST ].toString();
_port = deviceConfig[CONFIG_PORT].toInt(ARTNET_DEFAULT_PORT);
_artnet_universe = deviceConfig["universe"].toInt(1); _artnet_universe = deviceConfig["universe"].toInt(1);
_artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3); _artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
@ -38,6 +48,23 @@ bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
return isInitOK; return isInitOK;
} }
int LedDeviceUdpArtNet::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
// populates the headers // populates the headers
void LedDeviceUdpArtNet::prepare(unsigned this_universe, unsigned this_sequence, unsigned this_dmxChannelCount) void LedDeviceUdpArtNet::prepare(unsigned this_universe, unsigned this_sequence, unsigned this_dmxChannelCount)
{ {

View File

@ -69,6 +69,13 @@ private:
/// ///
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///

View File

@ -0,0 +1,163 @@
#include "LedDeviceUdpDdp.h"
#include <QtEndian>
#include <utils/NetUtils.h>
// DDP header format
// header is 10 bytes (14 if TIME flag used)
struct ddp_hdr_struct {
uint8_t flags1;
uint8_t flags2;
uint8_t type;
uint8_t id;
uint32_t offset;
uint16_t len;
};
// Constants
namespace {
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const ushort DDP_DEFAULT_PORT = 4048;
namespace DDP {
// DDP protocol header definitions
struct Header {
uint8_t flags1;
uint8_t flags2;
uint8_t type;
uint8_t id;
uint8_t offset[4];
uint8_t len[2];
};
static constexpr int HEADER_LEN = (sizeof(struct Header)); // header is 10 bytes (14 if TIME flag used)
static constexpr int MAX_LEDS = 480;
static constexpr int CHANNELS_PER_PACKET = MAX_LEDS*3;
namespace flags1 {
static constexpr auto VER_MASK = 0xc0;
static constexpr auto VER1 = 0x40;
static constexpr auto PUSH = 0x01;
static constexpr auto QUERY = 0x02;
static constexpr auto REPLY = 0x04;
static constexpr auto STORAGE = 0x08;
static constexpr auto TIME = 0x10;
} // namespace flags1
namespace id {
static constexpr auto DISPLAY = 1;
static constexpr auto CONTROL = 246;
static constexpr auto CONFIG = 250;
static constexpr auto STATUS = 251;
static constexpr auto DMXTRANSIT = 254;
static constexpr auto ALLDEVICES = 255;
} // namespace id
} // namespace DDP
} //End of constants
LedDeviceUdpDdp::LedDeviceUdpDdp(const QJsonObject &deviceConfig)
: ProviderUdp(deviceConfig)
,_packageSequenceNumber(0)
{
}
LedDevice* LedDeviceUdpDdp::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceUdpDdp(deviceConfig);
}
bool LedDeviceUdpDdp::init(const QJsonObject &deviceConfig)
{
bool isInitOK {false};
if ( ProviderUdp::init(deviceConfig) )
{
_hostName = _devConfig[ CONFIG_HOST ].toString();
_port = deviceConfig[CONFIG_PORT].toInt(DDP_DEFAULT_PORT);
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName) );
Debug(_log, "Port : %d", _port );
_ddpData.resize(DDP::HEADER_LEN + DDP::CHANNELS_PER_PACKET);
_ddpData[0] = DDP::flags1::VER1; // flags1
_ddpData[1] = 0; // flags2
_ddpData[2] = 1; // type
_ddpData[3] = DDP::id::DISPLAY; // id
isInitOK = true;
}
return isInitOK;
}
int LedDeviceUdpDdp::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
int LedDeviceUdpDdp::write(const std::vector<ColorRgb> &ledValues)
{
int rc {0};
int channelCount = static_cast<int>(_ledCount) * 3; // 1 channel for every R,G,B value
int packetCount = ((channelCount-1) / DDP::CHANNELS_PER_PACKET) + 1;
int channel = 0;
_ddpData[0] = DDP::flags1::VER1;
for (int currentPacket = 0; currentPacket < packetCount; currentPacket++)
{
if (_packageSequenceNumber > 15)
{
_packageSequenceNumber = 0;
}
int packetSize = DDP::CHANNELS_PER_PACKET;
if (currentPacket == (packetCount - 1))
{
// last packet, set the push flag
/*0*/_ddpData[0] = DDP::flags1::VER1 | DDP::flags1::PUSH;
if (channelCount % DDP::CHANNELS_PER_PACKET != 0)
{
packetSize = channelCount % DDP::CHANNELS_PER_PACKET;
}
}
/*1*/_ddpData[1] = static_cast<char>(_packageSequenceNumber++ & 0x0F);
/*4*/qToBigEndian<quint32>(static_cast<quint32>(channel), _ddpData.data() + 4);
/*8*/qToBigEndian<quint16>(static_cast<quint16>(packetSize), _ddpData.data() + 8);
_ddpData.replace(DDP::HEADER_LEN, channel, reinterpret_cast<const char*>(ledValues.data())+channel, packetSize);
_ddpData.resize(DDP::HEADER_LEN + packetSize);
rc = writeBytes(_ddpData);
if (rc != 0)
{
break;
}
channel += packetSize;
}
return rc;
}

View File

@ -0,0 +1,62 @@
#ifndef LEDEVICEUDPDDP_H
#define LEDEVICEUDPDDP_H
// hyperion includes
#include "ProviderUdp.h"
///
/// Implementation of the LedDevice interface for sending LED colors via UDP and the Distributed Display Protocol (DDP)
/// http://www.3waylabs.com/ddp/#Data%20Types
///
class LedDeviceUdpDdp : public virtual ProviderUdp
{
public:
///
/// @brief Constructs a LED-device fed via DDP
///
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpDdp(const QJsonObject &deviceConfig);
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
protected:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
int write(const std::vector<ColorRgb> & ledValues) override;
private:
QByteArray _ddpData;
int _packageSequenceNumber;
};
#endif // LEDEVICEUDPDDP_H

View File

@ -8,6 +8,13 @@
// hyperion local includes // hyperion local includes
#include "LedDeviceUdpE131.h" #include "LedDeviceUdpE131.h"
#include <utils/NetUtils.h>
// Constants
namespace {
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const ushort E131_DEFAULT_PORT = 5568; const ushort E131_DEFAULT_PORT = 5568;
@ -23,6 +30,7 @@ const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002;
//#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds //#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds
//#define E131_DISCOVERY_UNIVERSE 64214 //#define E131_DISCOVERY_UNIVERSE 64214
const int DMX_MAX = 512; // 512 usable slots const int DMX_MAX = 512; // 512 usable slots
}
LedDeviceUdpE131::LedDeviceUdpE131(const QJsonObject &deviceConfig) LedDeviceUdpE131::LedDeviceUdpE131(const QJsonObject &deviceConfig)
: ProviderUdp(deviceConfig) : ProviderUdp(deviceConfig)
@ -36,13 +44,14 @@ LedDevice* LedDeviceUdpE131::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig) bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
_port = E131_DEFAULT_PORT;
// Initialise sub-class // Initialise sub-class
if ( ProviderUdp::init(deviceConfig) ) if ( ProviderUdp::init(deviceConfig) )
{ {
_hostName = _devConfig[ CONFIG_HOST ].toString();
_port = deviceConfig[CONFIG_PORT].toInt(E131_DEFAULT_PORT);
_e131_universe = deviceConfig["universe"].toInt(1); _e131_universe = deviceConfig["universe"].toInt(1);
_e131_source_name = deviceConfig["source-name"].toString("hyperion on "+QHostInfo::localHostName()); _e131_source_name = deviceConfig["source-name"].toString("hyperion on "+QHostInfo::localHostName());
QString _json_cid = deviceConfig["cid"].toString(""); QString _json_cid = deviceConfig["cid"].toString("");
@ -70,6 +79,23 @@ bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
return isInitOK; return isInitOK;
} }
int LedDeviceUdpE131::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
// populates the headers // populates the headers
void LedDeviceUdpE131::prepare(unsigned this_universe, unsigned this_dmxChannelCount) void LedDeviceUdpE131::prepare(unsigned this_universe, unsigned this_dmxChannelCount)
{ {

View File

@ -114,6 +114,13 @@ private:
/// ///
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///

View File

@ -1,8 +1,12 @@
#include "LedDeviceUdpH801.h" #include "LedDeviceUdpH801.h"
#include <utils/NetUtils.h>
// Constants // Constants
namespace { namespace {
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const ushort H801_DEFAULT_PORT = 30977; const ushort H801_DEFAULT_PORT = 30977;
const char H801_DEFAULT_HOST[] = "255.255.255.255"; const char H801_DEFAULT_HOST[] = "255.255.255.255";
@ -20,16 +24,17 @@ LedDevice* LedDeviceUdpH801::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig) bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
/* The H801 port is fixed */ /* The H801 port is fixed */
_latchTime_ms = 10; _latchTime_ms = 10;
_port = H801_DEFAULT_PORT;
_defaultHost = H801_DEFAULT_HOST;
// Initialise sub-class // Initialise sub-class
if ( ProviderUdp::init(deviceConfig) ) if ( ProviderUdp::init(deviceConfig) )
{ {
_hostName = _devConfig[ CONFIG_HOST ].toString(H801_DEFAULT_HOST);
_port = deviceConfig[CONFIG_PORT].toInt(H801_DEFAULT_PORT);
_ids.clear(); _ids.clear();
QJsonArray lArray = deviceConfig["lightIds"].toArray(); QJsonArray lArray = deviceConfig["lightIds"].toArray();
for (int i = 0; i < lArray.size(); i++) for (int i = 0; i < lArray.size(); i++)
@ -47,14 +52,28 @@ bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig)
_message[_prefix_size + _colors + i * _id_size + 1] = (_ids[i] >> 0x08) & 0xFF; _message[_prefix_size + _colors + i * _id_size + 1] = (_ids[i] >> 0x08) & 0xFF;
_message[_prefix_size + _colors + i * _id_size + 2] = (_ids[i] >> 0x10) & 0xFF; _message[_prefix_size + _colors + i * _id_size + 2] = (_ids[i] >> 0x10) & 0xFF;
} }
Debug(_log, "H801 using %s:%d", _address.toString().toStdString().c_str(), _port);
isInitOK = true; isInitOK = true;
} }
return isInitOK; return isInitOK;
} }
int LedDeviceUdpH801::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
int LedDeviceUdpH801::write(const std::vector<ColorRgb> &ledValues) int LedDeviceUdpH801::write(const std::vector<ColorRgb> &ledValues)
{ {
ColorRgb color = ledValues[0]; ColorRgb color = ledValues[0];

View File

@ -37,6 +37,13 @@ private:
/// ///
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///

View File

@ -1,10 +1,14 @@
#include "LedDeviceUdpRaw.h" #include "LedDeviceUdpRaw.h"
#include <utils/NetUtils.h>
// Constants // Constants
namespace { namespace {
const bool verbose = false; const bool verbose = false;
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const ushort RAW_DEFAULT_PORT=5568; const ushort RAW_DEFAULT_PORT=5568;
const int UDP_MAX_LED_NUM = 490; const int UDP_MAX_LED_NUM = 490;
@ -22,33 +26,46 @@ LedDevice* LedDeviceUdpRaw::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig) bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig)
{ {
_port = RAW_DEFAULT_PORT; bool isInitOK {false};
bool isInitOK = false; if ( ProviderUdp::init(deviceConfig) )
if ( LedDevice::init(deviceConfig) )
{ {
// Initialise LedDevice configuration and execution environment if (this->getLedCount() > UDP_MAX_LED_NUM)
int configuredLedCount = this->getLedCount();
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
Debug(_log, "LedCount : %d", configuredLedCount);
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
Debug(_log, "LatchTime : %d", this->getLatchTime());
if (configuredLedCount > UDP_MAX_LED_NUM)
{ {
QString errorReason = QString("Device type %1 can only be run with maximum %2 LEDs!").arg(this->getActiveDeviceType()).arg(UDP_MAX_LED_NUM); QString errorReason = QString("Device type %1 can only be run with maximum %2 LEDs for streaming protocol = UDP-RAW!").arg(this->getActiveDeviceType()).arg(UDP_MAX_LED_NUM);
this->setInError ( errorReason ); this->setInError ( errorReason );
isInitOK = false;
} }
else else
{ {
// Initialise sub-class _hostName = deviceConfig[ CONFIG_HOST ].toString();
isInitOK = ProviderUdp::init(deviceConfig); _port = deviceConfig[CONFIG_PORT].toInt(RAW_DEFAULT_PORT);
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName) );
Debug(_log, "Port : %d", _port );
isInitOK = true;
} }
} }
return isInitOK; return isInitOK;
} }
int LedDeviceUdpRaw::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
{
if (ProviderUdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
return retval;
}
int LedDeviceUdpRaw::write(const std::vector<ColorRgb> &ledValues) int LedDeviceUdpRaw::write(const std::vector<ColorRgb> &ledValues)
{ {
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data()); const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data());
@ -59,8 +76,11 @@ int LedDeviceUdpRaw::write(const std::vector<ColorRgb> &ledValues)
QJsonObject LedDeviceUdpRaw::getProperties(const QJsonObject& params) QJsonObject LedDeviceUdpRaw::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; QJsonObject properties;
Info(_log, "Get properties for %s", QSTRING_CSTR(_activeDeviceType));
QJsonObject propertiesDetails; QJsonObject propertiesDetails;
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM); propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);

View File

@ -7,7 +7,7 @@
/// ///
/// Implementation of the LedDevice interface for sending LED colors via UDP /// Implementation of the LedDevice interface for sending LED colors via UDP
/// ///
class LedDeviceUdpRaw : public ProviderUdp class LedDeviceUdpRaw : public virtual ProviderUdp
{ {
public: public:
@ -44,6 +44,13 @@ protected:
/// ///
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///

View File

@ -1,11 +1,18 @@
// Local-Hyperion includes // Local-Hyperion includes
#include "LedDeviceWled.h" #include "LedDeviceWled.h"
#include <chrono>
#include <utils/QStringUtils.h> #include <utils/QStringUtils.h>
#include <utils/WaitTime.h> #include <utils/WaitTime.h>
#include <QThread>
#include <chrono> // mDNS discover
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#include <mdns/MdnsServiceRegister.h>
#endif
#include <utils/NetUtils.h>
#include <utils/version.hpp>
// Constants // Constants
namespace { namespace {
@ -13,16 +20,22 @@ namespace {
const bool verbose = false; const bool verbose = false;
// Configuration settings // Configuration settings
const char CONFIG_ADDRESS[] = "host"; const char CONFIG_HOST[] = "host";
const char CONFIG_STREAM_PROTOCOL[] = "streamProtocol";
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState"; const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
const char CONFIG_BRIGHTNESS[] = "brightness"; const char CONFIG_BRIGHTNESS[] = "brightness";
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness"; const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
const char CONFIG_SYNC_OVERWRITE[] = "overwriteSync"; const char CONFIG_SYNC_OVERWRITE[] = "overwriteSync";
// UDP elements const char DEFAULT_STREAM_PROTOCOL[] = "DDP";
const quint16 STREAM_DEFAULT_PORT = 19446;
// UDP-RAW
const int UDP_STREAM_DEFAULT_PORT = 19446;
const int UDP_MAX_LED_NUM = 490; const int UDP_MAX_LED_NUM = 490;
// DDP
const char WLED_VERSION_DDP[] = "0.11.0";
// WLED JSON-API elements // WLED JSON-API elements
const int API_DEFAULT_PORT = -1; //Use default port per communication scheme const int API_DEFAULT_PORT = -1; //Use default port per communication scheme
@ -46,7 +59,7 @@ constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 2000 };
} //End of constants } //End of constants
LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig) LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
: ProviderUdp(deviceConfig) : ProviderUdp(deviceConfig), LedDeviceUdpDdp(deviceConfig), LedDeviceUdpRaw(deviceConfig)
,_restApi(nullptr) ,_restApi(nullptr)
,_apiPort(API_DEFAULT_PORT) ,_apiPort(API_DEFAULT_PORT)
,_isBrightnessOverwrite(DEFAULT_IS_BRIGHTNESS_OVERWRITE) ,_isBrightnessOverwrite(DEFAULT_IS_BRIGHTNESS_OVERWRITE)
@ -54,7 +67,12 @@ LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
,_isSyncOverwrite(DEFAULT_IS_SYNC_OVERWRITE) ,_isSyncOverwrite(DEFAULT_IS_SYNC_OVERWRITE)
,_originalStateUdpnSend(false) ,_originalStateUdpnSend(false)
,_originalStateUdpnRecv(true) ,_originalStateUdpnRecv(true)
,_isStreamDDP(true)
{ {
#ifdef ENABLE_MDNS
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
#endif
} }
LedDeviceWled::~LedDeviceWled() LedDeviceWled::~LedDeviceWled()
@ -70,25 +88,30 @@ LedDevice* LedDeviceWled::construct(const QJsonObject &deviceConfig)
bool LedDeviceWled::init(const QJsonObject &deviceConfig) bool LedDeviceWled::init(const QJsonObject &deviceConfig)
{ {
bool isInitOK = false; bool isInitOK {false};
// Initialise LedDevice sub-class, ProviderUdp::init will be executed later, if connectivity is defined QString streamProtocol = _devConfig[CONFIG_STREAM_PROTOCOL].toString(DEFAULT_STREAM_PROTOCOL);
if ( LedDevice::init(deviceConfig) )
{
// Initialise LedDevice configuration and execution environment
int configuredLedCount = this->getLedCount();
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
Debug(_log, "LedCount : %d", configuredLedCount);
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
Debug(_log, "LatchTime : %d", this->getLatchTime());
if (configuredLedCount > UDP_MAX_LED_NUM) if (streamProtocol != DEFAULT_STREAM_PROTOCOL)
{ {
QString errorReason = QString("Device type %1 can only be run with maximum %2 LEDs!").arg(this->getActiveDeviceType()).arg(UDP_MAX_LED_NUM); _isStreamDDP = false;
this->setInError ( errorReason ); }
return false; Debug(_log, "Stream protocol : %s", QSTRING_CSTR(streamProtocol));
Debug(_log, "Stream DDP : %d", _isStreamDDP);
if (_isStreamDDP)
{
LedDeviceUdpDdp::init(deviceConfig);
}
else
{
_devConfig["port"] = UDP_STREAM_DEFAULT_PORT;
LedDeviceUdpRaw::init(_devConfig);
} }
if (!_isDeviceInError)
{
_apiPort = API_DEFAULT_PORT;
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE); _isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE);
_isSyncOverwrite = _devConfig[CONFIG_SYNC_OVERWRITE].toBool(DEFAULT_IS_SYNC_OVERWRITE); _isSyncOverwrite = _devConfig[CONFIG_SYNC_OVERWRITE].toBool(DEFAULT_IS_SYNC_OVERWRITE);
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE); _isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
@ -99,57 +122,78 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite); Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
Debug(_log, "Set Brightness to : %d", _brightness); Debug(_log, "Set Brightness to : %d", _brightness);
//Set hostname as per configuration isInitOK = true;
QString hostName = deviceConfig[ CONFIG_ADDRESS ].toString();
//If host not configured the init fails
if ( hostName.isEmpty() )
{
this->setInError("No target hostname nor IP defined");
return false;
}
else
{
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
_hostname = addressparts[0];
if ( addressparts.size() > 1 )
{
_apiPort = addressparts[1].toInt();
}
else
{
_apiPort = API_DEFAULT_PORT;
} }
if ( initRestAPI( _hostname, _apiPort ) )
{
// Update configuration with hostname without port
_devConfig["host"] = _hostname;
_devConfig["port"] = STREAM_DEFAULT_PORT;
isInitOK = ProviderUdp::init(_devConfig);
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
Debug(_log, "Port : %d", _port);
}
}
}
return isInitOK; return isInitOK;
} }
bool LedDeviceWled::initRestAPI(const QString &hostname, int port) bool LedDeviceWled::openRestAPI()
{ {
bool isInitOK = false; bool isInitOK {true};
if ( _restApi == nullptr ) if ( _restApi == nullptr )
{ {
_restApi = new ProviderRestApi(hostname, port); _restApi = new ProviderRestApi(_address.toString(), _apiPort);
_restApi->setBasePath( API_BASE_PATH ); _restApi->setLogger(_log);
isInitOK = true; _restApi->setBasePath( API_BASE_PATH );
} }
else
{
_restApi->setHost(_address.toString());
_restApi->setPort(_apiPort);
}
return isInitOK; return isInitOK;
} }
int LedDeviceWled::open()
{
int retval = -1;
_isDeviceReady = false;
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
{
if ( openRestAPI() )
{
if (_isStreamDDP)
{
if (LedDeviceUdpDdp::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
else
{
if (LedDeviceUdpRaw::open() == 0)
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
}
}
return retval;
}
int LedDeviceWled::close()
{
int retval = -1;
if (_isStreamDDP)
{
retval = LedDeviceUdpDdp::close();
}
else
{
retval = LedDeviceUdpRaw::close();
}
return retval;
}
QString LedDeviceWled::getOnOffRequest(bool isOn) const QString LedDeviceWled::getOnOffRequest(bool isOn) const
{ {
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE; QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
@ -316,6 +360,16 @@ QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/)
devicesDiscovered.insert("ledDeviceType", _activeDeviceType ); devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
QJsonArray deviceList; QJsonArray deviceList;
#ifdef ENABLE_MDNS
QString discoveryMethod("mDNS");
deviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(
MdnsServiceRegister::getServiceType(_activeDeviceType),
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
DEFAULT_DISCOVER_TIMEOUT
);
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
#endif
devicesDiscovered.insert("devices", deviceList); 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() );
@ -327,27 +381,16 @@ QJsonObject LedDeviceWled::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; QJsonObject properties;
QString hostName = params["host"].toString(""); _hostName = params[CONFIG_HOST].toString("");
_apiPort = API_DEFAULT_PORT;
if ( !hostName.isEmpty() ) Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
{
if ( openRestAPI() )
{ {
QString filter = params["filter"].toString(""); QString filter = params["filter"].toString("");
// Resolve hostname and port (or use default API port)
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
if ( addressparts.size() > 1)
{
apiPort = addressparts[1].toInt();
}
else
{
apiPort = API_DEFAULT_PORT;
}
initRestAPI(apiHost, apiPort);
_restApi->setPath(filter); _restApi->setPath(filter);
httpResponse response = _restApi->get(); httpResponse response = _restApi->get();
@ -357,11 +400,26 @@ QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
} }
QJsonObject propertiesDetails = response.getBody().object(); QJsonObject propertiesDetails = response.getBody().object();
semver::version currentVersion {""};
if (currentVersion.setVersion(propertiesDetails.value("ver").toString().toStdString()))
{
semver::version ddpVersion{WLED_VERSION_DDP};
if (currentVersion < ddpVersion)
{
Warning(_log, "DDP streaming not supported by your WLED device version [%s], minimum version expected [%s]. Fall back to UDP-Streaming (%d LEDs max)", currentVersion.getVersion().c_str(), ddpVersion.getVersion().c_str(), UDP_MAX_LED_NUM);
if (!propertiesDetails.isEmpty()) if (!propertiesDetails.isEmpty())
{ {
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM); propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);
} }
}
else
{
Info(_log, "DDP streaming is supported by your WLED device version [%s]. No limitation in number of LEDs.", currentVersion.getVersion().c_str(), ddpVersion.getVersion().c_str());
}
}
properties.insert("properties", propertiesDetails); properties.insert("properties", propertiesDetails);
}
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() ); DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
} }
@ -372,26 +430,15 @@ void LedDeviceWled::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());
QString hostName = params["host"].toString(""); _hostName = params[CONFIG_HOST].toString("");
_apiPort = API_DEFAULT_PORT;
if ( !hostName.isEmpty() ) Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
{ {
// Resolve hostname and port (or use default API port) if ( openRestAPI() )
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
if ( addressparts.size() > 1)
{ {
apiPort = addressparts[1].toInt();
}
else
{
apiPort = API_DEFAULT_PORT;
}
initRestAPI(apiHost, apiPort);
_isRestoreOrigState = true; _isRestoreOrigState = true;
storeState(); storeState();
@ -402,11 +449,21 @@ void LedDeviceWled::identify(const QJsonObject& params)
restoreState(); restoreState();
} }
}
} }
int LedDeviceWled::write(const std::vector<ColorRgb> &ledValues) int LedDeviceWled::write(const std::vector<ColorRgb> &ledValues)
{ {
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data()); int rc {0};
return writeBytes( _ledRGBCount, dataPtr); if (_isStreamDDP)
{
rc = LedDeviceUdpDdp::write(ledValues);
}
else
{
rc = LedDeviceUdpRaw::write(ledValues);
}
return rc;
} }

View File

@ -4,12 +4,13 @@
// LedDevice includes // LedDevice includes
#include <leddevice/LedDevice.h> #include <leddevice/LedDevice.h>
#include "ProviderRestApi.h" #include "ProviderRestApi.h"
#include "ProviderUdp.h" #include "LedDeviceUdpDdp.h"
#include "LedDeviceUdpRaw.h"
/// ///
/// Implementation of a WLED-device /// Implementation of a WLED-device
/// ///
class LedDeviceWled : public ProviderUdp class LedDeviceWled : public LedDeviceUdpDdp, LedDeviceUdpRaw
{ {
public: public:
@ -81,6 +82,20 @@ protected:
/// ///
bool init(const QJsonObject &deviceConfig) override; bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
///
/// @brief Closes the UDP device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
int close() override;
/// ///
/// @brief Writes the RGB-Color values to the LEDs. /// @brief Writes the RGB-Color values to the LEDs.
/// ///
@ -127,11 +142,9 @@ private:
/// ///
/// @brief Initialise the access to the REST-API wrapper /// @brief Initialise the access to the REST-API wrapper
/// ///
/// @param[in] host
/// @param[in] port
/// @return True, if success /// @return True, if success
/// ///
bool initRestAPI(const QString &hostname, int port ); bool openRestAPI();
/// ///
/// @brief Get command to power WLED-device on or off /// @brief Get command to power WLED-device on or off
@ -148,10 +161,12 @@ private:
bool sendStateUpdateRequest(const QString &request); bool sendStateUpdateRequest(const QString &request);
QString resolveAddress (const QString& hostName);
///REST-API wrapper ///REST-API wrapper
ProviderRestApi* _restApi; ProviderRestApi* _restApi;
QString _hostname; QString _hostAddress;
int _apiPort; int _apiPort;
QJsonObject _originalStateProperties; QJsonObject _originalStateProperties;
@ -162,6 +177,8 @@ private:
bool _isSyncOverwrite; bool _isSyncOverwrite;
bool _originalStateUdpnSend; bool _originalStateUdpnSend;
bool _originalStateUdpnRecv; bool _originalStateUdpnRecv;
bool _isStreamDDP;
}; };
#endif // LEDDEVICEWLED_H #endif // LEDDEVICEWLED_H

Some files were not shown because too many files have changed in this diff Show More