mDNS Support (#1452)

* Allow build, if no grabbers are enabled

* Align available functions to right Qt version

* Update to next development version

* Align available functions to right Qt version

* fix workflows (apt/nightly)

* Disable QNetworkConfigurationManager deprecation warnings

* Initial go on Smart Pointers

* Add Deallocation

* Correct QT_WARNING_DISABLE_DEPRECATED (available since 5.9)

* Cluster Build Variables

* Hyperion Light

* Address build warnings

* Hyperion Light - UI

* Update Protobuf to latest master

* Removed compiler warnings

* Added restart ability to systray

* Correct Protobuf

* Ignore 'no-return' warning on protobuf build

* hyperion-remote: Fix auto discovery of hyperion server

* Fix Qt version override

* Update changelog

* Remove Grabber Components, if no Grabber exists

* Standalone Grabber - Fix fps default

* Remote Control - Have Source Selction accrosswhole screen

* Enable Blackborder detection only, if relevant input sources available

* Enable Blackborder detection only, if relevant input sources available

* Remote UI - rearrange containers

* Checkout

* Fix compilation on windows

* Re-added qmdnsengine template cmake

* chrono added for linux

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

* hyperiond macos typo fix

* Fix macOS Bundle build

* Fix macOS bundle info details

* Correct CMake files

* Removed existing AVAHI/Bonjour (2)

* Share hyperion's services via mDNS

* Add mDNS Browser and mDNS for LED-Devices

* Support mDNS discovery for standalone grabbers

* Remove ZLib Dependency & Cleanup

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

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

* Fix save button state when switching between devices

* Removed sessions (of other hyperions)

* mDNS Publisher - Simplify service naming

* mDNS refactoring & Forwarder discovery

* mDNS Updates to use device service name

* Consistency of standalone grabbers with mDNS Service Registry

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

* Start JSON and WebServers only after Instance 0 is available

* Remove bespoke qDebug Output again

* MDNS updates and refactor Forwarder

* Minor updates

* Upgrade to CMake 3.1

* typo

* macOS fix

* Correct merge

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

* Cec handler library will load at runtime

* typo fix

* protobuf changes

* mDNS changes for Windows/macOS

* test window build qmdnsengine

* absolute path to protobuf cmake dir

* Rework Hue Wizard supporting mDNS

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

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

* Address LGTM findings

* Fix CI-Build, revert test changes

* Build Windows in Release mode to avoid python problem

* Correct that WebServerObject is available earlier

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

* Update content LEDs

* Rework mDNS Address lookup

* Fix LED UI

* Fix for non mDNS Services (ignore default port)

* Disbale device when now input is available

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

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

* Add UPD-DDP Device

* WLED support for DDP

* Fix printout

* LEDDevice - Allow more retries, udapte defaults

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

Co-authored-by: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com>
Co-authored-by: Paulchen Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
LordGrey
2022-05-01 19:42:47 +02:00
committed by GitHub
parent 3ef4ebc1a4
commit e9936e131b
148 changed files with 5885 additions and 4459 deletions

View File

@@ -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()));

View File

@@ -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))
{

View File

@@ -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.
///

View File

@@ -18,15 +18,20 @@ const int MAX_NUM_LEDS = 10000; // OPC can handle 21845 LEDs - in theory, fadeca
const int OPC_SET_PIXELS = 0; // OPC command codes
const int OPC_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();

View File

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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.
///

View File

@@ -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)
{

View File

@@ -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.
///

View File

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

View File

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

View File

@@ -8,6 +8,13 @@
// hyperion local includes
#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)
{

View File

@@ -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.
///

View File

@@ -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];

View File

@@ -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.
///

View File

@@ -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);

View File

@@ -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.
///

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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
///

View File

@@ -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);

View File

@@ -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:
///

View File

@@ -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())
{

View File

@@ -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

View File

@@ -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