mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
WLED segment streaming support (#1556)
* WLED segment streaming support * Address CodeQL findings * WLED - Remove that interim color is shown when WLED is powered off * Allow LEDDevice to stay on after streaming * Apply stayOn on segment streamed to * Fix LED-Matrix Layout: Add Cabling direction selection element again
This commit is contained in:
@@ -49,11 +49,13 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent)
|
||||
, _latchTime_ms(0)
|
||||
, _ledCount(0)
|
||||
, _isRestoreOrigState(false)
|
||||
, _isStayOnAfterStreaming(false)
|
||||
, _isEnabled(false)
|
||||
, _isDeviceInitialised(false)
|
||||
, _isDeviceReady(false)
|
||||
, _isOn(false)
|
||||
, _isDeviceInError(false)
|
||||
, _isDeviceRecoverable(false)
|
||||
, _lastWriteTime(QDateTime::currentDateTime())
|
||||
, _enableAttemptsTimer(nullptr)
|
||||
, _enableAttemptTimerInterval(DEFAULT_ENABLE_ATTEMPTS_INTERVAL)
|
||||
@@ -117,7 +119,7 @@ int LedDevice::close()
|
||||
return retval;
|
||||
}
|
||||
|
||||
void LedDevice::setInError(const QString& errorMsg)
|
||||
void LedDevice::setInError(const QString& errorMsg, bool isRecoverable)
|
||||
{
|
||||
_isOn = false;
|
||||
_isDeviceInError = true;
|
||||
@@ -125,6 +127,10 @@ void LedDevice::setInError(const QString& errorMsg)
|
||||
_isEnabled = false;
|
||||
this->stopRefreshTimer();
|
||||
|
||||
if (isRecoverable)
|
||||
{
|
||||
_isDeviceRecoverable = isRecoverable;
|
||||
}
|
||||
Error(_log, "Device disabled, device '%s' signals error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(errorMsg));
|
||||
emit enableStateChanged(_isEnabled);
|
||||
}
|
||||
@@ -170,7 +176,7 @@ void LedDevice::enable()
|
||||
{
|
||||
emit enableStateChanged(false);
|
||||
|
||||
if (_maxEnableAttempts > 0)
|
||||
if (_maxEnableAttempts > 0 && _isDeviceRecoverable)
|
||||
{
|
||||
Debug(_log, "Device's enablement failed - Start retry timer. Retried already done [%d], isEnabled: [%d]", _enableAttempts, _isEnabled);
|
||||
startEnableAttemptsTimer();
|
||||
@@ -257,27 +263,30 @@ void LedDevice::startEnableAttemptsTimer()
|
||||
{
|
||||
++_enableAttempts;
|
||||
|
||||
if (_enableAttempts <= _maxEnableAttempts)
|
||||
if (_isDeviceRecoverable)
|
||||
{
|
||||
if (_enableAttemptTimerInterval.count() > 0)
|
||||
if (_enableAttempts <= _maxEnableAttempts)
|
||||
{
|
||||
// setup enable retry timer
|
||||
if (_enableAttemptsTimer == nullptr)
|
||||
if (_enableAttemptTimerInterval.count() > 0)
|
||||
{
|
||||
_enableAttemptsTimer = new QTimer(this);
|
||||
_enableAttemptsTimer->setTimerType(Qt::PreciseTimer);
|
||||
connect(_enableAttemptsTimer, &QTimer::timeout, this, &LedDevice::enable);
|
||||
}
|
||||
_enableAttemptsTimer->setInterval(static_cast<int>(_enableAttemptTimerInterval.count() * 1000)); //NOLINT
|
||||
// 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();
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Device disabled. Maximum number of %d attempts enabling the device reached. Tried for %d seconds.", _maxEnableAttempts, _enableAttempts * _enableAttemptTimerInterval.count());
|
||||
_enableAttempts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,14 +461,16 @@ bool LedDevice::switchOff()
|
||||
|
||||
bool LedDevice::powerOff()
|
||||
{
|
||||
bool rc{ false };
|
||||
bool rc{ true };
|
||||
|
||||
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 (!_isStayOnAfterStreaming)
|
||||
{
|
||||
rc = true;
|
||||
Debug(_log, "Power Off: %s", QSTRING_CSTR(_activeDeviceType));
|
||||
// Simulate power-off by writing a final "Black" to have a defined outcome
|
||||
if (writeBlack() < 0)
|
||||
{
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@@ -23,35 +23,61 @@ const bool verbose = false;
|
||||
const char CONFIG_HOST[] = "host";
|
||||
const char CONFIG_STREAM_PROTOCOL[] = "streamProtocol";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
const char CONFIG_STAY_ON_AFTER_STREAMING[] = "stayOnAfterStreaming";
|
||||
|
||||
const char CONFIG_BRIGHTNESS[] = "brightness";
|
||||
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
|
||||
const char CONFIG_SYNC_OVERWRITE[] = "overwriteSync";
|
||||
|
||||
const char CONFIG_STREAM_SEGMENTS[] = "segments";
|
||||
const char CONFIG_STREAM_SEGMENT_ID[] = "streamSegmentId";
|
||||
const char CONFIG_SWITCH_OFF_OTHER_SEGMENTS[] = "switchOffOtherSegments";
|
||||
|
||||
const char DEFAULT_STREAM_PROTOCOL[] = "DDP";
|
||||
|
||||
// UDP-RAW
|
||||
const int UDP_STREAM_DEFAULT_PORT = 19446;
|
||||
const int UDP_MAX_LED_NUM = 490;
|
||||
|
||||
// DDP
|
||||
// Version constraints
|
||||
const char WLED_VERSION_DDP[] = "0.11.0";
|
||||
const char WLED_VERSION_SEGMENT_STREAMING[] = "0.13.3";
|
||||
|
||||
// WLED JSON-API elements
|
||||
const int API_DEFAULT_PORT = -1; //Use default port per communication scheme
|
||||
|
||||
const char API_BASE_PATH[] = "/json/";
|
||||
const char API_PATH_STATE[] = "state";
|
||||
const char API_PATH_INFO[] = "info";
|
||||
|
||||
// List of State Information
|
||||
// List of State keys
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_VALUE_TRUE[] = "true";
|
||||
const char STATE_VALUE_FALSE[] = "false";
|
||||
const char STATE_BRI[] = "bri";
|
||||
const char STATE_LIVE[] = "live";
|
||||
const char STATE_LOR[] = "lor";
|
||||
const char STATE_SEG[] = "seg";
|
||||
const char STATE_SEG_ID[] = "id";
|
||||
const char STATE_SEG_LEN[] = "len";
|
||||
const char STATE_SEG_FX[] = "fx";
|
||||
const char STATE_SEG_SX[] = "sx";
|
||||
const char STATE_MAINSEG[] = "mainseg";
|
||||
const char STATE_UDPN[] = "udpn";
|
||||
const char STATE_UDPN_SEND[] = "send";
|
||||
const char STATE_UDPN_RECV[] = "recv";
|
||||
const char STATE_TRANSITIONTIME_CURRENTCALL[] = "tt";
|
||||
|
||||
// List of Info keys
|
||||
const char INFO_VER[] = "ver";
|
||||
const char INFO_LIVESEG[] = "liveseg";
|
||||
|
||||
//Default state values
|
||||
const bool DEFAULT_IS_RESTORE_STATE = false;
|
||||
const bool DEFAULT_IS_STAY_ON_AFTER_STREAMING = false;
|
||||
const bool DEFAULT_IS_BRIGHTNESS_OVERWRITE = true;
|
||||
const int BRI_MAX = 255;
|
||||
const bool DEFAULT_IS_SYNC_OVERWRITE = true;
|
||||
const int DEFAULT_SEGMENT_ID = -1;
|
||||
const bool DEFAULT_IS_SWITCH_OFF_OTHER_SEGMENTS = true;
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 2000 };
|
||||
|
||||
@@ -61,12 +87,16 @@ LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig), LedDeviceUdpDdp(deviceConfig), LedDeviceUdpRaw(deviceConfig)
|
||||
,_restApi(nullptr)
|
||||
,_apiPort(API_DEFAULT_PORT)
|
||||
,_currentVersion("")
|
||||
,_isBrightnessOverwrite(DEFAULT_IS_BRIGHTNESS_OVERWRITE)
|
||||
,_brightness (BRI_MAX)
|
||||
,_isSyncOverwrite(DEFAULT_IS_SYNC_OVERWRITE)
|
||||
,_originalStateUdpnSend(false)
|
||||
,_originalStateUdpnRecv(true)
|
||||
,_isStreamDDP(true)
|
||||
,_streamSegmentId(DEFAULT_SEGMENT_ID)
|
||||
,_isSwitchOffOtherSegments(DEFAULT_IS_SWITCH_OFF_OTHER_SEGMENTS)
|
||||
,_isStreamToSegment(false)
|
||||
{
|
||||
#ifdef ENABLE_MDNS
|
||||
QMetaObject::invokeMethod(&MdnsBrowser::getInstance(), "browseForServiceType",
|
||||
@@ -112,6 +142,7 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE);
|
||||
_isStayOnAfterStreaming = _devConfig[CONFIG_STAY_ON_AFTER_STREAMING].toBool(DEFAULT_IS_STAY_ON_AFTER_STREAMING);
|
||||
_isSyncOverwrite = _devConfig[CONFIG_SYNC_OVERWRITE].toBool(DEFAULT_IS_SYNC_OVERWRITE);
|
||||
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
|
||||
_brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX);
|
||||
@@ -121,6 +152,23 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
|
||||
Debug(_log, "Set Brightness to : %d", _brightness);
|
||||
|
||||
|
||||
QJsonObject segments = _devConfig[CONFIG_STREAM_SEGMENTS].toObject();
|
||||
_streamSegmentId = segments[CONFIG_STREAM_SEGMENT_ID].toInt(DEFAULT_SEGMENT_ID);
|
||||
|
||||
if (_streamSegmentId > DEFAULT_SEGMENT_ID)
|
||||
{
|
||||
_isStreamToSegment = true;
|
||||
}
|
||||
_isSwitchOffOtherSegments = segments[CONFIG_SWITCH_OFF_OTHER_SEGMENTS].toBool(DEFAULT_IS_SWITCH_OFF_OTHER_SEGMENTS);
|
||||
|
||||
Debug(_log, "Stream to one segment: %s", _isStreamToSegment ? "Yes" : "No");
|
||||
if (_isStreamToSegment )
|
||||
{
|
||||
Debug(_log, "Stream to segment [%d]", _streamSegmentId);
|
||||
Debug(_log, "Switch-off other segments: %s", _isSwitchOffOtherSegments ? "Yes" : "No");
|
||||
}
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
|
||||
@@ -193,79 +241,187 @@ int LedDeviceWled::close()
|
||||
return retval;
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getOnOffRequest(bool isOn) const
|
||||
QJsonObject LedDeviceWled::getUdpnObject(bool isSendOn, bool isRecvOn) const
|
||||
{
|
||||
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "\"%1\":%2,\"%3\":%4" ).arg( STATE_ON, state).arg( STATE_LIVE, state);
|
||||
QJsonObject udpnObj
|
||||
{
|
||||
{STATE_UDPN_SEND, isSendOn},
|
||||
{STATE_UDPN_RECV, isRecvOn}
|
||||
};
|
||||
return udpnObj;
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getBrightnessRequest(int bri) const
|
||||
QJsonObject LedDeviceWled::getSegmentObject(int segmentId, bool isOn, int brightness) const
|
||||
{
|
||||
return QString( "\"bri\":%1" ).arg(bri);
|
||||
QJsonObject segmentObj
|
||||
{
|
||||
{STATE_SEG_ID, segmentId},
|
||||
{STATE_ON, isOn}
|
||||
};
|
||||
|
||||
if ( brightness > -1)
|
||||
{
|
||||
segmentObj.insert(STATE_BRI, brightness);
|
||||
}
|
||||
return segmentObj;
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getEffectRequest(int effect, int speed) const
|
||||
{
|
||||
return QString( "\"seg\":{\"fx\":%1,\"sx\":%2}" ).arg(effect).arg(speed);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getLorRequest(int lor) const
|
||||
{
|
||||
return QString( "\"lor\":%1" ).arg(lor);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getUdpnRequest(bool isSendOn, bool isRecvOn) const
|
||||
{
|
||||
QString send = isSendOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
QString recv = isRecvOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "\"udpn\":{\"send\":%1,\"recv\":%2}" ).arg(send, recv);
|
||||
}
|
||||
|
||||
bool LedDeviceWled::sendStateUpdateRequest(const QString &request)
|
||||
bool LedDeviceWled::sendStateUpdateRequest(const QJsonObject &request, const QString requestType)
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
httpResponse response1 = _restApi->put(QString("{%1}").arg(request));
|
||||
if ( response1.error() )
|
||||
httpResponse response = _restApi->put(request);
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("%1 request failed with error: '%2'").arg(requestType, response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
rc = false;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::isReadyForSegmentStreaming(semver::version& version) const
|
||||
{
|
||||
bool isReady{false};
|
||||
|
||||
if (version.isValid())
|
||||
{
|
||||
semver::version segmentStreamingVersion{WLED_VERSION_SEGMENT_STREAMING};
|
||||
if (version < segmentStreamingVersion)
|
||||
{
|
||||
Warning(_log, "Segment streaming not supported by your WLED device version [%s], minimum version expected [%s].", _currentVersion.getVersion().c_str(), segmentStreamingVersion.getVersion().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Segment streaming is supported by your WLED device version [%s].", _currentVersion.getVersion().c_str());
|
||||
isReady = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Version provided to test for streaming readiness is not valid ");
|
||||
}
|
||||
return isReady;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::isReadyForDDPStreaming(semver::version& version) const
|
||||
{
|
||||
bool isReady{false};
|
||||
|
||||
if (version.isValid())
|
||||
{
|
||||
semver::version ddpVersion{WLED_VERSION_DDP};
|
||||
if (version < 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "DDP streaming is supported by your WLED device version [%s]. No limitation in number of LEDs.", _currentVersion.getVersion().c_str());
|
||||
isReady = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Version provided to test for streaming readiness is not valid ");
|
||||
}
|
||||
return isReady;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::powerOn()
|
||||
{
|
||||
bool on = false;
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
//Power-on WLED device
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
QString cmd = getOnOffRequest(true);
|
||||
|
||||
if ( _isBrightnessOverwrite)
|
||||
QJsonObject cmd;
|
||||
if (_isStreamToSegment)
|
||||
{
|
||||
cmd += "," + getBrightnessRequest(_brightness);
|
||||
if (!isReadyForSegmentStreaming(_currentVersion))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_wledInfo[INFO_LIVESEG].toInt() == -1)
|
||||
{
|
||||
stopEnableAttemptsTimer();
|
||||
this->setInError( "Segment streaming configured, but \"Use main segment only\" in WLED Sync Interface configuration is not enabled!", false);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray();
|
||||
|
||||
bool isStreamSegmentIdFound { false };
|
||||
|
||||
QJsonArray segments;
|
||||
for (const auto& segmentItem : qAsConst(propertiesSegments))
|
||||
{
|
||||
QJsonObject segmentObj = segmentItem.toObject();
|
||||
|
||||
int segmentID = segmentObj.value(STATE_SEG_ID).toInt();
|
||||
if (segmentID == _streamSegmentId)
|
||||
{
|
||||
isStreamSegmentIdFound = true;
|
||||
int len = segmentObj.value(STATE_SEG_LEN).toInt();
|
||||
if (getLedCount() > len)
|
||||
{
|
||||
QString errorReason = QString("Too many LEDs [%1] configured for segment [%2], which supports maximum [%3] LEDs. Check your WLED setup!").arg(getLedCount()).arg(_streamSegmentId).arg(len);
|
||||
this->setInError(errorReason, false);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int brightness{ -1 };
|
||||
if (_isBrightnessOverwrite)
|
||||
{
|
||||
brightness = _brightness;
|
||||
}
|
||||
segments.append(getSegmentObject(segmentID, true, brightness));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isSwitchOffOtherSegments)
|
||||
{
|
||||
segments.append(getSegmentObject(segmentID, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isStreamSegmentIdFound)
|
||||
{
|
||||
QString errorReason = QString("Segment streaming to segment [%1] configured, but segment does not exist on WLED. Check your WLED setup!").arg(_streamSegmentId);
|
||||
this->setInError(errorReason, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
cmd.insert(STATE_SEG, segments);
|
||||
|
||||
//Set segment to be streamed to
|
||||
cmd.insert(STATE_MAINSEG, _streamSegmentId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isBrightnessOverwrite)
|
||||
{
|
||||
cmd.insert(STATE_BRI, _brightness);
|
||||
}
|
||||
}
|
||||
|
||||
cmd.insert(STATE_LIVE, true);
|
||||
cmd.insert(STATE_ON, true);
|
||||
|
||||
if (_isSyncOverwrite)
|
||||
{
|
||||
Debug( _log, "Disable synchronisation with other WLED devices");
|
||||
cmd += "," + getUdpnRequest(false, false);
|
||||
cmd.insert(STATE_UDPN, getUdpnObject(false, false));
|
||||
}
|
||||
|
||||
httpResponse response = _restApi->put(QString("{%1}").arg(cmd));
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Power-on request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
on = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
on = true;
|
||||
}
|
||||
on = sendStateUpdateRequest(cmd,"Power-on");
|
||||
}
|
||||
return on;
|
||||
}
|
||||
@@ -279,23 +435,25 @@ bool LedDeviceWled::powerOff()
|
||||
writeBlack();
|
||||
|
||||
//Power-off the WLED device physically
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
QJsonObject cmd;
|
||||
if (_isStreamToSegment)
|
||||
{
|
||||
QJsonArray segments;
|
||||
segments.append(getSegmentObject(_streamSegmentId, _isStayOnAfterStreaming));
|
||||
cmd.insert(STATE_SEG, segments);
|
||||
}
|
||||
|
||||
QString cmd = getOnOffRequest(false);
|
||||
cmd.insert(STATE_LIVE, false);
|
||||
cmd.insert(STATE_TRANSITIONTIME_CURRENTCALL, 0);
|
||||
cmd.insert(STATE_ON, _isStayOnAfterStreaming);
|
||||
|
||||
if (_isSyncOverwrite)
|
||||
{
|
||||
Debug( _log, "Restore synchronisation with other WLED devices");
|
||||
cmd += "," + getUdpnRequest(_originalStateUdpnSend, _originalStateUdpnRecv);
|
||||
cmd.insert(STATE_UDPN, getUdpnObject(_originalStateUdpnSend, _originalStateUdpnRecv));
|
||||
}
|
||||
|
||||
httpResponse response = _restApi->put(QString("{%1}").arg(cmd));
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Power-off request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
off = false;
|
||||
}
|
||||
off = sendStateUpdateRequest(cmd,"Power-off");
|
||||
}
|
||||
return off;
|
||||
}
|
||||
@@ -304,28 +462,33 @@ bool LedDeviceWled::storeState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState || _isSyncOverwrite )
|
||||
if ( _isRestoreOrigState || _isSyncOverwrite || _isStreamToSegment)
|
||||
{
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
_restApi->setPath("");
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(response.getErrorReason());
|
||||
QString errorReason = QString("Retrieving device properties failed with error: '%1'").arg(response.getErrorReason());
|
||||
setInError(errorReason);
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalStateProperties = response.getBody().object();
|
||||
_originalStateProperties = response.getBody().object().value(API_PATH_STATE).toObject();
|
||||
DebugIf(verbose, _log, "state: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
QJsonObject udpn = _originalStateProperties.value("udpn").toObject();
|
||||
QJsonObject udpn = _originalStateProperties.value(STATE_UDPN).toObject();
|
||||
if (!udpn.isEmpty())
|
||||
{
|
||||
_originalStateUdpnSend = udpn["send"].toBool(false);
|
||||
_originalStateUdpnRecv = udpn["recv"].toBool(true);
|
||||
_originalStateUdpnSend = udpn[STATE_UDPN_SEND].toBool(false);
|
||||
_originalStateUdpnRecv = udpn[STATE_UDPN_RECV].toBool(true);
|
||||
}
|
||||
|
||||
_wledInfo = response.getBody().object().value(API_PATH_INFO).toObject();
|
||||
DebugIf(verbose, _log, "info: [%s]", QString(QJsonDocument(_wledInfo).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
_currentVersion.setVersion(_wledInfo.value(INFO_VER).toString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,10 +503,29 @@ bool LedDeviceWled::restoreState()
|
||||
{
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
if (_isStreamToSegment)
|
||||
{
|
||||
QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray();
|
||||
QJsonArray segments;
|
||||
for (const auto& segmentItem : qAsConst(propertiesSegments))
|
||||
{
|
||||
QJsonObject segmentObj = segmentItem.toObject();
|
||||
|
||||
int segmentID = segmentObj.value(STATE_SEG_ID).toInt();
|
||||
if (segmentID == _streamSegmentId)
|
||||
{
|
||||
segmentObj[STATE_ON] = _isStayOnAfterStreaming;
|
||||
}
|
||||
segments.append(segmentObj);
|
||||
}
|
||||
_originalStateProperties[STATE_SEG] = segments;
|
||||
}
|
||||
|
||||
_originalStateProperties[STATE_LIVE] = false;
|
||||
_originalStateProperties[STATE_TRANSITIONTIME_CURRENTCALL] = 0;
|
||||
_originalStateProperties[STATE_ON] = _isStayOnAfterStreaming;
|
||||
|
||||
httpResponse response = _restApi->put(QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
httpResponse response = _restApi->put(_originalStateProperties);
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s restoring state failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
@@ -363,10 +545,10 @@ QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/)
|
||||
#ifdef ENABLE_MDNS
|
||||
QString discoveryMethod("mDNS");
|
||||
deviceList = MdnsBrowser::getInstance().getServicesDiscoveredJson(
|
||||
MdnsServiceRegister::getServiceType(_activeDeviceType),
|
||||
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
|
||||
DEFAULT_DISCOVER_TIMEOUT
|
||||
);
|
||||
MdnsServiceRegister::getServiceType(_activeDeviceType),
|
||||
MdnsServiceRegister::getServiceNameFilter(_activeDeviceType),
|
||||
DEFAULT_DISCOVER_TIMEOUT
|
||||
);
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
#endif
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
@@ -397,27 +579,21 @@ QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
|
||||
QJsonObject propertiesDetails = response.getBody().object();
|
||||
|
||||
semver::version currentVersion {""};
|
||||
if (currentVersion.setVersion(propertiesDetails.value("ver").toString().toStdString()))
|
||||
else
|
||||
{
|
||||
semver::version ddpVersion{WLED_VERSION_DDP};
|
||||
if (currentVersion < ddpVersion)
|
||||
QJsonObject propertiesDetails = response.getBody().object();
|
||||
|
||||
_wledInfo = propertiesDetails.value(API_PATH_INFO).toObject();
|
||||
_currentVersion.setVersion(_wledInfo.value(INFO_VER).toString().toStdString());
|
||||
if (!isReadyForDDPStreaming(_currentVersion))
|
||||
{
|
||||
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());
|
||||
}
|
||||
properties.insert("properties", propertiesDetails);
|
||||
}
|
||||
properties.insert("properties", propertiesDetails);
|
||||
}
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
@@ -441,8 +617,23 @@ void LedDeviceWled::identify(const QJsonObject& params)
|
||||
_isRestoreOrigState = true;
|
||||
storeState();
|
||||
|
||||
QString request = getOnOffRequest(true) + "," + getLorRequest(1) + "," + getEffectRequest(25);
|
||||
sendStateUpdateRequest(request);
|
||||
QJsonObject cmd;
|
||||
|
||||
cmd.insert(STATE_ON, true);
|
||||
cmd.insert(STATE_LOR, 1);
|
||||
|
||||
_streamSegmentId = params[CONFIG_STREAM_SEGMENT_ID].toInt(0);
|
||||
|
||||
QJsonObject segment;
|
||||
segment = getSegmentObject(_streamSegmentId, true, BRI_MAX);
|
||||
segment.insert(STATE_SEG_FX, 25);
|
||||
segment.insert(STATE_SEG_SX, 128);
|
||||
|
||||
QJsonArray segments;
|
||||
segments.append(segment);
|
||||
cmd.insert(STATE_SEG, segments);
|
||||
|
||||
sendStateUpdateRequest(cmd,"Identify");
|
||||
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "LedDeviceUdpDdp.h"
|
||||
#include "LedDeviceUdpRaw.h"
|
||||
|
||||
#include <utils/version.hpp>
|
||||
///
|
||||
/// Implementation of a WLED-device
|
||||
///
|
||||
@@ -146,20 +147,13 @@ private:
|
||||
///
|
||||
bool openRestAPI();
|
||||
|
||||
///
|
||||
/// @brief Get command to power WLED-device on or off
|
||||
///
|
||||
/// @param isOn True, if to switch on device
|
||||
/// @return Command to switch device on/off
|
||||
///
|
||||
QString getOnOffRequest (bool isOn ) const;
|
||||
QJsonObject getUdpnObject(bool send, bool recv) const;
|
||||
QJsonObject getSegmentObject(int segmentId, bool isOn, int brightness=-1) const;
|
||||
|
||||
QString getBrightnessRequest (int bri ) const;
|
||||
QString getEffectRequest(int effect, int speed=128) const;
|
||||
QString getLorRequest(int lor) const;
|
||||
QString getUdpnRequest(bool send, bool recv) const;
|
||||
bool sendStateUpdateRequest(const QJsonObject &request, const QString requestType = "");
|
||||
|
||||
bool sendStateUpdateRequest(const QString &request);
|
||||
bool isReadyForSegmentStreaming(semver::version& version) const;
|
||||
bool isReadyForDDPStreaming(semver::version& version) const;
|
||||
|
||||
QString resolveAddress (const QString& hostName);
|
||||
|
||||
@@ -169,8 +163,11 @@ private:
|
||||
QString _hostAddress;
|
||||
int _apiPort;
|
||||
|
||||
QJsonObject _wledInfo;
|
||||
QJsonObject _originalStateProperties;
|
||||
|
||||
semver::version _currentVersion;
|
||||
|
||||
bool _isBrightnessOverwrite;
|
||||
int _brightness;
|
||||
|
||||
@@ -179,6 +176,10 @@ private:
|
||||
bool _originalStateUdpnRecv;
|
||||
|
||||
bool _isStreamDDP;
|
||||
|
||||
int _streamSegmentId;
|
||||
bool _isSwitchOffOtherSegments;
|
||||
bool _isStreamToSegment;
|
||||
};
|
||||
|
||||
#endif // LEDDEVICEWLED_H
|
||||
|
@@ -207,12 +207,12 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
|
||||
return _rs232Port.isOpen();
|
||||
}
|
||||
|
||||
void ProviderRs232::setInError(const QString& errorMsg)
|
||||
void ProviderRs232::setInError(const QString& errorMsg, bool isRecoverable)
|
||||
{
|
||||
_rs232Port.clearError();
|
||||
this->close();
|
||||
|
||||
LedDevice::setInError( errorMsg );
|
||||
LedDevice::setInError( errorMsg, isRecoverable );
|
||||
}
|
||||
|
||||
int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data)
|
||||
|
@@ -119,9 +119,10 @@ protected slots:
|
||||
///
|
||||
/// @brief Set device in error state
|
||||
///
|
||||
/// @param errorMsg The error message to be logged
|
||||
/// @param[in] errorMsg The error message to be logged
|
||||
/// @param[in] isRecoverable If False, no further retries will be done
|
||||
///
|
||||
void setInError( const QString& errorMsg) override;
|
||||
void setInError( const QString& errorMsg, bool isRecoverable=true) override;
|
||||
|
||||
///
|
||||
/// @brief Handle any feedback provided by the device
|
||||
|
@@ -35,6 +35,46 @@
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"segments": {
|
||||
"type": "object",
|
||||
"title": "Segment streaming",
|
||||
"required": false,
|
||||
"properties": {
|
||||
"segmentList": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_segments_title",
|
||||
"enum": [ "-1" ],
|
||||
"default": "-1",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_dev_spec_segments_disabled_title" ]
|
||||
},
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"streamSegmentId": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_segmentId_title",
|
||||
"default": -1,
|
||||
"minimum": -1,
|
||||
"maximum": 16,
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"switchOffOtherSegments": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_segmentsSwitchOffOthers_title",
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 3
|
||||
}
|
||||
},
|
||||
"propertyOrder": 4,
|
||||
"additionalProperties": false
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
@@ -44,7 +84,18 @@
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_restoreOriginalState_title_info"
|
||||
},
|
||||
"propertyOrder": 4
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"stayOnAfterStreaming": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_stayOnAfterStreaming_title",
|
||||
"default": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_stayOnAfterStreaming_title_info"
|
||||
},
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"overwriteSync": {
|
||||
"type": "boolean",
|
||||
@@ -53,7 +104,7 @@
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 5
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"overwriteBrightness": {
|
||||
"type": "boolean",
|
||||
@@ -62,7 +113,7 @@
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 6
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"brightness": {
|
||||
"type": "integer",
|
||||
@@ -76,7 +127,7 @@
|
||||
}
|
||||
},
|
||||
"access": "advanced",
|
||||
"propertyOrder": 7
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
@@ -89,8 +140,8 @@
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_latchtime_title_info"
|
||||
},
|
||||
"propertyOrder": 8
|
||||
"propertyOrder": 12
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
Reference in New Issue
Block a user