LED Device Features, Fixes and Refactoring (Resubmit PR855) (#875)

* Refactor LedDevices - Initial version
* Small renamings
* Add WLED as own device
* Lpd8806 Remove open() method
* remove dependency on Qt 5.10
* Lpd8806 Remove open() method
* Update WS281x
* Update WS2812SPI
* Add writeBlack for WLED powerOff
* WLED remove extra bracket
* Allow different Nanoleaf panel numbering sequence (Feature req.#827)
* build(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#826)
* Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
  - [Release notes](https://github.com/faye/websocket-extensions-node/releases)
  - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
  - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)
* Fix typos
* Nanoleaf clean-up
* Yeelight support, generalize wizard elements
* Update Yeelight to handle quota in music mode
* Yeelight extend rage for extraTimeDarkness for testing
* Clean-up - Add commentary, Remove development debug statements
* Fix brightnessSwitchOffOnMinimum typo and default value
* Yeelight support restoreOriginalState, additional Fixes
* WLED - Remove UDP-Port, as it is not configurable
* Fix merging issue
* Remove QHostAddress::operator=(const QString&)' is deprecated
* Windows compile errors and (Qt 5.15 deprecation) warnings
* Fix order includes
* LedDeviceFile Support Qt5.7 and greater
* Windows compatibility and other Fixes
* Fix Qt Version compatability
* Rs232 - Resolve portname from unix /dev/ style, fix DMX sub-type support
* Disable WLED Wizard Button (until Wizard is available)
* Yeelight updates
* Add wrong log-type as per #505
* Fixes and Clean-up after clang-tidy report
* Fix udpe131 not enabled for generated CID
* Change timer into dynamic for Qt Thread-Affinity
* Hue clean-up and diyHue workaround
* Updates after review feedback by m-seker
* Add "chrono" includes
This commit is contained in:
LordGrey
2020-07-12 20:27:56 +02:00
committed by GitHub
parent 3b48d8c9d6
commit 7389068a66
125 changed files with 8864 additions and 3217 deletions

View File

@@ -1,5 +1,6 @@
// Local-Hyperion includes
#include "LedDeviceAtmoOrb.h"
#include <utils/QStringUtils.h>
// qt includes
#include <QUdpSocket>
@@ -19,7 +20,9 @@ LedDeviceAtmoOrb::LedDeviceAtmoOrb(const QJsonObject &deviceConfig)
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceAtmoOrb::construct(const QJsonObject &deviceConfig)
@@ -31,7 +34,7 @@ LedDeviceAtmoOrb::~LedDeviceAtmoOrb()
{
if ( _udpSocket != nullptr )
{
_udpSocket->deleteLater();
delete _udpSocket;
}
}
@@ -49,30 +52,31 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
_multiCastGroupPort = static_cast<quint16>(deviceConfig["port"].toInt(MULTICAST_GROUPL_DEFAULT_PORT));
_numLeds = deviceConfig["numLeds"].toInt(LEDS_DEFAULT_NUMBER);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
const QStringList orbIds = deviceConfig["orbIds"].toString().simplified().remove(" ").split(",", Qt::SkipEmptyParts);
#else
const QStringList orbIds = deviceConfig["orbIds"].toString().simplified().remove(" ").split(",", QString::SkipEmptyParts);
#endif
QStringList orbIds = QStringUtils::split(deviceConfig["orbIds"].toString().simplified().remove(" "),",", QStringUtils::SplitBehavior::SkipEmptyParts);
_orbIds.clear();
for(auto & id_str : orbIds)
for (auto & id_str : orbIds)
{
bool ok;
int id = id_str.toInt(&ok);
if (ok)
{
if ( id < 1 || id > 255 )
{
Warning(_log, "Skip orb id '%d'. IDs must be in range 1-255", id);
}
else
{
_orbIds.append(id);
}
}
else
{
Error(_log, "orb id '%s' is not a number", QSTRING_CSTR(id_str));
}
}
if ( _orbIds.size() == 0 )
if ( _orbIds.empty() )
{
this->setInError("No valid OrbIds found!");
isInitOK = false;
@@ -89,43 +93,40 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
int LedDeviceAtmoOrb::open()
{
int retval = -1;
_deviceReady = false;
_isDeviceReady = false;
if ( init(_devConfig) )
// Try to bind the UDP-Socket
if ( _udpSocket != nullptr )
{
// Try to bind the UDP-Socket
if ( _udpSocket != nullptr )
_groupAddress = QHostAddress(_multicastGroup);
if ( !_udpSocket->bind(QHostAddress::AnyIPv4, _multiCastGroupPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint) )
{
_groupAddress = QHostAddress(_multicastGroup);
if ( !_udpSocket->bind(QHostAddress::AnyIPv4, _multiCastGroupPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint) )
QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString(), _multicastGroup);
this->setInError( errortext );
}
else
{
_joinedMulticastgroup = _udpSocket->joinMulticastGroup(_groupAddress);
if ( !_joinedMulticastgroup )
{
QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString()).arg(_multicastGroup);
QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString(), _multicastGroup);
this->setInError( errortext );
}
else
{
_joinedMulticastgroup = _udpSocket->joinMulticastGroup(_groupAddress);
if ( !_joinedMulticastgroup )
{
QString errortext = QString ("(%1) %2, MulticastGroup: (%3)").arg(_udpSocket->error()).arg(_udpSocket->errorString()).arg(_multicastGroup);
this->setInError( errortext );
}
else
{
// Everything is OK, device is ready
_deviceReady = true;
setEnable(true);
retval = 0;
}
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
}
}
return retval;
}
void LedDeviceAtmoOrb::close()
int LedDeviceAtmoOrb::close()
{
LedDevice::close();
int retval = 0;
_isDeviceReady = false;
if ( _udpSocket != nullptr )
{
@@ -137,6 +138,7 @@ void LedDeviceAtmoOrb::close()
// Everything is OK -> device is closed
}
}
return retval;
}
int LedDeviceAtmoOrb::write(const std::vector <ColorRgb> &ledValues)
@@ -211,7 +213,9 @@ int LedDeviceAtmoOrb::write(const std::vector <ColorRgb> &ledValues)
void LedDeviceAtmoOrb::setColor(int orbId, const ColorRgb &color, int commandType)
{
QByteArray bytes;
bytes.resize(5 + _numLeds * 3);
// 5 bytes command-header + 3 bytes color information
bytes.resize(5 + 3);
bytes.fill('\0');
// Command identifier: C0FFEE
@@ -230,7 +234,6 @@ void LedDeviceAtmoOrb::setColor(int orbId, const ColorRgb &color, int commandTyp
bytes[6] = static_cast<char>(color.green);
bytes[7] = static_cast<char>(color.blue);
// TODO: Why is the datagram _numLeds * 3 in size, if only bypes 5,6,7 are updated with the color?
//std::cout << "Orb [" << orbId << "] Cmd [" << bytes.toHex(':').toStdString() <<"]"<< std::endl;
sendCommand(bytes);

View File

@@ -1,9 +1,8 @@
#pragma once
#ifndef LEDEVICEATMOORB_H
#define LEDEVICEATMOORB_H
// Qt includes
#include <QObject>
#include <QString>
#include <QNetworkAccessManager>
#include <QUdpSocket>
#include <QHostAddress>
#include <QVector>
@@ -25,38 +24,39 @@ class LedDeviceAtmoOrb : public LedDevice
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an AtmoOrb LED-device
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceAtmoOrb(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
/// constructs leddevice
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Destructor of this device
/// @brief Destructor of the LedDevice
///
virtual ~LedDeviceAtmoOrb() override;
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
protected:
///
/// Initialise device's network details
/// @brief Initialise the device's configuration
///
/// @return True if success
bool initNetwork();
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// Opens and initiatialises the output device
/// @brief Opens the output device.
///
/// @return Zero on succes (i.e. device is ready and enabled) else negative
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
@@ -65,18 +65,18 @@ protected:
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual void close() override;
virtual int close() override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
private:
///
/// Sends the given led-color values to the Orbs
///
/// @param ledValues The color-value per led
/// @return Zero on success else negative
///
virtual int write(const std::vector <ColorRgb> &ledValues) override;
///
/// Set Orbcolor
///
@@ -93,9 +93,6 @@ private:
///
void sendCommand(const QByteArray &bytes);
/// QNetworkAccessManager object for sending requests.
QNetworkAccessManager *_networkmanager;
/// QUdpSocket object used to send data over
QUdpSocket * _udpSocket;
@@ -132,3 +129,5 @@ private:
QMap<int, int> lastColorBlueMap;
};
#endif // LEDEVICEATMOORB_H

View File

@@ -6,22 +6,37 @@
typedef SSIZE_T ssize_t;
#endif
static const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
static const unsigned OPC_SYS_EX = 255; // OPC command codes
static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
// Constants
namespace {
const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 LEDs - in theory, fadecandy device should handle 10000 LEDs
const unsigned OPC_SET_PIXELS = 0; // OPC command codes
const unsigned OPC_SYS_EX = 255; // OPC command codes
const unsigned OPC_HEADER_SIZE = 4; // OPC header size
} //End of constants
// TCP elements
const quint16 STREAM_DEFAULT_PORT = 7890;
LedDeviceFadeCandy::LedDeviceFadeCandy(const QJsonObject &deviceConfig)
: LedDevice()
, _client(nullptr)
, _client(nullptr)
,_host()
,_port(STREAM_DEFAULT_PORT)
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceFadeCandy::~LedDeviceFadeCandy()
{
_client->deleteLater();
if ( _client != nullptr )
{
delete _client;
}
}
LedDevice* LedDeviceFadeCandy::construct(const QJsonObject &deviceConfig)
@@ -31,46 +46,59 @@ LedDevice* LedDeviceFadeCandy::construct(const QJsonObject &deviceConfig)
bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
if ( isInitOK )
if ( LedDevice::init(deviceConfig) )
{
if (_ledCount > MAX_NUM_LEDS)
if (getLedCount() > MAX_NUM_LEDS)
{
//Error(_log, "fadecandy/opc: Invalid attempt to write led values. Not more than %d leds are allowed.", MAX_NUM_LEDS);
QString errortext = QString ("More LED configured than allowed (%1)").arg(MAX_NUM_LEDS);
this->setInError(errortext);
isInitOK = false;
}
else
{
_host = deviceConfig["output"].toString("127.0.0.1");
_port = deviceConfig["port"].toInt(7890);
_channel = deviceConfig["channel"].toInt(0);
_gamma = deviceConfig["gamma"].toDouble(1.0);
_noDither = ! deviceConfig["dither"].toBool(false);
_noInterp = ! deviceConfig["interpolation"].toBool(false);
_manualLED = deviceConfig["manualLed"].toBool(false);
_ledOnOff = deviceConfig["ledOn"].toBool(false);
_setFcConfig = deviceConfig["setFcConfig"].toBool(false);
_host = deviceConfig["output"].toString("127.0.0.1");
_port = deviceConfig["port"].toInt(STREAM_DEFAULT_PORT);
_whitePoint_r = 1.0;
_whitePoint_g = 1.0;
_whitePoint_b = 1.0;
const QJsonArray whitePointConfig = deviceConfig["whitePoint"].toArray();
if ( !whitePointConfig.isEmpty() && whitePointConfig.size() == 3 )
//If host not configured the init fails
if ( _host.isEmpty() )
{
_whitePoint_r = whitePointConfig[0].toDouble() / 255.0;
_whitePoint_g = whitePointConfig[1].toDouble() / 255.0;
_whitePoint_b = whitePointConfig[2].toDouble() / 255.0;
this->setInError("No target hostname nor IP defined");
}
else
{
_channel = deviceConfig["channel"].toInt(0);
_gamma = deviceConfig["gamma"].toDouble(1.0);
_noDither = ! deviceConfig["dither"].toBool(false);
_noInterp = ! deviceConfig["interpolation"].toBool(false);
_manualLED = deviceConfig["manualLed"].toBool(false);
_ledOnOff = deviceConfig["ledOn"].toBool(false);
_setFcConfig = deviceConfig["setFcConfig"].toBool(false);
_opc_data.resize( _ledRGBCount + OPC_HEADER_SIZE );
_opc_data[0] = _channel;
_opc_data[1] = OPC_SET_PIXELS;
_opc_data[2] = _ledRGBCount >> 8;
_opc_data[3] = _ledRGBCount & 0xff;
_whitePoint_r = 1.0;
_whitePoint_g = 1.0;
_whitePoint_b = 1.0;
const QJsonArray whitePointConfig = deviceConfig["whitePoint"].toArray();
if ( !whitePointConfig.isEmpty() && whitePointConfig.size() == 3 )
{
_whitePoint_r = whitePointConfig[0].toDouble() / 255.0;
_whitePoint_g = whitePointConfig[1].toDouble() / 255.0;
_whitePoint_b = whitePointConfig[2].toDouble() / 255.0;
}
_opc_data.resize( _ledRGBCount + OPC_HEADER_SIZE );
_opc_data[0] = _channel;
_opc_data[1] = OPC_SET_PIXELS;
_opc_data[2] = _ledRGBCount >> 8;
_opc_data[3] = _ledRGBCount & 0xff;
if ( initNetwork() )
{
isInitOK = true;
}
}
}
}
return isInitOK;
@@ -78,62 +106,77 @@ bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
bool LedDeviceFadeCandy::initNetwork()
{
bool isInitOK = true;
bool isInitOK = false;
// TODO: Add Network-Error handling
_client = new QTcpSocket(this);
if ( _client == nullptr )
{
_client = new QTcpSocket(this);
isInitOK = true;
}
return isInitOK;
}
int LedDeviceFadeCandy::open()
{
int retval = -1;
_deviceReady = false;
QString errortext;
_isDeviceReady = false;
if ( init(_devConfig) )
// Try to open the LedDevice
if ( !tryConnect() )
{
if ( !initNetwork() )
{
this->setInError( "Network error!" );
}
else
{
_deviceReady = true;
setEnable(true);
retval = 0;
}
errortext = QString ("Failed to open device.");
this->setInError( errortext );
}
else
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
return retval;
}
void LedDeviceFadeCandy::close()
int LedDeviceFadeCandy::close()
{
LedDevice::close();
int retval = 0;
_isDeviceReady = false;
// LedDevice specific closing activites
_client->close();
// LedDevice specific closing activities
if ( _client != nullptr )
{
_client->close();
// Everything is OK -> device is closed
}
return retval;
}
bool LedDeviceFadeCandy::isConnected()
{
return _client->state() == QAbstractSocket::ConnectedState;
bool connected = false;
if ( _client != nullptr )
{
connected = _client->state() == QAbstractSocket::ConnectedState;
}
return connected;
}
bool LedDeviceFadeCandy::tryConnect()
{
if ( _client->state() == QAbstractSocket::UnconnectedState ) {
_client->connectToHost( _host, _port);
if ( _client->waitForConnected(1000) )
{
Info(_log,"fadecandy/opc: connected to %s:%i on channel %i", QSTRING_CSTR(_host), _port, _channel);
if (_setFcConfig)
if ( _client != nullptr )
{
if ( _client->state() == QAbstractSocket::UnconnectedState ) {
_client->connectToHost( _host, _port);
if ( _client->waitForConnected(1000) )
{
sendFadeCandyConfiguration();
Info(_log,"fadecandy/opc: connected to %s:%i on channel %i", QSTRING_CSTR(_host), _port, _channel);
if (_setFcConfig)
{
sendFadeCandyConfiguration();
}
}
}
}
return isConnected();
}
@@ -148,15 +191,16 @@ int LedDeviceFadeCandy::write( const std::vector<ColorRgb> & ledValues )
idx += 3;
}
return ( transferData()<0 ? -1 : 0 );
int retval = transferData()<0 ? -1 : 0;
return retval;
}
int LedDeviceFadeCandy::transferData()
{
if (LedDevice::enabled())
if ( isConnected() || tryConnect() )
return _client->write( _opc_data, _opc_data.size() );
if ( isConnected() || tryConnect() )
{
return _client->write( _opc_data, _opc_data.size() );
}
return -2;
}

View File

@@ -1,10 +1,11 @@
#pragma once
#ifndef LEDEVICEFADECANDY_H
#define LEDEVICEFADECANDY_H
// STL/Qt includes
#include <QTcpSocket>
#include <QString>
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
///
@@ -17,9 +18,9 @@ class LedDeviceFadeCandy : public LedDevice
public:
///
/// Constructs the LedDevice for fadecandy/opc server
/// @brief Constructs a LED-device for fadecandy/opc server
///
/// following code shows all config options
/// Following code shows all configuration options
/// @code
/// "device" :
/// {
@@ -37,84 +38,95 @@ public:
/// },
///@endcode
///
/// @param deviceConfig json config for fadecandy
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceFadeCandy(const QJsonObject &deviceConfig);
///
/// Destructor of the LedDevice; closes the tcp client
/// @brief Destructor of the LedDevice
///
virtual ~LedDeviceFadeCandy();
~LedDeviceFadeCandy() override;
/// constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
public slots:
///
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
///
virtual void close() override;
protected:
///
/// Initialise device's network details
/// @brief Initialise the Nanoleaf device's configuration and network address details
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
///
/// @brief Closes the output device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual int close() override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
private:
///
/// @brief Initialise device's network details
///
/// @return True if success
bool initNetwork();
///
/// Opens and initiatialises the output device
/// @brief try to establish connection to opc server, if not connected yet
///
/// @return Zero on succes (i.e. device is ready and enabled) else negative
///
virtual int open() override;
private:
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb>& ledValues) override;
/// try to establish connection to opc server, if not connected yet
///
/// @return true if connection is established
/// @return True, if connection is established
///
bool tryConnect();
/// return the conenction state
///
/// @return True if connection established
/// @brief Return the connection state
///
/// @return True, if connection established
///
bool isConnected();
/// transfer current opc_data buffer to opc server
///
/// @return amount of transfered bytes. -1 error while transfering, -2 error while connecting
/// @brief Transfer current opc_data buffer to opc server
///
/// @return amount of transferred bytes. -1 error while transferring, -2 error while connecting
///
int transferData();
/// send system exclusive commands
///
/// @param systemId fadecandy device identifier (for standard fadecandy always: 1)
/// @param commandId id of command
/// @param msg the sysEx message
/// @return amount bytes written, -1 if fail
/// @brief Send system exclusive commands
///
/// @param[in] systemId fadecandy device identifier (for standard fadecandy always: 1)
/// @param[in] commandId id of command
/// @param[in] msg the sysEx message
/// @return amount bytes written, -1 if failed
int sendSysEx(uint8_t systemId, uint8_t commandId, QByteArray msg);
/// sends the configuration to fcserver
///
/// @brief Sends the configuration to fadecandy cserver
///
void sendFadeCandyConfiguration();
QTcpSocket* _client;
@@ -135,3 +147,5 @@ private:
bool _ledOnOff;
};
#endif // LEDEVICEFADECANDY_H

View File

@@ -1,8 +1,8 @@
// Local-Hyperion includes
#include "LedDeviceNanoleaf.h"
// ssdp discover
#include <ssdp/SSDPDiscover.h>
#include <utils/QStringUtils.h>
// Qt includes
#include <QEventLoop>
@@ -12,61 +12,66 @@
#include <sstream>
#include <iomanip>
//
static const bool verbose = false;
static const bool verbose3 = false;
// Constants
namespace {
// Controller configuration settings
static const char CONFIG_ADDRESS[] = "host";
//static const char CONFIG_PORT[] = "port";
static const char CONFIG_AUTH_TOKEN[] ="token";
const bool verbose = false;
const bool verbose3 = false;
static const char CONFIG_PANEL_ORDER_TOP_DOWN[] ="panelOrderTopDown";
static const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] ="panelOrderLeftRight";
static const char CONFIG_PANEL_START_POS[] ="panelStartPos";
// Configuration settings
const char CONFIG_ADDRESS[] = "host";
//const char CONFIG_PORT[] = "port";
const char CONFIG_AUTH_TOKEN[] ="token";
const char CONFIG_PANEL_ORDER_TOP_DOWN[] ="panelOrderTopDown";
const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] ="panelOrderLeftRight";
const char CONFIG_PANEL_START_POS[] ="panelStartPos";
// Panel configuration settings
static const char PANEL_LAYOUT[] = "layout";
static const char PANEL_NUM[] = "numPanels";
static const char PANEL_ID[] = "panelId";
static const char PANEL_POSITIONDATA[] = "positionData";
static const char PANEL_SHAPE_TYPE[] = "shapeType";
//static const char PANEL_ORIENTATION[] = "0";
static const char PANEL_POS_X[] = "x";
static const char PANEL_POS_Y[] = "y";
const char PANEL_LAYOUT[] = "layout";
const char PANEL_NUM[] = "numPanels";
const char PANEL_ID[] = "panelId";
const char PANEL_POSITIONDATA[] = "positionData";
const char PANEL_SHAPE_TYPE[] = "shapeType";
//const char PANEL_ORIENTATION[] = "0";
const char PANEL_POS_X[] = "x";
const char PANEL_POS_Y[] = "y";
// List of State Information
static const char STATE_ON[] = "on";
static const char STATE_ONOFF_VALUE[] = "value";
static const char STATE_VALUE_TRUE[] = "true";
static const char STATE_VALUE_FALSE[] = "false";
const char STATE_ON[] = "on";
const char STATE_ONOFF_VALUE[] = "value";
const char STATE_VALUE_TRUE[] = "true";
const char STATE_VALUE_FALSE[] = "false";
// Device Data elements
static const char DEV_DATA_NAME[] = "name";
static const char DEV_DATA_MODEL[] = "model";
static const char DEV_DATA_MANUFACTURER[] = "manufacturer";
static const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
const char DEV_DATA_NAME[] = "name";
const char DEV_DATA_MODEL[] = "model";
const char DEV_DATA_MANUFACTURER[] = "manufacturer";
const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
// Nanoleaf Stream Control elements
//static const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
static const char STREAM_CONTROL_PORT[] = "streamControlPort";
//static const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
//const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
const char STREAM_CONTROL_PORT[] = "streamControlPort";
//const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222; //Fixed port for Canvas;
// Nanoleaf OpenAPI URLs
static const char API_DEFAULT_PORT[] = "16021";
static const char API_URL_FORMAT[] = "http://%1:%2/api/v1/%3/%4";
static const char API_ROOT[] = "";
//static const char API_EXT_MODE_STRING_V1[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}";
static const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
static const char API_STATE[] ="state";
static const char API_PANELLAYOUT[] = "panelLayout";
static const char API_EFFECT[] = "effects";
const int API_DEFAULT_PORT = 16021;
const char API_BASE_PATH[] = "/api/v1/%1/";
const char API_ROOT[] = "";
//const char API_EXT_MODE_STRING_V1[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}";
const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
const char API_STATE[] ="state";
const char API_PANELLAYOUT[] = "panelLayout";
const char API_EFFECT[] = "effects";
// Nanoleaf ssdp services
static const char SSDP_CANVAS[] = "nanoleaf:nl29";
static const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
const int SSDP_TIMEOUT = 5000; // timout in ms
const char SSDP_ID[] = "ssdp:all";
const char SSDP_FILTER_HEADER[] = "ST";
const char SSDP_CANVAS[] = "nanoleaf:nl29";
const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
} //End of constants
// Nanoleaf Panel Shapetypes
enum SHAPETYPES {
@@ -84,6 +89,23 @@ enum EXTCONTROLVERSIONS {
EXTCTRLVER_V2
};
LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig)
: ProviderUdp()
,_restApi(nullptr)
,_apiPort(API_DEFAULT_PORT)
,_topDown(true)
,_leftRight(true)
,_startPos(0)
,_endPos(0)
,_extControlVersion (EXTCTRLVER_V2),
_panelLedCount(0)
{
_devConfig = deviceConfig;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceNanoleaf(deviceConfig);
@@ -91,17 +113,11 @@ LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig)
LedDeviceNanoleaf::~LedDeviceNanoleaf()
{
_networkmanager->deleteLater();
}
LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
_deviceReady = false;
_networkmanager = nullptr;
_extControlVersion = EXTCTRLVER_V2;
_panelLedCount = 0;
if ( _restApi != nullptr )
{
delete _restApi;
_restApi = nullptr;
}
}
bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig)
@@ -116,74 +132,89 @@ bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig)
DebugIf(verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
if ( isInitOK )
if ( LedDevice::init(deviceConfig) )
{
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, "RefreshTime : %d", _refresh_timer_interval);
Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
Debug(_log, "LatchTime : %d", this->getLatchTime());
// Read panel organisation configuration
if ( deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].isString() )
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toString().toInt() == 0 ? true : false;
{
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toString().toInt() == 0;
}
else
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toInt() == 0 ? true : false;
{
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toInt() == 0;
}
if ( deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].isString() )
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toString().toInt() == 0 ? true : false;
{
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toString().toInt() == 0;
}
else
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toInt() == 0 ? true : false;
{
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toInt() == 0;
}
_startPos = deviceConfig[ CONFIG_PANEL_START_POS ].toInt(0);
_startPos = static_cast<uint>( deviceConfig[ CONFIG_PANEL_START_POS ].toInt(0) );
// TODO: Allow to handle port dynamically
//Set hostname as per configuration and_defaultHost default port
_hostname = deviceConfig[ CONFIG_ADDRESS ].toString();
_api_port = API_DEFAULT_PORT;
_auth_token = deviceConfig[ CONFIG_AUTH_TOKEN ].toString();
_apiPort = API_DEFAULT_PORT;
_authToken = deviceConfig[ CONFIG_AUTH_TOKEN ].toString();
//If host not configured then discover device
//If host not configured the init failed
if ( _hostname.isEmpty() )
{
//Discover Nanoleaf device
if ( !discoverDevice() )
this->setInError("No target hostname nor IP defined");
isInitOK = false;
}
else
{
if ( initRestAPI( _hostname, _apiPort, _authToken ) )
{
this->setInError("No target IP defined nor Nanoleaf device was discovered");
return false;
// 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);
}
}
}
// Set UDP streaming port
_devConfig["host"] = _hostname;
_devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
isInitOK = ProviderUdp::init(_devConfig);
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
Debug(_log, "Port : %d", _port);
}
return isInitOK;
}
bool LedDeviceNanoleaf::initLeds()
bool LedDeviceNanoleaf::initLedsConfiguration()
{
bool isInitOK = true;
//Get Nanoleaf device details and configuration
_networkmanager = new QNetworkAccessManager();
// Read Panel count and panel Ids
QString url = getUrl(_hostname, _api_port, _auth_token, API_ROOT );
QJsonDocument doc = getJson( url );
if ( this->isInError() )
_restApi->setPath(API_ROOT);
httpResponse response = _restApi->get();
if ( response.error() )
{
this->setInError ( response.getErrorReason() );
isInitOK = false;
}
else
{
QJsonObject jsonAllPanelInfo = doc.object();
QJsonObject jsonAllPanelInfo = response.getBody().object();
QString deviceName = jsonAllPanelInfo[DEV_DATA_NAME].toString();
_deviceModel = jsonAllPanelInfo[DEV_DATA_MODEL].toString();
@@ -205,7 +236,7 @@ bool LedDeviceNanoleaf::initLeds()
std::map<uint, std::map<uint, uint>> panelMap;
// Loop over all children.
for (const QJsonValue & value : positionData)
for (const QJsonValue value : positionData)
{
QJsonObject panelObj = value.toObject();
@@ -239,9 +270,13 @@ bool LedDeviceNanoleaf::initLeds()
DebugIf(verbose3, _log, "panelMap[%u][%u]=%u", posY->first, posX->first, posX->second );
if ( _topDown )
{
_panelIds.push_back(posX->second);
}
else
{
_panelIds.push_front(posX->second);
}
}
}
else
@@ -252,9 +287,13 @@ bool LedDeviceNanoleaf::initLeds()
DebugIf(verbose3, _log, "panelMap[%u][%u]=%u", posY->first, posX->first, posX->second );
if ( _topDown )
{
_panelIds.push_back(posX->second);
}
else
{
_panelIds.push_front(posX->second);
}
}
}
}
@@ -300,197 +339,199 @@ bool LedDeviceNanoleaf::initLeds()
}
}
}
return isInitOK;
}
bool LedDeviceNanoleaf::initRestAPI(const QString &hostname, const int port, const QString &token )
{
bool isInitOK = false;
if ( _restApi == nullptr )
{
_restApi = new ProviderRestApi(hostname, port );
//Base-path is api-path + authentication token
_restApi->setBasePath( QString(API_BASE_PATH).arg(token) );
isInitOK = true;
}
return isInitOK;
}
int LedDeviceNanoleaf::open()
{
int retval = -1;
_deviceReady = false;
_isDeviceReady = false;
if ( init(_devConfig) )
// Set Nanoleaf to External Control (UDP) mode
Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
QJsonDocument responseDoc = changeToExternalControlMode();
// Resolve port for Light Panels
QJsonObject jsonStreamControllInfo = responseDoc.object();
if ( ! jsonStreamControllInfo.isEmpty() )
{
if ( !initNetwork() )
{
this->setInError( "UDP Network error!" );
}
else
{
if ( initLeds() )
{
_deviceReady = true;
setEnable(true);
retval = 0;
}
}
//Set default streaming port
_port = static_cast<uchar>(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
}
if ( ProviderUdp::open() == 0 )
{
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
return retval;
}
bool LedDeviceNanoleaf::discoverDevice()
QJsonObject LedDeviceNanoleaf::discover()
{
QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
bool isDeviceFound (false);
// device searching by ssdp
QString address;
QJsonArray deviceList;
// Discover Nanoleaf Devices
SSDPDiscover discover;
// Discover Canvas device
address = discover.getFirstService(searchType::STY_WEBSERVER, SSDP_CANVAS, SSDP_TIMEOUT);
// Search for Canvas and Light-Panels
QString searchTargetFilter = QString("%1|%2").arg(SSDP_CANVAS, SSDP_LIGHTPANELS);
//No Canvas device not found
if ( address.isEmpty() ) {
// Discover Light Panels (Aurora) device
address = discover.getFirstService(searchType::STY_WEBSERVER, SSDP_LIGHTPANELS, SSDP_TIMEOUT);
discover.setSearchFilter(searchTargetFilter, SSDP_FILTER_HEADER);
QString searchTarget = SSDP_ID;
if ( address.isEmpty() ) {
Warning(_log, "No Nanoleaf device discovered");
}
if ( discover.discoverServices(searchTarget) > 0 )
{
deviceList = discover.getServicesDiscoveredJson();
}
// Canvas or Light Panels found
if ( ! address.isEmpty() ) {
Info(_log, "Nanoleaf device discovered at [%s]", QSTRING_CSTR( address ));
isDeviceFound = true;
// Resolve hostname and port (or use default API port)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList addressparts = address.split(":", Qt::SkipEmptyParts);
#else
QStringList addressparts = address.split(":", QString::SkipEmptyParts);
#endif
_hostname = addressparts[0];
_api_port = addressparts[1];
}
return isDeviceFound;
devicesDiscovered.insert("devices", deviceList);
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
return devicesDiscovered;
}
QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
{
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
QJsonObject properties;
// Get Nanoleaf device properties
QString host = params["host"].toString("");
if ( !host.isEmpty() )
{
QString authToken = params["token"].toString("");
QString filter = params["filter"].toString("");
// Resolve hostname and port (or use default API port)
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
if ( addressparts.size() > 1)
{
apiPort = addressparts[1].toInt();
}
else
{
apiPort = API_DEFAULT_PORT;
}
initRestAPI(apiHost, apiPort, authToken);
_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());
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
}
return properties;
}
void LedDeviceNanoleaf::identify(const QJsonObject& params)
{
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
QJsonObject properties;
// Get Nanoleaf device properties
QString host = params["host"].toString("");
if ( !host.isEmpty() )
{
QString authToken = params["token"].toString("");
// Resolve hostname and port (or use default API port)
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
if ( addressparts.size() > 1)
{
apiPort = addressparts[1].toInt();
}
else
{
apiPort = API_DEFAULT_PORT;
}
initRestAPI(apiHost, apiPort, authToken);
_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()));
}
}
}
bool LedDeviceNanoleaf::powerOn()
{
if ( _isDeviceReady)
{
//Power-on Nanoleaf device
_restApi->setPath(API_STATE);
_restApi->put( getOnOffRequest(true) );
}
return true;
}
bool LedDeviceNanoleaf::powerOff()
{
if ( _isDeviceReady)
{
//Power-off the Nanoleaf device physically
_restApi->setPath(API_STATE);
_restApi->put( getOnOffRequest(false) );
}
return true;
}
QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const
{
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON, STATE_ONOFF_VALUE, state);
}
QJsonDocument LedDeviceNanoleaf::changeToExternalControlMode()
{
QString url = getUrl(_hostname, _api_port, _auth_token, API_EFFECT );
QJsonDocument jsonDoc;
_extControlVersion = EXTCTRLVER_V2;
//Enable UDP Mode v2
jsonDoc= putJson(url, API_EXT_MODE_STRING_V2);
return jsonDoc;
}
_restApi->setPath(API_EFFECT);
httpResponse response =_restApi->put(API_EXT_MODE_STRING_V2);
QString LedDeviceNanoleaf::getUrl(QString host, QString port, QString auth_token, QString endpoint) const {
return QString(API_URL_FORMAT).arg(host, port, auth_token, endpoint);
}
QJsonDocument LedDeviceNanoleaf::getJson(QString url)
{
Debug(_log, "GET: [%s]", QSTRING_CSTR( url ));
// Perfrom request
QNetworkRequest request(url);
QNetworkReply* reply = _networkmanager->get(request);
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
// Go into the loop until the request is finished.
loop.exec();
QJsonDocument jsonDoc;
if(reply->operation() == QNetworkAccessManager::GetOperation)
{
jsonDoc = handleReply( reply );
}
// Free space.
reply->deleteLater();
// Return response
return jsonDoc;
}
QJsonDocument LedDeviceNanoleaf::putJson(QString url, QString json)
{
Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url ), QSTRING_CSTR( json ) );
// Perfrom request
QNetworkRequest request(url);
QNetworkReply* reply = _networkmanager->put(request, json.toUtf8());
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
// Go into the loop until the request is finished.
loop.exec();
QJsonDocument jsonDoc;
if(reply->operation() == QNetworkAccessManager::PutOperation)
{
jsonDoc = handleReply( reply );
}
// Free space.
reply->deleteLater();
// Return response
return jsonDoc;
}
QJsonDocument LedDeviceNanoleaf::handleReply(QNetworkReply* const &reply )
{
QJsonDocument jsonDoc;
int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode );
if(reply->error() == QNetworkReply::NoError)
{
if ( httpStatusCode != 204 ){
QByteArray response = reply->readAll();
QJsonParseError error;
jsonDoc = QJsonDocument::fromJson(response, &error);
if (error.error != QJsonParseError::NoError)
{
this->setInError ( "Got invalid response" );
}
else {
//Debug
QString strJson(jsonDoc.toJson(QJsonDocument::Compact));
DebugIf(verbose, _log, "Reply: [%s]", strJson.toUtf8().constData() );
}
}
}
else
{
QString errorReason;
if ( httpStatusCode > 0 ) {
QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
QString advise;
switch ( httpStatusCode ) {
case 400:
advise = "Check Request Body";
break;
case 401:
advise = "Check Authentication Token (API Key)";
break;
case 404:
advise = "Check Resource given";
break;
default:
break;
}
errorReason = QString ("%1:%2 [%3 %4] - %5").arg(_hostname, _api_port, QString(httpStatusCode) , httpReason, advise);
}
else {
errorReason = QString ("%1:%2 - %3").arg(_hostname, _api_port, reply->errorString());
}
this->setInError ( errorReason );
}
// Return response
return jsonDoc;
return response.getBody();
}
int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & ledValues)
{
int retVal = 0;
uint udpBufferSize;
@@ -573,46 +614,6 @@ int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & ledValues)
return retVal;
}
QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const
{
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON, STATE_ONOFF_VALUE, state);
}
int LedDeviceNanoleaf::switchOn()
{
if ( _deviceReady)
{
// Set Nanoleaf to External Control (UDP) mode
Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
QJsonDocument responseDoc = changeToExternalControlMode();
// Resolve port for Ligh Panels
QJsonObject jsonStreamControllInfo = responseDoc.object();
if ( ! jsonStreamControllInfo.isEmpty() ) {
_port = static_cast<uchar>(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
}
//Switch on Nanoleaf device
QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE );
putJson(url, this->getOnOffRequest(true) );
}
return 0;
}
int LedDeviceNanoleaf::switchOff()
{
//Set all LEDs to Black
int rc = LedDevice::switchOff();
if ( _deviceReady)
{
//Switch off Nanoleaf device physically
QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE );
putJson(url, getOnOffRequest(false) );
}
return rc;
}
std::string LedDeviceNanoleaf:: uint8_vector_to_hex_string( const std::vector<uint8_t>& buffer ) const
{
std::stringstream ss;

View File

@@ -1,12 +1,11 @@
#pragma once
#ifndef LEDEVICENANOLEAF_H
#define LEDEVICENANOLEAF_H
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
#include "ProviderRestApi.h"
#include "ProviderUdp.h"
// ssdp discover
#include <ssdp/SSDPDiscover.h>
// Qt includes
#include <QString>
#include <QNetworkAccessManager>
@@ -19,85 +18,141 @@ class LedDeviceNanoleaf : public ProviderUdp
{
public:
///
/// Constructs the LedDevice for Nanoleaf LightPanels (aka Aurora) or Canvas
/// @brief Constructs LED-device for Nanoleaf LightPanels (aka Aurora) or Canvas
///
/// following code shows all config options
/// following code shows all configuration options
/// @code
/// "device" :
/// {
/// "type" : "nanoleaf"
/// "output" : "hostname or IP", // Optional. If empty, device is tried to be discovered
/// "token" : "Authentication Token",
/// "type" : "nanoleaf"
/// "host" : "hostname or IP",
/// "token": "Authentication Token",
/// },
///@endcode
///
/// @param deviceConfig json config for nanoleaf
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceNanoleaf(const QJsonObject &deviceConfig);
///
/// Destructor of the LedDevice; closes the tcp client
/// @brief Destructor of the LED-device
///
virtual ~LedDeviceNanoleaf() override;
/// Constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
/// Switch the device on
virtual int switchOn() override;
///
/// @brief Discover Nanoleaf devices available (for configuration).
///
/// @return A JSON structure holding a list of devices found
///
virtual QJsonObject discover() override;
/// Switch the device off
virtual int switchOff() override;
///
/// @brief Get the Nanoleaf device's resource properties
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP [:port]",
/// "token" : "authentication token",
/// "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
///
virtual QJsonObject getProperties(const QJsonObject& params) override;
///
/// @brief Send an update to the Nanoleaf device to identify it.
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP [:port]",
/// "token" : "authentication token",
/// }
///@endcode
///
/// @param[in] params Parameters to address device
///
virtual void identify(const QJsonObject& params) override;
protected:
///
/// Writes the led color values to the led-device
/// @brief Initialise the Nanoleaf device's configuration and network address details
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
///
/// Initialise Nanoleaf device's configuration and network address details
///
/// @param deviceConfig the json device config
/// @return True if success
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
bool init(const QJsonObject &deviceConfig) override;
///
/// Get Nanoleaf device details and configuration
/// @brief Opens the output device.
///
/// @return True, if Nanoleaf device capabilities fit configuration
///
bool initLeds();
///
/// Opens and initiatialises the output device
///
/// @return Zero on succes (i.e. device is ready and enabled) else negative
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
private:
///
/// Discover Nanoleaf device via SSDP identifiers
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @return True, if Nanoleaf device was found
///
bool discoverDevice();
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
//////
virtual int write(const std::vector<ColorRgb> & ledValues) override;
///
/// Change Nanoleaf device to External Control (UDP) mode
/// @brief Power-/turn on the Nanoleaf device.
///
/// @brief Store the device's original state.
///
virtual bool powerOn() override;
///
/// @brief Power-/turn off the Nanoleaf device.
///
/// @return True if success
///
virtual bool powerOff() override;
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, const int port, const QString &token );
///
/// @brief Get Nanoleaf device details and configuration
///
/// @return True, if Nanoleaf device capabilities fit configuration
///
bool initLedsConfiguration();
///
/// @brief Change Nanoleaf device to External Control (UDP) mode
///
/// @return Response from device
///
///@brief
QJsonDocument changeToExternalControlMode();
///
/// Get command to switch Nanoleaf device on or off
/// @brief Get command to power Nanoleaf device on or off
///
/// @param isOn True, if to switch on device
/// @return Command to switch device on/off
@@ -105,54 +160,18 @@ private:
QString getOnOffRequest (bool isOn ) const;
///
/// Get command as url
///
/// @param host Hostname or IP
/// @param port IP-Port
/// @param _auth_token Authorization token
/// @param Endpoint command for request
/// @return Url to execute endpoint/command
///
QString getUrl(QString host, QString port, QString auth_token, QString endpoint) const;
///
/// Execute GET request
///
/// @param url GET request for url
/// @return Response from device
///
QJsonDocument getJson(QString url);
///
/// Execute PUT request
///
/// @param Url for PUT request
/// @param json Command for request
/// @return Response from device
///
QJsonDocument putJson(QString url, QString json);
///
/// Handle replys for GET and PUT requests
///
/// @param reply Network reply
/// @return Response for request, if no error
///
QJsonDocument handleReply(QNetworkReply* const &reply );
///
/// convert vector to hex string
/// @brief Convert vector to hex string
///
/// @param uint8_t vector
/// @return vector as string of hex values
std::string uint8_vector_to_hex_string( const std::vector<uint8_t>& buffer ) const;
// QNetworkAccessManager object for sending requests.
QNetworkAccessManager* _networkmanager;
///REST-API wrapper
ProviderRestApi* _restApi;
QString _hostname;
QString _api_port;
QString _auth_token;
int _apiPort;
QString _authToken;
bool _topDown;
bool _leftRight;
@@ -163,9 +182,13 @@ private:
QString _deviceModel;
QString _deviceFirmwareVersion;
ushort _extControlVersion;
/// The number of panels with leds
/// The number of panels with LEDs
uint _panelLedCount;
/// Array of the pannel ids.
/// Array of the panel ids.
QVector<uint> _panelIds;
};
#endif // LEDEVICENANOLEAF_H

