From 847655344869cb86739385c3323d430fd64d1e3d Mon Sep 17 00:00:00 2001 From: LordGrey Date: Fri, 16 Oct 2020 00:03:49 +0200 Subject: [PATCH] Razor Chroma Support - Initial version --- assets/webconfig/i18n/en.json | 1 + assets/webconfig/js/content_leds.js | 4 +- include/leddevice/LedDevice.h | 4 +- libsrc/leddevice/LedDeviceSchemas.qrc | 2 +- libsrc/leddevice/dev_net/LedDeviceRazer.cpp | 254 +++++++++++++++++++ libsrc/leddevice/dev_net/LedDeviceRazer.h | 101 ++++++++ libsrc/leddevice/dev_net/ProviderRestApi.cpp | 179 +++++++++---- libsrc/leddevice/dev_net/ProviderRestApi.h | 91 +++++-- libsrc/leddevice/schemas/schema-razer.json | 28 ++ 9 files changed, 588 insertions(+), 76 deletions(-) create mode 100644 libsrc/leddevice/dev_net/LedDeviceRazer.cpp create mode 100644 libsrc/leddevice/dev_net/LedDeviceRazer.h create mode 100644 libsrc/leddevice/schemas/schema-razer.json diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 6f8d3816..df22f96a 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -492,6 +492,7 @@ "edt_dev_spec_printTimeStamp_title": "Add timestamp", "edt_dev_spec_pwmChannel_title": "PWM channel", "edt_dev_spec_restoreOriginalState_title": "Restore lights' original state when disabled", + "edt_dev_spec_razor_device_title": "Razor Chroma Device", "edt_dev_spec_serial_title": "Serial number", "edt_dev_spec_spipath_title": "SPI path", "edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum", diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 21b7f32f..56fd9d7a 100644 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -571,8 +571,8 @@ $(document).ready(function() { var devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi']; var devRPiPWM = ['ws281x']; var devRPiGPIO = ['piblaster']; - var devNET = ['atmoorb', 'fadecandy', 'philipshue', 'nanoleaf', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw', 'wled', 'yeelight']; - var devUSB = ['adalight', 'dmx', 'atmo', 'hyperionusbasp', 'lightpack', 'multilightpack', 'paintpack', 'rawhid', 'sedu', 'tpm2', 'karate']; + var devNET = ['atmoorb', 'fadecandy', 'philipshue', 'nanoleaf', 'razer', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw', 'wled', 'yeelight']; + var devUSB = ['adalight', 'dmx', 'atmo', 'hyperionusbasp', 'lightpack', 'paintpack', 'rawhid', 'sedu', 'tpm2', 'karate']; var optArr = [[]]; optArr[1]=[]; diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index a9dcf0c5..03c2ddd7 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -414,14 +414,14 @@ protected slots: /// /// @return Zero on success else negative /// - int rewriteLEDs(); + virtual int rewriteLEDs(); /// /// @brief Set device in error state /// /// @param[in] errorMsg The error message to be logged /// - virtual void setInError( const QString& errorMsg); + virtual void setInError( const QString& errorMsg); private: diff --git a/libsrc/leddevice/LedDeviceSchemas.qrc b/libsrc/leddevice/LedDeviceSchemas.qrc index 8977d9ca..9ed5fc6f 100644 --- a/libsrc/leddevice/LedDeviceSchemas.qrc +++ b/libsrc/leddevice/LedDeviceSchemas.qrc @@ -11,7 +11,6 @@ schemas/schema-lightpack.json schemas/schema-lpd6803.json schemas/schema-lpd8806.json - schemas/schema-multilightpack.json schemas/schema-p9813.json schemas/schema-paintpack.json schemas/schema-philipshue.json @@ -36,5 +35,6 @@ schemas/schema-nanoleaf.json schemas/schema-wled.json schemas/schema-yeelight.json + schemas/schema-razer.json diff --git a/libsrc/leddevice/dev_net/LedDeviceRazer.cpp b/libsrc/leddevice/dev_net/LedDeviceRazer.cpp new file mode 100644 index 00000000..36c10370 --- /dev/null +++ b/libsrc/leddevice/dev_net/LedDeviceRazer.cpp @@ -0,0 +1,254 @@ +#include "LedDeviceRazer.h" +#include + +#if _WIN32 +#include +#endif +// Constants +namespace { +bool verbose = false; + +// Configuration settings +const char CONFIG_ADDRESS[] = "host"; +const char RAZOR_DEVICE_TYPE[] = "razorDevice"; + +// WLED JSON-API elements +const char API_DEFAULT_HOST[] = "localhost"; +const int API_DEFAULT_PORT = 54235; + +const char API_BASE_PATH[] = "/razer/chromasdk"; +const char API_PATH_INFO[] = "info"; +const char API_PATH_STATE[] = "state"; + +const char API_RESULT[] = "result"; + +// List of State Information +const char STATE_ON[] = "on"; +const char STATE_VALUE_TRUE[] = "true"; +const char STATE_VALUE_FALSE[] = "false"; +} //End of constants + +LedDeviceRazer::LedDeviceRazer(const QJsonObject& deviceConfig) + : LedDevice() + , _restApi(nullptr) + , _apiPort(API_DEFAULT_PORT) +{ + _devConfig = deviceConfig; + _isDeviceReady = false; + + _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower(); +} + +LedDevice* LedDeviceRazer::construct(const QJsonObject& deviceConfig) +{ + return new LedDeviceRazer(deviceConfig); +} + +bool LedDeviceRazer::init(const QJsonObject& deviceConfig) +{ + bool isInitOK = false; + setRewriteTime(1000); + connect(_refreshTimer, &QTimer::timeout, this, &LedDeviceRazer::rewriteLEDs); + + // 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); + + //Razor Chroma SDK allows localhost connection only + _hostname = API_DEFAULT_HOST; + _apiPort = API_DEFAULT_PORT; + + Debug(_log, "Hostname : %s", QSTRING_CSTR(_hostname)); + Debug(_log, "Port : %d", _apiPort); + + _razerDeviceType = deviceConfig[RAZOR_DEVICE_TYPE].toString("keyboard"); + + Debug(_log, "Razer device : %s", QSTRING_CSTR(_razerDeviceType)); + + if (initRestAPI(_hostname, _apiPort)) + { + isInitOK = true; + } + } + + return isInitOK; +} + +bool LedDeviceRazer::initRestAPI(const QString& hostname, int port) +{ + bool isInitOK = false; + + if (_restApi == nullptr) + { + _restApi = new ProviderRestApi(hostname, port); + _restApi->setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + isInitOK = true; + } + + return isInitOK; +} + +bool LedDeviceRazer::checkApiError(const httpResponse& response) +{ + bool apiError = false; + + if (response.error()) + { + this->setInError(response.getErrorReason()); + apiError = true; + } + else + { + QString errorReason; + + QString strJson(response.getBody().toJson(QJsonDocument::Compact)); + DebugIf(verbose, _log, "Reply: [%s]", strJson.toUtf8().constData()); + + QJsonObject jsonObj = response.getBody().object(); + + if (!jsonObj[API_RESULT].isNull()) + { + int resultCode = jsonObj[API_RESULT].toInt(); + + if (resultCode != 0) + { + errorReason = QString("Chroma SDK error (%1)").arg(resultCode); + this->setInError(errorReason); + apiError = true; + } + } + } + return apiError; +} + +int LedDeviceRazer::open() +{ + int retval = -1; + QString errortext; + _isDeviceReady = false; + + // Try to open the LedDevice + + QJsonObject obj; + + obj.insert("title", "Hyperion - Razer Chroma"); + obj.insert("description", "Hyperion to Razer Chroma interface"); + + QJsonObject authorDetails; + authorDetails.insert("name", "Hyperion Team"); + authorDetails.insert("contact", "https://github.com/hyperion-project/hyperion.ng"); + + obj.insert("author", authorDetails); + + QJsonArray deviceList = { "keyboard","mouse","headset","mousepad","keypad","chromalink" }; + obj.insert("device_supported", deviceList); + + obj.insert("category", "application"); + + QJsonDocument data = QJsonDocument(obj); + + _restApi->setPort(API_DEFAULT_PORT); + _restApi->setBasePath(API_BASE_PATH); + + httpResponse response = _restApi->post(data.toJson(QJsonDocument::Compact)); + if (!checkApiError(response)) + { + QJsonObject jsonObj = response.getBody().object(); + if (jsonObj["uri"].isNull()) + { + this->setInError("Chroma SDK error. No 'uri' received"); + } + else + { + _uri = jsonObj.value("uri").toString(); + _restApi->setUrl(_uri); + + DebugIf(verbose, _log, "Session-ID: %d, uri [%s]", jsonObj.value("sessionid").toInt(), QSTRING_CSTR(_uri.toString())); + + QJsonObject effectObj; + effectObj.insert("effect", "CHROMA_STATIC"); + QJsonObject param; + param.insert("color", 255); + effectObj.insert("param", param); + data = QJsonDocument(effectObj); + + _restApi->setPath(_razerDeviceType); + response = _restApi->put(data.toJson(QJsonDocument::Compact)); + + if (!checkApiError(response)) + { + _restApi->setPath(_razerDeviceType); + response = _restApi->put(data.toJson(QJsonDocument::Compact)); + + if (!checkApiError(response)) + { + // Everything is OK, device is ready + _isDeviceReady = true; + retval = 0; + } + } + } + } + return retval; +} + +int LedDeviceRazer::close() +{ + int retval = -1; + _isDeviceReady = false; + + if (!_uri.isEmpty()) + { + httpResponse response = _restApi->deleteResource(_uri); + if (!checkApiError(response)) + { + // Everything is OK -> device is closed + retval = 0; + } + } + return retval; +} + +int LedDeviceRazer::write(const std::vector& ledValues) +{ + int retval = -1; + + ColorRgb color = ledValues[0]; + int colorParam = (color.red * 65536) + (color.green * 256) + color.blue; + + QJsonObject effectObj; + effectObj.insert("effect", "CHROMA_STATIC"); + QJsonObject param; + param.insert("color", colorParam); + effectObj.insert("param", param); + QJsonDocument data = QJsonDocument(effectObj); + + _restApi->setPath(_razerDeviceType); + httpResponse response = _restApi->put(data.toJson(QJsonDocument::Compact)); + if (!checkApiError(response)) + { + retval = 0; + } + return retval; +} + +int LedDeviceRazer::rewriteLEDs() +{ + int retval = -1; + + _restApi->setPath("heartbeat"); + httpResponse response = _restApi->put(); + if (!checkApiError(response)) + { + retval = 0; + } + return retval; +} diff --git a/libsrc/leddevice/dev_net/LedDeviceRazer.h b/libsrc/leddevice/dev_net/LedDeviceRazer.h new file mode 100644 index 00000000..dbbb673a --- /dev/null +++ b/libsrc/leddevice/dev_net/LedDeviceRazer.h @@ -0,0 +1,101 @@ +#ifndef LEDEVICERAZER_H +#define LEDEVICERAZER_H + +// LedDevice includes +#include +#include "ProviderRestApi.h" + +/// +/// Implementation of a Razer Chroma LedDevice +/// Supported Razer Chroma device types: Keyboard, Mouse, Headset, Mousepad, Keypad, Chromalink +/// +class LedDeviceRazer : public LedDevice +{ +public: + + /// + /// @brief Constructs a specific LED-device + /// + /// @param deviceConfig Device's configuration as JSON-Object + /// + explicit LedDeviceRazer(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 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. + /// + /// @param[in] ledValues The RGB-color per LED + /// @return Zero on success, else negative + /// + int write(const std::vector& ledValues) override; + +protected slots: + + /// + /// @brief Write the last data to the LEDs again. + /// + /// @return Zero on success else negative + /// + int rewriteLEDs() override; + +private: + + /// + /// @brief Initialise the access to the REST-API wrapper + /// + /// @param[in] host + /// @param[in] port + /// @return True, if success + /// + bool initRestAPI(const QString& hostname, int port); + + /// + /// @brief Check, if Chroma SDK API response failed + /// + /// @param[in] http response, incl. the response by Chroma SDK in JSON-format + /// return True, API call failed + /// + bool checkApiError(const httpResponse& response); + + ///REST-API wrapper + ProviderRestApi* _restApi; + + QString _hostname; + int _apiPort; + QUrl _uri; + + QString _razerDeviceType; +}; + +#endif // LEDEVICERAZER_H diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.cpp b/libsrc/leddevice/dev_net/ProviderRestApi.cpp index 5ad71e90..2acd27a8 100644 --- a/libsrc/leddevice/dev_net/ProviderRestApi.cpp +++ b/libsrc/leddevice/dev_net/ProviderRestApi.cpp @@ -12,26 +12,25 @@ // Constants namespace { +bool verbose = false; + const QChar ONE_SLASH = '/'; } //End of constants -ProviderRestApi::ProviderRestApi(const QString &host, int port, const QString &basePath) +ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath) :_log(Logger::getInstance("LEDDEVICE")) - ,_networkManager(nullptr) - ,_scheme("http") - ,_hostname(host) - ,_port(port) + , _networkManager(nullptr) { _networkManager = new QNetworkAccessManager(); - _apiUrl.setScheme(_scheme); + _apiUrl.setScheme("http"); _apiUrl.setHost(host); _apiUrl.setPort(port); _basePath = basePath; } -ProviderRestApi::ProviderRestApi(const QString &host, int port) +ProviderRestApi::ProviderRestApi(const QString& host, int port) : ProviderRestApi(host, port, "") {} ProviderRestApi::ProviderRestApi() @@ -39,46 +38,52 @@ ProviderRestApi::ProviderRestApi() ProviderRestApi::~ProviderRestApi() { - if ( _networkManager != nullptr ) + if (_networkManager != nullptr) { delete _networkManager; } } -void ProviderRestApi::setBasePath(const QString &basePath) +void ProviderRestApi::setUrl(const QUrl& url) +{ + _apiUrl = url; + _basePath = url.path(); +} + +void ProviderRestApi::setBasePath(const QString& basePath) { _basePath.clear(); - appendPath (_basePath, basePath ); + appendPath(_basePath, basePath); } -void ProviderRestApi::setPath ( const QString &path ) +void ProviderRestApi::setPath(const QString& path) { _path.clear(); - appendPath (_path, path ); + appendPath(_path, path); } -void ProviderRestApi::appendPath ( const QString &path ) +void ProviderRestApi::appendPath(const QString& path) { - appendPath (_path, path ); + appendPath(_path, path); } -void ProviderRestApi::appendPath ( QString& path, const QString &appendPath) const +void ProviderRestApi::appendPath(QString& path, const QString& appendPath) const { - if ( !appendPath.isEmpty() && appendPath != ONE_SLASH ) + if (!appendPath.isEmpty() && appendPath != ONE_SLASH) { - if (path.isEmpty() || path == ONE_SLASH ) + if (path.isEmpty() || path == ONE_SLASH) { path.clear(); - if (appendPath[0] != ONE_SLASH ) + if (appendPath[0] != ONE_SLASH) { path.push_back(ONE_SLASH); } } - else if (path[path.size()-1] == ONE_SLASH && appendPath[0] == ONE_SLASH) + else if (path[path.size() - 1] == ONE_SLASH && appendPath[0] == ONE_SLASH) { path.chop(1); } - else if (path[path.size()-1] != ONE_SLASH && appendPath[0] != ONE_SLASH) + else if (path[path.size() - 1] != ONE_SLASH && appendPath[0] != ONE_SLASH) { path.push_back(ONE_SLASH); } @@ -91,12 +96,12 @@ void ProviderRestApi::appendPath ( QString& path, const QString &appendPath) con } } -void ProviderRestApi::setFragment(const QString &fragment) +void ProviderRestApi::setFragment(const QString& fragment) { _fragment = fragment; } -void ProviderRestApi::setQuery(const QUrlQuery &query) +void ProviderRestApi::setQuery(const QUrlQuery& query) { _query = query; } @@ -106,22 +111,22 @@ QUrl ProviderRestApi::getUrl() const QUrl url = _apiUrl; QString fullPath = _basePath; - appendPath (fullPath, _path ); + appendPath(fullPath, _path); url.setPath(fullPath); - url.setFragment( _fragment ); - url.setQuery( _query ); + url.setFragment(_fragment); + url.setQuery(_query); return url; } httpResponse ProviderRestApi::get() { - return get( getUrl() ); + return get(getUrl()); } -httpResponse ProviderRestApi::get(const QUrl &url) +httpResponse ProviderRestApi::get(const QUrl& url) { - Debug(_log, "GET: [%s]", QSTRING_CSTR( url.toString() )); + DebugIf(verbose,_log, "GET: [%s]", QSTRING_CSTR(url.toString())); // Perform request QNetworkRequest request(url); @@ -133,9 +138,9 @@ httpResponse ProviderRestApi::get(const QUrl &url) loop.exec(); httpResponse response; - if(reply->operation() == QNetworkAccessManager::GetOperation) + if (reply->operation() == QNetworkAccessManager::GetOperation) { - response = getResponse(reply ); + response = getResponse(reply); } // Free space. reply->deleteLater(); @@ -143,16 +148,18 @@ httpResponse ProviderRestApi::get(const QUrl &url) return response; } -httpResponse ProviderRestApi::put(const QString &body) +httpResponse ProviderRestApi::put(const QString& body) { - return put( getUrl(), body ); + return put(getUrl(), body); } -httpResponse ProviderRestApi::put(const QUrl &url, const QString &body) +httpResponse ProviderRestApi::put(const QUrl& url, const QString& body) { - Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url.toString() ), QSTRING_CSTR( body ) ); + DebugIf(verbose, _log, "PUT: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body)); // Perform request - QNetworkRequest request(url); + QNetworkRequest request(_networkRequestHeaders); + request.setUrl(url); + QNetworkReply* reply = _networkManager->put(request, body.toUtf8()); // Connect requestFinished signal to quit slot of the loop. QEventLoop loop; @@ -161,7 +168,7 @@ httpResponse ProviderRestApi::put(const QUrl &url, const QString &body) loop.exec(); httpResponse response; - if(reply->operation() == QNetworkAccessManager::PutOperation) + if (reply->operation() == QNetworkAccessManager::PutOperation) { response = getResponse(reply); } @@ -172,23 +179,80 @@ httpResponse ProviderRestApi::put(const QUrl &url, const QString &body) return response; } -httpResponse ProviderRestApi::getResponse(QNetworkReply* const &reply) +httpResponse ProviderRestApi::post(const QString& body) +{ + return post(getUrl(), body); +} + +httpResponse ProviderRestApi::post(const QUrl& url, const QString& body) +{ + DebugIf(verbose, _log, "POST: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body)); + // Perform request + QNetworkRequest request(_networkRequestHeaders); + request.setUrl(url); + + QNetworkReply* reply = _networkManager->post(request, body.toUtf8()); + // Connect requestFinished signal to quit slot of the loop. + QEventLoop loop; + loop.connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + // Go into the loop until the request is finished. + loop.exec(); + + httpResponse response; + if (reply->operation() == QNetworkAccessManager::PostOperation) + { + response = getResponse(reply); + } + // Free space. + reply->deleteLater(); + + // Return response + return response; +} + +httpResponse ProviderRestApi::deleteResource(const QUrl& url) +{ + DebugIf(verbose, _log, "DELETE: [%s]", QSTRING_CSTR(url.toString())); + // Perform request + QNetworkRequest request(_networkRequestHeaders); + request.setUrl(url); + + QNetworkReply* reply = _networkManager->deleteResource(request); + // Connect requestFinished signal to quit slot of the loop. + QEventLoop loop; + loop.connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + // Go into the loop until the request is finished. + loop.exec(); + + httpResponse response; + if (reply->operation() == QNetworkAccessManager::DeleteOperation) + { + response = getResponse(reply); + } + // Free space. + reply->deleteLater(); + + // Return response + return response; +} + +httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply) { httpResponse response; - int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); + int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); response.setHttpStatusCode(httpStatusCode); - Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode ); + DebugIf(verbose, _log, "Reply.error [%d], Reply.httpStatusCode [%d]", reply->error(), httpStatusCode); response.setNetworkReplyError(reply->error()); - if(reply->error() == QNetworkReply::NoError) + if (reply->error() == QNetworkReply::NoError) { - if ( httpStatusCode != 204 ){ + if (httpStatusCode != 204) { QByteArray replyData = reply->readAll(); - if ( !replyData.isEmpty()) + if (!replyData.isEmpty()) { QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(replyData, &error); @@ -202,23 +266,23 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const &reply) } else { - //std::cout << "Response: [" << QString (jsonDoc.toJson(QJsonDocument::Compact)).toStdString() << "]" << std::endl; - response.setBody( jsonDoc ); + //std::cout << "Response: [" << QString(jsonDoc.toJson(QJsonDocument::Compact)).toStdString() << "]" << std::endl; + response.setBody(jsonDoc); } } else { // Create valid body which is empty - response.setBody( QJsonDocument() ); + response.setBody(QJsonDocument()); } } } else { QString errorReason; - if ( httpStatusCode > 0 ) { - QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); + if (httpStatusCode > 0) { + QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); QString advise; - switch ( httpStatusCode ) { + switch (httpStatusCode) { case 400: advise = "Check Request Body"; break; @@ -231,7 +295,7 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const &reply) default: break; } - errorReason = QString ("[%3 %4] - %5").arg(QString(httpStatusCode) , httpReason, advise); + errorReason = QString("[%3 %4] - %5").arg(QString(httpStatusCode), httpReason, advise); } else { errorReason = reply->errorString(); @@ -240,8 +304,23 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const &reply) response.setErrorReason(errorReason); // Create valid body which is empty - response.setBody( QJsonDocument() ); + response.setBody(QJsonDocument()); } return response; } +void ProviderRestApi::setHeader(QNetworkRequest::KnownHeaders header, const QVariant& value) +{ + QVariant headerValue = _networkRequestHeaders.header(header); + if (headerValue.isNull()) + { + _networkRequestHeaders.setHeader(header, value); + } + else + { + if (!headerValue.toString().contains(value.toString())) + { + _networkRequestHeaders.setHeader(header, headerValue.toString() + "," + value.toString()); + } + } +} diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.h b/libsrc/leddevice/dev_net/ProviderRestApi.h index 5a66bb20..5ab5500e 100644 --- a/libsrc/leddevice/dev_net/ProviderRestApi.h +++ b/libsrc/leddevice/dev_net/ProviderRestApi.h @@ -18,20 +18,20 @@ class httpResponse public: httpResponse() = default; - bool error() const { return _hasError;} + bool error() const { return _hasError; } void setError(const bool hasError) { _hasError = hasError; } QJsonDocument getBody() const { return _responseBody; } - void setBody(const QJsonDocument &body) { _responseBody = body; } + void setBody(const QJsonDocument& body) { _responseBody = body; } QString getErrorReason() const { return _errorReason; } - void setErrorReason(const QString &errorReason) { _errorReason = errorReason; } + void setErrorReason(const QString& errorReason) { _errorReason = errorReason; } int getHttpStatusCode() const { return _httpStatusCode; } void setHttpStatusCode(int httpStatusCode) { _httpStatusCode = httpStatusCode; } QNetworkReply::NetworkError getNetworkReplyError() const { return _networkReplyError; } - void setNetworkReplyError (const QNetworkReply::NetworkError networkReplyError) { _networkReplyError = networkReplyError; } + void setNetworkReplyError(const QNetworkReply::NetworkError networkReplyError) { _networkReplyError = networkReplyError; } private: @@ -77,7 +77,7 @@ public: /// @param[in] host /// @param[in] port /// - explicit ProviderRestApi(const QString &host, int port); + explicit ProviderRestApi(const QString& host, int port); /// /// @brief Constructor of the REST-API wrapper @@ -86,13 +86,34 @@ public: /// @param[in] port /// @param[in] API base-path /// - explicit ProviderRestApi(const QString &host, int port, const QString &basePath); + explicit ProviderRestApi(const QString& host, int port, const QString& basePath); /// /// @brief Destructor of the REST-API wrapper /// virtual ~ProviderRestApi(); + /// + /// @brief Set an API's host + /// + /// @param[in] host + /// + void setHost(const QString& host) { _apiUrl.setHost(host); }; + + /// + /// @brief Set an API's port + /// + /// @param[in] port + /// + void setPort(const int port) { _apiUrl.setPort(port); }; + + /// + /// @brief Set an API's url + /// + /// @param[in] url, e.g. "http://locahost:60351/chromalink/" + /// + void setUrl(const QUrl& url); + /// /// @brief Get the URL as defined using scheme, host, port, API-basepath, path, query, fragment /// @@ -105,35 +126,35 @@ public: /// /// @param[in] basePath, e.g. "/api/v1/" or "/json" /// - void setBasePath(const QString &basePath); + void setBasePath(const QString& basePath); /// /// @brief Set an API's path to address resources /// /// @param[in] path, e.g. "/lights/1/state/" /// - void setPath ( const QString &path ); + void setPath(const QString& path); /// /// @brief Append an API's path element to path set before /// /// @param[in] path /// - void appendPath (const QString &appendPath); + void appendPath(const QString& appendPath); /// /// @brief Set an API's fragment /// /// @param[in] fragment, e.g. "question3" /// - void setFragment(const QString&fragment); + void setFragment(const QString& fragment); /// /// @brief Set an API's query string /// /// @param[in] query, e.g. "&A=128&FX=0" /// - void setQuery(const QUrlQuery &query); + void setQuery(const QUrlQuery& query); /// /// @brief Execute GET request @@ -148,7 +169,7 @@ public: /// @param[in] url GET request for URL /// @return Response The body of the response in JSON /// - httpResponse get(const QUrl &url); + httpResponse get(const QUrl& url); /// /// @brief Execute PUT request @@ -156,7 +177,7 @@ public: /// @param[in] body The body of the request in JSON /// @return Response The body of the response in JSON /// - httpResponse put(const QString &body = ""); + httpResponse put(const QString& body = ""); /// /// @brief Execute PUT request @@ -165,7 +186,7 @@ public: /// @param[in] body The body of the request in JSON /// @return Response The body of the response in JSON /// - httpResponse put(const QUrl &url, const QString &body = ""); + httpResponse put(const QUrl& url, const QString& body = ""); /// /// @brief Execute POST request @@ -173,7 +194,24 @@ public: /// @param[in] body The body of the request in JSON /// @return Response The body of the response in JSON /// - httpResponse post(QString body = ""); + httpResponse post(const QString& body = ""); + + /// + /// @brief Execute POST request + /// + /// @param[in] URL for POST request + /// @param[in] body The body of the request in JSON + /// @return Response The body of the response in JSON + /// + httpResponse post(const QUrl& url, const QString& body = ""); + + /// + /// @brief Execute DELETE request + /// + /// @param[in] URL (Resource) for DELETE request + /// @return Response The body of the response in JSON + /// + httpResponse deleteResource(const QUrl& url); /// /// @brief Handle responses for REST requests @@ -181,7 +219,21 @@ public: /// @param[in] reply Network reply /// @return Response The body of the response in JSON /// - httpResponse getResponse(QNetworkReply* const &reply); + httpResponse getResponse(QNetworkReply* const& reply); + + /// + /// Adds a header field. + /// + /// @param[in] The type of the header field. + /// @param[in] The value of the header field. + /// If the header field exists, the value will be combined as comma separated string. + + void setHeader(QNetworkRequest::KnownHeaders header, const QVariant& value); + + /// + /// Remove all header fields. + /// + void removeAllHeaders() { _networkRequestHeaders = QNetworkRequest(); }; private: @@ -191,7 +243,7 @@ private: /// @param[in/out] path to be updated /// @param[in] path, element to be appended /// - void appendPath (QString &path, const QString &appendPath) const; + void appendPath(QString& path, const QString& appendPath) const; Logger* _log; @@ -200,16 +252,13 @@ private: QUrl _apiUrl; - QString _scheme; - QString _hostname; - int _port; - QString _basePath; QString _path; QString _fragment; QUrlQuery _query; + QNetworkRequest _networkRequestHeaders; }; #endif // PROVIDERRESTKAPI_H diff --git a/libsrc/leddevice/schemas/schema-razer.json b/libsrc/leddevice/schemas/schema-razer.json new file mode 100644 index 00000000..36ce16ea --- /dev/null +++ b/libsrc/leddevice/schemas/schema-razer.json @@ -0,0 +1,28 @@ +{ + "type": "object", + "required": true, + "properties": { + "razorDevice": { + "type": "string", + "title": "edt_dev_spec_razor_device_title", + "enum": [ "keyboard", "mouse", "headset", "mousepad", "keypad", "chromalink" ], + "default": "keyboard", + "options": { + "enum_titles": [ "Keyboard", "Mouse", "Headset", "Mousepad", "Keypad", "Chromalink" ] + }, + "propertyOrder": 1 + }, + "latchTime": { + "type": "integer", + "title": "edt_dev_spec_latchtime_title", + "default": 0, + "append": "edt_append_ms", + "minimum": 0, + "maximum": 1000, + "access": "expert", + "propertyOrder": 2 + } + }, + "additionalProperties": true +} +