mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
LEDDevices - WLED enhancements and minor fixes (#1204)
* Yeelight minor updates * Add Timeout to REST API * LEDDevice - Correct storeState * Add WaitTime function * Always show HW-LEDCount for configuration * WLED - New features ("live" support, storing state and identification) * Yeelight - Refactoring * Cololight - Refactoring * Karate - getProperties Support * Atmo - getProperties Support * AtmoOrb - refactoring * Nanoleaf - Refactoring, New "Shapes" considerations * PhilipHue - Minor corrections * Update Changelog
This commit is contained in:
@@ -1,14 +1,20 @@
|
||||
// Local-Hyperion includes
|
||||
#include "LedDeviceWled.h"
|
||||
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
#include <utils/WaitTime.h>
|
||||
#include <QThread>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const bool verbose = false;
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_ADDRESS[] = "host";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
|
||||
// UDP elements
|
||||
const quint16 STREAM_DEFAULT_PORT = 19446;
|
||||
@@ -24,12 +30,11 @@ const char API_PATH_STATE[] = "state";
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_VALUE_TRUE[] = "true";
|
||||
const char STATE_VALUE_FALSE[] = "false";
|
||||
const char STATE_LIVE[] = "live";
|
||||
|
||||
// WLED ssdp services
|
||||
// TODO: WLED - Update ssdp discovery parameters when available
|
||||
const char SSDP_ID[] = "ssdp:all";
|
||||
const char SSDP_FILTER[] = "(.*)";
|
||||
const char SSDP_FILTER_HEADER[] = "ST";
|
||||
const int BRI_MAX = 255;
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 2000 };
|
||||
|
||||
} //End of constants
|
||||
|
||||
@@ -53,7 +58,6 @@ LedDevice* LedDeviceWled::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool isInitOK = false;
|
||||
|
||||
// Initialise LedDevice sub-class, ProviderUdp::init will be executed later, if connectivity is defined
|
||||
@@ -66,18 +70,21 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(false);
|
||||
Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
|
||||
|
||||
//Set hostname as per configuration
|
||||
QString address = deviceConfig[ CONFIG_ADDRESS ].toString();
|
||||
QString hostName = deviceConfig[ CONFIG_ADDRESS ].toString();
|
||||
|
||||
//If host not configured the init fails
|
||||
if ( address.isEmpty() )
|
||||
if ( hostName.isEmpty() )
|
||||
{
|
||||
this->setInError("No target hostname nor IP defined");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QStringList addressparts = QStringUtils::split(address,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
_hostname = addressparts[0];
|
||||
if ( addressparts.size() > 1 )
|
||||
{
|
||||
@@ -100,13 +107,11 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug(_log, "[%d]", isInitOK);
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::initRestAPI(const QString &hostname, int port)
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool isInitOK = false;
|
||||
|
||||
if ( _restApi == nullptr )
|
||||
@@ -116,38 +121,68 @@ bool LedDeviceWled::initRestAPI(const QString &hostname, int port)
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
|
||||
Debug(_log, "[%d]", isInitOK);
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getOnOffRequest(bool isOn) const
|
||||
{
|
||||
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "{\"%1\":%2}" ).arg( STATE_ON, state);
|
||||
return QString( "\"%1\":%2,\"%3\":%4" ).arg( STATE_ON, state).arg( STATE_LIVE, state);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getBrightnessRequest(int bri) const
|
||||
{
|
||||
return QString( "\"bri\":%1" ).arg(bri);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool LedDeviceWled::sendStateUpdateRequest(const QString &request)
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
httpResponse response1 = _restApi->put(QString("{%1}").arg(request));
|
||||
if ( response1.error() )
|
||||
{
|
||||
rc = false;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
bool LedDeviceWled::powerOn()
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool on = true;
|
||||
bool on = false;
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
//Power-on WLED device
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
httpResponse response = _restApi->put(getOnOffRequest(true));
|
||||
|
||||
httpResponse response = _restApi->put(QString("{%1,%2}").arg(getOnOffRequest(true)).arg(getBrightnessRequest(BRI_MAX)));
|
||||
if ( response.error() )
|
||||
{
|
||||
this->setInError ( response.getErrorReason() );
|
||||
QString errorReason = QString("Power-on request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
on = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
on = true;
|
||||
}
|
||||
}
|
||||
return on;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::powerOff()
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool off = true;
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
@@ -156,53 +191,89 @@ bool LedDeviceWled::powerOff()
|
||||
|
||||
//Power-off the WLED device physically
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
httpResponse response = _restApi->put(getOnOffRequest(false));
|
||||
httpResponse response = _restApi->put(QString("{%1}").arg(getOnOffRequest(false)));
|
||||
if ( response.error() )
|
||||
{
|
||||
this->setInError ( response.getErrorReason() );
|
||||
QString errorReason = QString("Power-off request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
off = false;
|
||||
}
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::storeState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
{
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(response.getErrorReason());
|
||||
setInError(errorReason);
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalStateProperties = response.getBody().object();
|
||||
DebugIf(verbose, _log, "state: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::restoreState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
{
|
||||
//powerOff();
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
_originalStateProperties[STATE_LIVE] = false;
|
||||
|
||||
httpResponse response = _restApi->put(QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s restoring state failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
// Discover WLED Devices
|
||||
SSDPDiscover discover;
|
||||
discover.skipDuplicateKeys(true);
|
||||
discover.setSearchFilter(SSDP_FILTER, SSDP_FILTER_HEADER);
|
||||
QString searchTarget = SSDP_ID;
|
||||
|
||||
if ( discover.discoverServices(searchTarget) > 0 )
|
||||
{
|
||||
deviceList = discover.getServicesDiscoveredJson();
|
||||
}
|
||||
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
// Get Nanoleaf device properties
|
||||
QString host = params["host"].toString("");
|
||||
if ( !host.isEmpty() )
|
||||
QString hostName = params["host"].toString("");
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
QString filter = params["filter"].toString("");
|
||||
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
@@ -226,49 +297,45 @@ QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
|
||||
|
||||
properties.insert("properties", response.getBody().object());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void LedDeviceWled::identify(const QJsonObject& /*params*/)
|
||||
void LedDeviceWled::identify(const QJsonObject& params)
|
||||
{
|
||||
#if 0
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QString host = params["host"].toString("");
|
||||
if ( !host.isEmpty() )
|
||||
QString hostName = params["host"].toString("");
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
if ( addressparts.size() > 1)
|
||||
{
|
||||
apiPort = addressparts[1].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiPort = API_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
// TODO: WLED::identify - Replace with valid identification code
|
||||
initRestAPI(apiHost, apiPort);
|
||||
|
||||
// initRestAPI(apiHost, apiPort);
|
||||
_isRestoreOrigState = true;
|
||||
storeState();
|
||||
|
||||
// QString resource = QString("%1/%2/%3").arg( API_LIGHTS ).arg( lightId ).arg( API_STATE);
|
||||
// _restApi->setPath(resource);
|
||||
QString request = getOnOffRequest(true) + "," + getLorRequest(1) + "," + getEffectRequest(25);
|
||||
sendStateUpdateRequest(request);
|
||||
|
||||
// QString stateCmd;
|
||||
// stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON ).arg( API_STATE_VALUE_TRUE );
|
||||
// stateCmd += QString("\"%1\":\"%2\"").arg( "alert" ).arg( "select" );
|
||||
// stateCmd = "{" + stateCmd + "}";
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
// // Perform request
|
||||
// httpResponse response = _restApi->put(stateCmd);
|
||||
// if ( response.error() )
|
||||
// {
|
||||
// Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
// }
|
||||
restoreState();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int LedDeviceWled::write(const std::vector<ColorRgb> &ledValues)
|
||||
|
Reference in New Issue
Block a user