File diff suppressed because it is too large Load Diff

View File

@@ -12,8 +12,9 @@
#include <QtCore/qmath.h>
#include <QStringList>
// Leddevice includes
// LedDevice includes
#include <leddevice/LedDevice.h>
#include "ProviderRestApi.h"
#include "ProviderUdpSSL.h"
/**
@@ -134,7 +135,7 @@ public:
///
/// @param transitionTime the transition time between colors in multiples of 100 ms
///
void setTransitionTime(unsigned int transitionTime);
void setTransitionTime(int transitionTime);
///
/// @param color the color to set
@@ -144,7 +145,7 @@ public:
unsigned int getId() const;
bool getOnOffState() const;
unsigned int getTransitionTime() const;
int getTransitionTime() const;
CiColor getColor() const;
///
@@ -162,7 +163,7 @@ private:
unsigned int _id;
unsigned int _ledidx;
bool _on;
unsigned int _transitionTime;
int _transitionTime;
CiColor _color;
/// darkes blue color in hue lamp GAMUT = black
CiColor _colorBlack;
@@ -185,14 +186,18 @@ class LedDevicePhilipsHueBridge : public ProviderUdpSSL
public:
explicit LedDevicePhilipsHueBridge(const QJsonObject &deviceConfig);
~LedDevicePhilipsHueBridge();
~LedDevicePhilipsHueBridge() override;
///
/// Sets configuration
/// @brief Initialise the access to the REST-API wrapper
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
/// @param[in] host
/// @param[in] port
/// @param[in] authentication token
///
/// @return True, if success
///
bool initRestAPI(const QString &hostname, const int port, const QString &token );
///
/// @param route the route of the POST request.
@@ -201,28 +206,56 @@ public:
///
QJsonDocument post(const QString& route, const QString& content);
void setLightState(unsigned int lightId = 0, QString state = "");
void setLightState(unsigned int lightId = 0, const QString &state = "");
const QMap<quint16,QJsonObject>& getLightMap();
const QMap<quint16,QJsonObject>& getGroupMap();
QString getGroupName(unsigned int groupId = 0);
QString getGroupName(quint16 groupId = 0);
QJsonArray getGroupLights(quint16 groupId = 0);
QJsonArray getGroupLights(unsigned int groupId = 0);
public slots:
///
/// Connect to bridge to check availbility and user
///
virtual int open(void) override;
virtual int open( const QString& hostname, const QString& port, const QString& username );
protected:
///
/// @brief Initialise the Hue-Bridge configuration and network address details
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the Hue-Bridge device and its SSL-connection
///
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open(void) override;
///
/// @brief Closes the Hue-Bridge device and its SSL-connection
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual int close() override;
///
/// @brief Check, if Hue API response indicate error
///
/// @param[in] response from Hue-Bridge in JSON-format
/// return True, Hue Bridge reports error
///
bool checkApiError(const QJsonDocument &response );
///REST-API wrapper
ProviderRestApi* _restApi;
/// Ip address of the bridge
QString _hostname;
QString _api_port;
int _apiPort;
/// User name for the API ("newdeveloper")
QString _username;
@@ -231,7 +264,7 @@ protected:
QJsonDocument getGroupState( unsigned int groupId );
QJsonDocument setGroupState( unsigned int groupId, bool state);
bool isStreamOwner(const QString streamOwner);
bool isStreamOwner(const QString &streamOwner);
bool initMaps();
void log(const char* msg, const char* type, ...);
@@ -240,56 +273,10 @@ protected:
private:
///
/// Discover device via SSDP identifiers
///
/// @return True, if device was found
///
bool discoverDevice();
///
/// Get command as url
///
/// @param host Hostname or IP
/// @param port IP-Port
/// @param _auth_token Authorization token
/// @param Endpoint command for request
/// @return Url to execute endpoint/command
///
QString getUrl(QString host, QString port, QString auth_token, QString endpoint) const;
///
/// Execute GET request
///
/// @param url GET request for url
/// @return Response from device
///
QJsonDocument getJson(QString url);
///
/// Execute PUT request
///
/// @param Url for PUT request
/// @param json Command for request
/// @return Response from device
///
QJsonDocument putJson(QString url, QString json);
///
/// Handle replys for GET and PUT requests
///
/// @param reply Network reply
/// @return Response for request, if no error
///
QJsonDocument handleReply(QNetworkReply* const &reply );
QJsonDocument getAllBridgeInfos();
void setBridgeConfig( QJsonDocument doc );
void setLightsMap( QJsonDocument doc );
void setGroupMap( QJsonDocument doc );
/// QNetworkAccessManager for sending requests.
QNetworkAccessManager* _networkmanager;
void setBridgeConfig( const QJsonDocument &doc );
void setLightsMap( const QJsonDocument &doc );
void setGroupMap( const QJsonDocument &doc );
//Philips Hue Bridge details
QString _deviceModel;
@@ -320,105 +307,209 @@ class LedDevicePhilipsHue: public LedDevicePhilipsHueBridge
public:
///
/// Constructs specific LedDevice
/// @brief Constructs LED-device for Philips Hue Lights system
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDevicePhilipsHue(const QJsonObject &deviceConfig);
///
/// Destructor of this device
/// @brief Destructor of the LED-device
///
virtual ~LedDevicePhilipsHue();
/// constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Sets configuration
/// @brief Discover devices of this type available (for configuration).
/// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways.
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
/// Switch the device on
virtual int switchOn() override;
/// Switch the device off
virtual int switchOff() override;
/// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
/// @return A JSON structure holding a list of devices found
///
/// @param map Map of lightid/value pairs of bridge
///
void newLights(QMap<quint16, QJsonObject> map);
virtual QJsonObject discover() override;
///
/// @brief Get the Hue Bridge device's resource properties
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP [: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
///
virtual QJsonObject getProperties(const QJsonObject& params) override;
///
/// @brief Send an update to the device to identify it.
///
/// Used in context of a set of devices of the same type.
///
/// @param[in] params Parameters to address device
///
virtual void identify(const QJsonObject& params) override;
///
/// @brief Get the number of LEDs supported by the device.
///
/// @return Number of device's LEDs
///
unsigned int getLightsCount() const { return _lightsCount; }
void setLightsCount( unsigned int lightsCount);
bool initStream();
bool getStreamGroupState();
bool setStreamGroupState(bool state);
bool startStream();
bool stopStream();
void setOnOffState(PhilipsHueLight& light, bool on);
void setTransitionTime(PhilipsHueLight& light);
void setColor(PhilipsHueLight& light, CiColor& color);
void setState(PhilipsHueLight& light, bool on, const CiColor& color);
void restoreOriginalState();
public slots:
///
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
/// @brief Stops the device.
///
virtual void close() override;
private slots:
/// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
/// Includes switching-off the device and stopping refreshes.
///
/// @param map Map of lightid/value pairs of bridge
///
bool updateLights(QMap<quint16, QJsonObject> map);
void noSignalTimeout();
virtual void stop() override;
protected:
///
/// Opens and initiatialises the output device
/// Initialise the device's configuration
///
/// @return Zero on succes (i.e. device is ready and enabled) else negative
/// @param deviceConfig Device's configuration in JSON
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device
///
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
///
/// Get Philips Hue device details and configuration
/// @brief Closes the output device.
///
/// @return True, if Nanoleaf device capabilities fit configuration
/// @return Zero on success (i.e. device is closed), else negative
///
bool initLeds();
bool reinitLeds();
virtual int close() override;
///
/// Writes the RGB-Color values to the leds.
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per led
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
/// @return Zero on success else negative
virtual int write(const std::vector<ColorRgb>& ledValues) override;
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
/// @brief Switch the LEDs on.
///
/// Takes care that the device is opened and powered-on.
/// Depending on the configuration, the device may store its current state for later restore.
/// @see powerOn, storeState
///
/// @return True if success
///
//virtual bool switchOn() override;
///
/// @brief Switch the LEDs off.
///
/// Takes care that the LEDs and device are switched-off and device is closed.
/// Depending on the configuration, the device may be powered-off or restored to its previous state.
/// @see powerOff, restoreState
///
/// @return True, if success
///
virtual bool switchOff() override;
///
/// @brief Power-/turn on the LED-device.
///
/// Powers-/Turns on the LED hardware, if supported.
///
/// @return True, if success
///
virtual bool powerOn() override;
///
/// @brief Power-/turn off the LED-device.
///
/// Depending on the device's capability, the device is powered-/turned off or
/// an off state is simulated by writing "Black to LED" (default).
///
/// @return True, if success
///
virtual bool powerOff() override;
///
/// @brief Store the device's original state.
///
/// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
///
/// @return True if success
///
virtual bool storeState() override;
///
/// @brief Restore the device's original state.
///
/// Restore the device's state as before hyperion color streaming started.
/// This includes the on/off state of the device.
///
/// @return True, if success
///
virtual 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();
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
/// 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);
///
/// @brief Set the number of LEDs supported by the device.
///
/// @rparam[in] Number of device's LEDs
//
void setLightsCount( unsigned int lightsCount);
bool openStream();
bool getStreamGroupState();
bool setStreamGroupState(bool state);
bool startStream();
bool stopStream();
void writeStream();
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
bool noSignalDetection();
@@ -430,21 +521,20 @@ private:
bool _switchOffOnBlack;
/// The brightness factor to multiply on color change.
double _brightnessFactor;
/// Transition time in multiples of 100 ms.
/// The default of the Hue lights is 400 ms, but we may want it snapier.
unsigned int _transitionTime;
/// Transition time in multiples of 100 ms.
/// The default of the Hue lights is 400 ms, but we may want it snappier.
int _transitionTime;
bool _isRestoreOrigState;
bool _lightStatesRestored;
bool _isInitLeds;
/// Array of the light ids.
std::vector<unsigned int> _lightIds;
std::vector<quint16> _lightIds;
/// Array to save the lamps.
std::vector<PhilipsHueLight> _lights;
unsigned int _lightsCount;
unsigned int _groupId;
quint16 _groupId;
double _brightnessMin;
double _brightnessMax;
@@ -452,7 +542,7 @@ private:
bool _allLightsBlack;
QTimer* _blackLightsTimer;
unsigned int _blackLightsTimeout;
int _blackLightsTimeout;
double _brightnessThreshold;
int _handshake_timeout_min;
@@ -466,4 +556,5 @@ private:
int start_retry_left;
int stop_retry_left;
};

View File

@@ -1,10 +1,14 @@
#include "LedDeviceTpm2net.h"
const ushort TPM2_DEFAULT_PORT = 65506;
LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
@@ -14,13 +18,19 @@ LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
{
bool isInitOK = false;
_port = TPM2_DEFAULT_PORT;
bool isInitOK = ProviderUdp::init(deviceConfig);
_tpm2_max = deviceConfig["max-packet"].toInt(170);
_tpm2ByteCount = 3 * _ledCount;
_tpm2TotalPackets = 1 + _tpm2ByteCount / _tpm2_max;
// Initialise sub-class
if ( ProviderUdp::init(deviceConfig) )
{
_tpm2_max = deviceConfig["max-packet"].toInt(170);
_tpm2ByteCount = 3 * _ledCount;
_tpm2TotalPackets = 1 + _tpm2ByteCount / _tpm2_max;
isInitOK = true;
}
return isInitOK;
}

View File

@@ -1,44 +1,53 @@
#pragma once
#ifndef LEDEVICETPM2NET_H
#define LEDEVICETPM2NET_H
// hyperion includes
#include "ProviderUdp.h"
const ushort TPM2_DEFAULT_PORT = 65506;
///
/// Implementation of the LedDevice interface for sending led colors via udp tpm2.net packets
/// Implementation of the LedDevice interface for sending LED colors via udp tpm2.net packets
///
class LedDeviceTpm2net : public ProviderUdp
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a TPM2 LED-device fed via UDP
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceTpm2net(const QJsonObject &deviceConfig);
/// constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
private:
///
/// Sets configuration
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
private:
///
/// Writes the led color values to the led-device
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues) override;
virtual int write(const std::vector<ColorRgb> & ledValues) override;
int _tpm2_max;
int _tpm2ByteCount;
int _tpm2TotalPackets;
int _tpm2ThisPacket;
};
#endif // LEDEVICETPM2NET_H

View File

@@ -1,3 +1,6 @@
// hyperion local includes
#include "LedDeviceUdpArtNet.h"
#ifdef _WIN32
#include <winsock.h>
#else
@@ -6,16 +9,18 @@
#include <QHostInfo>
// hyperion local includes
#include "LedDeviceUdpArtNet.h"
const ushort ARTNET_DEFAULT_PORT = 6454;
LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceUdpArtNet(deviceConfig);
@@ -23,12 +28,18 @@ LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
{
bool isInitOK = false;
_port = ARTNET_DEFAULT_PORT;
bool isInitOK = ProviderUdp::init(deviceConfig);
_artnet_universe = deviceConfig["universe"].toInt(1);
_artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
// Initialise sub-class
if ( ProviderUdp::init(deviceConfig) )
{
_artnet_universe = deviceConfig["universe"].toInt(1);
_artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
isInitOK = true;
}
return isInitOK;
}
@@ -51,7 +62,6 @@ void LedDeviceUdpArtNet::prepare(const unsigned this_universe, const unsigned th
artnet_packet.SubUni = this_universe & 0xff ;
artnet_packet.Net = (this_universe >> 8) & 0x7f;
artnet_packet.Length = htons(this_dmxChannelCount);
}
int LedDeviceUdpArtNet::write(const std::vector<ColorRgb> &ledValues)

View File

@@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEUDPARTNET_H
#define LEDEVICEUDPARTNET_H
// hyperion includes
#include "ProviderUdp.h"
@@ -13,9 +14,7 @@
*
**/
const ushort ARTNET_DEFAULT_PORT = 6454;
#define DMX_MAX 512 // 512 usable slots
const int DMX_MAX = 512; // 512 usable slots
// http://stackoverflow.com/questions/16396013/artnet-packet-structure
typedef union
@@ -23,7 +22,7 @@ typedef union
#pragma pack(push, 1)
struct {
char ID[8]; // "Art-Net"
uint16_t OpCode; // See Doc. Table 1 - OpCodes eg. 0x5000 OpOutput / OpDmx
uint16_t OpCode; // See Doc. Table 1 - OpCodes e.g. 0x5000 OpOutput / OpDmx
uint16_t ProtVer; // 0x0e00 (aka 14)
uint8_t Sequence; // monotonic counter
uint8_t Physical; // 0x00
@@ -39,42 +38,54 @@ typedef union
} artnet_packet_t;
///
/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets
/// Implementation of the LedDevice interface for sending LED colors to an Art-Net LED-device via UDP
///
class LedDeviceUdpArtNet : public ProviderUdp
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an Art-Net LED-device fed via UDP
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpArtNet(const QJsonObject &deviceConfig);
/// constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
private:
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues) override;
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
///
/// @brief Generate Art-Net communication header
///
void prepare(const unsigned this_universe, const unsigned this_sequence, const unsigned this_dmxChannelCount);
artnet_packet_t artnet_packet;
uint8_t _artnet_seq = 1;
int _artnet_channelsPerFixture = 3;
int _artnet_universe = 1;
};
#endif // LEDEVICEUDPARTNET_H

