mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
LED Device Features, Fixes and Refactoring (Resubmit PR855) (#875)
* Refactor LedDevices - Initial version * Small renamings * Add WLED as own device * Lpd8806 Remove open() method * remove dependency on Qt 5.10 * Lpd8806 Remove open() method * Update WS281x * Update WS2812SPI * Add writeBlack for WLED powerOff * WLED remove extra bracket * Allow different Nanoleaf panel numbering sequence (Feature req.#827) * build(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#826) * Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. - [Release notes](https://github.com/faye/websocket-extensions-node/releases) - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md) - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4) * Fix typos * Nanoleaf clean-up * Yeelight support, generalize wizard elements * Update Yeelight to handle quota in music mode * Yeelight extend rage for extraTimeDarkness for testing * Clean-up - Add commentary, Remove development debug statements * Fix brightnessSwitchOffOnMinimum typo and default value * Yeelight support restoreOriginalState, additional Fixes * WLED - Remove UDP-Port, as it is not configurable * Fix merging issue * Remove QHostAddress::operator=(const QString&)' is deprecated * Windows compile errors and (Qt 5.15 deprecation) warnings * Fix order includes * LedDeviceFile Support Qt5.7 and greater * Windows compatibility and other Fixes * Fix Qt Version compatability * Rs232 - Resolve portname from unix /dev/ style, fix DMX sub-type support * Disable WLED Wizard Button (until Wizard is available) * Yeelight updates * Add wrong log-type as per #505 * Fixes and Clean-up after clang-tidy report * Fix udpe131 not enabled for generated CID * Change timer into dynamic for Qt Thread-Affinity * Hue clean-up and diyHue workaround * Updates after review feedback by m-seker * Add "chrono" includes
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
// Local-Hyperion includes
|
||||
#include "LedDeviceNanoleaf.h"
|
||||
|
||||
// ssdp discover
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
// Qt includes
|
||||
#include <QEventLoop>
|
||||
@@ -12,61 +12,66 @@
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
//
|
||||
static const bool verbose = false;
|
||||
static const bool verbose3 = false;
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
// Controller configuration settings
|
||||
static const char CONFIG_ADDRESS[] = "host";
|
||||
//static const char CONFIG_PORT[] = "port";
|
||||
static const char CONFIG_AUTH_TOKEN[] ="token";
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
|
||||
static const char CONFIG_PANEL_ORDER_TOP_DOWN[] ="panelOrderTopDown";
|
||||
static const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] ="panelOrderLeftRight";
|
||||
static const char CONFIG_PANEL_START_POS[] ="panelStartPos";
|
||||
// Configuration settings
|
||||
const char CONFIG_ADDRESS[] = "host";
|
||||
//const char CONFIG_PORT[] = "port";
|
||||
const char CONFIG_AUTH_TOKEN[] ="token";
|
||||
|
||||
const char CONFIG_PANEL_ORDER_TOP_DOWN[] ="panelOrderTopDown";
|
||||
const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] ="panelOrderLeftRight";
|
||||
const char CONFIG_PANEL_START_POS[] ="panelStartPos";
|
||||
|
||||
// Panel configuration settings
|
||||
static const char PANEL_LAYOUT[] = "layout";
|
||||
static const char PANEL_NUM[] = "numPanels";
|
||||
static const char PANEL_ID[] = "panelId";
|
||||
static const char PANEL_POSITIONDATA[] = "positionData";
|
||||
static const char PANEL_SHAPE_TYPE[] = "shapeType";
|
||||
//static const char PANEL_ORIENTATION[] = "0";
|
||||
static const char PANEL_POS_X[] = "x";
|
||||
static const char PANEL_POS_Y[] = "y";
|
||||
const char PANEL_LAYOUT[] = "layout";
|
||||
const char PANEL_NUM[] = "numPanels";
|
||||
const char PANEL_ID[] = "panelId";
|
||||
const char PANEL_POSITIONDATA[] = "positionData";
|
||||
const char PANEL_SHAPE_TYPE[] = "shapeType";
|
||||
//const char PANEL_ORIENTATION[] = "0";
|
||||
const char PANEL_POS_X[] = "x";
|
||||
const char PANEL_POS_Y[] = "y";
|
||||
|
||||
// List of State Information
|
||||
static const char STATE_ON[] = "on";
|
||||
static const char STATE_ONOFF_VALUE[] = "value";
|
||||
static const char STATE_VALUE_TRUE[] = "true";
|
||||
static const char STATE_VALUE_FALSE[] = "false";
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_ONOFF_VALUE[] = "value";
|
||||
const char STATE_VALUE_TRUE[] = "true";
|
||||
const char STATE_VALUE_FALSE[] = "false";
|
||||
|
||||
// Device Data elements
|
||||
static const char DEV_DATA_NAME[] = "name";
|
||||
static const char DEV_DATA_MODEL[] = "model";
|
||||
static const char DEV_DATA_MANUFACTURER[] = "manufacturer";
|
||||
static const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
|
||||
const char DEV_DATA_NAME[] = "name";
|
||||
const char DEV_DATA_MODEL[] = "model";
|
||||
const char DEV_DATA_MANUFACTURER[] = "manufacturer";
|
||||
const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
|
||||
|
||||
// Nanoleaf Stream Control elements
|
||||
//static const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
|
||||
static const char STREAM_CONTROL_PORT[] = "streamControlPort";
|
||||
//static const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
|
||||
//const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
|
||||
const char STREAM_CONTROL_PORT[] = "streamControlPort";
|
||||
//const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
|
||||
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222; //Fixed port for Canvas;
|
||||
|
||||
// Nanoleaf OpenAPI URLs
|
||||
static const char API_DEFAULT_PORT[] = "16021";
|
||||
static const char API_URL_FORMAT[] = "http://%1:%2/api/v1/%3/%4";
|
||||
static const char API_ROOT[] = "";
|
||||
//static const char API_EXT_MODE_STRING_V1[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}";
|
||||
static const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
|
||||
static const char API_STATE[] ="state";
|
||||
static const char API_PANELLAYOUT[] = "panelLayout";
|
||||
static const char API_EFFECT[] = "effects";
|
||||
const int API_DEFAULT_PORT = 16021;
|
||||
const char API_BASE_PATH[] = "/api/v1/%1/";
|
||||
const char API_ROOT[] = "";
|
||||
//const char API_EXT_MODE_STRING_V1[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}";
|
||||
const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}";
|
||||
const char API_STATE[] ="state";
|
||||
const char API_PANELLAYOUT[] = "panelLayout";
|
||||
const char API_EFFECT[] = "effects";
|
||||
|
||||
// Nanoleaf ssdp services
|
||||
static const char SSDP_CANVAS[] = "nanoleaf:nl29";
|
||||
static const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
|
||||
const int SSDP_TIMEOUT = 5000; // timout in ms
|
||||
const char SSDP_ID[] = "ssdp:all";
|
||||
const char SSDP_FILTER_HEADER[] = "ST";
|
||||
const char SSDP_CANVAS[] = "nanoleaf:nl29";
|
||||
const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
|
||||
|
||||
} //End of constants
|
||||
|
||||
// Nanoleaf Panel Shapetypes
|
||||
enum SHAPETYPES {
|
||||
@@ -84,6 +89,23 @@ enum EXTCONTROLVERSIONS {
|
||||
EXTCTRLVER_V2
|
||||
};
|
||||
|
||||
LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp()
|
||||
,_restApi(nullptr)
|
||||
,_apiPort(API_DEFAULT_PORT)
|
||||
,_topDown(true)
|
||||
,_leftRight(true)
|
||||
,_startPos(0)
|
||||
,_endPos(0)
|
||||
,_extControlVersion (EXTCTRLVER_V2),
|
||||
_panelLedCount(0)
|
||||
{
|
||||
_devConfig = deviceConfig;
|
||||
_isDeviceReady = false;
|
||||
|
||||
_activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower();
|
||||
}
|
||||
|
||||
LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig)
|
||||
{
|
||||
return new LedDeviceNanoleaf(deviceConfig);
|
||||
@@ -91,17 +113,11 @@ LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
LedDeviceNanoleaf::~LedDeviceNanoleaf()
|
||||
{
|
||||
_networkmanager->deleteLater();
|
||||
}
|
||||
|
||||
LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp()
|
||||
{
|
||||
_devConfig = deviceConfig;
|
||||
_deviceReady = false;
|
||||
_networkmanager = nullptr;
|
||||
_extControlVersion = EXTCTRLVER_V2;
|
||||
_panelLedCount = 0;
|
||||
if ( _restApi != nullptr )
|
||||
{
|
||||
delete _restApi;
|
||||
_restApi = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig)
|
||||
@@ -116,74 +132,89 @@ bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig)
|
||||
|
||||
DebugIf(verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
bool isInitOK = LedDevice::init(deviceConfig);
|
||||
bool isInitOK = false;
|
||||
|
||||
if ( isInitOK )
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
{
|
||||
uint configuredLedCount = this->getLedCount();
|
||||
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
|
||||
Debug(_log, "LedCount : %u", configuredLedCount);
|
||||
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
|
||||
Debug(_log, "RefreshTime : %d", _refresh_timer_interval);
|
||||
Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms);
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
// Read panel organisation configuration
|
||||
if ( deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].isString() )
|
||||
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toString().toInt() == 0 ? true : false;
|
||||
{
|
||||
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toString().toInt() == 0;
|
||||
}
|
||||
else
|
||||
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toInt() == 0 ? true : false;
|
||||
{
|
||||
_topDown = deviceConfig[ CONFIG_PANEL_ORDER_TOP_DOWN ].toInt() == 0;
|
||||
}
|
||||
|
||||
if ( deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].isString() )
|
||||
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toString().toInt() == 0 ? true : false;
|
||||
{
|
||||
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toString().toInt() == 0;
|
||||
}
|
||||
else
|
||||
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toInt() == 0 ? true : false;
|
||||
{
|
||||
_leftRight = deviceConfig[ CONFIG_PANEL_ORDER_LEFT_RIGHT ].toInt() == 0;
|
||||
}
|
||||
|
||||
_startPos = deviceConfig[ CONFIG_PANEL_START_POS ].toInt(0);
|
||||
_startPos = static_cast<uint>( deviceConfig[ CONFIG_PANEL_START_POS ].toInt(0) );
|
||||
|
||||
// TODO: Allow to handle port dynamically
|
||||
|
||||
//Set hostname as per configuration and_defaultHost default port
|
||||
_hostname = deviceConfig[ CONFIG_ADDRESS ].toString();
|
||||
_api_port = API_DEFAULT_PORT;
|
||||
_auth_token = deviceConfig[ CONFIG_AUTH_TOKEN ].toString();
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = deviceConfig[ CONFIG_AUTH_TOKEN ].toString();
|
||||
|
||||
//If host not configured then discover device
|
||||
//If host not configured the init failed
|
||||
if ( _hostname.isEmpty() )
|
||||
{
|
||||
//Discover Nanoleaf device
|
||||
if ( !discoverDevice() )
|
||||
this->setInError("No target hostname nor IP defined");
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( initRestAPI( _hostname, _apiPort, _authToken ) )
|
||||
{
|
||||
this->setInError("No target IP defined nor Nanoleaf device was discovered");
|
||||
return false;
|
||||
// Read LedDevice configuration and validate against device configuration
|
||||
if ( initLedsConfiguration() )
|
||||
{
|
||||
// Set UDP streaming host and port
|
||||
_devConfig["host"] = _hostname;
|
||||
_devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
|
||||
|
||||
isInitOK = ProviderUdp::init(_devConfig);
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
|
||||
Debug(_log, "Port : %d", _port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set UDP streaming port
|
||||
_devConfig["host"] = _hostname;
|
||||
_devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
|
||||
isInitOK = ProviderUdp::init(_devConfig);
|
||||
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR( _hostname ));
|
||||
Debug(_log, "Port : %d", _port);
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::initLeds()
|
||||
bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
{
|
||||
bool isInitOK = true;
|
||||
|
||||
//Get Nanoleaf device details and configuration
|
||||
_networkmanager = new QNetworkAccessManager();
|
||||
|
||||
// Read Panel count and panel Ids
|
||||
QString url = getUrl(_hostname, _api_port, _auth_token, API_ROOT );
|
||||
QJsonDocument doc = getJson( url );
|
||||
if ( this->isInError() )
|
||||
_restApi->setPath(API_ROOT);
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
this->setInError ( response.getErrorReason() );
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonObject jsonAllPanelInfo = doc.object();
|
||||
QJsonObject jsonAllPanelInfo = response.getBody().object();
|
||||
|
||||
QString deviceName = jsonAllPanelInfo[DEV_DATA_NAME].toString();
|
||||
_deviceModel = jsonAllPanelInfo[DEV_DATA_MODEL].toString();
|
||||
@@ -205,7 +236,7 @@ bool LedDeviceNanoleaf::initLeds()
|
||||
std::map<uint, std::map<uint, uint>> panelMap;
|
||||
|
||||
// Loop over all children.
|
||||
for (const QJsonValue & value : positionData)
|
||||
for (const QJsonValue value : positionData)
|
||||
{
|
||||
QJsonObject panelObj = value.toObject();
|
||||
|
||||
@@ -239,9 +270,13 @@ bool LedDeviceNanoleaf::initLeds()
|
||||
DebugIf(verbose3, _log, "panelMap[%u][%u]=%u", posY->first, posX->first, posX->second );
|
||||
|
||||
if ( _topDown )
|
||||
{
|
||||
_panelIds.push_back(posX->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
_panelIds.push_front(posX->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -252,9 +287,13 @@ bool LedDeviceNanoleaf::initLeds()
|
||||
DebugIf(verbose3, _log, "panelMap[%u][%u]=%u", posY->first, posX->first, posX->second );
|
||||
|
||||
if ( _topDown )
|
||||
{
|
||||
_panelIds.push_back(posX->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
_panelIds.push_front(posX->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,197 +339,199 @@ bool LedDeviceNanoleaf::initLeds()
|
||||
}
|
||||
}
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::initRestAPI(const QString &hostname, const int port, const QString &token )
|
||||
{
|
||||
bool isInitOK = false;
|
||||
|
||||
if ( _restApi == nullptr )
|
||||
{
|
||||
_restApi = new ProviderRestApi(hostname, port );
|
||||
|
||||
//Base-path is api-path + authentication token
|
||||
_restApi->setBasePath( QString(API_BASE_PATH).arg(token) );
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::open()
|
||||
{
|
||||
int retval = -1;
|
||||
_deviceReady = false;
|
||||
_isDeviceReady = false;
|
||||
|
||||
if ( init(_devConfig) )
|
||||
// Set Nanoleaf to External Control (UDP) mode
|
||||
Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
|
||||
QJsonDocument responseDoc = changeToExternalControlMode();
|
||||
// Resolve port for Light Panels
|
||||
QJsonObject jsonStreamControllInfo = responseDoc.object();
|
||||
if ( ! jsonStreamControllInfo.isEmpty() )
|
||||
{
|
||||
if ( !initNetwork() )
|
||||
{
|
||||
this->setInError( "UDP Network error!" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( initLeds() )
|
||||
{
|
||||
_deviceReady = true;
|
||||
setEnable(true);
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
//Set default streaming port
|
||||
_port = static_cast<uchar>(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
|
||||
}
|
||||
|
||||
if ( ProviderUdp::open() == 0 )
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::discoverDevice()
|
||||
QJsonObject LedDeviceNanoleaf::discover()
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
bool isDeviceFound (false);
|
||||
// device searching by ssdp
|
||||
QString address;
|
||||
QJsonArray deviceList;
|
||||
|
||||
// Discover Nanoleaf Devices
|
||||
SSDPDiscover discover;
|
||||
|
||||
// Discover Canvas device
|
||||
address = discover.getFirstService(searchType::STY_WEBSERVER, SSDP_CANVAS, SSDP_TIMEOUT);
|
||||
// Search for Canvas and Light-Panels
|
||||
QString searchTargetFilter = QString("%1|%2").arg(SSDP_CANVAS, SSDP_LIGHTPANELS);
|
||||
|
||||
//No Canvas device not found
|
||||
if ( address.isEmpty() ) {
|
||||
// Discover Light Panels (Aurora) device
|
||||
address = discover.getFirstService(searchType::STY_WEBSERVER, SSDP_LIGHTPANELS, SSDP_TIMEOUT);
|
||||
discover.setSearchFilter(searchTargetFilter, SSDP_FILTER_HEADER);
|
||||
QString searchTarget = SSDP_ID;
|
||||
|
||||
if ( address.isEmpty() ) {
|
||||
Warning(_log, "No Nanoleaf device discovered");
|
||||
}
|
||||
if ( discover.discoverServices(searchTarget) > 0 )
|
||||
{
|
||||
deviceList = discover.getServicesDiscoveredJson();
|
||||
}
|
||||
|
||||
// Canvas or Light Panels found
|
||||
if ( ! address.isEmpty() ) {
|
||||
Info(_log, "Nanoleaf device discovered at [%s]", QSTRING_CSTR( address ));
|
||||
isDeviceFound = true;
|
||||
// Resolve hostname and port (or use default API port)
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QStringList addressparts = address.split(":", Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList addressparts = address.split(":", QString::SkipEmptyParts);
|
||||
#endif
|
||||
_hostname = addressparts[0];
|
||||
_api_port = addressparts[1];
|
||||
}
|
||||
return isDeviceFound;
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
// Get Nanoleaf device properties
|
||||
QString host = params["host"].toString("");
|
||||
if ( !host.isEmpty() )
|
||||
{
|
||||
QString authToken = params["token"].toString("");
|
||||
QString filter = params["filter"].toString("");
|
||||
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
if ( addressparts.size() > 1)
|
||||
{
|
||||
apiPort = addressparts[1].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiPort = API_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
initRestAPI(apiHost, apiPort, authToken);
|
||||
_restApi->setPath(filter);
|
||||
|
||||
// Perform request
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
|
||||
properties.insert("properties", response.getBody().object());
|
||||
|
||||
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
// Get Nanoleaf device properties
|
||||
QString host = params["host"].toString("");
|
||||
if ( !host.isEmpty() )
|
||||
{
|
||||
QString authToken = params["token"].toString("");
|
||||
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
if ( addressparts.size() > 1)
|
||||
{
|
||||
apiPort = addressparts[1].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiPort = API_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
initRestAPI(apiHost, apiPort, authToken);
|
||||
_restApi->setPath("identify");
|
||||
|
||||
// Perform request
|
||||
httpResponse response = _restApi->put();
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::powerOn()
|
||||
{
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
//Power-on Nanoleaf device
|
||||
_restApi->setPath(API_STATE);
|
||||
_restApi->put( getOnOffRequest(true) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::powerOff()
|
||||
{
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
//Power-off the Nanoleaf device physically
|
||||
_restApi->setPath(API_STATE);
|
||||
_restApi->put( getOnOffRequest(false) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const
|
||||
{
|
||||
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON, STATE_ONOFF_VALUE, state);
|
||||
}
|
||||
|
||||
QJsonDocument LedDeviceNanoleaf::changeToExternalControlMode()
|
||||
{
|
||||
|
||||
QString url = getUrl(_hostname, _api_port, _auth_token, API_EFFECT );
|
||||
QJsonDocument jsonDoc;
|
||||
|
||||
_extControlVersion = EXTCTRLVER_V2;
|
||||
//Enable UDP Mode v2
|
||||
jsonDoc= putJson(url, API_EXT_MODE_STRING_V2);
|
||||
|
||||
return jsonDoc;
|
||||
}
|
||||
_restApi->setPath(API_EFFECT);
|
||||
httpResponse response =_restApi->put(API_EXT_MODE_STRING_V2);
|
||||
|
||||
QString LedDeviceNanoleaf::getUrl(QString host, QString port, QString auth_token, QString endpoint) const {
|
||||
return QString(API_URL_FORMAT).arg(host, port, auth_token, endpoint);
|
||||
}
|
||||
|
||||
QJsonDocument LedDeviceNanoleaf::getJson(QString url)
|
||||
{
|
||||
|
||||
Debug(_log, "GET: [%s]", QSTRING_CSTR( url ));
|
||||
|
||||
// Perfrom request
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply* reply = _networkmanager->get(request);
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
|
||||
QJsonDocument jsonDoc;
|
||||
if(reply->operation() == QNetworkAccessManager::GetOperation)
|
||||
{
|
||||
jsonDoc = handleReply( reply );
|
||||
}
|
||||
// Free space.
|
||||
reply->deleteLater();
|
||||
// Return response
|
||||
return jsonDoc;
|
||||
}
|
||||
|
||||
QJsonDocument LedDeviceNanoleaf::putJson(QString url, QString json)
|
||||
{
|
||||
|
||||
Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url ), QSTRING_CSTR( json ) );
|
||||
// Perfrom request
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply* reply = _networkmanager->put(request, json.toUtf8());
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
|
||||
QJsonDocument jsonDoc;
|
||||
if(reply->operation() == QNetworkAccessManager::PutOperation)
|
||||
{
|
||||
jsonDoc = handleReply( reply );
|
||||
}
|
||||
// Free space.
|
||||
reply->deleteLater();
|
||||
|
||||
// Return response
|
||||
return jsonDoc;
|
||||
}
|
||||
|
||||
QJsonDocument LedDeviceNanoleaf::handleReply(QNetworkReply* const &reply )
|
||||
{
|
||||
|
||||
QJsonDocument jsonDoc;
|
||||
|
||||
int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
|
||||
Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode );
|
||||
|
||||
if(reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
if ( httpStatusCode != 204 ){
|
||||
QByteArray response = reply->readAll();
|
||||
QJsonParseError error;
|
||||
jsonDoc = QJsonDocument::fromJson(response, &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
this->setInError ( "Got invalid response" );
|
||||
}
|
||||
else {
|
||||
//Debug
|
||||
QString strJson(jsonDoc.toJson(QJsonDocument::Compact));
|
||||
DebugIf(verbose, _log, "Reply: [%s]", strJson.toUtf8().constData() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QString errorReason;
|
||||
if ( httpStatusCode > 0 ) {
|
||||
QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case 400:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case 401:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case 404:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("%1:%2 [%3 %4] - %5").arg(_hostname, _api_port, QString(httpStatusCode) , httpReason, advise);
|
||||
}
|
||||
else {
|
||||
errorReason = QString ("%1:%2 - %3").arg(_hostname, _api_port, reply->errorString());
|
||||
}
|
||||
this->setInError ( errorReason );
|
||||
}
|
||||
// Return response
|
||||
return jsonDoc;
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & ledValues)
|
||||
{
|
||||
|
||||
int retVal = 0;
|
||||
uint udpBufferSize;
|
||||
|
||||
@@ -573,46 +614,6 @@ int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & ledValues)
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const
|
||||
{
|
||||
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON, STATE_ONOFF_VALUE, state);
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::switchOn()
|
||||
{
|
||||
if ( _deviceReady)
|
||||
{
|
||||
// Set Nanoleaf to External Control (UDP) mode
|
||||
Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
|
||||
QJsonDocument responseDoc = changeToExternalControlMode();
|
||||
// Resolve port for Ligh Panels
|
||||
QJsonObject jsonStreamControllInfo = responseDoc.object();
|
||||
if ( ! jsonStreamControllInfo.isEmpty() ) {
|
||||
_port = static_cast<uchar>(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
|
||||
}
|
||||
|
||||
//Switch on Nanoleaf device
|
||||
QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE );
|
||||
putJson(url, this->getOnOffRequest(true) );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::switchOff()
|
||||
{
|
||||
//Set all LEDs to Black
|
||||
int rc = LedDevice::switchOff();
|
||||
|
||||
if ( _deviceReady)
|
||||
{
|
||||
//Switch off Nanoleaf device physically
|
||||
QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE );
|
||||
putJson(url, getOnOffRequest(false) );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::string LedDeviceNanoleaf:: uint8_vector_to_hex_string( const std::vector<uint8_t>& buffer ) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
Reference in New Issue
Block a user