mDNS Support (#1452)

* Allow build, if no grabbers are enabled

* Align available functions to right Qt version

* Update to next development version

* Align available functions to right Qt version

* fix workflows (apt/nightly)

* Disable QNetworkConfigurationManager deprecation warnings

* Initial go on Smart Pointers

* Add Deallocation

* Correct QT_WARNING_DISABLE_DEPRECATED (available since 5.9)

* Cluster Build Variables

* Hyperion Light

* Address build warnings

* Hyperion Light - UI

* Update Protobuf to latest master

* Removed compiler warnings

* Added restart ability to systray

* Correct Protobuf

* Ignore 'no-return' warning on protobuf build

* hyperion-remote: Fix auto discovery of hyperion server

* Fix Qt version override

* Update changelog

* Remove Grabber Components, if no Grabber exists

* Standalone Grabber - Fix fps default

* Remote Control - Have Source Selction accrosswhole screen

* Enable Blackborder detection only, if relevant input sources available

* Enable Blackborder detection only, if relevant input sources available

* Remote UI - rearrange containers

* Checkout

* Fix compilation on windows

* Re-added qmdnsengine template cmake

* chrono added for linux

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

* hyperiond macos typo fix

* Fix macOS Bundle build

* Fix macOS bundle info details

* Correct CMake files

* Removed existing AVAHI/Bonjour (2)

* Share hyperion's services via mDNS

* Add mDNS Browser and mDNS for LED-Devices

* Support mDNS discovery for standalone grabbers

* Remove ZLib Dependency & Cleanup

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

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

* Fix save button state when switching between devices

* Removed sessions (of other hyperions)

* mDNS Publisher - Simplify service naming

* mDNS refactoring & Forwarder discovery

* mDNS Updates to use device service name

* Consistency of standalone grabbers with mDNS Service Registry

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

* Start JSON and WebServers only after Instance 0 is available

* Remove bespoke qDebug Output again

* MDNS updates and refactor Forwarder

* Minor updates

* Upgrade to CMake 3.1

* typo

* macOS fix

* Correct merge

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

* Cec handler library will load at runtime

* typo fix

* protobuf changes

* mDNS changes for Windows/macOS

* test window build qmdnsengine

* absolute path to protobuf cmake dir

* Rework Hue Wizard supporting mDNS

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

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

* Address LGTM findings

* Fix CI-Build, revert test changes

* Build Windows in Release mode to avoid python problem

* Correct that WebServerObject is available earlier

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

* Update content LEDs

* Rework mDNS Address lookup

* Fix LED UI

* Fix for non mDNS Services (ignore default port)

* Disbale device when now input is available

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

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

* Add UPD-DDP Device

* WLED support for DDP

* Fix printout

* LEDDevice - Allow more retries, udapte defaults

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#include "LedDeviceYeelight.h"
#include "LedDeviceYeelight.h"
#include <ssdp/SSDPDiscover.h>
#include <utils/QStringUtils.h>
#include <chrono>
#include <thread>
// Qt includes
#include <QEventLoop>
@@ -11,8 +11,15 @@
#include <QColor>
#include <QDateTime>
#include <chrono>
#include <thread>
#include <ssdp/SSDPDiscover.h>
#include <utils/QStringUtils.h>
// mDNS discover
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#include <mdns/MdnsServiceRegister.h>
#endif
#include <utils/NetUtils.h>
// Constants
namespace {
@@ -28,6 +35,9 @@ constexpr std::chrono::milliseconds CONNECT_STREAM_TIMEOUT{1000}; // device stre
const bool TEST_CORRELATION_IDS = false; //Ignore, if yeelight sends responses in different order as request commands
// Configuration settings
const char CONFIG_HOST[] = "host";
const char CONFIG_PORT[] = "port";
const char CONFIG_LIGHTS [] = "lights";
const char CONFIG_COLOR_MODEL [] = "colorModel";
@@ -111,7 +121,6 @@ YeelightLight::YeelightLight( Logger *log, const QString &hostname, quint16 port
,_isInMusicMode(false)
{
_name = hostname;
}
YeelightLight::~YeelightLight()
@@ -151,25 +160,29 @@ bool YeelightLight::open()
}
else
{
_tcpSocket->connectToHost( _host, _port);
if ( _tcpSocket->waitForConnected( CONNECT_TIMEOUT.count() ) )
QHostAddress address;
if (NetUtils::resolveHostToAddress(_log, _host, address))
{
if ( _tcpSocket->state() != QAbstractSocket::ConnectedState )
_tcpSocket->connectToHost( address.toString(), _port);
if ( _tcpSocket->waitForConnected( CONNECT_TIMEOUT.count() ) )
{
if ( _tcpSocket->state() != QAbstractSocket::ConnectedState )
{
this->setInError( _tcpSocket->errorString() );
rc = false;
}
else
{
log (2,"open()","Successfully opened Yeelight: %s", QSTRING_CSTR(_host));
rc = true;
}
}
else
{
this->setInError( _tcpSocket->errorString() );
rc = false;
}
else
{
log (2,"open()","Successfully opened Yeelight: %s", QSTRING_CSTR(_host));
rc = true;
}
}
else
{
this->setInError( _tcpSocket->errorString() );
rc = false;
}
}
return rc;
@@ -1006,6 +1019,10 @@ LedDeviceYeelight::LedDeviceYeelight(const QJsonObject &deviceConfig)
,_debuglevel(0)
,_musicModeServerPort(-1)
{
#ifdef ENABLE_MDNS
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType)));
#endif
}
LedDeviceYeelight::~LedDeviceYeelight()
@@ -1034,12 +1051,6 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
if ( LedDevice::init(deviceConfig) )
{
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
Debug(_log, "LedCount : %d", this->getLedCount());
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
Debug(_log, "RewriteTime : %d", this->getRewriteTime());
Debug(_log, "LatchTime : %d", this->getLatchTime());
//Get device specific configuration
if ( deviceConfig[ CONFIG_COLOR_MODEL ].isString() )
@@ -1102,8 +1113,9 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
int configuredYeelightsCount = 0;
for (const QJsonValueRef light : configuredYeelightLights)
{
QString hostName = light.toObject().value("host").toString();
int port = light.toObject().value("port").toInt(API_DEFAULT_PORT);
QString hostName = light.toObject().value(CONFIG_HOST).toString();
int port = light.toObject().value(CONFIG_PORT).toInt(API_DEFAULT_PORT);
if ( !hostName.isEmpty() )
{
QString name = light.toObject().value("name").toString();
@@ -1133,9 +1145,8 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
_lightsAddressList.clear();
for (int j = 0; j < static_cast<int>( configuredLedCount ); ++j)
{
QString hostName = configuredYeelightLights[j].toObject().value("host").toString();
int port = configuredYeelightLights[j].toObject().value("port").toInt(API_DEFAULT_PORT);
QString hostName = configuredYeelightLights[j].toObject().value(CONFIG_HOST).toString();
int port = configuredYeelightLights[j].toObject().value(CONFIG_PORT).toInt(API_DEFAULT_PORT);
_lightsAddressList.append( { hostName, port} );
}
@@ -1160,13 +1171,10 @@ bool LedDeviceYeelight::startMusicModeServer()
if ( ! _tcpMusicModeServer->isListening() )
{
if (! _tcpMusicModeServer->listen())
if (! _tcpMusicModeServer->listen(QHostAddress::AnyIPv4))
{
QString errorReason = QString ("(%1) %2").arg(_tcpMusicModeServer->serverError()).arg( _tcpMusicModeServer->errorString());
Error( _log, "Error: MusicModeServer: %s", QSTRING_CSTR(errorReason));
QString errorReason = QString ("Failed to start music mode server: (%1) %2").arg(_tcpMusicModeServer->serverError()).arg( _tcpMusicModeServer->errorString());
this->setInError ( errorReason );
Error( _log, "Failed to start music mode server");
}
else
{
@@ -1182,12 +1190,14 @@ bool LedDeviceYeelight::startMusicModeServer()
}
if (_musicModeServerAddress.isNull())
{
Error(_log, "Failed to resolve IP for music mode server");
_tcpMusicModeServer->close();
QString errorReason = QString ("Network error - failed to resolve IP for music mode server");
this->setInError ( errorReason );
}
}
}
if ( _tcpMusicModeServer->isListening() )
if ( !_isDeviceInError && _tcpMusicModeServer->isListening() )
{
_musicModeServerPort = _tcpMusicModeServer->serverPort();
Debug (_log, "The music mode server is running at %s:%d", QSTRING_CSTR(_musicModeServerAddress.toString()), _musicModeServerPort);
@@ -1391,10 +1401,21 @@ QJsonObject LedDeviceYeelight::discover(const QJsonObject& /*params*/)
QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
QString discoveryMethod("ssdp");
QJsonArray deviceList;
deviceList = discover();
#ifdef ENABLE_MDNS
QString discoveryMethod("mDNS");
deviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(
MdnsServiceRegister::getServiceType(_activeDeviceType),
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
DEFAULT_DISCOVER_TIMEOUT
);
#else
QString discoveryMethod("ssdp");
deviceList = discover();
#endif
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
devicesDiscovered.insert("devices", deviceList);
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
@@ -1407,21 +1428,22 @@ QJsonObject LedDeviceYeelight::getProperties(const QJsonObject& params)
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
QJsonObject properties;
QString hostName = params["hostname"].toString("");
quint16 apiPort = static_cast<quint16>( params["port"].toInt(API_DEFAULT_PORT) );
QString hostName = params[CONFIG_HOST].toString("");
quint16 apiPort = static_cast<quint16>( params[CONFIG_PORT].toInt(API_DEFAULT_PORT) );
if ( !hostName.isEmpty() )
Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(hostName) );
QHostAddress address;
if (NetUtils::resolveHostToAddress(_log, hostName, address))
{
YeelightLight yeelight(_log, hostName, apiPort);
//yeelight.setDebuglevel(3);
YeelightLight yeelight(_log, address.toString(), apiPort);
if ( yeelight.open() )
{
properties.insert("properties", yeelight.getProperties());
yeelight.close();
}
}
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
return properties;
}
@@ -1430,15 +1452,15 @@ void LedDeviceYeelight::identify(const QJsonObject& params)
{
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
QString hostName = params["hostname"].toString("");
quint16 apiPort = static_cast<quint16>( params["port"].toInt(API_DEFAULT_PORT) );
Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(hostName), apiPort);
QString hostName = params[CONFIG_HOST].toString("");
quint16 apiPort = static_cast<quint16>( params[CONFIG_PORT].toInt(API_DEFAULT_PORT) );
if ( !hostName.isEmpty() )
Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(hostName) );
QHostAddress address;
if (NetUtils::resolveHostToAddress(_log, hostName, address))
{
YeelightLight yeelight(_log, hostName, apiPort);
//yeelight.setDebuglevel(3);
YeelightLight yeelight(_log, address.toString(), apiPort);
if ( yeelight.open() )
{
yeelight.identify();

View File

@@ -449,8 +449,8 @@ public:
/// Following parameters are required
/// @code
/// {
/// "hostname" : "hostname or IP",
/// "port" : port, default port 55443 is used when not provided
/// "host" : "hostname or IP",
/// "port" : port, default port 55443 is used when not provided
/// }
///@endcode
///
@@ -465,8 +465,8 @@ public:
/// Following parameters are required
/// @code
/// {
/// "hostname" : "hostname or IP",
/// "port" : port, default port 55443 is used when not provided
/// "host" : "hostname or IP",
/// "port" : port, default port 55443 is used when not provided
/// }
///@endcode
///

View File

@@ -136,13 +136,19 @@ httpResponse ProviderRestApi::get(const QUrl& url)
QNetworkRequest request(_networkRequestHeaders);
request.setUrl(url);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
#endif
QNetworkReply* reply = _networkManager->get(request);
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
QEventLoop::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
#endif
// Go into the loop until the request is finished.
loop.exec();
@@ -178,12 +184,18 @@ httpResponse ProviderRestApi::put(const QUrl &url, const QByteArray &body)
QNetworkRequest request(_networkRequestHeaders);
request.setUrl(url);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
#endif
QNetworkReply* reply = _networkManager->put(request, body);
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
QEventLoop::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
#endif
// Go into the loop until the request is finished.
loop.exec();
@@ -220,10 +232,19 @@ httpResponse ProviderRestApi::post(const QUrl& url, const QByteArray& body)
QNetworkRequest request(_networkRequestHeaders);
request.setUrl(url);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
#endif
QNetworkReply* reply = _networkManager->post(request, body);
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
QEventLoop::connect(reply,&QNetworkReply::finished,&loop,&QEventLoop::quit);
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
#endif
// Go into the loop until the request is finished.
loop.exec();
@@ -249,6 +270,10 @@ httpResponse ProviderRestApi::deleteResource(const QUrl& url)
QNetworkRequest request(_networkRequestHeaders);
request.setUrl(url);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
_networkManager->setTransferTimeout(DEFAULT_REST_TIMEOUT.count());
#endif
QNetworkReply* reply = _networkManager->deleteResource(request);
// Connect requestFinished signal to quit slot of the loop.
QEventLoop loop;
@@ -256,6 +281,10 @@ httpResponse ProviderRestApi::deleteResource(const QUrl& url)
// Go into the loop until the request is finished.
loop.exec();
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
#endif
httpResponse response;
if (reply->operation() == QNetworkAccessManager::DeleteOperation)
{
@@ -331,16 +360,9 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
}
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
}
else {
else
{
errorReason = reply->errorString();
if ( reply->error() == QNetworkReply::OperationCanceledError )
{
//Do not report errors caused by request cancellation because of timeouts
Debug(_log, "Reply: [%s]", QSTRING_CSTR(errorReason) );
}
else
{
response.setError(true);
response.setErrorReason(errorReason);

View File

@@ -77,12 +77,12 @@ public:
private:
QJsonDocument _responseBody;
QJsonDocument _responseBody {};
bool _hasError = false;
QString _errorReason;
int _httpStatusCode = 0;
QNetworkReply::NetworkError _networkReplyError = QNetworkReply::NoError;
QNetworkReply::NetworkError _networkReplyError { QNetworkReply::NoError };
};
///
@@ -291,6 +291,13 @@ public:
///
void removeAllHeaders() { _networkRequestHeaders = QNetworkRequest(); }
///
/// @brief Set the common logger for LED-devices.
///
/// @param[in] log The logger to be used
///
void setLogger(Logger* log) { _log = log; }
private:
///

View File

@@ -10,16 +10,19 @@
#include <QUdpSocket>
#include <QHostInfo>
// mDNS discover
#ifdef ENABLE_MDNS
#include <mdns/MdnsBrowser.h>
#endif
#include <utils/NetUtils.h>
// Local Hyperion includes
#include "ProviderUdp.h"
const ushort MAX_PORT = 65535;
ProviderUdp::ProviderUdp(const QJsonObject& deviceConfig)
: LedDevice(deviceConfig)
, _udpSocket(nullptr)
, _port(1)
, _defaultHost("127.0.0.1")
, _port(-1)
{
_latchTime_ms = 0;
}
@@ -29,83 +32,38 @@ ProviderUdp::~ProviderUdp()
delete _udpSocket;
}
bool ProviderUdp::init(const QJsonObject& deviceConfig)
{
bool isInitOK = false;
// Initialise sub-class
if (LedDevice::init(deviceConfig))
{
QString host = deviceConfig["host"].toString(_defaultHost);
if (_address.setAddress(host))
{
Debug(_log, "Successfully parsed %s as an IP-address.", QSTRING_CSTR(_address.toString()));
}
else
{
QHostInfo hostInfo = QHostInfo::fromName(host);
if (hostInfo.error() == QHostInfo::NoError)
{
_address = hostInfo.addresses().first();
Debug(_log, "Successfully resolved IP-address (%s) for hostname (%s).", QSTRING_CSTR(_address.toString()), QSTRING_CSTR(host));
}
else
{
QString errortext = QString("Failed resolving IP-address for [%1], (%2) %3").arg(host).arg(hostInfo.error()).arg(hostInfo.errorString());
this->setInError(errortext);
isInitOK = false;
}
}
if (!_isDeviceInError)
{
int config_port = deviceConfig["port"].toInt(_port);
if (config_port <= 0 || config_port > MAX_PORT)
{
QString errortext = QString("Invalid target port [%1]!").arg(config_port);
this->setInError(errortext);
isInitOK = false;
}
else
{
_port = static_cast<quint16>(config_port);
Debug(_log, "UDP socket will write to %s port: %u", QSTRING_CSTR(_address.toString()), _port);
_udpSocket = new QUdpSocket(this);
isInitOK = true;
}
}
}
return isInitOK;
}
int ProviderUdp::open()
{
int retval = -1;
_isDeviceReady = false;
// Try to bind the UDP-Socket
if (_udpSocket != nullptr)
if (!_isDeviceInError)
{
if (_udpSocket->state() != QAbstractSocket::BoundState)
{
QHostAddress localAddress = QHostAddress::Any;
quint16 localPort = 0;
if (!_udpSocket->bind(localAddress, localPort))
if (_udpSocket == nullptr)
{
QString warntext = QString("Could not bind local address: %1, (%2) %3").arg(localAddress.toString()).arg(_udpSocket->error()).arg(_udpSocket->errorString());
Warning(_log, "%s", QSTRING_CSTR(warntext));
_udpSocket = new QUdpSocket(this);
}
// Try to bind the UDP-Socket
if (_udpSocket != nullptr)
{
Info(_log, "Stream UDP data to %s port: %d", QSTRING_CSTR(_address.toString()), _port);
if (_udpSocket->state() != QAbstractSocket::BoundState)
{
QHostAddress localAddress = QHostAddress::Any;
quint16 localPort = 0;
if (!_udpSocket->bind(localAddress, localPort))
{
QString warntext = QString("Could not bind local address: %1, (%2) %3").arg(localAddress.toString()).arg(_udpSocket->error()).arg(_udpSocket->errorString());
Warning(_log, "%s", QSTRING_CSTR(warntext));
}
}
retval = 0;
}
else
{
this->setInError(" Open error. UDP Socket not initialised!");
}
}
// Everything is OK, device is ready
_isDeviceReady = true;
retval = 0;
}
else
{
this->setInError(" Open error. UDP Socket not initialised!");
}
return retval;
}
@@ -131,7 +89,7 @@ int ProviderUdp::close()
int ProviderUdp::writeBytes(const unsigned size, const uint8_t* data)
{
int rc = 0;
qint64 bytesWritten = _udpSocket->writeDatagram(reinterpret_cast<const char*>(data), size, _address, _port);
qint64 bytesWritten = _udpSocket->writeDatagram(reinterpret_cast<const char*>(data), size, _address, static_cast<quint16>(_port));
if (bytesWritten == -1 || bytesWritten != size)
{
@@ -144,7 +102,7 @@ int ProviderUdp::writeBytes(const unsigned size, const uint8_t* data)
int ProviderUdp::writeBytes(const QByteArray& bytes)
{
int rc = 0;
qint64 bytesWritten = _udpSocket->writeDatagram(bytes, _address, _port);
qint64 bytesWritten = _udpSocket->writeDatagram(bytes, _address, static_cast<quint16>(_port));
if (bytesWritten == -1 || bytesWritten != bytes.size())
{

View File

@@ -32,14 +32,6 @@ public:
protected:
///
/// @brief Initialise the UDP device's configuration and network address details
///
/// @param[in] deviceConfig the JSON device configuration
/// @return True, if success
///
bool init(const QJsonObject& deviceConfig) override;
///
/// @brief Opens the output device.
///
@@ -74,10 +66,10 @@ protected:
int writeBytes(const QByteArray& bytes);
///
QUdpSocket* _udpSocket;
QUdpSocket* _udpSocket;
QString _hostName;
QHostAddress _address;
quint16 _port;
QString _defaultHost;
int _port;
};
#endif // PROVIDERUDP_H

File diff suppressed because it is too large Load Diff

View File

@@ -23,19 +23,6 @@
#if defined(MBEDTLS_PLATFORM_C)
#include <mbedtls/platform.h>
#else
#include <stdio.h>
#include <stdlib.h>
#define mbedtls_time time
#define mbedtls_time_t time_t
#define mbedtls_printf printf
#define mbedtls_fprintf fprintf
#define mbedtls_snprintf snprintf
#define mbedtls_calloc calloc
#define mbedtls_free free
#define mbedtls_exit exit
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif
#include <string.h>
@@ -50,12 +37,6 @@
#include <mbedtls/error.h>
#include <mbedtls/debug.h>
//----------- END mbedtls
constexpr std::chrono::milliseconds STREAM_SSL_HANDSHAKE_TIMEOUT_MIN{400};
constexpr std::chrono::milliseconds STREAM_SSL_HANDSHAKE_TIMEOUT_MAX{1000};
constexpr std::chrono::milliseconds STREAM_SSL_READ_TIMEOUT{0};
class ProviderUdpSSL : public LedDevice
{
Q_OBJECT
@@ -71,6 +52,11 @@ public:
///
~ProviderUdpSSL() override;
///
QString _hostName;
QHostAddress _address;
int _port;
protected:
///
@@ -102,6 +88,18 @@ protected:
///
bool initNetwork();
///
/// @brief Start astreaming connection
///
/// @return True, if success
///
bool startConnection();
///
/// @brief Stop the streaming connection
///
void stopConnection();
///
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
/// values are latched.
@@ -109,7 +107,7 @@ protected:
/// @param[in] size The length of the data
/// @param[in] data The data
///
void writeBytes(unsigned int size, const uint8_t *data);
void writeBytes(unsigned int size, const uint8_t* data, bool flush = false);
///
/// get ciphersuites list from mbedtls_ssl_list_ciphersuites
@@ -118,36 +116,16 @@ protected:
///
virtual const int * getCiphersuites() const;
void sslLog(const QString &msg, const char* errorType = "debug");
void sslLog(const char* msg, const char* errorType = "debug");
void configLog(const char* msg, const char* type, ...);
/**
* Debug callback for mbed TLS
* Just prints on the USB serial port
*/
static void ProviderUdpSSLDebug(void* ctx, int level, const char* file, int line, const char* str);
/**
* Certificate verification callback for mbed TLS
* Here we only use it to display information on each cert in the chain
*/
static int ProviderUdpSSLVerify(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags);
///
/// closeSSLNotify and freeSSLConnection
///
void closeSSLConnection();
private:
bool initConnection();
bool seedingRNG();
bool setupStructure();
bool startUPDConnection();
bool setupPSK();
bool startSSLHandshake();
void handleReturn(int ret);
QString errorMsg(int ret);
void closeSSLNotify();
void freeSSLConnection();
@@ -160,24 +138,19 @@ private:
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_timing_delay_context timer;
QMutex _hueMutex;
QString _transport_type;
QString _custom;
QHostAddress _address;
QString _defaultHost;
int _port;
int _ssl_port;
QString _server_name;
QString _psk;
QString _psk_identity;
uint32_t _read_timeout;
int _handshake_attempts;
uint32_t _handshake_timeout_min;
uint32_t _handshake_timeout_max;
unsigned int _handshake_attempts;
int _retry_left;
bool _stopConnection;
bool _debugStreamer;
int _debugLevel;
bool _streamReady;
bool _streamPaused;
};
#endif // PROVIDERUDPSSL_H

View File

@@ -79,4 +79,4 @@ private:
};
#endif // LEDEVICETEMPLATE_H
#endif // LEDEVICEPIBLASTER_H

View File

@@ -44,13 +44,6 @@ bool ProviderRs232::init(const QJsonObject &deviceConfig)
// Initialise sub-class
if ( LedDevice::init(deviceConfig) )
{
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
Debug(_log, "LedCount : %d", this->getLedCount());
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
Debug(_log, "LatchTime : %d", this->getLatchTime());
_deviceName = deviceConfig["output"].toString("auto");
_isAutoDeviceName = _deviceName.toLower() == "auto";
@@ -89,7 +82,6 @@ int ProviderRs232::open()
{
int retval = -1;
_isDeviceReady = false;
_isInSwitchOff = false;
// open device physically
if ( tryOpen(_delayAfterConnect_ms) )
@@ -190,18 +182,6 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
{
QString errortext = QString("Invalid serial device name: %1 %2!").arg(_deviceName, _location);
this->setInError( errortext );
// List available device
for (auto &port : QSerialPortInfo::availablePorts() ) {
Debug(_log, "Avail. serial device: [%s]-(%s|%s), Manufacturer: %s, Description: %s",
QSTRING_CSTR(port.portName()),
QSTRING_CSTR(QString("0x%1").arg(port.vendorIdentifier(), 0, 16)),
QSTRING_CSTR(QString("0x%1").arg(port.productIdentifier(), 0, 16)),
QSTRING_CSTR(port.manufacturer()),
QSTRING_CSTR(port.description())
);
}
return false;
}
}
@@ -295,17 +275,21 @@ QString ProviderRs232::discoverFirst()
return "";
}
QJsonObject ProviderRs232::discover(const QJsonObject& /*params*/)
QJsonObject ProviderRs232::discover(const QJsonObject& params)
{
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject devicesDiscovered;
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
QJsonArray deviceList;
bool showAll = params["discoverAll"].toBool(false);
// Discover serial Devices
for (auto &port : QSerialPortInfo::availablePorts() )
{
if ( !port.isNull() && port.vendorIdentifier() != 0)
if ( !port.isNull() && (showAll || port.vendorIdentifier() != 0) )
{
QJsonObject portInfo;
portInfo.insert("description", port.description());
@@ -364,6 +348,8 @@ void ProviderRs232::identify(const QJsonObject& params)
QString deviceName = params["output"].toString("");
if (!deviceName.isEmpty())
{
Info(_log, "Identify %s, device: %s", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(deviceName) );
_devConfig = params;
init(_devConfig);
{

View File

@@ -74,12 +74,22 @@ protected:
bool powerOff() override;
///
/// @brief Discover first devices of a serial device available (for configuration)
/// @brief Discover first device of serial devices available (for configuration)
///
/// @return A string of the device found
///
QString discoverFirst() override;
///
/// @brief Discover serial devices available (for configuration).
///
/// Following parameters can be provided optional
/// @code
/// {
/// "discoverAll" : true/false , "true", in case devices without vendor-id are to be included in the discovery result
/// }
///@endcode
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found

View File

@@ -37,7 +37,6 @@ ProviderSpi::ProviderSpi(const QJsonObject &deviceConfig)
{
memset(&_spi, 0, sizeof(_spi));
_latchTime_ms = 1;
_isInSwitchOff = false;
}
ProviderSpi::~ProviderSpi()
@@ -69,7 +68,6 @@ int ProviderSpi::open()
int retval = -1;
QString errortext;
_isDeviceReady = false;
_isInSwitchOff = false;
const int bitsPerWord = 8;

View File

@@ -37,43 +37,74 @@
},
"useEntertainmentAPI": {
"type": "boolean",
"format": "checkbox",
"title": "edt_dev_spec_useEntertainmentAPI_title",
"default": true,
"propertyOrder": 5
},
"transitiontime": {
"type": "number",
"title": "edt_dev_spec_transistionTime_title",
"default": 1,
"append": "x100ms",
"options": {
"dependencies": {
"useEntertainmentAPI": false
}
},
"propertyOrder": 6
},
"switchOffOnBlack": {
"type": "boolean",
"format": "checkbox",
"title": "edt_dev_spec_switchOffOnBlack_title",
"default": false,
"propertyOrder": 7
"propertyOrder": 6
},
"restoreOriginalState": {
"type": "boolean",
"format": "checkbox",
"title": "edt_dev_spec_restoreOriginalState_title",
"default": true,
"default": false,
"propertyOrder": 7
},
"blackLevel": {
"type": "number",
"format": "stepper",
"title": "edt_dev_spec_brightnessThreshold_title",
"default": 0.009,
"step": 0.01,
"minimum": 0.001,
"maximum": 1.0,
"propertyOrder": 8
},
"onBlackTimeToPowerOff": {
"type": "integer",
"format": "stepper",
"step": 50,
"title": "edt_dev_spec_onBlackTimeToPowerOff",
"append": "edt_append_ms",
"minimum": 100,
"maximum": 100000,
"default": 600,
"required": true,
"propertyOrder": 9
},
"onBlackTimeToPowerOn": {
"type": "integer",
"format": "stepper",
"step": 50,
"title": "edt_dev_spec_onBlackTimeToPowerOn",
"append": "edt_append_ms",
"minimum": 100,
"maximum": 100000,
"default": 300,
"required": true,
"propertyOrder": 9
},
"candyGamma": {
"type": "boolean",
"format": "checkbox",
"title": "edt_dev_spec_candyGamma_title",
"default": true,
"propertyOrder": 10
},
"lightIds": {
"type": "array",
"title": "edt_dev_spec_lightid_title",
"minimum": 1,
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1,
"required": true,
"minimum": 0,
"title": "edt_dev_spec_lightid_itemtitle"
},
"options": {
@@ -81,10 +112,12 @@
"useEntertainmentAPI": false
}
},
"propertyOrder": 9
"propertyOrder": 11
},
"groupId": {
"type": "number",
"format": "stepper",
"step": 1,
"title": "edt_dev_spec_groupId_title",
"default": 0,
"options": {
@@ -92,41 +125,11 @@
"useEntertainmentAPI": true
}
},
"propertyOrder": 10
},
"blackLightsTimeout": {
"type": "number",
"title": "edt_dev_spec_blackLightsTimeout_title",
"default": 15000,
"step": 500,
"minimum": 10000,
"maximum": 60000,
"access": "advanced",
"append": "edt_append_ms",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 11
},
"brightnessThreshold": {
"type": "number",
"title": "edt_dev_spec_brightnessThreshold_title",
"default": 0,
"step": 0.005,
"minimum": 0,
"maximum": 1.0,
"access": "advanced",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 12
},
"brightnessFactor": {
"type": "number",
"format": "stepper",
"title": "edt_dev_spec_brightnessFactor_title",
"default": 1.0,
"step": 0.25,
@@ -135,6 +138,82 @@
"access": "advanced",
"propertyOrder": 13
},
"handshakeTimeoutMin": {
"type": "number",
"format": "stepper",
"title": "edt_dev_spec_sslHSTimeoutMin_title",
"default": 600,
"step": 100,
"minimum": 100,
"maximum": 30000,
"access": "expert",
"append": "edt_append_ms",
"required": true,
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 14
},
"handshakeTimeoutMax": {
"type": "number",
"format": "stepper",
"title": "edt_dev_spec_sslHSTimeoutMax_title",
"default": 1000,
"step": 100,
"minimum": 100,
"maximum": 30000,
"access": "expert",
"append": "edt_append_ms",
"required": true,
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 15
},
"verbose": {
"type": "boolean",
"format": "checkbox",
"title": "edt_dev_spec_verbose_title",
"default": false,
"access": "expert",
"propertyOrder": 16
},
"transitiontime": {
"type": "number",
"title": "edt_dev_spec_transistionTime_title",
"default": 1,
"minimum": 0,
"maximum": 100000,
"required": true,
"append": "x100ms",
"options": {
"dependencies": {
"useEntertainmentAPI": false
}
},
"propertyOrder": 17
},
"blackLightsTimeout": {
"type": "number",
"default": 5000,
"options": {
"hidden": true
},
"propertyOrder": 18
},
"brightnessThreshold": {
"type": "number",
"title": "edt_dev_spec_brightnessThreshold_title",
"default": 0.0001,
"options": {
"hidden": true
},
"propertyOrder": 19
},
"brightnessMin": {
"type": "number",
"title": "edt_dev_spec_brightnessMin_title",
@@ -144,11 +223,9 @@
"maximum": 1.0,
"access": "advanced",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
"hidden": true
},
"propertyOrder": 14
"propertyOrder": 20
},
"brightnessMax": {
"type": "number",
@@ -159,93 +236,8 @@
"maximum": 1.0,
"access": "advanced",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
"hidden": true
},
"propertyOrder": 15
},
"sslReadTimeout": {
"type": "number",
"title": "edt_dev_spec_sslReadTimeout_title",
"default": 0,
"step": 100,
"minimum": 0,
"maximum": 30000,
"access": "expert",
"append": "edt_append_ms",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 16
},
"sslHSTimeoutMin": {
"type": "number",
"title": "edt_dev_spec_sslHSTimeoutMin_title",
"default": 400,
"step": 100,
"minimum": 0,
"maximum": 30000,
"access": "expert",
"append": "edt_append_ms",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 17
},
"sslHSTimeoutMax": {
"type": "number",
"title": "edt_dev_spec_sslHSTimeoutMax_title",
"default": 1000,
"step": 100,
"minimum": 0,
"maximum": 30000,
"access": "expert",
"append": "edt_append_ms",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 18
},
"verbose": {
"type": "boolean",
"title": "edt_dev_spec_verbose_title",
"default": false,
"access": "expert",
"propertyOrder": 19
},
"debugStreamer": {
"type": "boolean",
"title": "edt_dev_spec_debugStreamer_title",
"default": false,
"access": "expert",
"options": {
"dependencies": {
"useEntertainmentAPI": true
}
},
"propertyOrder": 20
},
"debugLevel": {
"type": "string",
"title": "edt_dev_spec_debugLevel_title",
"enum": [ "0", "1", "2", "3", "4" ],
"default": "0",
"options": {
"enum_titles": [ "edt_conf_enum_dl_nodebug", "edt_conf_enum_dl_error", "edt_conf_enum_dl_statechange", "edt_conf_enum_dl_informational", "edt_conf_enum_dl_verbose" ],
"dependencies": {
"useEntertainmentAPI": true
}
},
"minimum": 0,
"maximum": 4,
"access": "expert",
"propertyOrder": 21
}
},

View File

@@ -0,0 +1,32 @@
{
"type": "object",
"required": true,
"properties": {
"host": {
"type": "string",
"title": "edt_dev_spec_targetIpHost_title",
"format": "hostname_or_ip",
"propertyOrder": 1
},
"port": {
"type": "integer",
"title": "edt_dev_spec_port_title",
"default": 4048,
"minimum": 0,
"maximum": 65535,
"access": "expert",
"propertyOrder": 2
},
"latchTime": {
"type": "integer",
"title": "edt_dev_spec_latchtime_title",
"default": 0,
"append": "edt_append_ms",
"minimum": 0,
"maximum": 1000,
"access": "expert",
"propertyOrder": 3
}
},
"additionalProperties": true
}

View File

@@ -24,6 +24,17 @@
"required": true,
"propertyOrder": 2
},
"streamProtocol": {
"type": "string",
"title": "edt_dev_spec_stream_protocol_title",
"enum": [ "DDP", "RAW" ],
"default": "DDP",
"options": {
"enum_titles": [ "edt_conf_enum_udp_ddp", "edt_conf_enum_udp_raw" ]
},
"access": "expert",
"propertyOrder": 3
},
"restoreOriginalState": {
"type": "boolean",
"format": "checkbox",
@@ -33,7 +44,7 @@
"options": {
"infoText": "edt_dev_spec_restoreOriginalState_title_info"
},
"propertyOrder": 3
"propertyOrder": 4
},
"overwriteSync": {
"type": "boolean",
@@ -42,7 +53,7 @@
"default": true,
"required": true,
"access": "advanced",
"propertyOrder": 4
"propertyOrder": 5
},
"overwriteBrightness": {
"type": "boolean",
@@ -51,7 +62,7 @@
"default": true,
"required": true,
"access": "advanced",
"propertyOrder": 5
"propertyOrder": 6
},
"brightness": {
"type": "integer",
@@ -65,7 +76,7 @@
}
},
"access": "advanced",
"propertyOrder": 6
"propertyOrder": 7
},
"latchTime": {
"type": "integer",
@@ -78,7 +89,7 @@
"options": {
"infoText": "edt_dev_spec_latchtime_title_info"
},
"propertyOrder": 7
"propertyOrder": 8
}
},
"additionalProperties": true