View File

@@ -9,18 +9,43 @@
// hyperion local includes
#include "LedDeviceUdpE131.h"
const ushort E131_DEFAULT_PORT = 5568;
/* defined parameters from http://tsp.esta.org/tsp/documents/docs/BSR_E1-31-20xx_CP-2014-1009r2.pdf */
const uint32_t VECTOR_ROOT_E131_DATA = 0x00000004;
//#define VECTOR_ROOT_E131_EXTENDED 0x00000008
const uint8_t VECTOR_DMP_SET_PROPERTY = 0x02;
const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002;
//#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001
//#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002
//#define VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST 0x00000001
//#define E131_E131_UNIVERSE_DISCOVERY_INTERVAL 10 // seconds
//#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()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceUdpE131::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceUdpE131(deviceConfig);
}
bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
{
bool isInitOK = false;
_port = E131_DEFAULT_PORT;
bool isInitOK = ProviderUdp::init(deviceConfig);
if ( isInitOK )
// Initialise sub-class
if ( ProviderUdp::init(deviceConfig) )
{
_e131_universe = deviceConfig["universe"].toInt(1);
_e131_source_name = deviceConfig["source-name"].toString("hyperion on "+QHostInfo::localHostName());
@@ -29,22 +54,26 @@ bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
if (_json_cid.isEmpty())
{
_e131_cid = QUuid::createUuid();
Debug( _log, "e131 no cid found, generated %s", QSTRING_CSTR(_e131_cid.toString()));
Debug( _log, "e131 no CID found, generated %s", QSTRING_CSTR(_e131_cid.toString()));
isInitOK = true;
}
else
{
_e131_cid = QUuid(_json_cid);
Debug( _log, "e131 cid found, using %s", QSTRING_CSTR(_e131_cid.toString()));
if ( !_e131_cid.isNull() )
{
Debug( _log, "e131 CID found, using %s", QSTRING_CSTR(_e131_cid.toString()));
isInitOK = true;
}
else
{
this->setInError("CID configured is not a valid UUID. Format expected is \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"");
}
}
}
return isInitOK;
}
LedDevice* LedDeviceUdpE131::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceUdpE131(deviceConfig);
}
// populates the headers
void LedDeviceUdpE131::prepare(const unsigned this_universe, const unsigned this_dmxChannelCount)
{
@@ -120,4 +149,3 @@ int LedDeviceUdpE131::write(const std::vector<ColorRgb> &ledValues)
return retVal;
}

