mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
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:
@@ -32,9 +32,9 @@ add_subdirectory(db)
|
||||
add_subdirectory(api)
|
||||
add_subdirectory(ssdp)
|
||||
|
||||
if(ENABLE_AVAHI)
|
||||
add_subdirectory(bonjour)
|
||||
endif()
|
||||
if(ENABLE_MDNS)
|
||||
add_subdirectory(mdns)
|
||||
endif()
|
||||
|
||||
if(ENABLE_EFFECTENGINE)
|
||||
add_subdirectory(effectengine)
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
|
||||
// hyperion includes
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
@@ -23,9 +24,6 @@
|
||||
#include <utils/ColorSys.h>
|
||||
#include <utils/Process.h>
|
||||
|
||||
// bonjour wrapper
|
||||
#include <bonjour/bonjourbrowserwrapper.h>
|
||||
|
||||
// ledmapping int <> string transform methods
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
|
||||
@@ -44,17 +42,13 @@ API::API(Logger *log, bool localConnection, QObject *parent)
|
||||
// Init
|
||||
_log = log;
|
||||
_authManager = AuthManager::getInstance();
|
||||
_instanceManager = HyperionIManager::getInstance();
|
||||
_instanceManager = HyperionIManager::getInstance();
|
||||
_localConnection = localConnection;
|
||||
|
||||
_authorized = false;
|
||||
_adminAuthorized = false;
|
||||
|
||||
_hyperion = _instanceManager->getHyperionInstance(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(_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()
|
||||
{
|
||||
assert(_hyperion);
|
||||
_hyperion = _instanceManager->getHyperionInstance(0);
|
||||
|
||||
bool apiAuthRequired = _authManager->isAuthRequired();
|
||||
|
||||
@@ -336,13 +330,6 @@ void API::stopInstance(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)
|
||||
{
|
||||
if (_adminAuthorized)
|
||||
|
@@ -13,7 +13,7 @@
|
||||
"subcommand": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["discover","getProperties","identify"]
|
||||
"enum": [ "discover", "getProperties", "identify", "addAuthorization" ]
|
||||
},
|
||||
"ledDeviceType": {
|
||||
"type" : "string",
|
||||
|
28
libsrc/api/JSONRPC_schema/schema-service.json
Normal file
28
libsrc/api/JSONRPC_schema/schema-service.json
Normal 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
|
||||
}
|
@@ -5,7 +5,7 @@
|
||||
"command": {
|
||||
"type" : "string",
|
||||
"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" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
<file alias="schema-instance">JSONRPC_schema/schema-instance.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-service">JSONRPC_schema/schema-service.json</file>
|
||||
<!-- 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-correction">JSONRPC_schema/schema-hyperion-classic.json</file>
|
||||
|
@@ -63,11 +63,6 @@
|
||||
#include <utils/Process.h>
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
// bonjour wrapper
|
||||
#ifdef ENABLE_AVAHI
|
||||
#include <bonjour/bonjourbrowserwrapper.h>
|
||||
#endif
|
||||
|
||||
// ledmapping int <> string transform methods
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
|
||||
@@ -77,6 +72,15 @@
|
||||
// auth manager
|
||||
#include <hyperion/AuthManager.h>
|
||||
|
||||
#ifdef ENABLE_MDNS
|
||||
// mDNS discover
|
||||
#include <mdns/MdnsBrowser.h>
|
||||
#include <mdns/MdnsServiceRegister.h>
|
||||
#else
|
||||
// ssdp discover
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#endif
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
// Constants
|
||||
@@ -98,8 +102,6 @@ void JsonAPI::initialize()
|
||||
{
|
||||
// init API, REQUIRED!
|
||||
API::init();
|
||||
// Initialise jsonCB with current instance
|
||||
_jsonCB->setSubscriptionsTo(_hyperion);
|
||||
|
||||
// setup auth interface
|
||||
connect(this, &API::onPendingTokenRequest, this, &JsonAPI::newPendingTokenRequest);
|
||||
@@ -112,7 +114,12 @@ void JsonAPI::initialize()
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
|
||||
// notify hyperion about a jsonMessageForward
|
||||
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
||||
if (_hyperion != nullptr)
|
||||
{
|
||||
// Initialise jsonCB with current instance
|
||||
_jsonCB->setSubscriptionsTo(_hyperion);
|
||||
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
|
||||
@@ -180,6 +187,12 @@ void JsonAPI::handleMessage(const QString &messageString, const QString &httpAut
|
||||
return;
|
||||
}
|
||||
proceed:
|
||||
if (_hyperion == nullptr)
|
||||
{
|
||||
sendErrorReply("Service Unavailable", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
// switch over all possible commands and handle them
|
||||
if (command == "color")
|
||||
handleColorCommand(message, command, tan);
|
||||
@@ -221,6 +234,8 @@ proceed:
|
||||
handleLedDeviceCommand(message, command, tan);
|
||||
else if (command == "inputsource")
|
||||
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
|
||||
else if (command == "clearall")
|
||||
@@ -627,6 +642,11 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
services.append("protobuffer");
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_MDNS)
|
||||
services.append("mDNS");
|
||||
#endif
|
||||
services.append("SSDP");
|
||||
|
||||
if (!availableScreenGrabbers.isEmpty() || !availableVideoGrabbers.isEmpty() || services.contains("flatbuffer") || services.contains("protobuffer"))
|
||||
{
|
||||
services.append("borderdetection");
|
||||
@@ -649,24 +669,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
info["components"] = component;
|
||||
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
|
||||
QJsonArray instanceInfo;
|
||||
for (const auto &entry : API::getAllInstanceData())
|
||||
@@ -1571,6 +1573,16 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &
|
||||
|
||||
sendSuccessReply(full_command, tan);
|
||||
}
|
||||
else if (subc == "addAuthorization")
|
||||
{
|
||||
ledDevice = LedDeviceFactory::construct(config);
|
||||
const QJsonObject& params = message["params"].toObject();
|
||||
const QJsonObject response = ledDevice->addAuthorization(params);
|
||||
|
||||
Debug(_log, "response: [%s]", QString(QJsonDocument(response).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
sendSuccessDataReply(QJsonDocument(response), full_command, tan);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendErrorReply("Unknown or missing subcommand", full_command, tan);
|
||||
@@ -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)
|
||||
{
|
||||
sendErrorReply("Command not implemented", command, tan);
|
||||
|
@@ -9,10 +9,6 @@
|
||||
// components
|
||||
|
||||
#include <hyperion/ComponentRegister.h>
|
||||
// bonjour wrapper
|
||||
#ifdef ENABLE_AVAHI
|
||||
#include <bonjour/bonjourbrowserwrapper.h>
|
||||
#endif
|
||||
// priorityMuxer
|
||||
|
||||
#include <hyperion/PriorityMuxer.h>
|
||||
@@ -33,12 +29,9 @@ JsonCB::JsonCB(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(nullptr)
|
||||
, _componentRegister(nullptr)
|
||||
#ifdef ENABLE_AVAHI
|
||||
, _bonjour(BonjourBrowserWrapper::getInstance())
|
||||
#endif
|
||||
, _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";
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
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 (unsubscribe)
|
||||
@@ -208,26 +191,6 @@ void JsonCB::handleComponentState(hyperion::Components comp, bool state)
|
||||
|
||||
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)
|
||||
{
|
||||
|
@@ -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()
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -8,6 +8,8 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
/* Enable to turn on detailed CEC logs */
|
||||
// #define VERBOSE_CEC
|
||||
@@ -34,15 +36,16 @@ bool CECHandler::start()
|
||||
if (_cecAdapter)
|
||||
return true;
|
||||
|
||||
Info(_logger, "Starting CEC handler");
|
||||
|
||||
_cecAdapter = LibCecInitialise(&_cecConfig);
|
||||
std::string library = std::string("" CEC_LIBRARY);
|
||||
_cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() : nullptr);
|
||||
if(!_cecAdapter)
|
||||
{
|
||||
Error(_logger, "Failed loading libcec.so");
|
||||
Error(_logger, "Failed to loading libcec.so");
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "CEC handler started");
|
||||
|
||||
auto adapters = getAdapters();
|
||||
if (adapters.isEmpty())
|
||||
{
|
||||
|
@@ -5,13 +5,14 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/cec)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/cec)
|
||||
|
||||
FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
add_library(cechandler ${CEC_SOURCES})
|
||||
add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}")
|
||||
|
||||
include_directories(${CEC_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(cechandler
|
||||
dl
|
||||
${CEC_LIBRARIES}
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
|
@@ -60,5 +60,10 @@ flatbuffers
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(flatbufserver mdns)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
|
@@ -37,6 +37,7 @@ FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString&
|
||||
|
||||
FlatBufferConnection::~FlatBufferConnection()
|
||||
{
|
||||
Debug(_log, "Closing connection to: %s:%u", QSTRING_CSTR(_host), _port);
|
||||
_timer.stop();
|
||||
_socket.close();
|
||||
}
|
||||
|
@@ -6,16 +6,18 @@
|
||||
#include <utils/NetOrigin.h>
|
||||
#include <utils/GlobalSignals.h>
|
||||
|
||||
// bonjour
|
||||
#ifdef ENABLE_AVAHI
|
||||
#include <bonjour/bonjourserviceregister.h>
|
||||
#endif
|
||||
|
||||
// qt
|
||||
#include <QJsonObject>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const char SERVICE_TYPE[] = "flatbuffer";
|
||||
|
||||
} //End of constants
|
||||
|
||||
FlatBufferServer::FlatBufferServer(const QJsonDocument& config, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _server(new QTcpServer(this))
|
||||
@@ -106,19 +108,8 @@ void FlatBufferServer::startServer()
|
||||
else
|
||||
{
|
||||
Info(_log,"Started on port %d", _port);
|
||||
#ifdef ENABLE_AVAHI
|
||||
if(_serviceRegister == nullptr)
|
||||
{
|
||||
_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
|
||||
|
||||
emit publishService(SERVICE_TYPE, _port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// STL includes
|
||||
#include <stdexcept>
|
||||
#include <chrono>
|
||||
|
||||
// project includes
|
||||
#include <forwarder/MessageForwarder.h>
|
||||
@@ -12,23 +12,46 @@
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// qt includes
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QHostInfo>
|
||||
#include <QNetworkInterface>
|
||||
#include <QThread>
|
||||
|
||||
#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)
|
||||
: _hyperion(hyperion)
|
||||
, _log(nullptr)
|
||||
, _muxer(_hyperion->getMuxerInstance())
|
||||
, _forwarder_enabled(true)
|
||||
, _priority(140)
|
||||
, _log(nullptr)
|
||||
, _muxer(_hyperion->getMuxerInstance())
|
||||
, _forwarder_enabled(false)
|
||||
, _priority(DEFAULT_FORWARDER_FLATBUFFFER_PRIORITY)
|
||||
, _messageForwarderFlatBufHelper(nullptr)
|
||||
{
|
||||
QString subComponent = hyperion->property("instance").toString();
|
||||
_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
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &MessageForwarder::handleSettingsUpdate);
|
||||
|
||||
@@ -37,82 +60,23 @@ MessageForwarder::MessageForwarder(Hyperion* hyperion)
|
||||
|
||||
// connect with Muxer visible priority changes
|
||||
connect(_muxer, &PriorityMuxer::visiblePriorityChanged, this, &MessageForwarder::handlePriorityChanges);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::NETFORWARD, _hyperion->getSetting(settings::NETFORWARD));
|
||||
}
|
||||
|
||||
MessageForwarder::~MessageForwarder()
|
||||
{
|
||||
while (!_forwardClients.isEmpty())
|
||||
{
|
||||
delete _forwardClients.takeFirst();
|
||||
}
|
||||
stopJsonTargets();
|
||||
stopFlatbufferTargets();
|
||||
}
|
||||
|
||||
|
||||
void MessageForwarder::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
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();
|
||||
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);
|
||||
|
||||
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);
|
||||
enableTargets(isForwarderEnabledinSettings, obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,30 +84,59 @@ void MessageForwarder::handleCompStateChangeRequest(hyperion::Components compone
|
||||
{
|
||||
if (component == hyperion::COMP_FORWARDER && _forwarder_enabled != enable)
|
||||
{
|
||||
_forwarder_enabled = enable;
|
||||
handleSettingsUpdate(settings::NETFORWARD, _hyperion->getSetting(settings::NETFORWARD));
|
||||
Info(_log, "Forwarder change state to %s", (_forwarder_enabled ? "enabled" : "disabled"));
|
||||
_hyperion->setNewComponentState(component, _forwarder_enabled);
|
||||
Info(_log, "Forwarder is %s", (enable ? "enabled" : "disabled"));
|
||||
QJsonDocument config {_hyperion->getSetting(settings::type::NETFORWARD)};
|
||||
enableTargets(enable, config.object());
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const QJsonObject obj = _hyperion->getSetting(settings::NETFORWARD).object();
|
||||
if (priority != 0 && _forwarder_enabled && obj["enable"].toBool())
|
||||
if (priority != 0 && _forwarder_enabled)
|
||||
{
|
||||
hyperion::Components activeCompId = _hyperion->getPriorityInfo(priority).componentId;
|
||||
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)
|
||||
{
|
||||
case hyperion::COMP_GRABBER:
|
||||
@@ -177,69 +170,131 @@ void MessageForwarder::addJsonTarget(const QJsonObject& targetConfig)
|
||||
{
|
||||
TargetHost targetHost;
|
||||
|
||||
QString config_host = targetConfig["host"].toString();
|
||||
if (NetUtils::resolveHostAddress(_log, config_host, targetHost.host))
|
||||
{
|
||||
int config_port = targetConfig["port"].toInt();
|
||||
if (NetUtils::isValidPort(_log, config_port, config_host))
|
||||
{
|
||||
targetHost.port = static_cast<quint16>(config_port);
|
||||
QString hostName = targetConfig["host"].toString();
|
||||
int port = targetConfig["port"].toInt();
|
||||
|
||||
// verify loop with JSON-server
|
||||
const QJsonObject& obj = _hyperion->getSetting(settings::JSONSERVER).object();
|
||||
if ((QNetworkInterface::allAddresses().indexOf(targetHost.host) != -1) && targetHost.port == static_cast<quint16>(obj["port"].toInt()))
|
||||
if (!hostName.isEmpty())
|
||||
{
|
||||
if (NetUtils::resolveHostToAddress(_log, hostName, targetHost.host, port))
|
||||
{
|
||||
QString address = targetHost.host.toString();
|
||||
if (hostName != address)
|
||||
{
|
||||
Error(_log, "Loop between JSON-Server and Forwarder! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(config_host), config_port);
|
||||
Info(_log, "Resolved hostname [%s] to address [%s]", QSTRING_CSTR(hostName), QSTRING_CSTR(address));
|
||||
}
|
||||
else
|
||||
|
||||
if (NetUtils::isValidPort(_log, port, targetHost.host.toString()))
|
||||
{
|
||||
if (_forwarder_enabled)
|
||||
targetHost.port = static_cast<quint16>(port);
|
||||
|
||||
// verify loop with JSON-server
|
||||
const QJsonObject& obj = _hyperion->getSetting(settings::JSONSERVER).object();
|
||||
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(targetHost.host.toString()), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageForwarder::addFlatbufferTarget(const QJsonObject& targetConfig)
|
||||
{
|
||||
TargetHost targetHost;
|
||||
|
||||
QString config_host = targetConfig["host"].toString();
|
||||
if (NetUtils::resolveHostAddress(_log, config_host, targetHost.host))
|
||||
{
|
||||
int config_port = targetConfig["port"].toInt();
|
||||
if (NetUtils::isValidPort(_log, config_port, config_host))
|
||||
{
|
||||
targetHost.port = static_cast<quint16>(config_port);
|
||||
QString hostName = targetConfig["host"].toString();
|
||||
int port = targetConfig["port"].toInt();
|
||||
|
||||
// verify loop with Flatbuffer-server
|
||||
const QJsonObject& obj = _hyperion->getSetting(settings::FLATBUFSERVER).object();
|
||||
if ((QNetworkInterface::allAddresses().indexOf(targetHost.host) != -1) && targetHost.port == static_cast<quint16>(obj["port"].toInt()))
|
||||
if (!hostName.isEmpty())
|
||||
{
|
||||
if (NetUtils::resolveHostToAddress(_log, hostName, targetHost.host, port))
|
||||
{
|
||||
QString address = targetHost.host.toString();
|
||||
if (hostName != address)
|
||||
{
|
||||
Error(_log, "Loop between Flatbuffer-Server and Forwarder! Configuration for host: %s, port: %d is ignored.", QSTRING_CSTR(config_host), config_port);
|
||||
Info(_log, "Resolved hostname [%s] to address [%s]", QSTRING_CSTR(hostName), QSTRING_CSTR(address));
|
||||
}
|
||||
else
|
||||
|
||||
if (NetUtils::isValidPort(_log, port, targetHost.host.toString()))
|
||||
{
|
||||
if (_forwarder_enabled)
|
||||
targetHost.port = static_cast<quint16>(port);
|
||||
|
||||
// verify loop with Flatbuffer-server
|
||||
const QJsonObject& obj = _hyperion->getSetting(settings::FLATBUFSERVER).object();
|
||||
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(targetHost.host.toString()), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
|
||||
FlatBufferConnection* flatbuf = new FlatBufferConnection("Forwarder", targetHost.host.toString(), _priority, false, targetHost.port);
|
||||
_forwardClients << flatbuf;
|
||||
if (_messageForwarderFlatBufHelper != nullptr)
|
||||
{
|
||||
emit _messageForwarderFlatBufHelper->addClient("Forwarder", targetHost, _priority, false);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (_forwarder_enabled)
|
||||
@@ -259,7 +374,7 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject& message)
|
||||
for (const auto& targetHost : qAsConst(_jsonTargets))
|
||||
{
|
||||
client.connectToHost(targetHost.host, targetHost.port);
|
||||
if (client.waitForConnected(500))
|
||||
if (client.waitForConnected(CONNECT_TIMEOUT.count()))
|
||||
{
|
||||
sendJsonMessage(message, &client);
|
||||
client.close();
|
||||
@@ -270,11 +385,13 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject& message)
|
||||
|
||||
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
|
||||
QJsonParseError error;
|
||||
QJsonDocument::fromJson(serializedReply, &error);
|
||||
/* QJsonDocument reply = */ QJsonDocument::fromJson(serializedReply, &error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
Error(_log, "Error while parsing reply: invalid json");
|
||||
Error(_log, "Error while parsing reply: invalid JSON");
|
||||
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;
|
||||
}
|
||||
|
@@ -41,7 +41,3 @@ endif()
|
||||
if(ENABLE_FORWARDER)
|
||||
target_link_libraries(hyperion forwarder)
|
||||
endif()
|
||||
|
||||
if (ENABLE_AVAHI)
|
||||
target_link_libraries(hyperion bonjour)
|
||||
endif ()
|
||||
|
@@ -149,6 +149,7 @@ void Hyperion::start()
|
||||
if (_instIndex == 0)
|
||||
{
|
||||
_messageForwarder = new MessageForwarder(this);
|
||||
_messageForwarder->handleSettingsUpdate(settings::NETFORWARD, getSetting(settings::NETFORWARD));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -698,7 +699,6 @@ void Hyperion::update()
|
||||
// Smoothing is disabled
|
||||
if (! _deviceSmooth->enabled())
|
||||
{
|
||||
//std::cout << "Hyperion::update()> Non-Smoothing - "; LedDevice::printLedValues ( _ledBuffer);
|
||||
emit ledDeviceData(_ledBuffer);
|
||||
}
|
||||
else
|
||||
@@ -710,11 +710,4 @@ void Hyperion::update()
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
else
|
||||
{
|
||||
//LEDDevice is disabled
|
||||
Debug(_log, "LEDDevice is disabled - no update required");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -22,11 +22,16 @@ HyperionIManager::HyperionIManager(const QString& rootPath, QObject* parent, boo
|
||||
|
||||
Hyperion* HyperionIManager::getHyperionInstance(quint8 instance)
|
||||
{
|
||||
Hyperion* pInstance {nullptr};
|
||||
if(_runningInstances.contains(instance))
|
||||
return _runningInstances.value(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);
|
||||
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)));
|
||||
pInstance = _runningInstances.value(0);
|
||||
}
|
||||
return pInstance;
|
||||
}
|
||||
|
||||
QVector<QVariantMap> HyperionIManager::getInstanceData() const
|
||||
|
@@ -22,15 +22,15 @@ const int PriorityMuxer::ENDLESS = -1;
|
||||
|
||||
PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent)
|
||||
: QObject(parent)
|
||||
, _log(nullptr)
|
||||
, _currentPriority(PriorityMuxer::LOWEST_PRIORITY)
|
||||
, _previousPriority(_currentPriority)
|
||||
, _manualSelectedPriority(MANUAL_SELECTED_PRIORITY)
|
||||
, _prevVisComp (hyperion::Components::COMP_COLOR)
|
||||
, _sourceAutoSelectEnabled(true)
|
||||
, _updateTimer(new QTimer(this))
|
||||
, _timer(new QTimer(this))
|
||||
, _blockTimer(new QTimer(this))
|
||||
, _log(nullptr)
|
||||
, _currentPriority(PriorityMuxer::LOWEST_PRIORITY)
|
||||
, _previousPriority(_currentPriority)
|
||||
, _manualSelectedPriority(MANUAL_SELECTED_PRIORITY)
|
||||
, _prevVisComp (hyperion::Components::COMP_COLOR)
|
||||
, _sourceAutoSelectEnabled(true)
|
||||
, _updateTimer(new QTimer(this))
|
||||
, _timer(new QTimer(this))
|
||||
, _blockTimer(new QTimer(this))
|
||||
{
|
||||
QString subComponent = parent->property("instance").toString();
|
||||
_log= Logger::getInstance("MUXER", subComponent);
|
||||
|
@@ -41,6 +41,33 @@
|
||||
},
|
||||
"access": "advanced",
|
||||
"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": {
|
||||
|
@@ -1,77 +1,106 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"title" : "edt_conf_fw_heading_title",
|
||||
"required" : true,
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean",
|
||||
"title": "edt_conf_general_enable_title",
|
||||
"required": true,
|
||||
"default": false,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"json": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_fw_json_title",
|
||||
"propertyOrder": 2,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "edt_conf_fw_json_itemtitle",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"format": "hostname_or_ip",
|
||||
"minLength": 7,
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"required": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"default": 19444,
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flat": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_fw_flat_title",
|
||||
"propertyOrder": 3,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "edt_conf_fw_flat_itemtitle",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"format": "hostname_or_ip",
|
||||
"minLength": 7,
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"required": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"default": 19400,
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
"type": "object",
|
||||
"title": "edt_conf_fw_heading_title",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean",
|
||||
"title": "edt_conf_general_enable_title",
|
||||
"required": true,
|
||||
"default": false,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"jsonapiselect": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"format": "select",
|
||||
"title": "edt_conf_fw_json_services_discovered_title",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"jsonapi": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_fw_json_title",
|
||||
"uniqueItems": true,
|
||||
"access": "expert",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "edt_conf_fw_json_itemtitle",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "edt_conf_fw_service_name_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"format": "hostname_or_ip",
|
||||
"minLength": 7,
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"flatbufferselect": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"format": "select",
|
||||
"title": "edt_conf_fw_flat_services_discovered_title",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"flatbuffer": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_fw_flat_title",
|
||||
"uniqueItems": true,
|
||||
"access": "expert",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "edt_conf_fw_flat_itemtitle",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "edt_conf_fw_service_name_title",
|
||||
"access": "expert",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"format": "hostname_or_ip",
|
||||
"minLength": 7,
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"default": 19400,
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"required": true,
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"propertyOrder": 5
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
|
@@ -13,3 +13,8 @@ target_link_libraries(jsonserver
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(jsonserver mdns)
|
||||
endif()
|
||||
|
||||
|
@@ -1,37 +1,35 @@
|
||||
// system includes
|
||||
#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
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QJsonDocument>
|
||||
#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)
|
||||
: QObject()
|
||||
, _server(new QTcpServer(this))
|
||||
, _openConnections()
|
||||
, _log(Logger::getInstance("JSONSERVER"))
|
||||
, _netOrigin(NetOrigin::getInstance())
|
||||
, _config(config)
|
||||
{
|
||||
Debug(_log, "Created instance");
|
||||
|
||||
// Set trigger for incoming connections
|
||||
connect(_server, &QTcpServer::newConnection, this, &JsonServer::newConnection);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::JSONSERVER, config);
|
||||
}
|
||||
|
||||
JsonServer::~JsonServer()
|
||||
@@ -39,31 +37,29 @@ JsonServer::~JsonServer()
|
||||
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()
|
||||
{
|
||||
if(_server->isListening())
|
||||
return;
|
||||
|
||||
if (!_server->listen(QHostAddress::Any, _port))
|
||||
if(!_server->isListening())
|
||||
{
|
||||
Error(_log,"Could not bind to port '%d', please use an available port", _port);
|
||||
return;
|
||||
if (!_server->listen(QHostAddress::Any, _port))
|
||||
{
|
||||
Error(_log,"Could not bind to port '%d', please use an available port", _port);
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log, "Started on port %d", _port);
|
||||
emit publishService(SERVICE_TYPE, _port);
|
||||
}
|
||||
}
|
||||
Info(_log, "Started on port %d", _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()
|
||||
|
@@ -141,3 +141,6 @@ if (ENABLE_DEV_USB_HID)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(leddevice mdns)
|
||||
endif()
|
||||
|
@@ -15,84 +15,91 @@
|
||||
//std includes
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_CURRENT_LED_COUNT[] = "currentLedCount";
|
||||
const char CONFIG_COLOR_ORDER[] = "colorOrder";
|
||||
const char CONFIG_AUTOSTART[] = "autoStart";
|
||||
const char CONFIG_LATCH_TIME[] = "latchTime";
|
||||
const char CONFIG_REWRITE_TIME[] = "rewriteTime";
|
||||
// Configuration settings
|
||||
const char CONFIG_CURRENT_LED_COUNT[] = "currentLedCount";
|
||||
const char CONFIG_COLOR_ORDER[] = "colorOrder";
|
||||
const char CONFIG_AUTOSTART[] = "autoStart";
|
||||
const char CONFIG_LATCH_TIME[] = "latchTime";
|
||||
const char CONFIG_REWRITE_TIME[] = "rewriteTime";
|
||||
|
||||
int DEFAULT_LED_COUNT = 1;
|
||||
const char DEFAULT_COLOR_ORDER[] = "RGB";
|
||||
const bool DEFAULT_IS_AUTOSTART = true;
|
||||
int DEFAULT_LED_COUNT{ 1 };
|
||||
const char DEFAULT_COLOR_ORDER[]{ "RGB" };
|
||||
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
|
||||
|
||||
LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _devConfig(deviceConfig)
|
||||
, _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _ledBuffer(0)
|
||||
, _refreshTimer(nullptr)
|
||||
, _refreshTimerInterval_ms(0)
|
||||
, _latchTime_ms(0)
|
||||
, _ledCount(0)
|
||||
, _isRestoreOrigState(false)
|
||||
, _isEnabled(false)
|
||||
, _isDeviceInitialised(false)
|
||||
, _isDeviceReady(false)
|
||||
, _isOn(false)
|
||||
, _isDeviceInError(false)
|
||||
, _isInSwitchOff (false)
|
||||
, _lastWriteTime(QDateTime::currentDateTime())
|
||||
, _isRefreshEnabled (false)
|
||||
, _isAutoStart(true)
|
||||
, _devConfig(deviceConfig)
|
||||
, _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _ledBuffer(0)
|
||||
, _refreshTimer(nullptr)
|
||||
, _enableAttemptsTimer(nullptr)
|
||||
, _refreshTimerInterval_ms(0)
|
||||
, _enableAttemptTimerInterval(DEFAULT_ENABLE_ATTEMPTS_INTERVAL)
|
||||
, _enableAttempts(0)
|
||||
, _maxEnableAttempts(DEFAULT_MAX_ENABLE_ATTEMPTS)
|
||||
, _latchTime_ms(0)
|
||||
, _ledCount(0)
|
||||
, _isRestoreOrigState(false)
|
||||
, _isEnabled(false)
|
||||
, _isDeviceInitialised(false)
|
||||
, _isDeviceReady(false)
|
||||
, _isOn(false)
|
||||
, _isDeviceInError(false)
|
||||
, _lastWriteTime(QDateTime::currentDateTime())
|
||||
, _isRefreshEnabled(false)
|
||||
, _isAutoStart(true)
|
||||
{
|
||||
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
|
||||
}
|
||||
|
||||
LedDevice::~LedDevice()
|
||||
{
|
||||
delete _refreshTimer;
|
||||
this->stopEnableAttemptsTimer();
|
||||
this->stopRefreshTimer();
|
||||
}
|
||||
|
||||
void LedDevice::start()
|
||||
{
|
||||
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();
|
||||
|
||||
_isDeviceInitialised = false;
|
||||
// General initialisation and configuration of LedDevice
|
||||
if ( init(_devConfig) )
|
||||
|
||||
if (init(_devConfig))
|
||||
{
|
||||
// Everything is OK -> enable device
|
||||
_isDeviceInitialised = true;
|
||||
|
||||
if (_isAutoStart)
|
||||
{
|
||||
this->enable();
|
||||
if (!_isEnabled)
|
||||
{
|
||||
Debug(_log, "Not enabled -> enable device");
|
||||
enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LedDevice::stop()
|
||||
{
|
||||
Debug(_log, "Stop device");
|
||||
this->disable();
|
||||
this->stopRefreshTimer();
|
||||
Info(_log, " Stopped LedDevice '%s'", QSTRING_CSTR(_activeDeviceType) );
|
||||
Info(_log, " Stopped LedDevice '%s'", QSTRING_CSTR(_activeDeviceType));
|
||||
}
|
||||
|
||||
int LedDevice::open()
|
||||
@@ -113,6 +120,7 @@ int LedDevice::close()
|
||||
|
||||
void LedDevice::setInError(const QString& errorMsg)
|
||||
{
|
||||
_isOn = false;
|
||||
_isDeviceInError = true;
|
||||
_isDeviceReady = false;
|
||||
_isEnabled = false;
|
||||
@@ -124,21 +132,53 @@ void LedDevice::setInError(const QString& errorMsg)
|
||||
|
||||
void LedDevice::enable()
|
||||
{
|
||||
if ( !_isEnabled )
|
||||
Debug(_log, "Enable device %s'", QSTRING_CSTR(_activeDeviceType));
|
||||
|
||||
if (!_isEnabled)
|
||||
{
|
||||
if (_enableAttemptsTimer != nullptr && _enableAttemptsTimer->isActive())
|
||||
{
|
||||
_enableAttemptsTimer->stop();
|
||||
}
|
||||
|
||||
_isDeviceInError = false;
|
||||
|
||||
if ( ! _isDeviceReady )
|
||||
if (!_isDeviceInitialised)
|
||||
{
|
||||
_isDeviceInitialised = init(_devConfig);
|
||||
}
|
||||
|
||||
if (!_isDeviceReady)
|
||||
{
|
||||
open();
|
||||
}
|
||||
|
||||
if ( _isDeviceReady )
|
||||
bool isEnableFailed(true);
|
||||
|
||||
if (_isDeviceReady)
|
||||
{
|
||||
_isEnabled = true;
|
||||
if ( switchOn() )
|
||||
if (switchOn())
|
||||
{
|
||||
stopEnableAttemptsTimer();
|
||||
_isEnabled = true;
|
||||
isEnableFailed = false;
|
||||
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()
|
||||
{
|
||||
if ( _isEnabled )
|
||||
Debug(_log, "Disable device %s'", QSTRING_CSTR(_activeDeviceType));
|
||||
if (_isEnabled)
|
||||
{
|
||||
_isEnabled = false;
|
||||
this->stopEnableAttemptsTimer();
|
||||
this->stopRefreshTimer();
|
||||
|
||||
switchOff();
|
||||
@@ -163,47 +205,110 @@ void LedDevice::setActiveDeviceType(const QString& 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);
|
||||
_isAutoStart = deviceConfig[CONFIG_AUTOSTART].toBool(DEFAULT_IS_AUTOSTART);
|
||||
|
||||
setLedCount( deviceConfig[CONFIG_CURRENT_LED_COUNT].toInt(DEFAULT_LED_COUNT) ); // property injected to reflect real led count
|
||||
setLatchTime( deviceConfig[CONFIG_LATCH_TIME].toInt( _latchTime_ms ) );
|
||||
setRewriteTime ( deviceConfig[CONFIG_REWRITE_TIME].toInt( _refreshTimerInterval_ms) );
|
||||
setLedCount(deviceConfig[CONFIG_CURRENT_LED_COUNT].toInt(DEFAULT_LED_COUNT)); // property injected to reflect real led count
|
||||
setColorOrder(deviceConfig[CONFIG_COLOR_ORDER].toString(DEFAULT_COLOR_ORDER));
|
||||
setLatchTime(deviceConfig[CONFIG_LATCH_TIME].toInt(_latchTime_ms));
|
||||
setRewriteTime(deviceConfig[CONFIG_REWRITE_TIME].toInt(_refreshTimerInterval_ms));
|
||||
setAutoStart(deviceConfig[CONFIG_AUTOSTART].toBool(DEFAULT_IS_AUTOSTART));
|
||||
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;
|
||||
}
|
||||
|
||||
void LedDevice::startRefreshTimer()
|
||||
{
|
||||
if ( _isDeviceReady && _isEnabled )
|
||||
if (_refreshTimerInterval_ms > 0)
|
||||
{
|
||||
_refreshTimer->start();
|
||||
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();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Device is not ready to start a refresh timer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LedDevice::stopRefreshTimer()
|
||||
{
|
||||
if ( _refreshTimer != nullptr )
|
||||
if (_refreshTimer != nullptr)
|
||||
{
|
||||
//Debug(_log, "Stopping refresh timer");
|
||||
_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;
|
||||
if ( !_isEnabled || !_isOn || !_isDeviceReady || _isDeviceInError )
|
||||
if (!_isEnabled || !_isOn || !_isDeviceReady || _isDeviceInError)
|
||||
{
|
||||
//std::cout << "LedDevice::updateLeds(), LedDevice NOT ready! ";
|
||||
retval = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
qint64 elapsedTimeMs = _lastWriteTime.msecsTo( QDateTime::currentDateTime() );
|
||||
qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
|
||||
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;
|
||||
@@ -211,16 +316,16 @@ int LedDevice::updateLeds(const std::vector<ColorRgb>& ledValues)
|
||||
_lastWriteTime = QDateTime::currentDateTime();
|
||||
|
||||
// if device requires refreshing, save Led-Values and restart the timer
|
||||
if ( _isRefreshEnabled && _isEnabled )
|
||||
if (_isRefreshEnabled && _isEnabled)
|
||||
{
|
||||
this->startRefreshTimer();
|
||||
_lastLedValues = ledValues;
|
||||
this->startRefreshTimer();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//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
|
||||
this->stopRefreshTimer();
|
||||
@@ -234,18 +339,21 @@ int LedDevice::rewriteLEDs()
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if ( _isDeviceReady && _isEnabled )
|
||||
if (_isEnabled && _isOn && _isDeviceReady && !_isDeviceInError)
|
||||
{
|
||||
// qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
|
||||
// std::cout << "LedDevice::rewriteLEDs(): Rewrite LEDs now, elapsedTime [" << elapsedTimeMs << "] ms" << std::endl;
|
||||
// //:TESTING: Inject "white" output records to differentiate from normal writes
|
||||
// _lastLedValues.clear();
|
||||
// _lastLedValues.resize(static_cast<unsigned long>(_ledCount), ColorRgb::WHITE);
|
||||
// printLedValues(_lastLedValues);
|
||||
// //:TESTING:
|
||||
// qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
|
||||
// std::cout << "LedDevice::rewriteLEDs(): Rewrite LEDs now, elapsedTime [" << elapsedTimeMs << "] ms" << std::endl;
|
||||
// //:TESTING: Inject "white" output records to differentiate from normal writes
|
||||
// _lastLedValues.clear();
|
||||
// _lastLedValues.resize(static_cast<unsigned long>(_ledCount), ColorRgb::WHITE);
|
||||
// printLedValues(_lastLedValues);
|
||||
// //:TESTING:
|
||||
|
||||
retval = write(_lastLedValues);
|
||||
_lastWriteTime = QDateTime::currentDateTime();
|
||||
if (!_lastLedValues.empty())
|
||||
{
|
||||
retval = write(_lastLedValues);
|
||||
_lastWriteTime = QDateTime::currentDateTime();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -257,6 +365,7 @@ int LedDevice::rewriteLEDs()
|
||||
|
||||
int LedDevice::writeBlack(int numberOfWrites)
|
||||
{
|
||||
Debug(_log, "Set LED strip to black to switch of LEDs");
|
||||
return writeColor(ColorRgb::BLACK, numberOfWrites);
|
||||
}
|
||||
|
||||
@@ -273,7 +382,7 @@ int LedDevice::writeColor(const ColorRgb& color, int numberOfWrites)
|
||||
QTimer::singleShot(_latchTime_ms, &loop, &QEventLoop::quit);
|
||||
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);
|
||||
}
|
||||
return rc;
|
||||
@@ -281,24 +390,31 @@ int LedDevice::writeColor(const ColorRgb& color, int numberOfWrites)
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
_isInSwitchOff = false;
|
||||
emit enableStateChanged(_isEnabled);
|
||||
rc = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed switching device %s ON", QSTRING_CSTR(_activeDeviceType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,32 +423,40 @@ bool LedDevice::switchOn()
|
||||
|
||||
bool LedDevice::switchOff()
|
||||
{
|
||||
bool rc = false;
|
||||
bool rc{ false };
|
||||
|
||||
if ( !_isOn )
|
||||
if (!_isOn)
|
||||
{
|
||||
rc = true;
|
||||
}
|
||||
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;
|
||||
_isInSwitchOff = true;
|
||||
|
||||
rc = true;
|
||||
|
||||
if ( _isDeviceReady )
|
||||
if (_isDeviceReady)
|
||||
{
|
||||
if ( _isRestoreOrigState )
|
||||
if (_isRestoreOrigState)
|
||||
{
|
||||
//Restore devices state
|
||||
restoreState();
|
||||
}
|
||||
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 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
|
||||
if ( writeBlack() >= 0 )
|
||||
if (writeBlack() >= 0)
|
||||
{
|
||||
rc = true;
|
||||
}
|
||||
@@ -354,15 +480,18 @@ bool LedDevice::powerOff()
|
||||
|
||||
bool LedDevice::powerOn()
|
||||
{
|
||||
bool rc = true;
|
||||
bool rc{ true };
|
||||
|
||||
Debug(_log, "Power On: %s", QSTRING_CSTR(_activeDeviceType));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool LedDevice::storeState()
|
||||
{
|
||||
bool rc = true;
|
||||
bool rc{ true };
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
if (_isRestoreOrigState)
|
||||
{
|
||||
// Save device's original state
|
||||
// _originalStateValues = get device's state;
|
||||
@@ -373,9 +502,9 @@ bool LedDevice::storeState()
|
||||
|
||||
bool LedDevice::restoreState()
|
||||
{
|
||||
bool rc = true;
|
||||
bool rc{ true };
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
if (_isRestoreOrigState)
|
||||
{
|
||||
// Restore device's original state
|
||||
// update device using _originalStateValues
|
||||
@@ -393,7 +522,7 @@ QJsonObject LedDevice::discover(const QJsonObject& /*params*/)
|
||||
QJsonArray 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;
|
||||
}
|
||||
|
||||
@@ -401,75 +530,98 @@ QString LedDevice::discoverFirst()
|
||||
{
|
||||
QString deviceDiscovered;
|
||||
|
||||
Debug(_log, "deviceDiscovered: [%s]", QSTRING_CSTR(deviceDiscovered) );
|
||||
Debug(_log, "deviceDiscovered: [%s]", QSTRING_CSTR(deviceDiscovered));
|
||||
return deviceDiscovered;
|
||||
}
|
||||
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
void LedDevice::setLogger(Logger* log)
|
||||
{
|
||||
_log = log;
|
||||
}
|
||||
|
||||
void LedDevice::setLedCount(int ledCount)
|
||||
{
|
||||
assert(ledCount >= 0);
|
||||
_ledCount = ledCount;
|
||||
_ledRGBCount = _ledCount * sizeof(ColorRgb);
|
||||
_ledCount = static_cast<uint>(ledCount);
|
||||
_ledRGBCount = _ledCount * sizeof(ColorRgb);
|
||||
_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);
|
||||
_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
|
||||
if (_refreshTimer != nullptr)
|
||||
void LedDevice::setRewriteTime(int rewriteTime_ms)
|
||||
{
|
||||
_refreshTimerInterval_ms = qMax(rewriteTime_ms, 0);
|
||||
|
||||
if (_refreshTimerInterval_ms > 0)
|
||||
{
|
||||
_refreshTimerInterval_ms = rewriteTime_ms;
|
||||
_isRefreshEnabled = true;
|
||||
|
||||
if (_refreshTimerInterval_ms > 0)
|
||||
if (_refreshTimerInterval_ms <= _latchTime_ms)
|
||||
{
|
||||
|
||||
_isRefreshEnabled = true;
|
||||
|
||||
if (_refreshTimerInterval_ms <= _latchTime_ms)
|
||||
{
|
||||
int new_refresh_timer_interval = _latchTime_ms + 10;
|
||||
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;
|
||||
_refreshTimer->setInterval(_refreshTimerInterval_ms);
|
||||
}
|
||||
|
||||
Debug(_log, "Refresh interval = %dms", _refreshTimerInterval_ms);
|
||||
_refreshTimer->setInterval(_refreshTimerInterval_ms);
|
||||
|
||||
_lastWriteTime = QDateTime::currentDateTime();
|
||||
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);
|
||||
_refreshTimerInterval_ms = new_refresh_timer_interval;
|
||||
}
|
||||
|
||||
Debug(_log, "RewriteTime updated to %dms", _refreshTimerInterval_ms);
|
||||
Debug(_log, "Refresh interval = %dms", _refreshTimerInterval_ms);
|
||||
startRefreshTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
_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)
|
||||
{
|
||||
std::cout << "LedValues [" << ledValues.size() <<"] [";
|
||||
std::cout << "LedValues [" << ledValues.size() << "] [";
|
||||
for (const ColorRgb& color : ledValues)
|
||||
{
|
||||
std::cout << color;
|
||||
@@ -477,24 +629,24 @@ void LedDevice::printLedValues(const std::vector<ColorRgb>& ledValues)
|
||||
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;
|
||||
}
|
||||
|
||||
QByteArray bytes (reinterpret_cast<const char*>(data), number);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return bytes.toHex(':');
|
||||
#else
|
||||
return bytes.toHex();
|
||||
#endif
|
||||
QByteArray bytes(reinterpret_cast<const char*>(data), number);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return bytes.toHex(':');
|
||||
#else
|
||||
return bytes.toHex();
|
||||
#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();
|
||||
}
|
||||
@@ -505,3 +657,47 @@ QString LedDevice::toHex(const QByteArray& data, int number) const
|
||||
return data.left(number).toHex();
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,7 @@
|
||||
<file alias="schema-udpartnet">schemas/schema-artnet.json</file>
|
||||
<file alias="schema-udph801">schemas/schema-h801.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-ws2812spi">schemas/schema-ws2812spi.json</file>
|
||||
<file alias="schema-apa104">schemas/schema-apa104.json</file>
|
||||
|
@@ -67,9 +67,6 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config)
|
||||
// further signals
|
||||
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::switchOff, _ledDevice, &LedDevice::switchOff);
|
||||
|
||||
@@ -81,6 +78,100 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config)
|
||||
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()
|
||||
{
|
||||
// make sure the resources are loaded (they may be left out after static linking)
|
||||
@@ -115,101 +206,3 @@ QJsonObject LedDeviceWrapper::getLedDeviceSchemas()
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ public:
|
||||
///
|
||||
/// Sets configuration
|
||||
///
|
||||
/// @para#endif // LEDEVICETEMPLATE_Hm deviceConfig the json device config
|
||||
/// @param deviceConfig the json device config
|
||||
/// @return true if success
|
||||
bool init(const QJsonObject &deviceConfig) override;
|
||||
|
||||
|
@@ -45,23 +45,16 @@ LedDeviceAtmoOrb::~LedDeviceAtmoOrb()
|
||||
|
||||
bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
bool isInitOK {false};
|
||||
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
{
|
||||
|
||||
_multicastGroup = deviceConfig["host"].toString(MULTICAST_GROUP_DEFAULT_ADDRESS);
|
||||
_multiCastGroupPort = static_cast<quint16>(deviceConfig["port"].toInt(MULTICAST_GROUP_DEFAULT_PORT));
|
||||
_useOrbSmoothing = deviceConfig["useOrbSmoothing"].toBool(false);
|
||||
_skipSmoothingDiff = deviceConfig["skipSmoothingDiff"].toInt(0);
|
||||
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, "MulticastGroupPort: %d", _multiCastGroupPort);
|
||||
Debug(_log, "Orb ID list : %s", QSTRING_CSTR(deviceConfig["orbIds"].toString()));
|
||||
|
@@ -8,21 +8,27 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
// mDNS discover
|
||||
#ifdef ENABLE_MDNS
|
||||
#include <mdns/MdnsBrowser.h>
|
||||
#include <mdns/MdnsServiceRegister.h>
|
||||
#endif
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
|
||||
// Configuration settings
|
||||
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_HW_LED_COUNT[] = "hardwareLedCount";
|
||||
|
||||
const int COLOLIGHT_BEADS_PER_MODULE = 19;
|
||||
|
||||
const int STREAM_DEFAULT_PORT = 8900;
|
||||
|
||||
// Cololight discovery service
|
||||
|
||||
const int API_DEFAULT_PORT = 8900;
|
||||
|
||||
const char DISCOVERY_ADDRESS[] = "255.255.255.255";
|
||||
const quint16 DISCOVERY_PORT = 12345;
|
||||
const char DISCOVERY_MESSAGE[] = "Z-SEARCH * \r\n";
|
||||
@@ -46,6 +52,11 @@ LedDeviceCololight::LedDeviceCololight(const QJsonObject& deviceConfig)
|
||||
, _distance(0)
|
||||
, _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_SECU), sizeof(PACKET_SECU));
|
||||
}
|
||||
@@ -57,22 +68,13 @@ LedDevice* LedDeviceCololight::construct(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
|
||||
Debug(_log, "DeviceType : %s", QSTRING_CSTR(this->getActiveDeviceType()));
|
||||
Debug(_log, "ColorOrder : %s", QSTRING_CSTR(this->getColorOrder()));
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
if (initLedsConfiguration())
|
||||
{
|
||||
initDirectColorCmdTemplate();
|
||||
isInitOK = true;
|
||||
}
|
||||
_hostName = _devConfig[ CONFIG_HOST ].toString();
|
||||
_port = STREAM_DEFAULT_PORT;
|
||||
isInitOK = true;
|
||||
}
|
||||
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 isCmdOK = false;
|
||||
@@ -652,10 +675,19 @@ QJsonObject LedDeviceCololight::discover(const QJsonObject& /*params*/)
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QString discoveryMethod("ssdp");
|
||||
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();
|
||||
#endif
|
||||
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
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());
|
||||
QJsonObject properties;
|
||||
|
||||
QString hostName = params["host"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>(params["port"].toInt(API_DEFAULT_PORT));
|
||||
|
||||
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;
|
||||
|
||||
deviceConfig.insert("host", hostName);
|
||||
deviceConfig.insert("port", apiPort);
|
||||
|
||||
if (ProviderUdp::init(deviceConfig))
|
||||
if (ProviderUdp::open() == 0)
|
||||
{
|
||||
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());
|
||||
|
||||
QString hostName = params["host"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>(params["port"].toInt(API_DEFAULT_PORT));
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_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;
|
||||
|
||||
deviceConfig.insert("host", hostName);
|
||||
deviceConfig.insert("port", apiPort);
|
||||
if (ProviderUdp::init(deviceConfig))
|
||||
if (ProviderUdp::open() == 0)
|
||||
{
|
||||
if (setStateDirect(false) && setState(true))
|
||||
{
|
||||
|
@@ -161,6 +161,13 @@ protected:
|
||||
///
|
||||
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.
|
||||
///
|
||||
|
@@ -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_SYS_EX = 255; // OPC command codes
|
||||
const int OPC_HEADER_SIZE = 4; // OPC header size
|
||||
} //End of constants
|
||||
|
||||
// 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;
|
||||
|
||||
} //End of constants
|
||||
|
||||
LedDeviceFadeCandy::LedDeviceFadeCandy(const QJsonObject& deviceConfig)
|
||||
: LedDevice(deviceConfig)
|
||||
, _client(nullptr)
|
||||
, _host()
|
||||
, _hostName()
|
||||
, _port(STREAM_DEFAULT_PORT)
|
||||
{
|
||||
}
|
||||
@@ -43,7 +48,7 @@ LedDevice* LedDeviceFadeCandy::construct(const QJsonObject& deviceConfig)
|
||||
|
||||
bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
bool isInitOK {false};
|
||||
|
||||
if (LedDevice::init(deviceConfig))
|
||||
{
|
||||
@@ -55,11 +60,11 @@ bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
|
||||
}
|
||||
else
|
||||
{
|
||||
_host = deviceConfig["host"].toString("127.0.0.1");
|
||||
_port = deviceConfig["port"].toInt(STREAM_DEFAULT_PORT);
|
||||
_hostName = _devConfig[ CONFIG_HOST ].toString(DEFAULT_HOST);
|
||||
_port = deviceConfig[CONFIG_PORT].toInt(STREAM_DEFAULT_PORT);
|
||||
|
||||
//If host not configured the init fails
|
||||
if (_host.isEmpty())
|
||||
if (_hostName.isEmpty())
|
||||
{
|
||||
this->setInError("No target hostname nor IP defined");
|
||||
}
|
||||
@@ -90,10 +95,7 @@ bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
|
||||
_opc_data[1] = OPC_SET_PIXELS;
|
||||
qToBigEndian<quint16>(static_cast<quint16>(_ledRGBCount), _opc_data.data() + 2);
|
||||
|
||||
if (initNetwork())
|
||||
{
|
||||
isInitOK = true;
|
||||
}
|
||||
isInitOK = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,12 +104,11 @@ bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
|
||||
|
||||
bool LedDeviceFadeCandy::initNetwork()
|
||||
{
|
||||
bool isInitOK = false;
|
||||
bool isInitOK = true;
|
||||
|
||||
if (_client == nullptr)
|
||||
{
|
||||
_client = new QTcpSocket(this);
|
||||
isInitOK = true;
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
@@ -118,17 +119,20 @@ int LedDeviceFadeCandy::open()
|
||||
QString errortext;
|
||||
_isDeviceReady = false;
|
||||
|
||||
if (initNetwork())
|
||||
{
|
||||
// Try to open the LedDevice
|
||||
if (!tryConnect())
|
||||
{
|
||||
errortext = QString("Failed to open device.");
|
||||
this->setInError(errortext);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
if (!tryConnect())
|
||||
{
|
||||
errortext = QString("Failed to open device.");
|
||||
this->setInError(errortext);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -162,10 +166,10 @@ bool LedDeviceFadeCandy::tryConnect()
|
||||
if (_client != nullptr)
|
||||
{
|
||||
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()))
|
||||
{
|
||||
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)
|
||||
{
|
||||
sendFadeCandyConfiguration();
|
||||
|
@@ -130,7 +130,7 @@ private:
|
||||
void sendFadeCandyConfiguration();
|
||||
|
||||
QTcpSocket* _client;
|
||||
QString _host;
|
||||
QString _hostName;
|
||||
int _port;
|
||||
int _channel;
|
||||
QByteArray _opc_data;
|
||||
|
@@ -1,16 +1,23 @@
|
||||
// Local-Hyperion includes
|
||||
#include "LedDeviceNanoleaf.h"
|
||||
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
//std includes
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// Qt includes
|
||||
#include <QNetworkReply>
|
||||
#include <QtEndian>
|
||||
|
||||
//std includes
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
// mDNS discover
|
||||
#ifdef ENABLE_MDNS
|
||||
#include <mdns/MdnsBrowser.h>
|
||||
#include <mdns/MdnsServiceRegister.h>
|
||||
#endif
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
@@ -18,7 +25,7 @@ const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_ADDRESS[] = "host";
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_AUTH_TOKEN[] = "token";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
const char CONFIG_BRIGHTNESS[] = "brightness";
|
||||
@@ -115,6 +122,10 @@ LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject& deviceConfig)
|
||||
, _extControlVersion(EXTCTRLVER_V2)
|
||||
, _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)
|
||||
@@ -130,6 +141,8 @@ LedDeviceNanoleaf::~LedDeviceNanoleaf()
|
||||
|
||||
bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
{
|
||||
bool isInitOK {false};
|
||||
|
||||
// Overwrite non supported/required features
|
||||
setLatchTime(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());
|
||||
|
||||
bool isInitOK = false;
|
||||
|
||||
if (LedDevice::init(deviceConfig))
|
||||
if ( ProviderUdp::init(deviceConfig) )
|
||||
{
|
||||
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, "RewriteTime : %d", this->getRewriteTime());
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
//Set hostname as per configuration and default port
|
||||
_hostName = deviceConfig[CONFIG_HOST].toString();
|
||||
_port = STREAM_CONTROL_DEFAULT_PORT;
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = deviceConfig[CONFIG_AUTH_TOKEN].toString();
|
||||
|
||||
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE);
|
||||
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
|
||||
_brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX);
|
||||
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName) );
|
||||
Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
|
||||
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
|
||||
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;
|
||||
}
|
||||
|
||||
_startPos = deviceConfig[CONFIG_PANEL_START_POS].toInt(0);
|
||||
|
||||
//Set hostname as per configuration and_defaultHost default port
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
isInitOK = true;
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
@@ -358,18 +341,17 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
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)
|
||||
{
|
||||
_restApi = new ProviderRestApi(hostname, port);
|
||||
_restApi = new ProviderRestApi(_address.toString(), _apiPort);
|
||||
_restApi->setLogger(_log);
|
||||
|
||||
//Base-path is api-path + authentication token
|
||||
_restApi->setBasePath(QString(API_BASE_PATH).arg(token));
|
||||
|
||||
isInitOK = true;
|
||||
_restApi->setBasePath(QString(API_BASE_PATH).arg(_authToken));
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
@@ -379,13 +361,27 @@ int LedDeviceNanoleaf::open()
|
||||
int retval = -1;
|
||||
_isDeviceReady = false;
|
||||
|
||||
if (ProviderUdp::open() == 0)
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
// Read LedDevice configuration and validate against device configuration
|
||||
if (initLedsConfiguration())
|
||||
{
|
||||
if (ProviderUdp::open() == 0)
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_restApi->setHost(_address.toString());
|
||||
_restApi->setPort(_apiPort);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -414,13 +410,23 @@ QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QString discoveryMethod("ssdp");
|
||||
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();
|
||||
#endif
|
||||
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
@@ -431,27 +437,29 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
QJsonObject properties;
|
||||
|
||||
// Get Nanoleaf device properties
|
||||
QString hostName = params["host"].toString("");
|
||||
_hostName = params[CONFIG_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))
|
||||
{
|
||||
QString authToken = params["token"].toString("");
|
||||
QString filter = params["filter"].toString("");
|
||||
|
||||
initRestAPI(hostName, API_DEFAULT_PORT, authToken);
|
||||
_restApi->setPath(filter);
|
||||
|
||||
// Perform request
|
||||
httpResponse response = _restApi->get();
|
||||
if (response.error())
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
Warning(_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
QString filter = params["filter"].toString("");
|
||||
_restApi->setPath(filter);
|
||||
|
||||
// Perform request
|
||||
httpResponse response = _restApi->get();
|
||||
if (response.error())
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -460,19 +468,24 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QString hostName = params["host"].toString("");
|
||||
if (!hostName.isEmpty())
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
|
||||
_authToken = params["token"].toString("");
|
||||
|
||||
Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
|
||||
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
QString authToken = params["token"].toString("");
|
||||
|
||||
initRestAPI(hostName, API_DEFAULT_PORT, authToken);
|
||||
_restApi->setPath("identify");
|
||||
|
||||
// Perform request
|
||||
httpResponse response = _restApi->put();
|
||||
if (response.error())
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
Warning(_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
_restApi->setPath("identify");
|
||||
|
||||
// Perform request
|
||||
httpResponse response = _restApi->put();
|
||||
if (response.error())
|
||||
{
|
||||
Warning(_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -662,7 +675,7 @@ bool LedDeviceNanoleaf::restoreState()
|
||||
Warning (_log, "%s restoring effect failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
} else {
|
||||
Warning (_log, "%s restoring effect failed with error: Cannot restore dynamic or solid effect. 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;
|
||||
}
|
||||
break;
|
||||
|
@@ -150,13 +150,9 @@ private:
|
||||
///
|
||||
/// @brief Initialise the access to the REST-API wrapper
|
||||
///
|
||||
/// @param[in] host
|
||||
/// @param[in] port
|
||||
/// @param[in] authentication token
|
||||
///
|
||||
/// @return True, if success
|
||||
///
|
||||
bool initRestAPI(const QString& hostname, int port, const QString& token);
|
||||
bool openRestAPI();
|
||||
|
||||
///
|
||||
/// @brief Get Nanoleaf device details and configuration
|
||||
@@ -188,9 +184,7 @@ private:
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
|
||||
QString _hostName;
|
||||
int _apiPort;
|
||||
int _apiPort;
|
||||
QString _authToken;
|
||||
|
||||
bool _topDown;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,6 @@
|
||||
|
||||
// Qt includes
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QStringList>
|
||||
@@ -85,7 +84,7 @@ struct CiColor
|
||||
///
|
||||
/// @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
|
||||
@@ -149,8 +148,9 @@ public:
|
||||
/// @param bridge the bridge
|
||||
/// @param id the light id
|
||||
///
|
||||
PhilipsHueLight(Logger* log, unsigned int id, QJsonObject values, unsigned int ledidx);
|
||||
~PhilipsHueLight();
|
||||
PhilipsHueLight(Logger* log, int id, QJsonObject values, int ledidx,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
|
||||
///
|
||||
/// @param on
|
||||
@@ -167,11 +167,12 @@ public:
|
||||
///
|
||||
void setColor(const CiColor& color);
|
||||
|
||||
unsigned int getId() const;
|
||||
int getId() const;
|
||||
|
||||
bool getOnOffState() const;
|
||||
int getTransitionTime() const;
|
||||
CiColor getColor() const;
|
||||
bool hasColor() const;
|
||||
|
||||
///
|
||||
/// @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);
|
||||
QString getOriginalState() const;
|
||||
|
||||
bool isBusy();
|
||||
bool isBlack(bool isBlack);
|
||||
bool isWhite(bool isWhite);
|
||||
void setBlack();
|
||||
void blackScreenTriggered();
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
/// light id
|
||||
unsigned int _id;
|
||||
unsigned int _ledidx;
|
||||
int _id;
|
||||
int _ledidx;
|
||||
bool _on;
|
||||
int _transitionTime;
|
||||
CiColor _color;
|
||||
bool _hasColor;
|
||||
/// darkes blue color in hue lamp GAMUT = black
|
||||
CiColor _colorBlack;
|
||||
/// The model id of the hue lamp which is used to determine the color space.
|
||||
@@ -201,6 +208,12 @@ private:
|
||||
|
||||
QString _originalState;
|
||||
CiColor _originalColor;
|
||||
qint64 _lastSendColorTime;
|
||||
qint64 _lastBlackTime;
|
||||
qint64 _lastWhiteTime;
|
||||
bool _blackScreenTriggered;
|
||||
qint64 _onBlackTimeToPowerOff;
|
||||
qint64 _onBlackTimeToPowerOn;
|
||||
};
|
||||
|
||||
class LedDevicePhilipsHueBridge : public ProviderUdpSSL
|
||||
@@ -215,13 +228,9 @@ public:
|
||||
///
|
||||
/// @brief Initialise the access to the REST-API wrapper
|
||||
///
|
||||
/// @param[in] host
|
||||
/// @param[in] port
|
||||
/// @param[in] authentication token
|
||||
///
|
||||
/// @return True, if success
|
||||
///
|
||||
bool initRestAPI(const QString &hostname, int port, const QString &token );
|
||||
bool openRestAPI();
|
||||
|
||||
///
|
||||
/// @brief Perform a REST-API GET
|
||||
@@ -238,20 +247,18 @@ public:
|
||||
/// @param route the route 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);
|
||||
void setLightState(unsigned int lightId = 0, const QString &state = "");
|
||||
QJsonDocument getLightState( int lightId);
|
||||
void setLightState( int lightId = 0, const QString &state = "");
|
||||
|
||||
QMap<quint16,QJsonObject> getLightMap() const;
|
||||
QMap<int,QJsonObject> getLightMap() const;
|
||||
|
||||
QMap<quint16,QJsonObject> getGroupMap() const;
|
||||
|
||||
QString getGroupName(quint16 groupId = 0) const;
|
||||
|
||||
QJsonArray getGroupLights(quint16 groupId = 0) const;
|
||||
QMap<int,QJsonObject> getGroupMap() const;
|
||||
|
||||
QString getGroupName(int groupId = 0) const;
|
||||
|
||||
QJsonArray getGroupLights(int groupId = 0) const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -281,23 +288,66 @@ protected:
|
||||
/// @brief Check, if Hue API response indicate error
|
||||
///
|
||||
/// @param[in] response from Hue-Bridge in JSON-format
|
||||
/// @param[in] suppressError Treat an error as a warning
|
||||
///
|
||||
/// return True, Hue Bridge reports error
|
||||
///
|
||||
bool checkApiError(const QJsonDocument &response );
|
||||
bool 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
|
||||
ProviderRestApi* _restApi;
|
||||
|
||||
/// Ip address of the bridge
|
||||
QString _hostname;
|
||||
int _apiPort;
|
||||
/// User name for the API ("newdeveloper")
|
||||
QString _username;
|
||||
QString _authToken;
|
||||
|
||||
bool _useHueEntertainmentAPI;
|
||||
|
||||
QJsonDocument getGroupState( unsigned int groupId );
|
||||
QJsonDocument setGroupState( unsigned int groupId, bool state);
|
||||
QJsonDocument getGroupState( int groupId );
|
||||
QJsonDocument setGroupState( int groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
bool initMaps();
|
||||
@@ -308,6 +358,14 @@ protected:
|
||||
|
||||
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();
|
||||
void setBridgeConfig( const QJsonDocument &doc );
|
||||
void setLightsMap( const QJsonDocument &doc );
|
||||
@@ -324,8 +382,8 @@ private:
|
||||
|
||||
bool _isHueEntertainmentReady;
|
||||
|
||||
QMap<quint16,QJsonObject> _lightsMap;
|
||||
QMap<quint16,QJsonObject> _groupsMap;
|
||||
QMap<int,QJsonObject> _lightsMap;
|
||||
QMap<int,QJsonObject> _groupsMap;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -360,34 +418,6 @@ public:
|
||||
/// @return LedDevice constructed
|
||||
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.
|
||||
///
|
||||
@@ -412,7 +442,7 @@ public:
|
||||
///
|
||||
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 setColor(PhilipsHueLight& light, CiColor& color);
|
||||
void setState(PhilipsHueLight& light, bool on, const CiColor& color);
|
||||
@@ -443,13 +473,6 @@ protected:
|
||||
///
|
||||
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.
|
||||
///
|
||||
@@ -465,7 +488,7 @@ protected:
|
||||
/// Depending on the configuration, the device may store its current state for later restore.
|
||||
/// @see powerOn, storeState
|
||||
///
|
||||
/// @return True if success
|
||||
/// @return True, if success
|
||||
///
|
||||
bool switchOn() override;
|
||||
|
||||
@@ -518,28 +541,17 @@ protected:
|
||||
///
|
||||
bool restoreState() override;
|
||||
|
||||
private slots:
|
||||
|
||||
void noSignalTimeout();
|
||||
|
||||
private:
|
||||
|
||||
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();
|
||||
|
||||
/// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
|
||||
///
|
||||
/// @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.
|
||||
@@ -554,13 +566,9 @@ private:
|
||||
bool startStream();
|
||||
bool stopStream();
|
||||
|
||||
void writeStream();
|
||||
void writeStream(bool flush = false);
|
||||
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
|
||||
|
||||
bool noSignalDetection();
|
||||
|
||||
void stopBlackTimeoutTimer();
|
||||
|
||||
QByteArray prepareStreamData() const;
|
||||
|
||||
///
|
||||
@@ -574,32 +582,28 @@ private:
|
||||
bool _isInitLeds;
|
||||
|
||||
/// Array of the light ids.
|
||||
std::vector<quint16> _lightIds;
|
||||
std::vector<int> _lightIds;
|
||||
/// Array to save the lamps.
|
||||
std::vector<PhilipsHueLight> _lights;
|
||||
|
||||
unsigned int _lightsCount;
|
||||
quint16 _groupId;
|
||||
int _lightsCount;
|
||||
int _groupId;
|
||||
|
||||
double _brightnessMin;
|
||||
double _brightnessMax;
|
||||
|
||||
bool _allLightsBlack;
|
||||
|
||||
QTimer* _blackLightsTimer;
|
||||
int _blackLightsTimeout;
|
||||
double _brightnessThreshold;
|
||||
|
||||
int _handshake_timeout_min;
|
||||
int _handshake_timeout_max;
|
||||
int _ssl_read_timeout;
|
||||
double _blackLevel;
|
||||
int _onBlackTimeToPowerOff;
|
||||
int _onBlackTimeToPowerOn;
|
||||
bool _candyGamma;
|
||||
|
||||
// TODO: Check what is the correct class
|
||||
uint32_t _handshake_timeout_min;
|
||||
uint32_t _handshake_timeout_max;
|
||||
bool _stopConnection;
|
||||
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
int start_retry_left;
|
||||
int stop_retry_left;
|
||||
|
||||
qint64 _lastConfirm;
|
||||
int _lastId;
|
||||
bool _groupStreamState;
|
||||
};
|
||||
|
@@ -65,13 +65,6 @@ bool LedDeviceRazer::init(const QJsonObject& deviceConfig)
|
||||
// Initialise sub-class
|
||||
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
|
||||
_hostname = API_DEFAULT_HOST;
|
||||
@@ -86,6 +79,7 @@ bool LedDeviceRazer::init(const QJsonObject& deviceConfig)
|
||||
Debug(_log, "Razer Device : %s", QSTRING_CSTR(_razerDeviceType));
|
||||
Debug(_log, "Single Color : %d", _isSingleColor);
|
||||
|
||||
int configuredLedCount = this->getLedCount();
|
||||
if (resolveDeviceProperties(_razerDeviceType))
|
||||
{
|
||||
if (_isSingleColor && configuredLedCount > 1)
|
||||
@@ -125,6 +119,8 @@ bool LedDeviceRazer::initRestAPI(const QString& hostname, int port)
|
||||
if (_restApi == nullptr)
|
||||
{
|
||||
_restApi = new ProviderRestApi(hostname, port);
|
||||
_restApi->setLogger(_log);
|
||||
|
||||
_restApi->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
isInitOK = true;
|
||||
|
@@ -1,6 +1,13 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
@@ -20,13 +27,14 @@ LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
|
||||
_port = TPM2_DEFAULT_PORT;
|
||||
bool isInitOK {false};
|
||||
|
||||
// Initialise sub-class
|
||||
if ( ProviderUdp::init(deviceConfig) )
|
||||
{
|
||||
_hostName = _devConfig[ CONFIG_HOST ].toString();
|
||||
_port = deviceConfig[CONFIG_PORT].toInt(TPM2_DEFAULT_PORT);
|
||||
|
||||
_tpm2_max = deviceConfig["max-packet"].toInt(170);
|
||||
_tpm2ByteCount = 3 * _ledCount;
|
||||
_tpm2TotalPackets = (_tpm2ByteCount / _tpm2_max) + ((_tpm2ByteCount % _tpm2_max) != 0);
|
||||
@@ -38,6 +46,23 @@ bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
|
||||
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 retVal = 0;
|
||||
|
@@ -41,6 +41,13 @@ private:
|
||||
///
|
||||
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.
|
||||
///
|
||||
|
@@ -9,7 +9,16 @@
|
||||
|
||||
#include <QHostInfo>
|
||||
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_PORT[] = "port";
|
||||
|
||||
const ushort ARTNET_DEFAULT_PORT = 6454;
|
||||
}
|
||||
|
||||
LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
@@ -23,13 +32,14 @@ LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
|
||||
_port = ARTNET_DEFAULT_PORT;
|
||||
bool isInitOK {false};
|
||||
|
||||
// Initialise sub-class
|
||||
if ( ProviderUdp::init(deviceConfig) )
|
||||
{
|
||||
_hostName = _devConfig[ CONFIG_HOST ].toString();
|
||||
_port = deviceConfig[CONFIG_PORT].toInt(ARTNET_DEFAULT_PORT);
|
||||
|
||||
_artnet_universe = deviceConfig["universe"].toInt(1);
|
||||
_artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
|
||||
|
||||
@@ -38,6 +48,23 @@ bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
|
||||
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
|
||||
void LedDeviceUdpArtNet::prepare(unsigned this_universe, unsigned this_sequence, unsigned this_dmxChannelCount)
|
||||
{
|
||||
|
@@ -69,6 +69,13 @@ private:
|
||||
///
|
||||
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.
|
||||
///
|
||||
|
163
libsrc/leddevice/dev_net/LedDeviceUdpDdp.cpp
Normal file
163
libsrc/leddevice/dev_net/LedDeviceUdpDdp.cpp
Normal 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;
|
||||
}
|
||||
|
62
libsrc/leddevice/dev_net/LedDeviceUdpDdp.h
Normal file
62
libsrc/leddevice/dev_net/LedDeviceUdpDdp.h
Normal 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
|
@@ -8,6 +8,13 @@
|
||||
|
||||
// hyperion local includes
|
||||
#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;
|
||||
|
||||
@@ -23,6 +30,7 @@ const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002;
|
||||
//#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds
|
||||
//#define E131_DISCOVERY_UNIVERSE 64214
|
||||
const int DMX_MAX = 512; // 512 usable slots
|
||||
}
|
||||
|
||||
LedDeviceUdpE131::LedDeviceUdpE131(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
@@ -36,13 +44,14 @@ LedDevice* LedDeviceUdpE131::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
|
||||
_port = E131_DEFAULT_PORT;
|
||||
bool isInitOK {false};
|
||||
|
||||
// Initialise sub-class
|
||||
if ( ProviderUdp::init(deviceConfig) )
|
||||
{
|
||||
_hostName = _devConfig[ CONFIG_HOST ].toString();
|
||||
_port = deviceConfig[CONFIG_PORT].toInt(E131_DEFAULT_PORT);
|
||||
|
||||
_e131_universe = deviceConfig["universe"].toInt(1);
|
||||
_e131_source_name = deviceConfig["source-name"].toString("hyperion on "+QHostInfo::localHostName());
|
||||
QString _json_cid = deviceConfig["cid"].toString("");
|
||||
@@ -70,6 +79,23 @@ bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
|
||||
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
|
||||
void LedDeviceUdpE131::prepare(unsigned this_universe, unsigned this_dmxChannelCount)
|
||||
{
|
||||
|
@@ -114,6 +114,13 @@ private:
|
||||
///
|
||||
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.
|
||||
///
|
||||
|
@@ -1,8 +1,12 @@
|
||||
#include "LedDeviceUdpH801.h"
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_PORT[] = "port";
|
||||
|
||||
const ushort H801_DEFAULT_PORT = 30977;
|
||||
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 isInitOK = false;
|
||||
bool isInitOK {false};
|
||||
|
||||
/* The H801 port is fixed */
|
||||
_latchTime_ms = 10;
|
||||
_port = H801_DEFAULT_PORT;
|
||||
_defaultHost = H801_DEFAULT_HOST;
|
||||
|
||||
// Initialise sub-class
|
||||
if ( ProviderUdp::init(deviceConfig) )
|
||||
{
|
||||
_hostName = _devConfig[ CONFIG_HOST ].toString(H801_DEFAULT_HOST);
|
||||
_port = deviceConfig[CONFIG_PORT].toInt(H801_DEFAULT_PORT);
|
||||
|
||||
_ids.clear();
|
||||
QJsonArray lArray = deviceConfig["lightIds"].toArray();
|
||||
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 + 2] = (_ids[i] >> 0x10) & 0xFF;
|
||||
}
|
||||
|
||||
Debug(_log, "H801 using %s:%d", _address.toString().toStdString().c_str(), _port);
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
ColorRgb color = ledValues[0];
|
||||
|
@@ -37,6 +37,13 @@ private:
|
||||
///
|
||||
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.
|
||||
///
|
||||
|
@@ -1,10 +1,14 @@
|
||||
#include "LedDeviceUdpRaw.h"
|
||||
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const bool verbose = false;
|
||||
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_PORT[] = "port";
|
||||
const ushort RAW_DEFAULT_PORT=5568;
|
||||
const int UDP_MAX_LED_NUM = 490;
|
||||
|
||||
@@ -22,33 +26,46 @@ LedDevice* LedDeviceUdpRaw::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
_port = RAW_DEFAULT_PORT;
|
||||
bool isInitOK {false};
|
||||
|
||||
bool isInitOK = false;
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
if ( ProviderUdp::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 (this->getLedCount() > 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 );
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialise sub-class
|
||||
isInitOK = ProviderUdp::init(deviceConfig);
|
||||
_hostName = deviceConfig[ CONFIG_HOST ].toString();
|
||||
_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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
QJsonObject properties;
|
||||
|
||||
Info(_log, "Get properties for %s", QSTRING_CSTR(_activeDeviceType));
|
||||
|
||||
QJsonObject propertiesDetails;
|
||||
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
///
|
||||
/// Implementation of the LedDevice interface for sending LED colors via UDP
|
||||
///
|
||||
class LedDeviceUdpRaw : public ProviderUdp
|
||||
class LedDeviceUdpRaw : public virtual ProviderUdp
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -44,6 +44,13 @@ protected:
|
||||
///
|
||||
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.
|
||||
///
|
||||
|
@@ -1,11 +1,18 @@
|
||||
// Local-Hyperion includes
|
||||
#include "LedDeviceWled.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <utils/QStringUtils.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
|
||||
namespace {
|
||||
@@ -13,16 +20,22 @@ namespace {
|
||||
const bool verbose = false;
|
||||
|
||||
// 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_BRIGHTNESS[] = "brightness";
|
||||
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
|
||||
const char CONFIG_SYNC_OVERWRITE[] = "overwriteSync";
|
||||
|
||||
// UDP elements
|
||||
const quint16 STREAM_DEFAULT_PORT = 19446;
|
||||
const char DEFAULT_STREAM_PROTOCOL[] = "DDP";
|
||||
|
||||
// UDP-RAW
|
||||
const int UDP_STREAM_DEFAULT_PORT = 19446;
|
||||
const int UDP_MAX_LED_NUM = 490;
|
||||
|
||||
// DDP
|
||||
const char WLED_VERSION_DDP[] = "0.11.0";
|
||||
|
||||
// WLED JSON-API elements
|
||||
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
|
||||
|
||||
LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
: ProviderUdp(deviceConfig), LedDeviceUdpDdp(deviceConfig), LedDeviceUdpRaw(deviceConfig)
|
||||
,_restApi(nullptr)
|
||||
,_apiPort(API_DEFAULT_PORT)
|
||||
,_isBrightnessOverwrite(DEFAULT_IS_BRIGHTNESS_OVERWRITE)
|
||||
@@ -54,7 +67,12 @@ LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
|
||||
,_isSyncOverwrite(DEFAULT_IS_SYNC_OVERWRITE)
|
||||
,_originalStateUdpnSend(false)
|
||||
,_originalStateUdpnRecv(true)
|
||||
,_isStreamDDP(true)
|
||||
{
|
||||
#ifdef ENABLE_MDNS
|
||||
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
|
||||
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
|
||||
#endif
|
||||
}
|
||||
|
||||
LedDeviceWled::~LedDeviceWled()
|
||||
@@ -70,25 +88,30 @@ LedDevice* LedDeviceWled::construct(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
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
QString streamProtocol = _devConfig[CONFIG_STREAM_PROTOCOL].toString(DEFAULT_STREAM_PROTOCOL);
|
||||
|
||||
if (streamProtocol != DEFAULT_STREAM_PROTOCOL)
|
||||
{
|
||||
// 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());
|
||||
_isStreamDDP = false;
|
||||
}
|
||||
Debug(_log, "Stream protocol : %s", QSTRING_CSTR(streamProtocol));
|
||||
Debug(_log, "Stream DDP : %d", _isStreamDDP);
|
||||
|
||||
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);
|
||||
this->setInError ( errorReason );
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
_isSyncOverwrite = _devConfig[CONFIG_SYNC_OVERWRITE].toBool(DEFAULT_IS_SYNC_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, "Set Brightness to : %d", _brightness);
|
||||
|
||||
//Set hostname as per configuration
|
||||
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);
|
||||
}
|
||||
}
|
||||
isInitOK = true;
|
||||
}
|
||||
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::initRestAPI(const QString &hostname, int port)
|
||||
bool LedDeviceWled::openRestAPI()
|
||||
{
|
||||
bool isInitOK = false;
|
||||
bool isInitOK {true};
|
||||
|
||||
if ( _restApi == nullptr )
|
||||
{
|
||||
_restApi = new ProviderRestApi(hostname, port);
|
||||
_restApi->setBasePath( API_BASE_PATH );
|
||||
_restApi = new ProviderRestApi(_address.toString(), _apiPort);
|
||||
_restApi->setLogger(_log);
|
||||
|
||||
isInitOK = true;
|
||||
_restApi->setBasePath( API_BASE_PATH );
|
||||
}
|
||||
else
|
||||
{
|
||||
_restApi->setHost(_address.toString());
|
||||
_restApi->setPort(_apiPort);
|
||||
}
|
||||
|
||||
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 state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
@@ -316,6 +360,16 @@ QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/)
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
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);
|
||||
DebugIf(verbose, _log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
@@ -327,41 +381,45 @@ QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
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))
|
||||
{
|
||||
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)
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
apiPort = addressparts[1].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiPort = API_DEFAULT_PORT;
|
||||
}
|
||||
QString filter = params["filter"].toString("");
|
||||
_restApi->setPath(filter);
|
||||
|
||||
initRestAPI(apiHost, apiPort);
|
||||
_restApi->setPath(filter);
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
QJsonObject propertiesDetails = response.getBody().object();
|
||||
|
||||
QJsonObject propertiesDetails = response.getBody().object();
|
||||
if (!propertiesDetails.isEmpty())
|
||||
{
|
||||
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);
|
||||
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())
|
||||
{
|
||||
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() );
|
||||
}
|
||||
@@ -372,41 +430,40 @@ void LedDeviceWled::identify(const QJsonObject& params)
|
||||
{
|
||||
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)
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
if ( addressparts.size() > 1)
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
apiPort = addressparts[1].toInt();
|
||||
_isRestoreOrigState = true;
|
||||
storeState();
|
||||
|
||||
QString request = getOnOffRequest(true) + "," + getLorRequest(1) + "," + getEffectRequest(25);
|
||||
sendStateUpdateRequest(request);
|
||||
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
restoreState();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiPort = API_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
initRestAPI(apiHost, apiPort);
|
||||
|
||||
_isRestoreOrigState = true;
|
||||
storeState();
|
||||
|
||||
QString request = getOnOffRequest(true) + "," + getLorRequest(1) + "," + getEffectRequest(25);
|
||||
sendStateUpdateRequest(request);
|
||||
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
restoreState();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -4,12 +4,13 @@
|
||||
// LedDevice includes
|
||||
#include <leddevice/LedDevice.h>
|
||||
#include "ProviderRestApi.h"
|
||||
#include "ProviderUdp.h"
|
||||
#include "LedDeviceUdpDdp.h"
|
||||
#include "LedDeviceUdpRaw.h"
|
||||
|
||||
///
|
||||
/// Implementation of a WLED-device
|
||||
///
|
||||
class LedDeviceWled : public ProviderUdp
|
||||
class LedDeviceWled : public LedDeviceUdpDdp, LedDeviceUdpRaw
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -81,6 +82,20 @@ protected:
|
||||
///
|
||||
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.
|
||||
///
|
||||
@@ -127,11 +142,9 @@ private:
|
||||
///
|
||||
/// @brief Initialise the access to the REST-API wrapper
|
||||
///
|
||||
/// @param[in] host
|
||||
/// @param[in] port
|
||||
/// @return True, if success
|
||||
///
|
||||
bool initRestAPI(const QString &hostname, int port );
|
||||
bool openRestAPI();
|
||||
|
||||
///
|
||||
/// @brief Get command to power WLED-device on or off
|
||||
@@ -148,10 +161,12 @@ private:
|
||||
|
||||
bool sendStateUpdateRequest(const QString &request);
|
||||
|
||||
QString resolveAddress (const QString& hostName);
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
|
||||
QString _hostname;
|
||||
QString _hostAddress;
|
||||
int _apiPort;
|
||||
|
||||
QJsonObject _originalStateProperties;
|
||||
@@ -162,6 +177,8 @@ private:
|
||||
bool _isSyncOverwrite;
|
||||
bool _originalStateUdpnSend;
|
||||
bool _originalStateUdpnRecv;
|
||||
|
||||
bool _isStreamDDP;
|
||||
};
|
||||
|
||||
#endif // LEDDEVICEWLED_H
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include "LedDeviceYeelight.h"
|
||||
#include "LedDeviceYeelight.h"
|
||||
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
// Qt includes
|
||||
#include <QEventLoop>
|
||||
@@ -11,8 +11,15 @@
|
||||
#include <QColor>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
// mDNS discover
|
||||
#ifdef ENABLE_MDNS
|
||||
#include <mdns/MdnsBrowser.h>
|
||||
#include <mdns/MdnsServiceRegister.h>
|
||||
#endif
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
@@ -28,6 +35,9 @@ constexpr std::chrono::milliseconds CONNECT_STREAM_TIMEOUT{1000}; // device stre
|
||||
const bool TEST_CORRELATION_IDS = false; //Ignore, if yeelight sends responses in different order as request commands
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_PORT[] = "port";
|
||||
|
||||
const char CONFIG_LIGHTS [] = "lights";
|
||||
|
||||
const char CONFIG_COLOR_MODEL [] = "colorModel";
|
||||
@@ -111,7 +121,6 @@ YeelightLight::YeelightLight( Logger *log, const QString &hostname, quint16 port
|
||||
,_isInMusicMode(false)
|
||||
{
|
||||
_name = hostname;
|
||||
|
||||
}
|
||||
|
||||
YeelightLight::~YeelightLight()
|
||||
@@ -151,25 +160,29 @@ bool YeelightLight::open()
|
||||
}
|
||||
else
|
||||
{
|
||||
_tcpSocket->connectToHost( _host, _port);
|
||||
|
||||
if ( _tcpSocket->waitForConnected( CONNECT_TIMEOUT.count() ) )
|
||||
QHostAddress address;
|
||||
if (NetUtils::resolveHostToAddress(_log, _host, address))
|
||||
{
|
||||
if ( _tcpSocket->state() != QAbstractSocket::ConnectedState )
|
||||
_tcpSocket->connectToHost( address.toString(), _port);
|
||||
|
||||
if ( _tcpSocket->waitForConnected( CONNECT_TIMEOUT.count() ) )
|
||||
{
|
||||
if ( _tcpSocket->state() != QAbstractSocket::ConnectedState )
|
||||
{
|
||||
this->setInError( _tcpSocket->errorString() );
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
log (2,"open()","Successfully opened Yeelight: %s", QSTRING_CSTR(_host));
|
||||
rc = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setInError( _tcpSocket->errorString() );
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
log (2,"open()","Successfully opened Yeelight: %s", QSTRING_CSTR(_host));
|
||||
rc = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setInError( _tcpSocket->errorString() );
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@@ -1006,6 +1019,10 @@ LedDeviceYeelight::LedDeviceYeelight(const QJsonObject &deviceConfig)
|
||||
,_debuglevel(0)
|
||||
,_musicModeServerPort(-1)
|
||||
{
|
||||
#ifdef ENABLE_MDNS
|
||||
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
|
||||
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
|
||||
#endif
|
||||
}
|
||||
|
||||
LedDeviceYeelight::~LedDeviceYeelight()
|
||||
@@ -1034,12 +1051,6 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
{
|
||||
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
|
||||
Debug(_log, "LedCount : %d", this->getLedCount());
|
||||
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
|
||||
Debug(_log, "RewriteTime : %d", this->getRewriteTime());
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
//Get device specific configuration
|
||||
|
||||
if ( deviceConfig[ CONFIG_COLOR_MODEL ].isString() )
|
||||
@@ -1102,8 +1113,9 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
int configuredYeelightsCount = 0;
|
||||
for (const QJsonValueRef light : configuredYeelightLights)
|
||||
{
|
||||
QString hostName = light.toObject().value("host").toString();
|
||||
int port = light.toObject().value("port").toInt(API_DEFAULT_PORT);
|
||||
QString hostName = light.toObject().value(CONFIG_HOST).toString();
|
||||
int port = light.toObject().value(CONFIG_PORT).toInt(API_DEFAULT_PORT);
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
QString name = light.toObject().value("name").toString();
|
||||
@@ -1133,9 +1145,8 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
_lightsAddressList.clear();
|
||||
for (int j = 0; j < static_cast<int>( configuredLedCount ); ++j)
|
||||
{
|
||||
QString hostName = configuredYeelightLights[j].toObject().value("host").toString();
|
||||
int port = configuredYeelightLights[j].toObject().value("port").toInt(API_DEFAULT_PORT);
|
||||
|
||||
QString hostName = configuredYeelightLights[j].toObject().value(CONFIG_HOST).toString();
|
||||
int port = configuredYeelightLights[j].toObject().value(CONFIG_PORT).toInt(API_DEFAULT_PORT);
|
||||
_lightsAddressList.append( { hostName, port} );
|
||||
}
|
||||
|
||||
@@ -1160,13 +1171,10 @@ bool LedDeviceYeelight::startMusicModeServer()
|
||||
|
||||
if ( ! _tcpMusicModeServer->isListening() )
|
||||
{
|
||||
if (! _tcpMusicModeServer->listen())
|
||||
if (! _tcpMusicModeServer->listen(QHostAddress::AnyIPv4))
|
||||
{
|
||||
QString errorReason = QString ("(%1) %2").arg(_tcpMusicModeServer->serverError()).arg( _tcpMusicModeServer->errorString());
|
||||
Error( _log, "Error: MusicModeServer: %s", QSTRING_CSTR(errorReason));
|
||||
QString errorReason = QString ("Failed to start music mode server: (%1) %2").arg(_tcpMusicModeServer->serverError()).arg( _tcpMusicModeServer->errorString());
|
||||
this->setInError ( errorReason );
|
||||
|
||||
Error( _log, "Failed to start music mode server");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1182,12 +1190,14 @@ bool LedDeviceYeelight::startMusicModeServer()
|
||||
}
|
||||
if (_musicModeServerAddress.isNull())
|
||||
{
|
||||
Error(_log, "Failed to resolve IP for music mode server");
|
||||
_tcpMusicModeServer->close();
|
||||
QString errorReason = QString ("Network error - failed to resolve IP for music mode server");
|
||||
this->setInError ( errorReason );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( _tcpMusicModeServer->isListening() )
|
||||
if ( !_isDeviceInError && _tcpMusicModeServer->isListening() )
|
||||
{
|
||||
_musicModeServerPort = _tcpMusicModeServer->serverPort();
|
||||
Debug (_log, "The music mode server is running at %s:%d", QSTRING_CSTR(_musicModeServerAddress.toString()), _musicModeServerPort);
|
||||
@@ -1391,10 +1401,21 @@ QJsonObject LedDeviceYeelight::discover(const QJsonObject& /*params*/)
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QString discoveryMethod("ssdp");
|
||||
QJsonArray deviceList;
|
||||
deviceList = discover();
|
||||
|
||||
#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();
|
||||
#endif
|
||||
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
@@ -1407,21 +1428,22 @@ QJsonObject LedDeviceYeelight::getProperties(const QJsonObject& params)
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
QString hostName = params["hostname"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>( params["port"].toInt(API_DEFAULT_PORT) );
|
||||
QString hostName = params[CONFIG_HOST].toString("");
|
||||
quint16 apiPort = static_cast<quint16>( params[CONFIG_PORT].toInt(API_DEFAULT_PORT) );
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(hostName) );
|
||||
|
||||
QHostAddress address;
|
||||
if (NetUtils::resolveHostToAddress(_log, hostName, address))
|
||||
{
|
||||
YeelightLight yeelight(_log, hostName, apiPort);
|
||||
|
||||
//yeelight.setDebuglevel(3);
|
||||
YeelightLight yeelight(_log, address.toString(), apiPort);
|
||||
if ( yeelight.open() )
|
||||
{
|
||||
properties.insert("properties", yeelight.getProperties());
|
||||
yeelight.close();
|
||||
}
|
||||
}
|
||||
Debug(_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;
|
||||
}
|
||||
@@ -1430,15 +1452,15 @@ void LedDeviceYeelight::identify(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
QString hostName = params["hostname"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>( params["port"].toInt(API_DEFAULT_PORT) );
|
||||
Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(hostName), apiPort);
|
||||
QString hostName = params[CONFIG_HOST].toString("");
|
||||
quint16 apiPort = static_cast<quint16>( params[CONFIG_PORT].toInt(API_DEFAULT_PORT) );
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(hostName) );
|
||||
|
||||
QHostAddress address;
|
||||
if (NetUtils::resolveHostToAddress(_log, hostName, address))
|
||||
{
|
||||
YeelightLight yeelight(_log, hostName, apiPort);
|
||||
//yeelight.setDebuglevel(3);
|
||||
|
||||
YeelightLight yeelight(_log, address.toString(), apiPort);
|
||||
if ( yeelight.open() )
|
||||
{
|
||||
yeelight.identify();
|
||||
|
@@ -449,8 +449,8 @@ public:
|
||||
/// Following parameters are required
|
||||
/// @code
|
||||
/// {
|
||||
/// "hostname" : "hostname or IP",
|
||||
/// "port" : port, default port 55443 is used when not provided
|
||||
/// "host" : "hostname or IP",
|
||||
/// "port" : port, default port 55443 is used when not provided
|
||||
/// }
|
||||
///@endcode
|
||||
///
|
||||
@@ -465,8 +465,8 @@ public:
|
||||
/// Following parameters are required
|
||||
/// @code
|
||||
/// {
|
||||
/// "hostname" : "hostname or IP",
|
||||
/// "port" : port, default port 55443 is used when not provided
|
||||
/// "host" : "hostname or IP",
|
||||
/// "port" : port, default port 55443 is used when not provided
|
||||
/// }
|
||||
///@endcode
|
||||
///
|
||||
|
@@ -136,13 +136,19 @@ httpResponse ProviderRestApi::get(const QUrl& url)
|
||||
QNetworkRequest request(_networkRequestHeaders);
|
||||
request.setUrl(url);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
QNetworkReply* reply = _networkManager->get(request);
|
||||
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
QEventLoop::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
@@ -178,12 +184,18 @@ httpResponse ProviderRestApi::put(const QUrl &url, const QByteArray &body)
|
||||
QNetworkRequest request(_networkRequestHeaders);
|
||||
request.setUrl(url);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
QNetworkReply* reply = _networkManager->put(request, body);
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
QEventLoop::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
@@ -220,10 +232,19 @@ httpResponse ProviderRestApi::post(const QUrl& url, const QByteArray& body)
|
||||
QNetworkRequest request(_networkRequestHeaders);
|
||||
request.setUrl(url);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
QNetworkReply* reply = _networkManager->post(request, body);
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
QEventLoop::connect(reply,&QNetworkReply::finished,&loop,&QEventLoop::quit);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
|
||||
@@ -249,6 +270,10 @@ httpResponse ProviderRestApi::deleteResource(const QUrl& url)
|
||||
QNetworkRequest request(_networkRequestHeaders);
|
||||
request.setUrl(url);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
QNetworkReply* reply = _networkManager->deleteResource(request);
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
@@ -256,6 +281,10 @@ httpResponse ProviderRestApi::deleteResource(const QUrl& url)
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
|
||||
#endif
|
||||
|
||||
httpResponse response;
|
||||
if (reply->operation() == QNetworkAccessManager::DeleteOperation)
|
||||
{
|
||||
@@ -331,16 +360,9 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
}
|
||||
else {
|
||||
|
||||
else
|
||||
{
|
||||
errorReason = reply->errorString();
|
||||
|
||||
if ( reply->error() == QNetworkReply::OperationCanceledError )
|
||||
{
|
||||
//Do not report errors caused by request cancellation because of timeouts
|
||||
Debug(_log, "Reply: [%s]", QSTRING_CSTR(errorReason) );
|
||||
}
|
||||
else
|
||||
{
|
||||
response.setError(true);
|
||||
response.setErrorReason(errorReason);
|
||||
|
@@ -77,12 +77,12 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
QJsonDocument _responseBody;
|
||||
QJsonDocument _responseBody {};
|
||||
bool _hasError = false;
|
||||
QString _errorReason;
|
||||
|
||||
int _httpStatusCode = 0;
|
||||
QNetworkReply::NetworkError _networkReplyError = QNetworkReply::NoError;
|
||||
QNetworkReply::NetworkError _networkReplyError { QNetworkReply::NoError };
|
||||
};
|
||||
|
||||
///
|
||||
@@ -291,6 +291,13 @@ public:
|
||||
///
|
||||
void removeAllHeaders() { _networkRequestHeaders = QNetworkRequest(); }
|
||||
|
||||
///
|
||||
/// @brief Set the common logger for LED-devices.
|
||||
///
|
||||
/// @param[in] log The logger to be used
|
||||
///
|
||||
void setLogger(Logger* log) { _log = log; }
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
|
@@ -10,16 +10,19 @@
|
||||
#include <QUdpSocket>
|
||||
#include <QHostInfo>
|
||||
|
||||
// mDNS discover
|
||||
#ifdef ENABLE_MDNS
|
||||
#include <mdns/MdnsBrowser.h>
|
||||
#endif
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
// Local Hyperion includes
|
||||
#include "ProviderUdp.h"
|
||||
|
||||
const ushort MAX_PORT = 65535;
|
||||
|
||||
ProviderUdp::ProviderUdp(const QJsonObject& deviceConfig)
|
||||
: LedDevice(deviceConfig)
|
||||
, _udpSocket(nullptr)
|
||||
, _port(1)
|
||||
, _defaultHost("127.0.0.1")
|
||||
, _port(-1)
|
||||
{
|
||||
_latchTime_ms = 0;
|
||||
}
|
||||
@@ -29,83 +32,38 @@ ProviderUdp::~ProviderUdp()
|
||||
delete _udpSocket;
|
||||
}
|
||||
|
||||
bool ProviderUdp::init(const QJsonObject& deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
|
||||
// Initialise sub-class
|
||||
if (LedDevice::init(deviceConfig))
|
||||
{
|
||||
QString host = deviceConfig["host"].toString(_defaultHost);
|
||||
|
||||
if (_address.setAddress(host))
|
||||
{
|
||||
Debug(_log, "Successfully parsed %s as an IP-address.", QSTRING_CSTR(_address.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
QHostInfo hostInfo = QHostInfo::fromName(host);
|
||||
if (hostInfo.error() == QHostInfo::NoError)
|
||||
{
|
||||
_address = hostInfo.addresses().first();
|
||||
Debug(_log, "Successfully resolved IP-address (%s) for hostname (%s).", QSTRING_CSTR(_address.toString()), QSTRING_CSTR(host));
|
||||
}
|
||||
else
|
||||
{
|
||||
QString errortext = QString("Failed resolving IP-address for [%1], (%2) %3").arg(host).arg(hostInfo.error()).arg(hostInfo.errorString());
|
||||
this->setInError(errortext);
|
||||
isInitOK = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isDeviceInError)
|
||||
{
|
||||
int config_port = deviceConfig["port"].toInt(_port);
|
||||
if (config_port <= 0 || config_port > MAX_PORT)
|
||||
{
|
||||
QString errortext = QString("Invalid target port [%1]!").arg(config_port);
|
||||
this->setInError(errortext);
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_port = static_cast<quint16>(config_port);
|
||||
Debug(_log, "UDP socket will write to %s port: %u", QSTRING_CSTR(_address.toString()), _port);
|
||||
|
||||
_udpSocket = new QUdpSocket(this);
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
int ProviderUdp::open()
|
||||
{
|
||||
int retval = -1;
|
||||
_isDeviceReady = false;
|
||||
|
||||
// Try to bind the UDP-Socket
|
||||
if (_udpSocket != nullptr)
|
||||
if (!_isDeviceInError)
|
||||
{
|
||||
if (_udpSocket->state() != QAbstractSocket::BoundState)
|
||||
{
|
||||
QHostAddress localAddress = QHostAddress::Any;
|
||||
quint16 localPort = 0;
|
||||
if (!_udpSocket->bind(localAddress, localPort))
|
||||
if (_udpSocket == nullptr)
|
||||
{
|
||||
QString warntext = QString("Could not bind local address: %1, (%2) %3").arg(localAddress.toString()).arg(_udpSocket->error()).arg(_udpSocket->errorString());
|
||||
Warning(_log, "%s", QSTRING_CSTR(warntext));
|
||||
_udpSocket = new QUdpSocket(this);
|
||||
}
|
||||
|
||||
// Try to bind the UDP-Socket
|
||||
if (_udpSocket != nullptr)
|
||||
{
|
||||
Info(_log, "Stream UDP data to %s port: %d", QSTRING_CSTR(_address.toString()), _port);
|
||||
if (_udpSocket->state() != QAbstractSocket::BoundState)
|
||||
{
|
||||
QHostAddress localAddress = QHostAddress::Any;
|
||||
quint16 localPort = 0;
|
||||
if (!_udpSocket->bind(localAddress, localPort))
|
||||
{
|
||||
QString warntext = QString("Could not bind local address: %1, (%2) %3").arg(localAddress.toString()).arg(_udpSocket->error()).arg(_udpSocket->errorString());
|
||||
Warning(_log, "%s", QSTRING_CSTR(warntext));
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setInError(" Open error. UDP Socket not initialised!");
|
||||
}
|
||||
}
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setInError(" Open error. UDP Socket not initialised!");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -131,7 +89,7 @@ int ProviderUdp::close()
|
||||
int ProviderUdp::writeBytes(const unsigned size, const uint8_t* data)
|
||||
{
|
||||
int rc = 0;
|
||||
qint64 bytesWritten = _udpSocket->writeDatagram(reinterpret_cast<const char*>(data), size, _address, _port);
|
||||
qint64 bytesWritten = _udpSocket->writeDatagram(reinterpret_cast<const char*>(data), size, _address, static_cast<quint16>(_port));
|
||||
|
||||
if (bytesWritten == -1 || bytesWritten != size)
|
||||
{
|
||||
@@ -144,7 +102,7 @@ int ProviderUdp::writeBytes(const unsigned size, const uint8_t* data)
|
||||
int ProviderUdp::writeBytes(const QByteArray& bytes)
|
||||
{
|
||||
int rc = 0;
|
||||
qint64 bytesWritten = _udpSocket->writeDatagram(bytes, _address, _port);
|
||||
qint64 bytesWritten = _udpSocket->writeDatagram(bytes, _address, static_cast<quint16>(_port));
|
||||
|
||||
if (bytesWritten == -1 || bytesWritten != bytes.size())
|
||||
{
|
||||
|
@@ -32,14 +32,6 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
/// @brief Initialise the UDP device's configuration and network address details
|
||||
///
|
||||
/// @param[in] deviceConfig the JSON device configuration
|
||||
/// @return True, if success
|
||||
///
|
||||
bool init(const QJsonObject& deviceConfig) override;
|
||||
|
||||
///
|
||||
/// @brief Opens the output device.
|
||||
///
|
||||
@@ -74,10 +66,10 @@ protected:
|
||||
int writeBytes(const QByteArray& bytes);
|
||||
|
||||
///
|
||||
QUdpSocket* _udpSocket;
|
||||
QUdpSocket* _udpSocket;
|
||||
QString _hostName;
|
||||
QHostAddress _address;
|
||||
quint16 _port;
|
||||
QString _defaultHost;
|
||||
int _port;
|
||||
};
|
||||
|
||||
#endif // PROVIDERUDP_H
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -23,19 +23,6 @@
|
||||
|
||||
#if defined(MBEDTLS_PLATFORM_C)
|
||||
#include <mbedtls/platform.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#define mbedtls_time time
|
||||
#define mbedtls_time_t time_t
|
||||
#define mbedtls_printf printf
|
||||
#define mbedtls_fprintf fprintf
|
||||
#define mbedtls_snprintf snprintf
|
||||
#define mbedtls_calloc calloc
|
||||
#define mbedtls_free free
|
||||
#define mbedtls_exit exit
|
||||
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
|
||||
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
@@ -50,12 +37,6 @@
|
||||
#include <mbedtls/error.h>
|
||||
#include <mbedtls/debug.h>
|
||||
|
||||
//----------- END mbedtls
|
||||
|
||||
constexpr std::chrono::milliseconds STREAM_SSL_HANDSHAKE_TIMEOUT_MIN{400};
|
||||
constexpr std::chrono::milliseconds STREAM_SSL_HANDSHAKE_TIMEOUT_MAX{1000};
|
||||
constexpr std::chrono::milliseconds STREAM_SSL_READ_TIMEOUT{0};
|
||||
|
||||
class ProviderUdpSSL : public LedDevice
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -71,6 +52,11 @@ public:
|
||||
///
|
||||
~ProviderUdpSSL() override;
|
||||
|
||||
///
|
||||
QString _hostName;
|
||||
QHostAddress _address;
|
||||
int _port;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
@@ -102,6 +88,18 @@ protected:
|
||||
///
|
||||
bool initNetwork();
|
||||
|
||||
///
|
||||
/// @brief Start astreaming connection
|
||||
///
|
||||
/// @return True, if success
|
||||
///
|
||||
bool startConnection();
|
||||
|
||||
///
|
||||
/// @brief Stop the streaming connection
|
||||
///
|
||||
void stopConnection();
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
@@ -109,7 +107,7 @@ protected:
|
||||
/// @param[in] size The length of the data
|
||||
/// @param[in] data The data
|
||||
///
|
||||
void writeBytes(unsigned int size, const uint8_t *data);
|
||||
void writeBytes(unsigned int size, const uint8_t* data, bool flush = false);
|
||||
|
||||
///
|
||||
/// get ciphersuites list from mbedtls_ssl_list_ciphersuites
|
||||
@@ -118,36 +116,16 @@ protected:
|
||||
///
|
||||
virtual const int * getCiphersuites() const;
|
||||
|
||||
void sslLog(const QString &msg, const char* errorType = "debug");
|
||||
void sslLog(const char* msg, const char* errorType = "debug");
|
||||
void configLog(const char* msg, const char* type, ...);
|
||||
|
||||
/**
|
||||
* Debug callback for mbed TLS
|
||||
* Just prints on the USB serial port
|
||||
*/
|
||||
static void ProviderUdpSSLDebug(void* ctx, int level, const char* file, int line, const char* str);
|
||||
|
||||
/**
|
||||
* Certificate verification callback for mbed TLS
|
||||
* Here we only use it to display information on each cert in the chain
|
||||
*/
|
||||
static int ProviderUdpSSLVerify(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags);
|
||||
|
||||
///
|
||||
/// closeSSLNotify and freeSSLConnection
|
||||
///
|
||||
void closeSSLConnection();
|
||||
|
||||
private:
|
||||
|
||||
bool initConnection();
|
||||
|
||||
bool seedingRNG();
|
||||
bool setupStructure();
|
||||
bool startUPDConnection();
|
||||
|
||||
bool setupPSK();
|
||||
bool startSSLHandshake();
|
||||
void handleReturn(int ret);
|
||||
|
||||
QString errorMsg(int ret);
|
||||
void closeSSLNotify();
|
||||
void freeSSLConnection();
|
||||
@@ -160,24 +138,19 @@ private:
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_timing_delay_context timer;
|
||||
|
||||
QMutex _hueMutex;
|
||||
QString _transport_type;
|
||||
QString _custom;
|
||||
QHostAddress _address;
|
||||
QString _defaultHost;
|
||||
int _port;
|
||||
int _ssl_port;
|
||||
QString _server_name;
|
||||
QString _psk;
|
||||
QString _psk_identity;
|
||||
uint32_t _read_timeout;
|
||||
|
||||
int _handshake_attempts;
|
||||
uint32_t _handshake_timeout_min;
|
||||
uint32_t _handshake_timeout_max;
|
||||
unsigned int _handshake_attempts;
|
||||
int _retry_left;
|
||||
bool _stopConnection;
|
||||
bool _debugStreamer;
|
||||
int _debugLevel;
|
||||
|
||||
bool _streamReady;
|
||||
bool _streamPaused;
|
||||
};
|
||||
|
||||
#endif // PROVIDERUDPSSL_H
|
||||
|
@@ -79,4 +79,4 @@ private:
|
||||
|
||||
};
|
||||
|
||||
#endif // LEDEVICETEMPLATE_H
|
||||
#endif // LEDEVICEPIBLASTER_H
|
||||
|
@@ -44,13 +44,6 @@ bool ProviderRs232::init(const QJsonObject &deviceConfig)
|
||||
// Initialise sub-class
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
{
|
||||
|
||||
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());
|
||||
|
||||
_deviceName = deviceConfig["output"].toString("auto");
|
||||
_isAutoDeviceName = _deviceName.toLower() == "auto";
|
||||
|
||||
@@ -89,7 +82,6 @@ int ProviderRs232::open()
|
||||
{
|
||||
int retval = -1;
|
||||
_isDeviceReady = false;
|
||||
_isInSwitchOff = false;
|
||||
|
||||
// open device physically
|
||||
if ( tryOpen(_delayAfterConnect_ms) )
|
||||
@@ -190,18 +182,6 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
|
||||
{
|
||||
QString errortext = QString("Invalid serial device name: %1 %2!").arg(_deviceName, _location);
|
||||
this->setInError( errortext );
|
||||
|
||||
// List available device
|
||||
for (auto &port : QSerialPortInfo::availablePorts() ) {
|
||||
Debug(_log, "Avail. serial device: [%s]-(%s|%s), Manufacturer: %s, Description: %s",
|
||||
QSTRING_CSTR(port.portName()),
|
||||
QSTRING_CSTR(QString("0x%1").arg(port.vendorIdentifier(), 0, 16)),
|
||||
QSTRING_CSTR(QString("0x%1").arg(port.productIdentifier(), 0, 16)),
|
||||
QSTRING_CSTR(port.manufacturer()),
|
||||
QSTRING_CSTR(port.description())
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -295,17 +275,21 @@ QString ProviderRs232::discoverFirst()
|
||||
return "";
|
||||
}
|
||||
|
||||
QJsonObject ProviderRs232::discover(const QJsonObject& /*params*/)
|
||||
QJsonObject ProviderRs232::discover(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
bool showAll = params["discoverAll"].toBool(false);
|
||||
|
||||
// Discover serial Devices
|
||||
for (auto &port : QSerialPortInfo::availablePorts() )
|
||||
{
|
||||
if ( !port.isNull() && port.vendorIdentifier() != 0)
|
||||
if ( !port.isNull() && (showAll || port.vendorIdentifier() != 0) )
|
||||
{
|
||||
QJsonObject portInfo;
|
||||
portInfo.insert("description", port.description());
|
||||
@@ -364,6 +348,8 @@ void ProviderRs232::identify(const QJsonObject& params)
|
||||
QString deviceName = params["output"].toString("");
|
||||
if (!deviceName.isEmpty())
|
||||
{
|
||||
Info(_log, "Identify %s, device: %s", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(deviceName) );
|
||||
|
||||
_devConfig = params;
|
||||
init(_devConfig);
|
||||
{
|
||||
|
@@ -74,12 +74,22 @@ protected:
|
||||
bool powerOff() override;
|
||||
|
||||
///
|
||||
/// @brief Discover first devices of a serial device available (for configuration)
|
||||
/// @brief Discover first device of serial devices available (for configuration)
|
||||
///
|
||||
/// @return A string of the device found
|
||||
///
|
||||
QString discoverFirst() override;
|
||||
|
||||
///
|
||||
/// @brief Discover serial devices available (for configuration).
|
||||
///
|
||||
/// Following parameters can be provided optional
|
||||
/// @code
|
||||
/// {
|
||||
/// "discoverAll" : true/false , "true", in case devices without vendor-id are to be included in the discovery result
|
||||
/// }
|
||||
///@endcode
|
||||
///
|
||||
/// @param[in] params Parameters used to overwrite discovery default behaviour
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
|
@@ -37,7 +37,6 @@ ProviderSpi::ProviderSpi(const QJsonObject &deviceConfig)
|
||||
{
|
||||
memset(&_spi, 0, sizeof(_spi));
|
||||
_latchTime_ms = 1;
|
||||
_isInSwitchOff = false;
|
||||
}
|
||||
|
||||
ProviderSpi::~ProviderSpi()
|
||||
@@ -69,7 +68,6 @@ int ProviderSpi::open()
|
||||
int retval = -1;
|
||||
QString errortext;
|
||||
_isDeviceReady = false;
|
||||
_isInSwitchOff = false;
|
||||
|
||||
const int bitsPerWord = 8;
|
||||
|
||||
|
@@ -37,43 +37,74 @@
|
||||
},
|
||||
"useEntertainmentAPI": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_useEntertainmentAPI_title",
|
||||
"default": true,
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"transitiontime": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_transistionTime_title",
|
||||
"default": 1,
|
||||
"append": "x100ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"switchOffOnBlack": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_switchOffOnBlack_title",
|
||||
"default": false,
|
||||
"propertyOrder": 7
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": true,
|
||||
"default": false,
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"blackLevel": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"title": "edt_dev_spec_brightnessThreshold_title",
|
||||
"default": 0.009,
|
||||
"step": 0.01,
|
||||
"minimum": 0.001,
|
||||
"maximum": 1.0,
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"onBlackTimeToPowerOff": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"step": 50,
|
||||
"title": "edt_dev_spec_onBlackTimeToPowerOff",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 100,
|
||||
"maximum": 100000,
|
||||
"default": 600,
|
||||
"required": true,
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"onBlackTimeToPowerOn": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"step": 50,
|
||||
"title": "edt_dev_spec_onBlackTimeToPowerOn",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 100,
|
||||
"maximum": 100000,
|
||||
"default": 300,
|
||||
"required": true,
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"candyGamma": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_candyGamma_title",
|
||||
"default": true,
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"lightIds": {
|
||||
"type": "array",
|
||||
"title": "edt_dev_spec_lightid_title",
|
||||
"minimum": 1,
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"required": true,
|
||||
"minimum": 0,
|
||||
"title": "edt_dev_spec_lightid_itemtitle"
|
||||
},
|
||||
"options": {
|
||||
@@ -81,10 +112,12 @@
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 9
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"groupId": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"step": 1,
|
||||
"title": "edt_dev_spec_groupId_title",
|
||||
"default": 0,
|
||||
"options": {
|
||||
@@ -92,41 +125,11 @@
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"blackLightsTimeout": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_blackLightsTimeout_title",
|
||||
"default": 15000,
|
||||
"step": 500,
|
||||
"minimum": 10000,
|
||||
"maximum": 60000,
|
||||
"access": "advanced",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"brightnessThreshold": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessThreshold_title",
|
||||
"default": 0,
|
||||
"step": 0.005,
|
||||
"minimum": 0,
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 12
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"title": "edt_dev_spec_brightnessFactor_title",
|
||||
"default": 1.0,
|
||||
"step": 0.25,
|
||||
@@ -135,6 +138,82 @@
|
||||
"access": "advanced",
|
||||
"propertyOrder": 13
|
||||
},
|
||||
"handshakeTimeoutMin": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"title": "edt_dev_spec_sslHSTimeoutMin_title",
|
||||
"default": 600,
|
||||
"step": 100,
|
||||
"minimum": 100,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"required": true,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 14
|
||||
},
|
||||
"handshakeTimeoutMax": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"title": "edt_dev_spec_sslHSTimeoutMax_title",
|
||||
"default": 1000,
|
||||
"step": 100,
|
||||
"minimum": 100,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"required": true,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 15
|
||||
},
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_verbose_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 16
|
||||
},
|
||||
"transitiontime": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_transistionTime_title",
|
||||
"default": 1,
|
||||
"minimum": 0,
|
||||
"maximum": 100000,
|
||||
"required": true,
|
||||
"append": "x100ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 17
|
||||
},
|
||||
"blackLightsTimeout": {
|
||||
"type": "number",
|
||||
"default": 5000,
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"propertyOrder": 18
|
||||
},
|
||||
"brightnessThreshold": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessThreshold_title",
|
||||
"default": 0.0001,
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"propertyOrder": 19
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessMin_title",
|
||||
@@ -144,11 +223,9 @@
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
"hidden": true
|
||||
},
|
||||
"propertyOrder": 14
|
||||
"propertyOrder": 20
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "number",
|
||||
@@ -159,93 +236,8 @@
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
"hidden": true
|
||||
},
|
||||
"propertyOrder": 15
|
||||
},
|
||||
"sslReadTimeout": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_sslReadTimeout_title",
|
||||
"default": 0,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 16
|
||||
},
|
||||
"sslHSTimeoutMin": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_sslHSTimeoutMin_title",
|
||||
"default": 400,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 17
|
||||
},
|
||||
"sslHSTimeoutMax": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_sslHSTimeoutMax_title",
|
||||
"default": 1000,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 18
|
||||
},
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_verbose_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 19
|
||||
},
|
||||
"debugStreamer": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_debugStreamer_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 20
|
||||
},
|
||||
"debugLevel": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_debugLevel_title",
|
||||
"enum": [ "0", "1", "2", "3", "4" ],
|
||||
"default": "0",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_dl_nodebug", "edt_conf_enum_dl_error", "edt_conf_enum_dl_statechange", "edt_conf_enum_dl_informational", "edt_conf_enum_dl_verbose" ],
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 4,
|
||||
"access": "expert",
|
||||
"propertyOrder": 21
|
||||
}
|
||||
},
|
||||
|
32
libsrc/leddevice/schemas/schema-udpddp.json
Normal file
32
libsrc/leddevice/schemas/schema-udpddp.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"format": "hostname_or_ip",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"default": 4048,
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
@@ -24,6 +24,17 @@
|
||||
"required": true,
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"streamProtocol": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_stream_protocol_title",
|
||||
"enum": [ "DDP", "RAW" ],
|
||||
"default": "DDP",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_udp_ddp", "edt_conf_enum_udp_raw" ]
|
||||
},
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
@@ -33,7 +44,7 @@
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_restoreOriginalState_title_info"
|
||||
},
|
||||
"propertyOrder": 3
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"overwriteSync": {
|
||||
"type": "boolean",
|
||||
@@ -42,7 +53,7 @@
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 4
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"overwriteBrightness": {
|
||||
"type": "boolean",
|
||||
@@ -51,7 +62,7 @@
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 5
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"brightness": {
|
||||
"type": "integer",
|
||||
@@ -65,7 +76,7 @@
|
||||
}
|
||||
},
|
||||
"access": "advanced",
|
||||
"propertyOrder": 6
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
@@ -78,7 +89,7 @@
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_latchtime_title_info"
|
||||
},
|
||||
"propertyOrder": 7
|
||||
"propertyOrder": 8
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
16
libsrc/mdns/CMakeLists.txt
Normal file
16
libsrc/mdns/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/mdns)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/mdns)
|
||||
|
||||
FILE ( GLOB MDNS_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
add_library(mdns ${MDNS_SOURCES})
|
||||
|
||||
include_directories(${QMDNS_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(mdns
|
||||
hyperion-utils
|
||||
${QMDNS_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories(mdns PUBLIC ${QMDNS_INCLUDE_DIR})
|
471
libsrc/mdns/MdnsBrowser.cpp
Normal file
471
libsrc/mdns/MdnsBrowser.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
#include <mdns/MdnsBrowser.h>
|
||||
#include <qmdnsengine/message.h>
|
||||
#include <qmdnsengine/service.h>
|
||||
|
||||
//Qt includes
|
||||
#include <QThread>
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QHostAddress>
|
||||
#include <QRegularExpression>
|
||||
|
||||
// Utility includes
|
||||
#include <HyperionConfig.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/WaitTime.h>
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
namespace {
|
||||
const bool verboseBrowser = false;
|
||||
} //End of constants
|
||||
|
||||
MdnsBrowser::MdnsBrowser(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("MDNS"))
|
||||
{
|
||||
qRegisterMetaType<QHostAddress>("QHostAddress");
|
||||
}
|
||||
|
||||
MdnsBrowser::~MdnsBrowser()
|
||||
{
|
||||
qDeleteAll(_browsedServiceTypes);
|
||||
}
|
||||
|
||||
void MdnsBrowser::browseForServiceType(const QByteArray& serviceType)
|
||||
{
|
||||
if (!_browsedServiceTypes.contains(serviceType))
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Start new mDNS browser for serviceType [%s], Thread: %s", serviceType.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
QMdnsEngine::Browser* newBrowser = new QMdnsEngine::Browser(&_server, serviceType, &_cache);
|
||||
|
||||
QObject::connect(newBrowser, &QMdnsEngine::Browser::serviceAdded, this, &MdnsBrowser::onServiceAdded);
|
||||
QObject::connect(newBrowser, &QMdnsEngine::Browser::serviceUpdated, this, &MdnsBrowser::onServiceUpdated);
|
||||
QObject::connect(newBrowser, &QMdnsEngine::Browser::serviceRemoved, this, &MdnsBrowser::onServiceRemoved);
|
||||
|
||||
_browsedServiceTypes.insert(serviceType, newBrowser);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Use existing mDNS browser for serviceType [%s], Thread: %s", serviceType.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
}
|
||||
}
|
||||
|
||||
void MdnsBrowser::onServiceAdded(const QMdnsEngine::Service& service)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Discovered service [%s] at host: %s, port: %u, Thread: %s", service.name().constData(), service.hostname().constData(), service.port(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
emit serviceFound(service);
|
||||
}
|
||||
|
||||
void MdnsBrowser::onServiceUpdated(const QMdnsEngine::Service& service)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "[%s], Name: [%s], Port: [%u], Thread: %s", service.type().constData(), service.name().constData(), service.port(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
}
|
||||
|
||||
void MdnsBrowser::onServiceRemoved(const QMdnsEngine::Service& service)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "[%s], Name: [%s], Port: [%u], Thread: %s", service.type().constData(), service.name().constData(), service.port(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
emit serviceRemoved(service);
|
||||
}
|
||||
|
||||
QHostAddress MdnsBrowser::getHostFirstAddress(const QByteArray& hostname)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "for hostname [%s], Thread: %s", hostname.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
QByteArray toBeResolvedHostName {hostname};
|
||||
|
||||
QHostAddress hostAddress;
|
||||
|
||||
if (toBeResolvedHostName.endsWith(".local"))
|
||||
{
|
||||
toBeResolvedHostName.append('.');
|
||||
}
|
||||
if (toBeResolvedHostName.endsWith(".local."))
|
||||
{
|
||||
QList<QMdnsEngine::Record> aRecords;
|
||||
if (_cache.lookupRecords(toBeResolvedHostName, QMdnsEngine::A, aRecords))
|
||||
{
|
||||
foreach(QMdnsEngine::Record record, aRecords)
|
||||
{
|
||||
// Do not publish link local addresses
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
if (!record.address().isLinkLocal())
|
||||
#else
|
||||
if (!record.address().toString().startsWith("fe80"))
|
||||
#endif
|
||||
{
|
||||
hostAddress = record.address();
|
||||
DebugIf(verboseBrowser, _log, "Hostname [%s] translates to IPv4-address [%s]", toBeResolvedHostName.constData(), QSTRING_CSTR(hostAddress.toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QList<QMdnsEngine::Record> aaaaRecords;
|
||||
if (_cache.lookupRecords(toBeResolvedHostName, QMdnsEngine::AAAA, aaaaRecords))
|
||||
{
|
||||
foreach(QMdnsEngine::Record record, aaaaRecords)
|
||||
{
|
||||
// Do not publish link local addresses
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
if (!record.address().isLinkLocal())
|
||||
#else
|
||||
if (!record.address().toString().startsWith("fe80"))
|
||||
#endif
|
||||
{
|
||||
hostAddress = record.address();
|
||||
DebugIf(verboseBrowser, _log, "Hostname [%s] translates to IPv6-address [%s]", toBeResolvedHostName.constData(), QSTRING_CSTR(hostAddress.toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "IP-address for hostname [%s] not yet in cache, start resolver.", toBeResolvedHostName.constData());
|
||||
qRegisterMetaType<QMdnsEngine::Message>("Message");
|
||||
auto* resolver = new QMdnsEngine::Resolver(&_server, toBeResolvedHostName, &_cache);
|
||||
connect(resolver, &QMdnsEngine::Resolver::resolved, this, &MdnsBrowser::onHostNameResolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hostAddress;
|
||||
}
|
||||
|
||||
void MdnsBrowser::onHostNameResolved(const QHostAddress& address)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "for address [%s], Thread: %s", QSTRING_CSTR(address.toString()), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
// Do not publish link local addresses
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
if (!address.isLinkLocal())
|
||||
#else
|
||||
if (!address.toString().startsWith("fe80"))
|
||||
#endif
|
||||
{
|
||||
emit addressResolved(address);
|
||||
}
|
||||
}
|
||||
|
||||
bool MdnsBrowser::resolveAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, std::chrono::milliseconds timeout)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Get address for hostname [%s], Thread: %s", QSTRING_CSTR(hostname), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
bool isHostAddressOK{ false };
|
||||
if (hostname.endsWith(".local") || hostname.endsWith(".local."))
|
||||
{
|
||||
hostAddress = getHostFirstAddress(hostname.toUtf8());
|
||||
|
||||
if (hostAddress.isNull())
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Wait for resolver on hostname [%s]", QSTRING_CSTR(hostname));
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer t;
|
||||
QObject::connect(&MdnsBrowser::getInstance(), &MdnsBrowser::addressResolved, &loop, &QEventLoop::quit);
|
||||
|
||||
weakConnect(&MdnsBrowser::getInstance(), &MdnsBrowser::addressResolved,
|
||||
[&hostAddress, hostname](const QHostAddress& resolvedAddress) {
|
||||
DebugIf(verboseBrowser, Logger::getInstance("MDNS"), "Resolver resolved hostname [%s] to address [%s], Thread: %s", QSTRING_CSTR(hostname), QSTRING_CSTR(resolvedAddress.toString()), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
hostAddress = resolvedAddress;
|
||||
});
|
||||
|
||||
QTimer::connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
t.start(static_cast<int>(timeout.count()));
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (!hostAddress.isNull())
|
||||
{
|
||||
Debug(log, "Resolved mDNS hostname [%s] to address [%s]", QSTRING_CSTR(hostname), QSTRING_CSTR(hostAddress.toString()));
|
||||
isHostAddressOK = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(log, "Resolved mDNS hostname [%s] timed out", QSTRING_CSTR(hostname));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(log, "Hostname [%s] is not an mDNS hostname.", QSTRING_CSTR(hostname));
|
||||
isHostAddressOK = false;
|
||||
}
|
||||
return isHostAddressOK;
|
||||
}
|
||||
|
||||
QMdnsEngine::Record MdnsBrowser::getServiceInstanceRecord(const QByteArray& serviceInstance, const std::chrono::milliseconds waitTime) const
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Get service instance [%s] details, Thread: %s",serviceInstance.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
QByteArray service{ serviceInstance };
|
||||
|
||||
if (!service.endsWith('.'))
|
||||
{
|
||||
service.append('.');
|
||||
}
|
||||
|
||||
QMdnsEngine::Record srvRecord;
|
||||
bool found{ false };
|
||||
int retries = 5;
|
||||
do
|
||||
{
|
||||
if (_cache.lookupRecord(service, QMdnsEngine::SRV, srvRecord))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
wait(waitTime);
|
||||
--retries;
|
||||
}
|
||||
|
||||
} while (!found && retries >= 0);
|
||||
|
||||
if (found)
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "Service record found for service instance [%s]", service.constData());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "No service record found for service instance [%s]", service.constData());
|
||||
}
|
||||
return srvRecord;
|
||||
}
|
||||
|
||||
QMdnsEngine::Service MdnsBrowser::getFirstService(const QByteArray& serviceType, const QString& filter, const std::chrono::milliseconds waitTime) const
|
||||
{
|
||||
DebugIf(verboseBrowser,_log, "Get first service of type [%s], matching name: [%s]", QSTRING_CSTR(QString(serviceType)), QSTRING_CSTR(filter));
|
||||
|
||||
QMdnsEngine::Service service;
|
||||
|
||||
QRegularExpression regEx(filter);
|
||||
if (!regEx.isValid()) {
|
||||
QString errorString = regEx.errorString();
|
||||
int errorOffset = regEx.patternErrorOffset();
|
||||
|
||||
Error(_log, "Filtering regular expression [%s] error [%d]:[%s]", QSTRING_CSTR(filter), errorOffset, QSTRING_CSTR(errorString));
|
||||
}
|
||||
else
|
||||
{
|
||||
QList<QMdnsEngine::Record> ptrRecords;
|
||||
|
||||
bool found {false};
|
||||
int retries = 3;
|
||||
do
|
||||
{
|
||||
if (_cache.lookupRecords(serviceType, QMdnsEngine::PTR, ptrRecords))
|
||||
{
|
||||
for (int ptrCounter = 0; ptrCounter < ptrRecords.size(); ++ptrCounter)
|
||||
{
|
||||
QByteArray serviceNameFull = ptrRecords.at(ptrCounter).target();
|
||||
|
||||
QRegularExpressionMatch match = regEx.match(serviceNameFull.constData());
|
||||
if (match.hasMatch())
|
||||
{
|
||||
QMdnsEngine::Record srvRecord;
|
||||
if (!_cache.lookupRecord(serviceNameFull, QMdnsEngine::SRV, srvRecord))
|
||||
{
|
||||
DebugIf(verboseBrowser, _log, "No SRV record for [%s] found, skip entry", serviceNameFull.constData());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serviceNameFull.endsWith("." + serviceType))
|
||||
{
|
||||
service.setName(serviceNameFull.left(serviceNameFull.length() - serviceType.length() - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
service.setName(srvRecord.name());
|
||||
}
|
||||
service.setPort(srvRecord.port());
|
||||
|
||||
QByteArray hostName = srvRecord.target();
|
||||
//Remove trailing dot
|
||||
hostName.chop(1);
|
||||
service.setHostname(hostName);
|
||||
service.setAttributes(srvRecord.attributes());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wait(waitTime);
|
||||
--retries;
|
||||
}
|
||||
|
||||
} while (!found && retries >= 0);
|
||||
|
||||
if (found)
|
||||
{
|
||||
DebugIf(verboseBrowser,_log, "Service of type [%s] found", serviceType.constData());
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "No service of type [%s] found", serviceType.constData());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
QJsonArray MdnsBrowser::getServicesDiscoveredJson(const QByteArray& serviceType, const QString& filter, const std::chrono::milliseconds waitTime) const
|
||||
{
|
||||
DebugIf(verboseBrowser,_log, "Get services of type [%s], matching name: [%s], Thread: %s", QSTRING_CSTR(QString(serviceType)), QSTRING_CSTR(filter), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
QJsonArray result;
|
||||
|
||||
QRegularExpression regEx(filter);
|
||||
if (!regEx.isValid()) {
|
||||
QString errorString = regEx.errorString();
|
||||
int errorOffset = regEx.patternErrorOffset();
|
||||
|
||||
Error(_log, "Filtering regular expression [%s] error [%d]:[%s]", QSTRING_CSTR(filter), errorOffset, QSTRING_CSTR(errorString));
|
||||
}
|
||||
else
|
||||
{
|
||||
QList<QMdnsEngine::Record> ptrRecords;
|
||||
|
||||
int retries = 3;
|
||||
do
|
||||
{
|
||||
if (_cache.lookupRecords(serviceType, QMdnsEngine::PTR, ptrRecords))
|
||||
{
|
||||
for (int ptrCounter = 0; ptrCounter < ptrRecords.size(); ++ptrCounter)
|
||||
{
|
||||
QByteArray serviceName = ptrRecords.at(ptrCounter).target();
|
||||
|
||||
QRegularExpressionMatch match = regEx.match(serviceName.constData());
|
||||
if (match.hasMatch())
|
||||
{
|
||||
QMdnsEngine::Record srvRecord;
|
||||
if (!_cache.lookupRecord(serviceName, QMdnsEngine::SRV, srvRecord))
|
||||
{
|
||||
Debug(_log, "No SRV record for [%s] found, skip entry", serviceName.constData());
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonObject obj;
|
||||
QString domain = "local.";
|
||||
|
||||
obj.insert("id", serviceName.constData());
|
||||
|
||||
QString service = serviceName;
|
||||
service.chop(1);
|
||||
obj.insert("service", service);
|
||||
obj.insert("type", serviceType.constData());
|
||||
|
||||
QString name;
|
||||
if (serviceName.endsWith("." + serviceType))
|
||||
{
|
||||
name = serviceName.left(serviceName.length() - serviceType.length() - 1);
|
||||
obj.insert("name", QString(name));
|
||||
}
|
||||
|
||||
QByteArray hostName = srvRecord.target();
|
||||
//Remove trailing dot
|
||||
hostName.chop(1);
|
||||
|
||||
obj.insert("hostname", QString(hostName));
|
||||
obj.insert("domain", domain);
|
||||
|
||||
//Tag records where the service is provided by this host
|
||||
QByteArray localHostname = QHostInfo::localHostName().toUtf8();
|
||||
localHostname = localHostname.replace('.', '-');
|
||||
|
||||
bool isSameHost {false};
|
||||
if ( name == localHostname )
|
||||
{
|
||||
isSameHost = true;
|
||||
}
|
||||
obj.insert("sameHost", isSameHost);
|
||||
|
||||
quint16 port = srvRecord.port();
|
||||
obj.insert("port", port);
|
||||
|
||||
QMdnsEngine::Record txtRecord;
|
||||
if (_cache.lookupRecord(serviceName, QMdnsEngine::TXT, txtRecord))
|
||||
{
|
||||
QMap<QByteArray, QByteArray> txtAttributes = txtRecord.attributes();
|
||||
|
||||
QVariantMap txtMap;
|
||||
QMapIterator<QByteArray, QByteArray> i(txtAttributes);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
txtMap.insert(i.key(), i.value());
|
||||
}
|
||||
obj.insert("txt", QJsonObject::fromVariantMap(txtMap));
|
||||
}
|
||||
result << obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( result.isEmpty())
|
||||
{
|
||||
wait(waitTime);
|
||||
--retries;
|
||||
}
|
||||
|
||||
} while (result.isEmpty() && retries >= 0);
|
||||
|
||||
if (!result.isEmpty())
|
||||
{
|
||||
DebugIf(verboseBrowser,_log, "result: [%s]", QString(QJsonDocument(result).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "No service of type [%s] found", serviceType.constData());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MdnsBrowser::printCache(const QByteArray& name, quint16 type) const
|
||||
{
|
||||
DebugIf(verboseBrowser,_log, "for type: ", QSTRING_CSTR(QMdnsEngine::typeName(type)));
|
||||
QList<QMdnsEngine::Record> records;
|
||||
if (_cache.lookupRecords(name, type, records))
|
||||
{
|
||||
qDebug() << "";
|
||||
foreach(QMdnsEngine::Record record, records)
|
||||
{
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << "], ttl : " << record.ttl();
|
||||
|
||||
switch (record.type()) {
|
||||
case QMdnsEngine::PTR:
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", target : " << record.target();
|
||||
break;
|
||||
|
||||
case QMdnsEngine::SRV:
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", target : " << record.target();
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", port : " << record.port();
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", priority : " << record.priority();
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", weight : " << record.weight();
|
||||
break;
|
||||
case QMdnsEngine::TXT:
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", attributes: " << record.attributes();
|
||||
break;
|
||||
|
||||
case QMdnsEngine::NSEC:
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", nextDomNam: " << record.nextDomainName();
|
||||
break;
|
||||
|
||||
case QMdnsEngine::A:
|
||||
case QMdnsEngine::AAAA:
|
||||
qDebug() << QMdnsEngine::typeName(record.type()) << "," << record.name() << ", address : " << record.address();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugIf(verboseBrowser,_log, "Cash is empty for type: ", QSTRING_CSTR(QMdnsEngine::typeName(type)));
|
||||
}
|
||||
}
|
85
libsrc/mdns/MdnsProvider.cpp
Normal file
85
libsrc/mdns/MdnsProvider.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <mdns/MdnsProvider.h>
|
||||
#include <mdns/MdnsServiceRegister.h>
|
||||
|
||||
//Qt includes
|
||||
#include <QHostInfo>
|
||||
#include <QThread>
|
||||
|
||||
// Utility includes
|
||||
#include <utils/Logger.h>
|
||||
#include <HyperionConfig.h>
|
||||
#include <hyperion/AuthManager.h>
|
||||
|
||||
namespace {
|
||||
const bool verboseProvider = false;
|
||||
} //End of constants
|
||||
|
||||
MdnsProvider::MdnsProvider(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("MDNS"))
|
||||
, _server(nullptr)
|
||||
, _hostname(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void MdnsProvider::init()
|
||||
{
|
||||
_server = new QMdnsEngine::Server();
|
||||
_hostname = new QMdnsEngine::Hostname(_server);
|
||||
|
||||
connect(_hostname, &QMdnsEngine::Hostname::hostnameChanged, this, &MdnsProvider::onHostnameChanged);
|
||||
DebugIf(verboseProvider, _log, "Hostname [%s], isRegistered [%d]", _hostname->hostname().constData(), _hostname->isRegistered());
|
||||
}
|
||||
|
||||
MdnsProvider::~MdnsProvider()
|
||||
{
|
||||
qDeleteAll(_providedServiceTypes);
|
||||
|
||||
_hostname->deleteLater();
|
||||
_server->deleteLater();
|
||||
}
|
||||
|
||||
void MdnsProvider::publishService(const QString& serviceType, quint16 servicePort, const QByteArray& serviceName)
|
||||
{
|
||||
QMdnsEngine::Provider* provider(nullptr);
|
||||
|
||||
QByteArray type = MdnsServiceRegister::getServiceType(serviceType);
|
||||
if (!type.isEmpty())
|
||||
{
|
||||
DebugIf(verboseProvider, _log, "Publish new mDNS serviceType [%s], Thread: %s", type.constData(), QSTRING_CSTR(QThread::currentThread()->objectName()));
|
||||
|
||||
if (!_providedServiceTypes.contains(type))
|
||||
{
|
||||
provider = new QMdnsEngine::Provider(_server, _hostname);
|
||||
_providedServiceTypes.insert(type, provider);
|
||||
}
|
||||
else
|
||||
{
|
||||
provider = _providedServiceTypes[type];
|
||||
}
|
||||
|
||||
QMdnsEngine::Service service;
|
||||
service.setType(type);
|
||||
service.setPort(servicePort);
|
||||
|
||||
QByteArray name(QHostInfo::localHostName().toUtf8());
|
||||
if (!serviceName.isEmpty())
|
||||
{
|
||||
name.prepend(serviceName + "@");
|
||||
}
|
||||
service.setName(name);
|
||||
|
||||
QByteArray id = AuthManager::getInstance()->getID().toUtf8();
|
||||
const QMap<QByteArray, QByteArray> attributes = { {"id", id}, {"version", HYPERION_VERSION} };
|
||||
service.setAttributes(attributes);
|
||||
|
||||
DebugIf(verboseProvider, _log, "[%s], Name: [%s], Port: [%u] ", service.type().constData(), service.name().constData(), service.port());
|
||||
|
||||
provider->update(service);
|
||||
}
|
||||
}
|
||||
|
||||
void MdnsProvider::onHostnameChanged(const QByteArray& hostname)
|
||||
{
|
||||
DebugIf(verboseProvider, _log, "mDNS-hostname changed to hostname [%s]", hostname.constData());
|
||||
}
|
@@ -35,7 +35,7 @@ endif()
|
||||
target_link_libraries(protoclient
|
||||
hyperion
|
||||
hyperion-utils
|
||||
libprotobuf
|
||||
${PROTOBUF_LIBRARIES}
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
|
||||
@@ -45,3 +45,7 @@ target_link_libraries(protoserver
|
||||
protoclient
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(protoserver mdns)
|
||||
endif()
|
||||
|
@@ -10,6 +10,13 @@
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const char SERVICE_TYPE[] = "protobuffer";
|
||||
|
||||
} //End of constants
|
||||
|
||||
ProtoServer::ProtoServer(const QJsonDocument& config, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _server(new QTcpServer(this))
|
||||
@@ -95,11 +102,12 @@ void ProtoServer::startServer()
|
||||
{
|
||||
if(!_server->listen(QHostAddress::Any, _port))
|
||||
{
|
||||
Error(_log,"Failed to bind port %d", _port);
|
||||
Error(_log,"Failed to bind port %d", _port);
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log,"Started on port %d", _port);
|
||||
Info(_log,"Started on port %d", _port);
|
||||
emit publishService(SERVICE_TYPE, _port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -78,7 +78,7 @@ namespace JsonUtils {
|
||||
++errorLine;
|
||||
}
|
||||
}
|
||||
Error(log,"Failed to parse json data from %s: Error: %s at Line: %i, Column: %i", QSTRING_CSTR(path), QSTRING_CSTR(error.errorString()), errorLine, errorColumn);
|
||||
Error(log, "Failed to parse json data from %s: Error: %s at Line: %i, Column: %i, Data: '%s'", QSTRING_CSTR(path), QSTRING_CSTR(error.errorString()), errorLine, errorColumn, QSTRING_CSTR(data));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@@ -25,3 +25,7 @@ target_link_libraries(webserver
|
||||
hyperion-api
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
)
|
||||
|
||||
if(ENABLE_MDNS)
|
||||
target_link_libraries(webserver mdns)
|
||||
endif()
|
||||
|
@@ -6,21 +6,25 @@
|
||||
#include <QFileInfo>
|
||||
#include <QJsonObject>
|
||||
|
||||
// bonjour
|
||||
#ifdef ENABLE_AVAHI
|
||||
#include <bonjour/bonjourserviceregister.h>
|
||||
#endif
|
||||
// netUtil
|
||||
#include <utils/NetUtils.h>
|
||||
|
||||
WebServer::WebServer(const QJsonDocument& config, bool useSsl, QObject * parent)
|
||||
: QObject(parent)
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const char HTTP_SERVICE_TYPE[] = "http";
|
||||
const char HTTPS_SERVICE_TYPE[] = "https";
|
||||
const char HYPERION_SERVICENAME[] = "Hyperion";
|
||||
|
||||
} //End of constants
|
||||
|
||||
WebServer::WebServer(const QJsonDocument& config, bool useSsl, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _config(config)
|
||||
, _useSsl(useSsl)
|
||||
, _log(Logger::getInstance("WEBSERVER"))
|
||||
, _server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WebServer::~WebServer()
|
||||
@@ -30,64 +34,60 @@ WebServer::~WebServer()
|
||||
|
||||
void WebServer::initServer()
|
||||
{
|
||||
Debug(_log, "Initialize Webserver");
|
||||
_server = new QtHttpServer (this);
|
||||
_server->setServerName (QStringLiteral ("Hyperion Webserver"));
|
||||
Debug(_log, "Initialize %s-Webserver", _useSsl ? "https" : "http");
|
||||
_server = new QtHttpServer(this);
|
||||
_server->setServerName(QStringLiteral("Hyperion %1-Webserver").arg(_useSsl ? "https" : "http"));
|
||||
|
||||
if(_useSsl)
|
||||
if (_useSsl)
|
||||
{
|
||||
_server->setUseSecure();
|
||||
WEBSERVER_DEFAULT_PORT = 8092;
|
||||
}
|
||||
|
||||
connect (_server, &QtHttpServer::started, this, &WebServer::onServerStarted);
|
||||
connect (_server, &QtHttpServer::stopped, this, &WebServer::onServerStopped);
|
||||
connect (_server, &QtHttpServer::error, this, &WebServer::onServerError);
|
||||
connect(_server, &QtHttpServer::started, this, &WebServer::onServerStarted);
|
||||
connect(_server, &QtHttpServer::stopped, this, &WebServer::onServerStopped);
|
||||
connect(_server, &QtHttpServer::error, this, &WebServer::onServerError);
|
||||
|
||||
// create StaticFileServing
|
||||
_staticFileServing = new StaticFileServing (this);
|
||||
_staticFileServing = new StaticFileServing(this);
|
||||
connect(_server, &QtHttpServer::requestNeedsReply, _staticFileServing, &StaticFileServing::onRequestNeedsReply);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::WEBSERVER, _config);
|
||||
}
|
||||
|
||||
void WebServer::onServerStarted (quint16 port)
|
||||
void WebServer::onServerStarted(quint16 port)
|
||||
{
|
||||
_inited = true;
|
||||
|
||||
Info(_log, "'%s' started on port %d",_server->getServerName().toStdString().c_str(), port);
|
||||
Info(_log, "'%s' started on port %d", _server->getServerName().toStdString().c_str(), port);
|
||||
|
||||
#ifdef ENABLE_AVAHI
|
||||
if(_serviceRegister == nullptr)
|
||||
if (_useSsl)
|
||||
{
|
||||
_serviceRegister = new BonjourServiceRegister(this);
|
||||
_serviceRegister->registerService("_hyperiond-http._tcp", port);
|
||||
emit publishService(HTTPS_SERVICE_TYPE, _port, HYPERION_SERVICENAME);
|
||||
}
|
||||
else if( _serviceRegister->getPort() != port)
|
||||
else
|
||||
{
|
||||
delete _serviceRegister;
|
||||
_serviceRegister = new BonjourServiceRegister(this);
|
||||
_serviceRegister->registerService("_hyperiond-http._tcp", port);
|
||||
emit publishService(HTTP_SERVICE_TYPE, _port, HYPERION_SERVICENAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
emit stateChange(true);
|
||||
}
|
||||
|
||||
void WebServer::onServerStopped ()
|
||||
void WebServer::onServerStopped()
|
||||
{
|
||||
Info(_log, "Stopped %s", _server->getServerName().toStdString().c_str());
|
||||
emit stateChange(false);
|
||||
}
|
||||
|
||||
void WebServer::onServerError (QString msg)
|
||||
void WebServer::onServerError(QString msg)
|
||||
{
|
||||
Error(_log, "%s", msg.toStdString().c_str());
|
||||
}
|
||||
|
||||
void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::WEBSERVER)
|
||||
if (type == settings::WEBSERVER)
|
||||
{
|
||||
Debug(_log, "Apply Webserver settings");
|
||||
const QJsonObject& obj = config.object();
|
||||
@@ -95,7 +95,7 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c
|
||||
_baseUrl = obj["document_root"].toString(WEBSERVER_DEFAULT_PATH);
|
||||
|
||||
|
||||
if ( (_baseUrl != ":/webconfig") && !_baseUrl.trimmed().isEmpty())
|
||||
if ((_baseUrl != ":/webconfig") && !_baseUrl.trimmed().isEmpty())
|
||||
{
|
||||
QFileInfo info(_baseUrl);
|
||||
if (!info.exists() || !info.isDir())
|
||||
@@ -112,18 +112,18 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c
|
||||
|
||||
// ssl different port
|
||||
quint16 newPort = _useSsl ? obj["sslPort"].toInt(WEBSERVER_DEFAULT_PORT) : obj["port"].toInt(WEBSERVER_DEFAULT_PORT);
|
||||
if(_port != newPort)
|
||||
if (_port != newPort)
|
||||
{
|
||||
_port = newPort;
|
||||
stop();
|
||||
}
|
||||
|
||||
// eval if the port is available, will be incremented if not
|
||||
if(!_server->isListening())
|
||||
if (!_server->isListening())
|
||||
NetUtils::portAvailable(_port, _log);
|
||||
|
||||
// on ssl we want .key .cert and probably key password
|
||||
if(_useSsl)
|
||||
if (_useSsl)
|
||||
{
|
||||
QString keyPath = obj["keyPath"].toString(WEBSERVER_DEFAULT_KEY_PATH);
|
||||
QString crtPath = obj["crtPath"].toString(WEBSERVER_DEFAULT_CRT_PATH);
|
||||
@@ -132,7 +132,7 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c
|
||||
QList<QSslCertificate> currCerts = _server->getCertificates();
|
||||
|
||||
// check keyPath
|
||||
if ( (keyPath != WEBSERVER_DEFAULT_KEY_PATH) && !keyPath.trimmed().isEmpty())
|
||||
if ((keyPath != WEBSERVER_DEFAULT_KEY_PATH) && !keyPath.trimmed().isEmpty())
|
||||
{
|
||||
QFileInfo kinfo(keyPath);
|
||||
if (!kinfo.exists())
|
||||
@@ -145,7 +145,7 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c
|
||||
keyPath = WEBSERVER_DEFAULT_KEY_PATH;
|
||||
|
||||
// check crtPath
|
||||
if ( (crtPath != WEBSERVER_DEFAULT_CRT_PATH) && !crtPath.trimmed().isEmpty())
|
||||
if ((crtPath != WEBSERVER_DEFAULT_CRT_PATH) && !crtPath.trimmed().isEmpty())
|
||||
{
|
||||
QFileInfo cinfo(crtPath);
|
||||
if (!cinfo.exists())
|
||||
@@ -161,21 +161,22 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c
|
||||
QFile cfile(crtPath);
|
||||
cfile.open(QIODevice::ReadOnly);
|
||||
QList<QSslCertificate> validList;
|
||||
QList<QSslCertificate> cList = QSslCertificate::fromDevice(&cfile, QSsl::Pem);
|
||||
QList<QSslCertificate> cList = QSslCertificate::fromDevice(&cfile, QSsl::Pem);
|
||||
cfile.close();
|
||||
|
||||
// Filter for valid certs
|
||||
for(const auto & entry : cList){
|
||||
if(!entry.isNull() && QDateTime::currentDateTime().daysTo(entry.expiryDate()) > 0)
|
||||
for (const auto& entry : cList) {
|
||||
if (!entry.isNull() && QDateTime::currentDateTime().daysTo(entry.expiryDate()) > 0)
|
||||
validList.append(entry);
|
||||
else
|
||||
Error(_log, "The provided SSL certificate is invalid/not supported/reached expiry date ('%s')", crtPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
if(!validList.isEmpty()){
|
||||
Debug(_log,"Setup SSL certificate");
|
||||
if (!validList.isEmpty()) {
|
||||
Debug(_log, "Setup SSL certificate");
|
||||
_server->setCertificates(validList);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Error(_log, "No valid SSL certificate has been found ('%s')", crtPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
@@ -186,10 +187,11 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c
|
||||
QSslKey key(&kfile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, obj["keyPassPhrase"].toString().toUtf8());
|
||||
kfile.close();
|
||||
|
||||
if(key.isNull()){
|
||||
if (key.isNull()) {
|
||||
Error(_log, "The provided SSL key is invalid or not supported use RSA encrypt and PEM format ('%s')", keyPath.toUtf8().constData());
|
||||
} else {
|
||||
Debug(_log,"Setup private SSL key");
|
||||
}
|
||||
else {
|
||||
Debug(_log, "Setup private SSL key");
|
||||
_server->setPrivateKey(key);
|
||||
}
|
||||
}
|
||||
@@ -209,7 +211,7 @@ void WebServer::stop()
|
||||
_server->stop();
|
||||
}
|
||||
|
||||
void WebServer::setSSDPDescription(const QString & desc)
|
||||
void WebServer::setSSDPDescription(const QString& desc)
|
||||
{
|
||||
_staticFileServing->setSSDPDescription(desc);
|
||||
}
|
||||
|
Reference in New Issue
Block a user