View File

@@ -1,4 +1,5 @@
#pragma once
#ifndef LEDEVICEUDPE131_H
#define LEDEVICEUDPE131_H
// hyperion includes
#include "ProviderUdp.h"
@@ -18,32 +19,30 @@
*
**/
const ushort E131_DEFAULT_PORT = 5568;
/* E1.31 Packet Offsets */
#define E131_ROOT_PREAMBLE_SIZE 0
#define E131_ROOT_POSTAMBLE_SIZE 2
#define E131_ROOT_ID 4
#define E131_ROOT_FLENGTH 16
#define E131_ROOT_VECTOR 18
#define E131_ROOT_CID 22
//#define E131_ROOT_PREAMBLE_SIZE 0
//#define E131_ROOT_POSTAMBLE_SIZE 2
//#define E131_ROOT_ID 4
//#define E131_ROOT_FLENGTH 16
//#define E131_ROOT_VECTOR 18
//#define E131_ROOT_CID 22
#define E131_FRAME_FLENGTH 38
#define E131_FRAME_VECTOR 40
#define E131_FRAME_SOURCE 44
#define E131_FRAME_PRIORITY 108
#define E131_FRAME_RESERVED 109
#define E131_FRAME_SEQ 111
#define E131_FRAME_OPT 112
#define E131_FRAME_UNIVERSE 113
//#define E131_FRAME_FLENGTH 38
//#define E131_FRAME_VECTOR 40
//#define E131_FRAME_SOURCE 44
//#define E131_FRAME_PRIORITY 108
//#define E131_FRAME_RESERVED 109
//#define E131_FRAME_SEQ 111
//#define E131_FRAME_OPT 112
//#define E131_FRAME_UNIVERSE 113
#define E131_DMP_FLENGTH 115
#define E131_DMP_VECTOR 117
#define E131_DMP_TYPE 118
#define E131_DMP_ADDR_FIRST 119
#define E131_DMP_ADDR_INC 121
#define E131_DMP_COUNT 123
#define E131_DMP_DATA 125
//#define E131_DMP_FLENGTH 115
//#define E131_DMP_VECTOR 117
//#define E131_DMP_TYPE 118
//#define E131_DMP_ADDR_FIRST 119
//#define E131_DMP_ADDR_INC 121
//#define E131_DMP_COUNT 123
const unsigned int E131_DMP_DATA=125;
/* E1.31 Packet Structure */
typedef union
@@ -83,51 +82,49 @@ typedef union
uint8_t raw[638];
} e131_packet_t;
/* defined parameters from http://tsp.esta.org/tsp/documents/docs/BSR_E1-31-20xx_CP-2014-1009r2.pdf */
#define VECTOR_ROOT_E131_DATA 0x00000004
#define VECTOR_ROOT_E131_EXTENDED 0x00000008
#define VECTOR_DMP_SET_PROPERTY 0x02
#define VECTOR_E131_DATA_PACKET 0x00000002
#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001
#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002
#define VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST 0x00000001
#define E131_E131_UNIVERSE_DISCOVERY_INTERVAL 10 // seconds
#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds
#define E131_DISCOVERY_UNIVERSE 64214
#define DMX_MAX 512 // 512 usable slots
///
/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets
///
class LedDeviceUdpE131 : public ProviderUdp
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an E1.31 LED-device fed via UDP
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpE131(const QJsonObject &deviceConfig);
/// constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
private:
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues) override;
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
///
/// @brief Generate E1.31 communication header
///
void prepare(const unsigned this_universe, const unsigned this_dmxChannelCount);
e131_packet_t e131_packet;
@@ -137,3 +134,5 @@ private:
QString _e131_source_name;
QUuid _e131_cid;
};
#endif // LEDEVICEUDPE131_H

View File

@@ -1,13 +1,20 @@
#include "LedDeviceUdpH801.h"
// Constants
namespace {
const ushort H801_DEFAULT_PORT = 30977;
static const char H801_DEFAULT_HOST[] = "255.255.255.255";
const char H801_DEFAULT_HOST[] = "255.255.255.255";
} //End of constants
LedDeviceUdpH801::LedDeviceUdpH801(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceUdpH801::construct(const QJsonObject &deviceConfig)
@@ -17,13 +24,15 @@ LedDevice* LedDeviceUdpH801::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig)
{
bool isInitOK = false;
/* The H801 port is fixed */
_latchTime_ms = 10;
_port = H801_DEFAULT_PORT;
_defaultHost = H801_DEFAULT_HOST;
bool isInitOK = ProviderUdp::init(deviceConfig);
if ( isInitOK )
// Initialise sub-class
if ( ProviderUdp::init(deviceConfig) )
{
_ids.clear();
QJsonArray lArray = deviceConfig["lightIds"].toArray();
@@ -44,6 +53,8 @@ bool LedDeviceUdpH801::init(const QJsonObject &deviceConfig)
}
Debug(_log, "H801 using %s:%d", _address.toString().toStdString().c_str(), _port);
isInitOK = true;
}
return isInitOK;
}

View File

@@ -1,16 +1,50 @@
#pragma once
#ifndef LEDEVICEUDPH801_H
#define LEDEVICEUDPH801_H
// hyperion includes
#include "ProviderUdp.h"
///
/// Implementation of the LedDevice interface for sending led colors via udp.
/// Implementation of the LedDevice interface for sending LED colors to a H801 LED-device via UDP
///
///
class LedDeviceUdpH801: public ProviderUdp
{
protected:
public:
///
/// @brief Constructs a H801 LED-device fed via UDP
///
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpH801(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);
private:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
QList<int> _ids;
QByteArray _message;
const int _prefix_size = 2;
@@ -18,29 +52,6 @@ protected:
const int _id_size = 3;
const int _suffix_size = 1;
public:
///
/// Constructs specific LedDevice
///
/// @param deviceConfig json device config
///
explicit LedDeviceUdpH801(const QJsonObject &deviceConfig);
/// constructs leddevice
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
private:
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues) override;
};
#endif // LEDEVICEUDPH801_H

View File

@@ -1,10 +1,14 @@
#include "LedDeviceUdpRaw.h"
const ushort RAW_DEFAULT_PORT=5568;
LedDeviceUdpRaw::LedDeviceUdpRaw(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_devConfig = deviceConfig;
_deviceReady = false;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDevice* LedDeviceUdpRaw::construct(const QJsonObject &deviceConfig)
@@ -15,6 +19,8 @@ LedDevice* LedDeviceUdpRaw::construct(const QJsonObject &deviceConfig)
bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig)
{
_port = RAW_DEFAULT_PORT;
// Initialise sub-class
bool isInitOK = ProviderUdp::init(deviceConfig);
return isInitOK;
}
@@ -23,5 +29,5 @@ int LedDeviceUdpRaw::write(const std::vector<ColorRgb> &ledValues)
{
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data());
return writeBytes((unsigned)_ledRGBCount, dataPtr);
return writeBytes(_ledRGBCount, dataPtr);
}

View File

@@ -1,39 +1,48 @@
#pragma once
#ifndef LEDEVICEUDPRAW_H
#define LEDEVICEUDPRAW_H
// hyperion includes
#include "ProviderUdp.h"
#define RAW_DEFAULT_PORT 5568
///
/// Implementation of the LedDevice interface for sending led colors via udp.
/// Implementation of the LedDevice interface for sending LED colors via UDP
///
class LedDeviceUdpRaw : public ProviderUdp
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs a LED-device fed via UDP
///
/// @param deviceConfig json device config
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceUdpRaw(const QJsonObject &deviceConfig);
/// constructs leddevice
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig) override;
protected:
private:
///
/// Writes the led color values to the led-device
/// @brief Initialise the device's configuration
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual int write(const std::vector<ColorRgb> &ledValues) override;
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
};
#endif // LEDEVICEUDPRAW_H

View File

@@ -0,0 +1,291 @@
// Local-Hyperion includes
#include "LedDeviceWled.h"
#include <ssdp/SSDPDiscover.h>
#include <utils/QStringUtils.h>
// Constants
namespace {
// Configuration settings
const char CONFIG_ADDRESS[] = "host";
// UDP elements
const quint16 STREAM_DEFAULT_PORT = 19446;
// WLED JSON-API elements
const int API_DEFAULT_PORT = -1; //Use default port per communication scheme
const char API_BASE_PATH[] = "/json/";
const char API_PATH_INFO[] = "info";
const char API_PATH_STATE[] = "state";
// List of State Information
const char STATE_ON[] = "on";
const char STATE_VALUE_TRUE[] = "true";
const char STATE_VALUE_FALSE[] = "false";
// WLED ssdp services
// TODO: WLED - Update ssdp discovery parameters when available
const char SSDP_ID[] = "ssdp:all";
const char SSDP_FILTER[] = "(.*)";
const char SSDP_FILTER_HEADER[] = "ST";
} //End of constants
LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
: ProviderUdp()
,_restApi(nullptr)
,_apiPort(API_DEFAULT_PORT)
{
_devConfig = deviceConfig;
_isDeviceReady = false;
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
}
LedDeviceWled::~LedDeviceWled()
{
if ( _restApi != nullptr )
{
delete _restApi;
_restApi = nullptr;
}
}
LedDevice* LedDeviceWled::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceWled(deviceConfig);
}
bool LedDeviceWled::init(const QJsonObject &deviceConfig)
{
Debug(_log, "");
bool isInitOK = false;
// Initialise LedDevice sub-class, ProviderUdp::init will be executed later, if connectivity is defined
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());
//Set hostname as per configuration
QString address = deviceConfig[ CONFIG_ADDRESS ].toString();
//If host not configured the init fails
if ( address.isEmpty() )
{
this->setInError("No target hostname nor IP defined");
return false;
}
else
{
QStringList addressparts = QStringUtils::split(address,":", 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);
}
}
}
Debug(_log, "[%d]", isInitOK);
return isInitOK;
}
bool LedDeviceWled::initRestAPI(const QString &hostname, const int port )
{
Debug(_log, "");
bool isInitOK = false;
if ( _restApi == nullptr )
{
_restApi = new ProviderRestApi(hostname, port);
_restApi->setBasePath( API_BASE_PATH );
isInitOK = true;
}
Debug(_log, "[%d]", isInitOK);
return isInitOK;
}
QString LedDeviceWled::getOnOffRequest (bool isOn ) const
{
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
return QString( "{\"%1\":%2}" ).arg( STATE_ON, state);
}
bool LedDeviceWled::powerOn()
{
Debug(_log, "");
bool on = true;
if ( _isDeviceReady)
{
//Power-on WLED device
_restApi->setPath(API_PATH_STATE);
httpResponse response = _restApi->put(getOnOffRequest(true));
if ( response.error() )
{
this->setInError ( response.getErrorReason() );
on = false;
}
}
return on;
}
bool LedDeviceWled::powerOff()
{
Debug(_log, "");
bool off = true;
if ( _isDeviceReady)
{
// Write a final "Black" to have a defined outcome
writeBlack();
//Power-off the WLED device physically
_restApi->setPath(API_PATH_STATE);
httpResponse response = _restApi->put(getOnOffRequest(false));
if ( response.error() )
{
this->setInError ( response.getErrorReason() );
off = false;
}
}
return off;
}
QJsonObject LedDeviceWled::discover()
{
QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
QJsonArray deviceList;
// Discover WLED Devices
SSDPDiscover discover;
discover.skipDuplicateKeys(true);
discover.setSearchFilter(SSDP_FILTER, SSDP_FILTER_HEADER);
QString searchTarget = SSDP_ID;
if ( discover.discoverServices(searchTarget) > 0 )
{
deviceList = discover.getServicesDiscoveredJson();
}
devicesDiscovered.insert("devices", deviceList);
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
return devicesDiscovered;
}
QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
{
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
QJsonObject properties;
// Get Nanoleaf device properties
QString host = params["host"].toString("");
if ( !host.isEmpty() )
{
QString filter = params["filter"].toString("");
// Resolve hostname and port (or use default API port)
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
if ( addressparts.size() > 1)
{
apiPort = addressparts[1].toInt();
}
else
{
apiPort = API_DEFAULT_PORT;
}
if ( filter.startsWith("/") )
filter.remove(0,1);
initRestAPI(apiHost, apiPort);
_restApi->setPath(API_PATH_INFO);
// Perform request
// TODO: WLED::getProperties - Check, if filter is supported
httpResponse response = _restApi->put(filter);
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());
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
}
return properties;
}
void LedDeviceWled::identify(const QJsonObject& params)
{
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
QJsonObject properties;
// Get Nanoleaf device properties
QString host = params["host"].toString("");
if ( !host.isEmpty() )
{
// Resolve hostname and port (or use default API port)
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
QString apiHost = addressparts[0];
int apiPort;
if ( addressparts.size() > 1)
apiPort = addressparts[1].toInt();
else
apiPort = API_DEFAULT_PORT;
// TODO: WLED::identify - Replace with valid identification code
// initRestAPI(apiHost, apiPort);
// QString resource = QString("%1/%2/%3").arg( API_LIGHTS ).arg( lightId ).arg( API_STATE);
// _restApi->setPath(resource);
// QString stateCmd;
// stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON ).arg( API_STATE_VALUE_TRUE );
// stateCmd += QString("\"%1\":\"%2\"").arg( "alert" ).arg( "select" );
// stateCmd = "{" + stateCmd + "}";
// // Perform request
// httpResponse response = _restApi->put(stateCmd);
// if ( response.error() )
// {
// Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
// }
}
}
int LedDeviceWled::write(const std::vector<ColorRgb> &ledValues)
{
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(ledValues.data());
return writeBytes( _ledRGBCount, dataPtr);
}

View File

@@ -0,0 +1,132 @@
#ifndef LEDDEVICEWLED_H
#define LEDDEVICEWLED_H
// LedDevice includes
#include <leddevice/LedDevice.h>
#include "ProviderRestApi.h"
#include "ProviderUdp.h"
///
/// Implementation of a WLED-device
/// ...
///
///
class LedDeviceWled : public ProviderUdp
{
public:
///
/// @brief Constructs a WLED-device
///
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceWled(const QJsonObject &deviceConfig);
///
/// @brief Destructor of the WLED-device
///
virtual ~LedDeviceWled() override;
///
/// @brief Constructs the WLED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// @brief Discover WLED devices available (for configuration).
///
/// @return A JSON structure holding a list of devices found
///
virtual QJsonObject discover() override;
///
/// @brief Get the WLED device's resource properties
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP [:port]",
/// "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
///
virtual QJsonObject getProperties(const QJsonObject& params) override;
///
/// @brief Send an update to the WLED device to identify it.
///
/// Following parameters are required
/// @code
/// {
/// "host" : "hostname or IP [:port]",
/// }
///@endcode
///
/// @param[in] params Parameters to address device
///
virtual void identify(const QJsonObject& params) override;
protected:
///
/// @brief Initialise the WLED device's configuration and network address details
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
///
/// @brief Power-/turn on the WLED device.
///
/// @brief Store the device's original state.
///
virtual bool powerOn() override;
///
/// @brief Power-/turn off the WLED device.
///
/// @return True if success
///
virtual bool powerOff() override;
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, const int port );
///
/// @brief Get command to power WLED-device on or off
///
/// @param isOn True, if to switch on device
/// @return Command to switch device on/off
///
QString getOnOffRequest (bool isOn ) const;
///REST-API wrapper
ProviderRestApi* _restApi;
QString _hostname;
int _apiPort;
};
#endif // LEDDEVICEWLED_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,627 @@
#ifndef LEDEVICEYEELIGHT_H
#define LEDEVICEYEELIGHT_H
// LedDevice includes
#include <leddevice/LedDevice.h>
// Qt includes
#include <QTcpSocket>
#include <QHostAddress>
#include <QTcpServer>
#include <QColor>
#include <chrono>
// Constants
namespace {
// List of State Information
const char API_METHOD_POWER[] = "set_power";
const char API_METHOD_POWER_ON[] = "on";
const char API_METHOD_POWER_OFF[] = "off";
const char API_METHOD_MUSIC_MODE[] = "set_music";
const int API_METHOD_MUSIC_MODE_ON = 1;
const int API_METHOD_MUSIC_MODE_OFF = 0;
const char API_METHOD_SETRGB[] = "set_rgb";
const char API_METHOD_SETSCENE[] = "set_scene";
const char API_METHOD_GETPROP[] = "get_prop";
const char API_PARAM_EFFECT_SUDDEN[] = "sudden";
const char API_PARAM_EFFECT_SMOOTH[] = "smooth";
constexpr std::chrono::milliseconds API_PARAM_DURATION{50};
constexpr std::chrono::milliseconds API_PARAM_DURATION_POWERONOFF{1000};
constexpr std::chrono::milliseconds API_PARAM_EXTRA_TIME_DARKNESS{200};
} //End of constants
///
/// Response object for Yeelight-API calls and JSON-responses
///
class YeelightResponse
{
public:
enum API_REPLY{
API_OK,
API_ERROR,
API_NOTIFICATION,
};
explicit YeelightResponse() {}
API_REPLY error() { return _error;}
void setError(const YeelightResponse::API_REPLY replyType) { _error = replyType; }
QJsonArray getResult() const { return _resultArray; }
void setResult(const QJsonArray &result) { _resultArray = result; }
int getErrorCode() const { return _errorCode; }
void setErrorCode(const int &errorCode) { _errorCode = errorCode; _error = API_ERROR;}
QString getErrorReason() const { return _errorReason; }
void setErrorReason(const QString &errorReason) { _errorReason = errorReason; }
private:
QJsonArray _resultArray;
API_REPLY _error = API_OK;
int _errorCode = 0;
QString _errorReason;
};
///
/// Implementation of one Yeelight light.
///
class YeelightLight
{
public:
enum API_EFFECT{
API_EFFECT_SMOOTH,
API_EFFECT_SUDDEN
};
enum API_MODE{
API_TURN_ON_MODE,
API_CT_MODE,
API_RGB_MODE,
API_HSV_MODE,
API_COLOR_FLOW_MODE,
API_NIGHT_LIGHT_MODE
};
/// @brief Constructs one Yeelight light
///
/// @param[in] log Logger instance
/// @param[in] hostname or IP-address
/// @param[in] port, default port 55443 is used when not provided
///
YeelightLight( Logger *log, const QString &hostname, quint16 port);
///
/// @brief Destructor of the Yeelight light
///
virtual ~YeelightLight();
///
/// @brief Set the Yeelight light connectivity parameters
///
/// @param[in] hostname or IP-address
/// @param[in] port, default port 55443 is used when not provided
///
void setHostname( const QString &hostname, quint16 port);
///
/// @brief Set the Yeelight light name
///
/// @param[in] name
///
void setName( const QString& name ) { _name = name; }
///
/// @brief Get the Yeelight light name
///
/// @return The Yeelight light name
///
QString getName() const { return _name; }
///
/// @brief Opens the Yeelight light connectivity
///
/// @return True, on success (i.e. device is open)
///
bool open();
///
/// @brief Closes the Yeelight light connectivity
///
/// @return True, on success (i.e. device is closed)
///
bool close();
///
/// @brief Send a command to light up Yeelight light to allow identification
///
/// @return True, if success
///
bool identify();
///
/// @brief Execute a Yeelight-API command
///
/// @param[in] command The API command request in JSON
/// @return 0: success, -1: error, -2: command quota exceeded
///
int writeCommand( const QJsonDocument &command );
///
/// @brief Execute a Yeelight-API command
///
/// @param[in] command The API command request in JSON
/// @param[out] result The response to the command in JSON
/// @return 0: success, -1: error, -2: command quota exceeded
///
int writeCommand( const QJsonDocument &command, QJsonArray &result );
///
/// @brief Stream a Yeelight-API command
///
/// Yeelight must be in music mode, i.e. Streaming socket is established
///
/// @param[in] command The API command request in JSON
/// @return True, on success
///
bool streamCommand( const QJsonDocument &command );
///
/// @brief Set the Yeelight light streaming socket
///
/// @param[in] socket
///
void setStreamSocket( QTcpSocket* socket );
///
/// @brief Power on/off on the Yeelight light
///
/// @param[in] on True: power on, False: power off
///
/// @return True, if success
///
bool setPower( bool on );
///
/// @brief Power on/off on the Yeelight light
///
/// @param[in] on True: power on, False: power off
/// @param[in] effect Transition effect, sudden or smooth
/// @param[in] duration Duration of the transition, if smooth
/// @param[in] mode Color mode after powering on
///
/// @return True, if success
///
bool setPower( bool on, API_EFFECT effect, int duration, API_MODE mode = API_RGB_MODE );
///
/// @brief Set the Yeelight light to the given color (using RGB mode)
///
/// @param[in] color as RGB value
///
/// @return True, if success
///
bool setColorRGB( const ColorRgb &color );
///
/// @brief Set the Yeelight light to the given color (using HSV mode)
///
/// @param[in] color as RGB value
///
/// @return True, if success
///
bool setColorHSV( const ColorRgb &color );
///
/// @brief Set the Yeelight light effect and duration while transiting between color updates
///
/// @param[in] effect Transition effect, sudden or smooth
/// @param[in] duration Duration of the transition, if smooth
///
void setTransitionEffect ( API_EFFECT effect ,int duration = API_PARAM_DURATION.count() );
///
/// @brief Set the Yeelight light brightness configuration behaviour
///
/// @param[in] min Minimum Brightness (in %). Every value lower than minimum will be set to minimum.
/// @param[in] max Maximum Brightness (in %). Every value greater than maximum will be set to maximum.
/// @param[in] switchoff True, power-off light, if brightness is lower then minimum
/// @param[in] extraTime Additional time (in ms), which added to transition duration while powering-off
/// @param[in] factor Brightness factor to multiply on color change.
///
void setBrightnessConfig (int min = 1, int max = 100, bool switchoff = false, int extraTime = 0, double factor = 1);
///
/// @brief Set the Yeelight light into music-mode
///
/// @param[in] on True: music-mode on, False: music-mode off
/// @param[in] hostAddress of the music-mode server
/// @param[in] port of the music-mode server
///
bool setMusicMode( bool on, const QHostAddress &hostAddress = {} , int port = -1 );
///
/// @brief Set the wait-time between two Yeelight light commands
///
/// The write of a command is delayed by the given wait-time, if the last write happen in the wait-time time frame.
/// Used to avoid that the Yeelight light runs into the quota exceed error scenario.
/// A Yeelight light can do 60 commands/min ( -> wait-time = 1000ms).
///
/// @param[in] waitTime in milliseconds
///
void setQuotaWaitTime( int waitTime ) { _waitTimeQuota = waitTime; }
///
/// @brief Get the Yeelight light properties
///
/// @return properties as JSON-object
///
QJsonObject getProperties();
///
/// @brief Get the Yeelight light properties and store them along the Yeelight light for later access
///
void storeState();
///
/// @brief Restore the Yeelight light's original state.
///
/// Restore the device's state as before hyperion color streaming started.
///
/// @return True, if success
///
virtual bool restoreState();
///
/// @brief Check, if light was originally powered on before hyperion color streaming started..
///
/// @return True, if light was on at start
///
bool wasOriginallyOn() const { return _power == API_METHOD_POWER_ON ? true : false; }
///
/// @brief Check, if the Yeelight light is ready for updates
///
/// @return True, if ready
///
bool isReady() const { return !_isInError; }
///
/// @brief Check, if the Yeelight light is powered on
///
/// @return True, if powered on
///
bool isOn() const { return _isOn; }
///
/// @brief Check, if the Yeelight light is in music-mode
///
/// @return True, if in music mode
///
bool isInMusicMode( bool deviceCheck = false );
///
/// @brief Set the Yeelight light in error state
///
/// @param[in] errorMsg The error message to be logged
///
void setInError( const QString& errorMsg );
///
/// @brief Set the Yeelight light debug-level
///
/// @param[in] level Debug level (0: no debug output, 1-3: verbosity level)
///
void setDebuglevel ( int level ) { _debugLevel = level; }
private:
YeelightResponse handleResponse(int correlationID, QByteArray const &response );
///
/// @brief Build Yeelight-API command
///
/// @param[in] method Control method to be invoked
/// @param[in] params Parameters for control method
/// @return Yeelight-API command in JSON format
///
QJsonDocument getCommand(const QString &method, const QJsonArray &params);
///
/// @brief Map Yeelight light properties into the Yeelight light members for direct access
///
/// @param[in] properties Yeelight light's properties as JSON-Object
///
void mapProperties(const QJsonObject &properties);
///
/// @brief Write a Yeelight light specific log-line for debugging purposed
///
/// @param[in] logLevel Debug level (0: no debug output, 1-3: verbosity level)
/// @param[in] msg Log message prefix (max 20 characters)
/// @param[in] type log message text
/// @param[in] ... variable input to log message text
/// ///
void log(const int logLevel,const char* msg, const char* type, ...);
Logger* _log;
int _debugLevel;
/// Error status of Yeelight light
bool _isInError;
/// IP address/port of the Yeelight light
QString _host;
quint16 _port;
/// Yeelight light communication socket
QTcpSocket* _tcpSocket;
/// Music mode server communication socket
QTcpSocket* _tcpStreamSocket;
/// ID of last command written or streamed
int _correlationID;
/// Timestamp of last write
qint64 _lastWriteTime;
/// Last color written to Yeelight light (RGB represented as QColor)
QColor _color;
/// Last color written to Yeelight light (RGB represented as int)
int _lastColorRgbValue;
/// Yeelight light behavioural parameters
API_EFFECT _transitionEffect;
int _transitionDuration;
int _extraTimeDarkness;
int _brightnessMin;
bool _isBrightnessSwitchOffMinimum;
int _brightnessMax;
double _brightnessFactor;
QString _transitionEffectParam;
/// Wait time to avoid quota exceed scenario
int _waitTimeQuota;
/// Yeelight light properties
QJsonObject _originalStateProperties;
QString _name;
QString _model;
QString _power;
QString _fw_ver;
int _colorRgbValue;
int _bright;
int _ct;
/// Yeelight light status
bool _isOn;
bool _isInMusicMode;
};
///
/// Implementation of the LedDevice interface for sending to
/// Yeelight devices via network
///
class LedDeviceYeelight : public LedDevice
{
public:
///
/// @brief Constructs a Yeelight LED-device serving multiple lights
///
/// @param deviceConfig Device's configuration as JSON-Object
///
explicit LedDeviceYeelight(const QJsonObject &deviceConfig);
///
/// @brief Destructor of the LedDevice
///
virtual ~LedDeviceYeelight() override;
///
/// @brief Constructs the LED-device
///
/// @param[in] deviceConfig Device's configuration as JSON-Object
/// @return LedDevice constructed
///
static LedDevice* construct(const QJsonObject &deviceConfig);
///
/// @brief Discover Yeelight devices available (for configuration).
///
/// @return A JSON structure holding a list of devices found
///
virtual QJsonObject discover() override;
///
/// @brief Get a Yeelight device's resource properties
///
/// Following parameters are required
/// @code
/// {
/// "hostname" : "hostname or IP",
/// "port" : port, default port 55443 is used when not provided
/// }
///@endcode
///
/// @param[in] params Parameters to query device
/// @return A JSON structure holding the device's properties
///
virtual QJsonObject getProperties(const QJsonObject& params) override;
///
/// @brief Send an update to the Yeelight device to identify it.
///
/// Following parameters are required
/// @code
/// {
/// "hostname" : "hostname or IP",
/// "port" : port, default port 55443 is used when not provided
/// }
///@endcode
///
/// @param[in] params Parameters to address device
///
virtual void identify(const QJsonObject& params) override;
protected:
///
/// @brief Initialise the device's configuration
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @brief Opens the output device.
///
/// @return Zero on success (i.e. device is ready), else negative
///
virtual int open() override;
///
/// @brief Closes the output device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual int close() override;
///
/// @brief Writes the RGB-Color values to the LEDs.
///
/// @param[in] ledValues The RGB-color per LED
/// @return Zero on success, else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues) override;
///
/// @brief Power-/turn on the Nanoleaf device.
///
/// @brief Store the device's original state.
///
virtual bool powerOn() override;
///
/// @brief Power-/turn off the Nanoleaf device.
///
/// @return True if success
///
virtual bool powerOff() override;
///
/// @brief Store the device's original state.
///
/// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
///
/// @return True if success
///
virtual bool storeState() override;
///
/// @brief Restore the device's original state.
///
/// Restore the device's state as before hyperion color streaming started.
/// This includes the on/off state of the device.
///
/// @return True, if success
///
virtual bool restoreState() override;
private:
struct yeelightAddress {
QString host;
int port;
bool operator == (yeelightAddress const& a) const
{
return ((host == a.host) && (port == a.port));
}
};
enum COLOR_MODEL{
MODEL_HSV,
MODEL_RGB
};
///
/// @brief Start music-mode server
///
/// @return True, if music mode server is running
///
bool startMusicModeServer();
///
/// @brief Stop music-mode server
///
/// @return True, if music mode server has been stopped
///
bool stopMusicModeServer();
///
/// @brief Update list of Yeelight lights handled by the LED-device
///
/// @param[in] list List of Yeelight lights
///
/// @return False, if no lights were provided
///
bool updateLights(const QVector<yeelightAddress> &list);
///
/// @brief Set the number of Yeelight lights handled by the LED-device
///
/// @param[in] lightsCount Number of Yeelight lights
///
void setLightsCount( unsigned int lightsCount ) { _lightsCount = lightsCount; }
///
/// @brief Get the number of Yeelight lights handled by the LED-device
///
/// @return Number of Yeelight lights
///
uint getLightsCount() const { return _lightsCount; }
/// Array of the Yeelight addresses handled by the LED-device
QVector<yeelightAddress> _lightsAddressList;
/// Array to save the lights
std::vector<YeelightLight> _lights;
unsigned int _lightsCount;
/// Yeelight configuration/behavioural parameters
int _outputColorModel;
YeelightLight::API_EFFECT _transitionEffect;
int _transitionDuration;
int _extraTimeDarkness;
int _brightnessMin;
bool _isBrightnessSwitchOffMinimum;
int _brightnessMax;
double _brightnessFactor;
int _waitTimeQuota;
int _debuglevel;
///Music mode Server details
QHostAddress _musicModeServerAddress;
int _musicModeServerPort;
QTcpServer* _tcpMusicModeServer = nullptr;
};
#endif // LEDEVICEYEELIGHT_H

View File

@@ -0,0 +1,247 @@
// Local-Hyperion includes
#include "ProviderRestApi.h"
// Qt includes
#include <QEventLoop>
#include <QNetworkReply>
#include <QByteArray>
//std includes
#include <iostream>
// Constants
namespace {
const QChar ONE_SLASH = '/';
} //End of constants
ProviderRestApi::ProviderRestApi(const QString &host, const int &port, const QString &basePath)
:_log(Logger::getInstance("LEDDEVICE"))
,_networkManager(nullptr)
,_scheme("http")
,_hostname(host)
,_port(port)
{
_networkManager = new QNetworkAccessManager();
_apiUrl.setScheme(_scheme);
_apiUrl.setHost(host);
_apiUrl.setPort(port);
_basePath = basePath;
}
ProviderRestApi::ProviderRestApi(const QString &host, const int &port)
: ProviderRestApi(host, port, "") {}
ProviderRestApi::ProviderRestApi()
: ProviderRestApi("", -1) {}
ProviderRestApi::~ProviderRestApi()
{
if ( _networkManager != nullptr )
{
delete _networkManager;
}
}
void ProviderRestApi::setBasePath(const QString &basePath)
{
_basePath.clear();
appendPath (_basePath, basePath );
}
void ProviderRestApi::setPath ( const QString &path )
{
_path.clear();
appendPath (_path, path );
}
void ProviderRestApi::appendPath ( const QString &path )
{
appendPath (_path, path );
}
void ProviderRestApi::appendPath ( QString& path, const QString &appendPath) const
{
if ( !appendPath.isEmpty() && appendPath != ONE_SLASH )
{
if (path.isEmpty() || path == ONE_SLASH )
{
path.clear();
if (appendPath[0] != ONE_SLASH )
{
path.push_back(ONE_SLASH);
}
}
else if (path[path.size()-1] == ONE_SLASH && appendPath[0] == ONE_SLASH)
{
path.chop(1);
}
else if (path[path.size()-1] != ONE_SLASH && appendPath[0] != ONE_SLASH)
{
path.push_back(ONE_SLASH);
}
else
{
// Only one slash.
}
path.append(appendPath);
}
}
void ProviderRestApi::setFragment(const QString &fragment)
{
_fragment = fragment;
}
void ProviderRestApi::setQuery(const QUrlQuery &query)
{
_query = query;
}
QUrl ProviderRestApi::getUrl() const
{
QUrl url = _apiUrl;
QString fullPath = _basePath;
appendPath (fullPath, _path );
url.setPath(fullPath);
url.setFragment( _fragment );
url.setQuery( _query );
return url;
}
httpResponse ProviderRestApi::get()
{
return get( getUrl() );
}
httpResponse ProviderRestApi::get(const QUrl &url)
{
Debug(_log, "GET: [%s]", QSTRING_CSTR( url.toString() ));
// Perform request
QNetworkRequest request(url);
QNetworkReply* reply = _networkManager->get(request);
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
// Go into the loop until the request is finished.
loop.exec();
httpResponse response;
if(reply->operation() == QNetworkAccessManager::GetOperation)
{
response = getResponse(reply );
}
// Free space.
reply->deleteLater();
// Return response
return response;
}
httpResponse ProviderRestApi::put(const QString &body)
{
return put( getUrl(), body );
}
httpResponse ProviderRestApi::put(const QUrl &url, const QString &body)
{
Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url.toString() ), QSTRING_CSTR( body ) );
// Perform request
QNetworkRequest request(url);
QNetworkReply* reply = _networkManager->put(request, body.toUtf8());
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
// Go into the loop until the request is finished.
loop.exec();
httpResponse response;
if(reply->operation() == QNetworkAccessManager::PutOperation)
{
response = getResponse(reply);
}
// Free space.
reply->deleteLater();
// Return response
return response;
}
httpResponse ProviderRestApi::getResponse(QNetworkReply* const &reply)
{
httpResponse response;
int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
response.setHttpStatusCode(httpStatusCode);
Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode );
response.setNetworkReplyError(reply->error());
if(reply->error() == QNetworkReply::NoError)
{
if ( httpStatusCode != 204 ){
QByteArray replyData = reply->readAll();
if ( !replyData.isEmpty())
{
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(replyData, &error);
if (error.error != QJsonParseError::NoError)
{
//Received not valid JSON response
//std::cout << "Response: [" << replyData.toStdString() << "]" << std::endl;
response.setError(true);
response.setErrorReason(error.errorString());
}
else
{
//std::cout << "Response: [" << QString (jsonDoc.toJson(QJsonDocument::Compact)).toStdString() << "]" << std::endl;
response.setBody( jsonDoc );
}
}
else
{ // Create valid body which is empty
response.setBody( QJsonDocument() );
}
}
}
else
{
QString errorReason;
if ( httpStatusCode > 0 ) {
QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
QString advise;
switch ( httpStatusCode ) {
case 400:
advise = "Check Request Body";
break;
case 401:
advise = "Check Authentication Token (API Key)";
break;
case 404:
advise = "Check Resource given";
break;
default:
break;
}
errorReason = QString ("[%3 %4] - %5").arg(QString(httpStatusCode) , httpReason, advise);
}
else {
errorReason = reply->errorString();
}
response.setError(true);
response.setErrorReason(errorReason);
// Create valid body which is empty
response.setBody( QJsonDocument() );
}
return response;
}

View File

@@ -0,0 +1,216 @@
#ifndef PROVIDERRESTKAPI_H
#define PROVIDERRESTKAPI_H
// Local-Hyperion includes
#include <utils/Logger.h>
// Qt includes
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QJsonDocument>
///
/// Response object for REST-API calls and JSON-responses
///
class httpResponse
{
public:
explicit httpResponse() {}
bool error() { return _hasError;}
void setError(const bool hasError) { _hasError = hasError; }
QJsonDocument getBody() const { return _responseBody; }
void setBody(const QJsonDocument &body) { _responseBody = body; }
QString getErrorReason() const { return _errorReason; }
void setErrorReason(const QString &errorReason) { _errorReason = errorReason; }
int getHttpStatusCode() const { return _httpStatusCode; }
void setHttpStatusCode(const int httpStatusCode) { _httpStatusCode = httpStatusCode; }
QNetworkReply::NetworkError getNetworkReplyError() const { return _networkReplyError; }
void setNetworkReplyError (const QNetworkReply::NetworkError networkReplyError) { _networkReplyError = networkReplyError; }
private:
QJsonDocument _responseBody;
bool _hasError = false;
QString _errorReason;
int _httpStatusCode = 0;
QNetworkReply::NetworkError _networkReplyError = QNetworkReply::NoError;
};
///
/// Wrapper class supporting REST-API calls with JSON requests and responses
///
/// Usage sample:
/// @code
///
/// ProviderRestApi* _restApi = new ProviderRestApi(hostname, port );
///
/// _restApi->setBasePath( QString("/api/%1/").arg(token) );
/// _restApi->setPath( QString("%1/%2").arg( "groups" ).arg( groupId ) );
///
/// httpResponse response = _restApi->get();
/// if ( !response.error() )
/// response.getBody();
///
/// delete _restApi;
///
///@endcode
///
class ProviderRestApi
{
public:
///
/// @brief Constructor of the REST-API wrapper
///
explicit ProviderRestApi();
///
/// @brief Constructor of the REST-API wrapper
///
/// @param[in] host
/// @param[in] port
///
explicit ProviderRestApi(const QString &host, const int &port);
///
/// @brief Constructor of the REST-API wrapper
///
/// @param[in] host
/// @param[in] port
/// @param[in] API base-path
///
explicit ProviderRestApi(const QString &host, const int &port, const QString &basePath);
///
/// @brief Destructor of the REST-API wrapper
///
virtual ~ProviderRestApi();
///
/// @brief Get the URL as defined using scheme, host, port, API-basepath, path, query, fragment
///
/// @return url
///
QUrl getUrl() const;
///
/// @brief Set an API's base path (the stable path element before addressing resources)
///
/// @param[in] basePath, e.g. "/api/v1/" or "/json"
///
void setBasePath(const QString &basePath);
///
/// @brief Set an API's path to address resources
///
/// @param[in] path, e.g. "/lights/1/state/"
///
void setPath ( const QString &path );
///
/// @brief Append an API's path element to path set before
///
/// @param[in] path
///
void appendPath (const QString &appendPath);
///
/// @brief Set an API's fragment
///
/// @param[in] fragment, e.g. "question3"
///
void setFragment(const QString&fragment);
///
/// @brief Set an API's query string
///
/// @param[in] query, e.g. "&A=128&FX=0"
///
void setQuery(const QUrlQuery &query);
///
/// @brief Execute GET request
///
/// @return Response The body of the response in JSON
///
httpResponse get();
///
/// @brief Execute GET request
///
/// @param[in] url GET request for URL
/// @return Response The body of the response in JSON
///
httpResponse get(const QUrl &url);
///
/// @brief Execute PUT request
///
/// @param[in] body The body of the request in JSON
/// @return Response The body of the response in JSON
///
httpResponse put(const QString &body = "");
///
/// @brief Execute PUT request
///
/// @param[in] URL for PUT request
/// @param[in] body The body of the request in JSON
/// @return Response The body of the response in JSON
///
httpResponse put(const QUrl &url, const QString &body = "");
///
/// @brief Execute POST request
///
/// @param[in] body The body of the request in JSON
/// @return Response The body of the response in JSON
///
httpResponse post(QString body = "");
///
/// @brief Handle responses for REST requests
///
/// @param[in] reply Network reply
/// @return Response The body of the response in JSON
///
httpResponse getResponse(QNetworkReply* const &reply);
private:
///
/// @brief Append an API's path element to path given as param
///
/// @param[in/out] path to be updated
/// @param[in] path, element to be appended
///
void appendPath (QString &path, const QString &appendPath) const;
Logger* _log;
// QNetworkAccessManager object for sending REST-requests.
QNetworkAccessManager* _networkManager;
QUrl _apiUrl;
QString _scheme;
QString _hostname;
int _port;
QString _basePath;
QString _path;
QString _fragment;
QUrlQuery _query;
};
#endif // PROVIDERRESTKAPI_H

View File

@@ -22,7 +22,7 @@ ProviderUdp::ProviderUdp()
, _port(1)
, _defaultHost("127.0.0.1")
{
_deviceReady = false;
_isDeviceReady = false;
_latchTime_ms = 1;
}
@@ -30,59 +30,65 @@ ProviderUdp::~ProviderUdp()
{
if ( _udpSocket != nullptr )
{
_udpSocket->deleteLater();
delete _udpSocket;
}
}
bool ProviderUdp::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
QString host = deviceConfig["host"].toString(_defaultHost);
// 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.", deviceConfig["host"].toString().toStdString().c_str());
}
else
{
Debug( _log, "Failed to parse [%s] as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
QHostInfo info = QHostInfo::fromName(host);
if (info.addresses().isEmpty())
if (_address.setAddress(host) )
{
Debug( _log, "Failed to parse [%s] as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
QString errortext = QString ("Invalid target address [%1]!").arg(host);
this->setInError ( errortext );
return false;
Debug( _log, "Successfully parsed %s as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
}
else
{
Debug( _log, "Successfully parsed %s as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
_address = info.addresses().first();
Debug( _log, "Failed to parse [%s] as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
QHostInfo info = QHostInfo::fromName(host);
if (info.addresses().isEmpty())
{
Debug( _log, "Failed to parse [%s] as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
QString errortext = QString ("Invalid target address [%1]!").arg(host);
this->setInError ( errortext );
return false;
}
else
{
Debug( _log, "Successfully parsed %s as a hostname.", deviceConfig["host"].toString().toStdString().c_str());
_address = info.addresses().first();
}
}
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<int>(config_port);
Debug( _log, "UDP using %s:%d", _address.toString().toStdString().c_str() , _port );
_udpSocket = new QUdpSocket(this);
isInitOK = true;
}
}
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<int>(config_port);
Debug( _log, "UDP using %s:%d", _address.toString().toStdString().c_str() , _port );
}
return isInitOK;
}
bool ProviderUdp::initNetwork()
int ProviderUdp::open()
{
bool isInitOK = false;
_udpSocket = new QUdpSocket(this);
int retval = -1;
_isDeviceReady = false;
// Try to bind the UDP-Socket
if ( _udpSocket != nullptr )
@@ -94,37 +100,21 @@ bool ProviderUdp::initNetwork()
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));
}
isInitOK = true;
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
return isInitOK;
}
int ProviderUdp::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
if ( init(_devConfig) )
else
{
if ( ! initNetwork())
{
this->setInError( "UDP Network error!" );
}
else
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
retval = 0;
}
this->setInError( " Open error. UDP Socket not initialised!" );
}
return retval;
}
void ProviderUdp::close()
int ProviderUdp::close()
{
LedDevice::close();
int retval = 0;
_isDeviceReady = false;
if ( _udpSocket != nullptr )
{
@@ -136,6 +126,7 @@ void ProviderUdp::close()
// Everything is OK -> device is closed
}
}
return retval;
}
int ProviderUdp::writeBytes(const unsigned size, const uint8_t * data)

View File

@@ -1,13 +1,15 @@
#pragma once
#ifndef PROVIDERUDP_H
#define PROVIDERUDP_H
// LedDevice includes
#include <leddevice/LedDevice.h>
// Hyperion includes
#include <leddevice/LedDevice.h>
#include <utils/Logger.h>
// qt
// Qt includes
#include <QHostAddress>
class QUdpSocket;
#include <QUdpSocket>
///
/// The ProviderUdp implements an abstract base-class for LedDevices using UDP packets.
@@ -15,53 +17,49 @@ class QUdpSocket;
class ProviderUdp : public LedDevice
{
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an UDP LED-device
///
ProviderUdp();
///
/// Destructor of the LedDevice; closes the output device if it is open
/// @brief Destructor of the UDP LED-device
///
virtual ~ProviderUdp() override;
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
public slots:
///
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
///
virtual void close() override;
protected:
///
/// Initialise device's network details
/// @brief Initialise the UDP device's configuration and network address details
///
/// @return True if success
bool initNetwork();
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// Opens and configures the output device
/// @brief Opens the output device.
///
/// @return Zero on succes else negative
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
virtual int open() override;
///
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
/// @brief Closes the UDP device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual int close() override;
///
/// @brief Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
/// values are latched.
///
/// @param[in] size The length of the data
/// @param[in] data The data
///
/// @return Zero on succes else negative
/// @return Zero on success, else negative
///
int writeBytes(const unsigned size, const uint8_t *data);
@@ -71,3 +69,5 @@ protected:
ushort _port;
QString _defaultHost;
};
#endif // PROVIDERUDP_H

View File

@@ -12,6 +12,9 @@
// Local Hyperion includes
#include "ProviderUdpSSL.h"
const int MAX_RETRY = 5;
const ushort MAX_PORT_SSL = 65535;
ProviderUdpSSL::ProviderUdpSSL()
: LedDevice()
, client_fd()
@@ -29,16 +32,16 @@ ProviderUdpSSL::ProviderUdpSSL()
, _server_name()
, _psk()
, _psk_identity()
, _read_timeout(0)
, _handshake_timeout_min(400)
, _handshake_timeout_max(1000)
, _read_timeout(STREAM_SSL_READ_TIMEOUT.count())
, _handshake_timeout_min(STREAM_SSL_HANDSHAKE_TIMEOUT_MIN.count())
, _handshake_timeout_max(STREAM_SSL_HANDSHAKE_TIMEOUT_MAX.count())
, _handshake_attempts(5)
, _retry_left(MAX_RETRY)
, _stopConnection(true)
, _debugStreamer(false)
, _debugLevel(0)
{
_deviceReady = false;
_isDeviceReady = false;
_latchTime_ms = 1;
}
@@ -48,114 +51,131 @@ ProviderUdpSSL::~ProviderUdpSSL()
bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
bool isInitOK = false;
_debugStreamer = deviceConfig["debugStreamer"].toBool(false);
_debugLevel = deviceConfig["debugLevel"].toString().toInt(0);
//PSK Pre Shared Key
_psk = deviceConfig["psk"].toString();
_psk_identity = deviceConfig["psk_identity"].toString();
_port = deviceConfig["sslport"].toInt(2100);
_server_name = deviceConfig["servername"].toString();
if( deviceConfig.contains("transport_type") ) _transport_type = deviceConfig["transport_type"].toString("DTLS");
if( deviceConfig.contains("seed_custom") ) _custom = deviceConfig["seed_custom"].toString("dtls_client");
if( deviceConfig.contains("retry_left") ) _retry_left = deviceConfig["retry_left"].toInt(MAX_RETRY);
if( deviceConfig.contains("read_timeout") ) _read_timeout = deviceConfig["read_timeout"].toInt(0);
if( deviceConfig.contains("hs_timeout_min") ) _handshake_timeout_min = deviceConfig["hs_timeout_min"].toInt(400);
if( deviceConfig.contains("hs_timeout_max") ) _handshake_timeout_max = deviceConfig["hs_timeout_max"].toInt(1000);
if( deviceConfig.contains("hs_attempts") ) _handshake_attempts = deviceConfig["hs_attempts"].toInt(5);
QString host = deviceConfig["host"].toString(_defaultHost);
QStringList debugLevels = QStringList() << "No Debug" << "Error" << "State Change" << "Informational" << "Verbose";
configLog( "SSL Streamer Debug", "%s", ( _debugStreamer ) ? "yes" : "no" );
configLog( "SSL DebugLevel", "[%d] %s", _debugLevel, QSTRING_CSTR( debugLevels[ _debugLevel ]) );
configLog( "SSL Servername", "%s", QSTRING_CSTR( _server_name ) );
configLog( "SSL Host", "%s", QSTRING_CSTR( host ) );
configLog( "SSL Port", "%d", _port );
configLog( "PSK", "%s", QSTRING_CSTR( _psk ) );
configLog( "PSK-Identity", "%s", QSTRING_CSTR( _psk_identity ) );
configLog( "SSL Transport Type", "%s", QSTRING_CSTR( _transport_type ) );
configLog( "SSL Seed Custom", "%s", QSTRING_CSTR( _custom ) );
configLog( "SSL Retry Left", "%d", _retry_left );
configLog( "SSL Read Timeout", "%d", _read_timeout );
configLog( "SSL Handshake Timeout min", "%d", _handshake_timeout_min );
configLog( "SSL Handshake Timeout max", "%d", _handshake_timeout_max );
configLog( "SSL Handshake attempts", "%d", _handshake_attempts );
if ( _address.setAddress(host) )
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR( host ) );
}
else
{
Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR( host ) );
QHostInfo info = QHostInfo::fromName(host);
if ( info.addresses().isEmpty() )
_debugStreamer = deviceConfig["debugStreamer"].toBool(false);
_debugLevel = deviceConfig["debugLevel"].toString().toInt(0);
//PSK Pre Shared Key
_psk = deviceConfig["psk"].toString();
_psk_identity = deviceConfig["psk_identity"].toString();
_port = deviceConfig["sslport"].toInt(2100);
_server_name = deviceConfig["servername"].toString();
if( deviceConfig.contains("transport_type") ) _transport_type = deviceConfig["transport_type"].toString("DTLS");
if( deviceConfig.contains("seed_custom") ) _custom = deviceConfig["seed_custom"].toString("dtls_client");
if( deviceConfig.contains("retry_left") ) _retry_left = deviceConfig["retry_left"].toInt(MAX_RETRY);
if( deviceConfig.contains("read_timeout") ) _read_timeout = deviceConfig["read_timeout"].toInt(0);
if( deviceConfig.contains("hs_timeout_min") ) _handshake_timeout_min = deviceConfig["hs_timeout_min"].toInt(400);
if( deviceConfig.contains("hs_timeout_max") ) _handshake_timeout_max = deviceConfig["hs_timeout_max"].toInt(1000);
if( deviceConfig.contains("hs_attempts") ) _handshake_attempts = deviceConfig["hs_attempts"].toInt(5);
QString host = deviceConfig["host"].toString(_defaultHost);
QStringList debugLevels = QStringList() << "No Debug" << "Error" << "State Change" << "Informational" << "Verbose";
configLog( "SSL Streamer Debug", "%s", ( _debugStreamer ) ? "yes" : "no" );
configLog( "SSL DebugLevel", "[%d] %s", _debugLevel, QSTRING_CSTR( debugLevels[ _debugLevel ]) );
configLog( "SSL Servername", "%s", QSTRING_CSTR( _server_name ) );
configLog( "SSL Host", "%s", QSTRING_CSTR( host ) );
configLog( "SSL Port", "%d", _port );
configLog( "PSK", "%s", QSTRING_CSTR( _psk ) );
configLog( "PSK-Identity", "%s", QSTRING_CSTR( _psk_identity ) );
configLog( "SSL Transport Type", "%s", QSTRING_CSTR( _transport_type ) );
configLog( "SSL Seed Custom", "%s", QSTRING_CSTR( _custom ) );
configLog( "SSL Retry Left", "%d", _retry_left );
configLog( "SSL Read Timeout", "%d", _read_timeout );
configLog( "SSL Handshake Timeout min", "%d", _handshake_timeout_min );
configLog( "SSL Handshake Timeout max", "%d", _handshake_timeout_max );
configLog( "SSL Handshake attempts", "%d", _handshake_attempts );
if ( _address.setAddress(host) )
{
Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR( host ) );
QString errortext = QString("Invalid target address [%1]!").arg(host);
Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR( host ) );
}
else
{
Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR( host ) );
QHostInfo info = QHostInfo::fromName(host);
if ( info.addresses().isEmpty() )
{
Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR( host ) );
QString errortext = QString("Invalid target address [%1]!").arg(host);
this->setInError( errortext );
isInitOK = false;
}
else
{
Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR( host ) );
_address = info.addresses().first();
}
}
int config_port = deviceConfig["sslport"].toInt(_port);
if ( config_port <= 0 || config_port > MAX_PORT_SSL )
{
QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
this->setInError( errortext );
isInitOK = false;
}
else
{
Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR( host ) );
_address = info.addresses().first();
_ssl_port = config_port;
Debug( _log, "UDP SSL using %s:%u", QSTRING_CSTR( _address.toString() ), _ssl_port );
isInitOK = true;
}
}
int config_port = deviceConfig["sslport"].toInt(_port);
if ( config_port <= 0 || config_port > MAX_PORT_SSL )
{
QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
this->setInError( errortext );
isInitOK = false;
}
else
{
_ssl_port = config_port;
Debug( _log, "UDP SSL using %s:%u", QSTRING_CSTR( _address.toString() ), _ssl_port );
}
return isInitOK;
}
int ProviderUdpSSL::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
_isDeviceReady = false;
if ( init(_devConfig) )
// TODO: Question: Just checking .... Is this one time initialisation or required with every open request (during switch-off/switch-on)?
// In case one time initialisation, it should go to the init method.
// Everything that is required to pen a UDP-SSL connection again (after it maybe was closed remotely should go here)
if ( !initNetwork() )
{
if ( !initNetwork() )
{
this->setInError( "UDP SSL Network error!" );
}
else
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
retval = 0;
}
this->setInError( "UDP SSL Network error!" );
}
else
{
// Everything is OK -> enable device
_isDeviceReady = true;
retval = 0;
}
return retval;
}
void ProviderUdpSSL::close()
int ProviderUdpSSL::close()
{
LedDevice::close();
closeSSLConnection();
// LedDevice specific closing activities
int retval = 0;
_isDeviceReady = false;
// TODO: You may want to check, if the device is already closed or close it and return, if ok or not
// Test, if device requires closing
if ( true /*If device is still open*/ )
{
// Close device
LedDevice::close();
closeSSLConnection();
// Everything is OK -> device is closed
}
return retval;
}
void ProviderUdpSSL::closeSSLConnection()
{
if( _deviceReady && !_stopConnection )
if( _isDeviceReady && !_stopConnection )
{
closeSSLNotify();
freeSSLConnection();
@@ -412,7 +432,7 @@ bool ProviderUdpSSL::startSSLHandshake()
void ProviderUdpSSL::freeSSLConnection()
{
sslLog( "SSL Connection cleanup..." );
sslLog( "SSL Connection clean-up..." );
_stopConnection = true;
@@ -425,15 +445,15 @@ void ProviderUdpSSL::freeSSLConnection()
mbedtls_x509_crt_free(&cacert);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
sslLog( "SSL Connection cleanup...ok" );
sslLog( "SSL Connection clean-up...ok" );
}
catch (std::exception &e)
{
sslLog( QString("SSL Connection cleanup Error: %s").arg( e.what() ) );
sslLog( QString("SSL Connection clean-up Error: %s").arg( e.what() ) );
}
catch (...)
{
sslLog( "SSL Connection cleanup Error: <unknown>" );
sslLog( "SSL Connection clean-up Error: <unknown>" );
}
}

View File

@@ -1,4 +1,5 @@
#pragma once
#ifndef PROVIDERUDPSSL_H
#define PROVIDERUDPSSL_H
#include <leddevice/LedDevice.h>
#include <utils/Logger.h>
@@ -36,6 +37,7 @@
#include <string.h>
#include <cstring>
#include <chrono>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl_ciphersuites.h>
@@ -45,12 +47,11 @@
#include <mbedtls/error.h>
#include <mbedtls/debug.h>
#define READ_TIMEOUT_MS 1000
#define MAX_RETRY 5
//----------- END mbedtls
const ushort MAX_PORT_SSL = 65535;
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
{
@@ -58,44 +59,45 @@ class ProviderUdpSSL : public LedDevice
public:
///
/// Constructs specific LedDevice
/// @brief Constructs an UDP SSL LED-device
///
ProviderUdpSSL();
///
/// Destructor of the LedDevice; closes the output device if it is open
/// @brief Destructor of the LED-device
///
virtual ~ProviderUdpSSL() override;
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
public slots:
///
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
///
virtual void close() override;
protected:
///
/// Initialise device's network details
/// @brief Initialise the UDP-SSL device's configuration and network address details
///
/// @return True if success
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success#endif // PROVIDERUDP_H
///
bool initNetwork();
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// Opens and configures the output device
/// @brief Opens the output device.
///
/// @return Zero on succes else negative
/// @return Zero on success (i.e. device is ready), else negative
///
int open() override;
virtual int open() override;
///
/// @brief Closes the output device.
///
/// @return Zero on success (i.e. device is closed), else negative
///
virtual int close() override;
///
/// @brief Initialise device's network details
///
/// @return True, if success
///
bool initNetwork();
///
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
@@ -208,3 +210,5 @@ private:
bool _debugStreamer;
int _debugLevel;
};
#endif // PROVIDERUDPSSL_H