mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Philips Hue APIv2 support (#1637)
* Support Philips Hue APIv2 and refactoring * Fix MDNSBrower - if timeout during host resolvment occurs * Hue API v2 - Migrate database * Fix macOS build * Handle network timeout before any other error * Address CodeQL findings * Clean-up and Fixes * Only getProperties, if username is available * Option to layout by entertainment area center * Fix Wizard --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -16,31 +16,6 @@
|
||||
#include "ProviderRestApi.h"
|
||||
#include "ProviderUdpSSL.h"
|
||||
|
||||
//Streaming message header and payload definition
|
||||
const uint8_t HEADER[] =
|
||||
{
|
||||
'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', //protocol
|
||||
0x01, 0x00, //version 1.0
|
||||
0x01, //sequence number 1
|
||||
0x00, 0x00, //Reserved write 0’s
|
||||
0x01, //xy Brightness
|
||||
0x00, // Reserved, write 0’s
|
||||
};
|
||||
|
||||
const uint8_t PAYLOAD_PER_LIGHT[] =
|
||||
{
|
||||
0x01, 0x00, 0x06, //light ID
|
||||
//color: 16 bpc
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
/*
|
||||
(message.R >> 8) & 0xff, message.R & 0xff,
|
||||
(message.G >> 8) & 0xff, message.G & 0xff,
|
||||
(message.B >> 8) & 0xff, message.B & 0xff
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* A XY color point in the color space of the hue system without brightness.
|
||||
*/
|
||||
@@ -145,12 +120,18 @@ public:
|
||||
/// Constructs the light.
|
||||
///
|
||||
/// @param log the logger
|
||||
/// @param bridge the bridge
|
||||
/// @param useApiV2 make use of Hue API version 2
|
||||
/// @param id the light id
|
||||
/// @param lightAttributes the light's attributes as provied by the Hue Bridge
|
||||
/// @param onBlackTimeToPowerOff Timeframe of Black output that triggers powering off the light
|
||||
/// @param onBlackTimeToPowerOn Timeframe of non Black output that triggers powering on the light
|
||||
///
|
||||
PhilipsHueLight(Logger* log, int id, QJsonObject values, int ledidx,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
PhilipsHueLight(Logger* log, bool useApiV2, const QString& id, const QJsonObject& lightAttributes,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
|
||||
void setDeviceDetails(const QJsonObject& details);
|
||||
void setEntertainmentSrvDetails(const QJsonObject& details);
|
||||
|
||||
///
|
||||
/// @param on
|
||||
@@ -167,7 +148,14 @@ public:
|
||||
///
|
||||
void setColor(const CiColor& color);
|
||||
|
||||
int getId() const;
|
||||
QString getId() const;
|
||||
QString getdeviceId() const;
|
||||
QString getProduct() const;
|
||||
QString getModel() const;
|
||||
QString getName() const;
|
||||
QString getArcheType() const;
|
||||
|
||||
int getMaxSegments() const;
|
||||
|
||||
bool getOnOffState() const;
|
||||
int getTransitionTime() const;
|
||||
@@ -179,7 +167,7 @@ public:
|
||||
CiColorTriangle getColorSpace() const;
|
||||
|
||||
void saveOriginalState(const QJsonObject& values);
|
||||
QString getOriginalState() const;
|
||||
QJsonObject getOriginalState() const;
|
||||
|
||||
bool isBusy();
|
||||
bool isBlack(bool isBlack);
|
||||
@@ -189,24 +177,30 @@ public:
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
/// light id
|
||||
int _id;
|
||||
int _ledidx;
|
||||
bool _useApiV2;
|
||||
|
||||
QString _id;
|
||||
QString _deviceId;
|
||||
QString _product;
|
||||
QString _model;
|
||||
QString _name;
|
||||
QString _archeType;
|
||||
QString _gamutType;
|
||||
|
||||
int _maxSegments;
|
||||
|
||||
bool _on;
|
||||
int _transitionTime;
|
||||
CiColor _color;
|
||||
bool _hasColor;
|
||||
/// darkes blue color in hue lamp GAMUT = black
|
||||
CiColor _colorBlack;
|
||||
/// The model id of the hue lamp which is used to determine the color space.
|
||||
QString _modelId;
|
||||
QString _lightname;
|
||||
CiColorTriangle _colorSpace;
|
||||
|
||||
/// The json string of the original state.
|
||||
QJsonObject _originalStateJSON;
|
||||
|
||||
QString _originalState;
|
||||
QJsonObject _originalState;
|
||||
CiColor _originalColor;
|
||||
qint64 _lastSendColorTime;
|
||||
qint64 _lastBlackTime;
|
||||
@@ -242,23 +236,40 @@ public:
|
||||
QJsonDocument get(const QString& route);
|
||||
|
||||
///
|
||||
/// @brief Perform a REST-API POST
|
||||
/// @brief Perform a REST-API GET
|
||||
///
|
||||
/// @param route the route of the POST request.
|
||||
/// @param content the content of the POST request.
|
||||
/// @param routeElements the route's elements of the GET request.
|
||||
///
|
||||
QJsonDocument put(const QString& route, const QString& content, bool supressError = false);
|
||||
/// @return the content of the GET request.
|
||||
///
|
||||
QJsonDocument get(const QStringList& routeElements);
|
||||
|
||||
QJsonDocument getLightState( int lightId);
|
||||
void setLightState( int lightId = 0, const QString &state = "");
|
||||
///
|
||||
/// @brief Perform a REST-API PUT
|
||||
///
|
||||
/// @param routeElements the route's elements of the PUT request.
|
||||
/// @param content the content of the PUT request.
|
||||
/// @param supressError Treat an error as a warning
|
||||
///
|
||||
/// @return the content of the PUT request.
|
||||
///
|
||||
QJsonDocument put(const QStringList& routeElements, const QJsonObject& content, bool supressError = false);
|
||||
|
||||
QMap<int,QJsonObject> getLightMap() const;
|
||||
QJsonDocument retrieveBridgeDetails();
|
||||
QJsonObject getDeviceDetails(const QString& deviceId);
|
||||
QJsonObject getEntertainmentSrvDetails(const QString& deviceId);
|
||||
|
||||
QMap<int,QJsonObject> getGroupMap() const;
|
||||
QJsonObject getLightDetails(const QString& lightId);
|
||||
QJsonDocument setLightState(const QString& lightId, const QJsonObject& state);
|
||||
|
||||
QString getGroupName(int groupId = 0) const;
|
||||
QMap<QString,QJsonObject> getDevicesMap() const;
|
||||
QMap<QString,QJsonObject> getLightMap() const;
|
||||
QMap<QString,QJsonObject> getGroupMap() const;
|
||||
QMap<QString,QJsonObject> getEntertainmentMap() const;
|
||||
|
||||
QJsonArray getGroupLights(int groupId = 0) const;
|
||||
QString getGroupName(const QString& groupId) const;
|
||||
QStringList getGroupLights(const QString& groupId) const;
|
||||
int getGroupChannelsCount(const QString& groupId) const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -289,7 +300,7 @@ protected:
|
||||
///
|
||||
/// @param[in] response from Hue-Bridge in JSON-format
|
||||
/// @param[in] suppressError Treat an error as a warning
|
||||
///
|
||||
///
|
||||
/// return True, Hue Bridge reports error
|
||||
///
|
||||
bool checkApiError(const QJsonDocument& response, bool supressError = false);
|
||||
@@ -338,23 +349,41 @@ protected:
|
||||
///
|
||||
QJsonObject addAuthorization(const QJsonObject& params) override;
|
||||
|
||||
bool isApiEntertainmentReady(const QString& apiVersion);
|
||||
bool isAPIv2Ready (int swVersion);
|
||||
|
||||
int getFirmwareVerion() { return _deviceFirmwareVersion; }
|
||||
void setBridgeDetails( const QJsonDocument &doc, bool isLogging = false );
|
||||
|
||||
void setBaseApiEnvironment(bool apiV2 = true, const QString& path = "");
|
||||
|
||||
QJsonDocument getGroupDetails( const QString& groupId );
|
||||
QJsonDocument setGroupState( const QString& groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
|
||||
bool initDevicesMap();
|
||||
bool initLightsMap();
|
||||
bool initGroupsMap();
|
||||
bool initEntertainmentSrvsMap();
|
||||
|
||||
void log(const char* msg, const char* type, ...) const;
|
||||
|
||||
bool configureSsl();
|
||||
const int * getCiphersuites() const override;
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
int _apiPort;
|
||||
/// User name for the API ("newdeveloper")
|
||||
QString _authToken;
|
||||
QString _applicationID;
|
||||
|
||||
bool _useHueEntertainmentAPI;
|
||||
bool _useEntertainmentAPI;
|
||||
bool _useApiV2;
|
||||
bool _isAPIv2Ready;
|
||||
|
||||
QJsonDocument getGroupState( int groupId );
|
||||
QJsonDocument setGroupState( int groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
bool initMaps();
|
||||
|
||||
void log(const char* msg, const char* type, ...) const;
|
||||
|
||||
const int * getCiphersuites() const override;
|
||||
bool _isDiyHue;
|
||||
|
||||
private:
|
||||
|
||||
@@ -364,16 +393,25 @@ private:
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonArray discover();
|
||||
QJsonArray discoverSsdp();
|
||||
|
||||
QJsonDocument getAllBridgeInfos();
|
||||
void setBridgeConfig( const QJsonDocument &doc );
|
||||
QJsonDocument retrieveDeviceDetails(const QString& deviceId = "");
|
||||
QJsonDocument retrieveLightDetails(const QString& lightId = "");
|
||||
QJsonDocument retrieveGroupDetails(const QString& groupId = "");
|
||||
QJsonDocument retrieveEntertainmentSrvDetails(const QString& deviceId = "");
|
||||
|
||||
bool retrieveApplicationId();
|
||||
|
||||
void setDevicesMap( const QJsonDocument &doc );
|
||||
void setLightsMap( const QJsonDocument &doc );
|
||||
void setGroupMap( const QJsonDocument &doc );
|
||||
void setEntertainmentSrvMap( const QJsonDocument &doc );
|
||||
|
||||
//Philips Hue Bridge details
|
||||
QString _deviceName;
|
||||
QString _deviceBridgeId;
|
||||
QString _deviceModel;
|
||||
QString _deviceFirmwareVersion;
|
||||
int _deviceFirmwareVersion;
|
||||
QString _deviceAPIVersion;
|
||||
|
||||
uint _api_major;
|
||||
@@ -382,8 +420,12 @@ private:
|
||||
|
||||
bool _isHueEntertainmentReady;
|
||||
|
||||
QMap<int,QJsonObject> _lightsMap;
|
||||
QMap<int,QJsonObject> _groupsMap;
|
||||
QMap<QString,QJsonObject> _devicesMap;
|
||||
QMap<QString,QJsonObject> _lightsMap;
|
||||
QMap<QString,QJsonObject> _groupsMap;
|
||||
QMap<QString,QJsonObject> _entertainmentMap;
|
||||
|
||||
int _lightsCount;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -440,7 +482,7 @@ public:
|
||||
///
|
||||
/// @return Number of device's LEDs
|
||||
///
|
||||
unsigned int getLightsCount() const { return _lightsCount; }
|
||||
int getLightsCount() const { return _lightsCount; }
|
||||
|
||||
void setOnOffState(PhilipsHueLight& light, bool on, bool force = false);
|
||||
void setTransitionTime(PhilipsHueLight& light);
|
||||
@@ -547,18 +589,18 @@ private:
|
||||
|
||||
bool setLights();
|
||||
|
||||
/// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
|
||||
/// creates new PhilipsHueLight(s) based on user lightId with bridge feedback
|
||||
///
|
||||
/// @param map Map of lightid/value pairs of bridge
|
||||
/// @param map Map of lightId/value pairs of bridge
|
||||
///
|
||||
bool updateLights(const QMap<int, QJsonObject> &map);
|
||||
bool updateLights(const QMap<QString, QJsonObject> &map);
|
||||
|
||||
///
|
||||
/// @brief Set the number of LEDs supported by the device.
|
||||
///
|
||||
/// @rparam[in] Number of device's LEDs
|
||||
//
|
||||
void setLightsCount( unsigned int lightsCount);
|
||||
void setLightsCount(int lightsCount);
|
||||
|
||||
bool openStream();
|
||||
bool getStreamGroupState();
|
||||
@@ -566,10 +608,8 @@ private:
|
||||
bool startStream();
|
||||
bool stopStream();
|
||||
|
||||
void writeStream(bool flush = false);
|
||||
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
|
||||
|
||||
QByteArray prepareStreamData() const;
|
||||
int writeStreamData(const std::vector<ColorRgb>& ledValues, bool flush = false);
|
||||
|
||||
///
|
||||
bool _switchOffOnBlack;
|
||||
@@ -582,12 +622,15 @@ private:
|
||||
bool _isInitLeds;
|
||||
|
||||
/// Array of the light ids.
|
||||
std::vector<int> _lightIds;
|
||||
QStringList _lightIds;
|
||||
/// Array to save the lamps.
|
||||
std::vector<PhilipsHueLight> _lights;
|
||||
|
||||
int _lightsCount;
|
||||
int _groupId;
|
||||
int _channelsCount;
|
||||
QString _groupId;
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
int _blackLightsTimeout;
|
||||
double _blackLevel;
|
||||
@@ -595,15 +638,5 @@ private:
|
||||
int _onBlackTimeToPowerOn;
|
||||
bool _candyGamma;
|
||||
|
||||
// TODO: Check what is the correct class
|
||||
uint32_t _handshake_timeout_min;
|
||||
uint32_t _handshake_timeout_max;
|
||||
bool _stopConnection;
|
||||
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
qint64 _lastConfirm;
|
||||
int _lastId;
|
||||
bool _groupStreamState;
|
||||
};
|
||||
|
@@ -2,11 +2,18 @@
|
||||
#include "ProviderRestApi.h"
|
||||
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QFile>
|
||||
|
||||
#include <QSslSocket>
|
||||
|
||||
//std includes
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
@@ -30,12 +37,12 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _networkManager(nullptr)
|
||||
, _requestTimeout(DEFAULT_REST_TIMEOUT)
|
||||
,_isSeflSignedCertificateAccpeted(false)
|
||||
{
|
||||
_networkManager = new QNetworkAccessManager();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
_networkManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
#endif
|
||||
|
||||
_apiUrl.setScheme(scheme);
|
||||
_apiUrl.setHost(host);
|
||||
_apiUrl.setPort(port);
|
||||
@@ -46,7 +53,7 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: ProviderRestApi(scheme, host, port, "") {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath)
|
||||
: ProviderRestApi("http", host, port, basePath) {}
|
||||
: ProviderRestApi((port == 443) ? "https" : "http", host, port, basePath) {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port)
|
||||
: ProviderRestApi(host, port, "") {}
|
||||
@@ -59,18 +66,33 @@ ProviderRestApi::~ProviderRestApi()
|
||||
delete _networkManager;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setScheme(const QString& scheme)
|
||||
{
|
||||
_apiUrl.setScheme(scheme);
|
||||
}
|
||||
|
||||
void ProviderRestApi::setUrl(const QUrl& url)
|
||||
{
|
||||
_apiUrl = url;
|
||||
_basePath = url.path();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QStringList& pathElements)
|
||||
{
|
||||
setBasePath(pathElements.join(ONE_SLASH));
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QString& basePath)
|
||||
{
|
||||
_basePath.clear();
|
||||
appendPath(_basePath, basePath);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearBasePath()
|
||||
{
|
||||
_basePath.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setPath(const QStringList& pathElements)
|
||||
{
|
||||
_path.clear();
|
||||
@@ -83,6 +105,11 @@ void ProviderRestApi::setPath(const QString& path)
|
||||
appendPath(_path, path);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearPath()
|
||||
{
|
||||
_path.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::appendPath(const QString& path)
|
||||
{
|
||||
appendPath(_path, path);
|
||||
@@ -204,6 +231,7 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation
|
||||
QDateTime start = QDateTime::currentDateTime();
|
||||
QString opCode;
|
||||
QNetworkReply* reply;
|
||||
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
opCode = "GET";
|
||||
@@ -255,11 +283,11 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
HttpStatusCode httpStatusCode = static_cast<HttpStatusCode>(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
||||
response.setHttpStatusCode(httpStatusCode);
|
||||
response.setNetworkReplyError(reply->error());
|
||||
response.setHeaders(reply->rawHeaderPairs());
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
QByteArray replyData = reply->readAll();
|
||||
|
||||
if (!replyData.isEmpty())
|
||||
{
|
||||
QJsonParseError error;
|
||||
@@ -284,40 +312,41 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
else
|
||||
{
|
||||
QString errorReason;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case HttpStatusCode::BadRequest:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
break;
|
||||
case HttpStatusCode::NotFound:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
advise = httpReason;
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
if (reply->error() == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
errorReason = "Network request timeout error";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reply->error() == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
errorReason = "Network request timeout error";
|
||||
qDebug() << "httpStatusCode: "<< httpStatusCode;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case HttpStatusCode::BadRequest:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
break;
|
||||
case HttpStatusCode::NotFound:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
advise = httpReason;
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorReason = reply->errorString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
response.setError(true);
|
||||
response.setErrorReason(errorReason);
|
||||
}
|
||||
@@ -344,3 +373,121 @@ void ProviderRestApi::setHeader(const QByteArray &headerName, const QByteArray &
|
||||
{
|
||||
_networkRequestHeaders.setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
void httpResponse::setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs)
|
||||
{
|
||||
_responseHeaders.clear();
|
||||
for (const auto &item: pairs)
|
||||
{
|
||||
_responseHeaders[item.first] = item.second;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray httpResponse::getHeader(const QByteArray header) const
|
||||
{
|
||||
return _responseHeaders.value(header);
|
||||
}
|
||||
|
||||
bool ProviderRestApi::setCaCertificate(const QString& caFileName)
|
||||
{
|
||||
bool rc {false};
|
||||
/// Add our own CA to the default SSL configuration
|
||||
QSslConfiguration configuration = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
QFile caFile (caFileName);
|
||||
if (!caFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error(_log,"Unable to open CA-Certificate file: %s", QSTRING_CSTR(caFileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
QSslCertificate cert (&caFile);
|
||||
caFile.close();
|
||||
|
||||
QList<QSslCertificate> allowedCAs;
|
||||
allowedCAs << cert;
|
||||
configuration.setCaCertificates(allowedCAs);
|
||||
|
||||
QSslConfiguration::setDefaultConfiguration(configuration);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
if (QSslSocket::supportsSsl())
|
||||
{
|
||||
QObject::connect( _networkManager, &QNetworkAccessManager::sslErrors, this, &ProviderRestApi::onSslErrors, Qt::UniqueConnection );
|
||||
_networkManager->connectToHostEncrypted(_apiUrl.host(), _apiUrl.port(), configuration);
|
||||
rc = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ProviderRestApi::acceptSelfSignedCertificates(bool isAccepted)
|
||||
{
|
||||
_isSeflSignedCertificateAccpeted = isAccepted;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setAlternateServerIdentity(const QString& serverIdentity)
|
||||
{
|
||||
_serverIdentity = serverIdentity;
|
||||
}
|
||||
|
||||
QString ProviderRestApi::getAlternateServerIdentity() const
|
||||
{
|
||||
return _serverIdentity;
|
||||
}
|
||||
|
||||
bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) const
|
||||
{
|
||||
bool isServerIdentified {false};
|
||||
|
||||
// Perform common name validation
|
||||
QSslCertificate serverCertificate = sslConfig.peerCertificate();
|
||||
QStringList commonName = serverCertificate.subjectInfo(QSslCertificate::CommonName);
|
||||
if ( commonName.contains(getAlternateServerIdentity(), Qt::CaseInsensitive) )
|
||||
{
|
||||
isServerIdentified = true;
|
||||
}
|
||||
|
||||
return isServerIdentified;
|
||||
}
|
||||
|
||||
void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
|
||||
{
|
||||
int ignoredErrorCount {0};
|
||||
for (const QSslError &error : errors)
|
||||
{
|
||||
bool ignoreSslError{false};
|
||||
|
||||
switch (error.error()) {
|
||||
case QSslError::HostNameMismatch :
|
||||
if (checkServerIdentity(reply->sslConfiguration()) )
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
case QSslError::SelfSignedCertificate :
|
||||
if (_isSeflSignedCertificateAccpeted)
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignoreSslError)
|
||||
{
|
||||
++ignoredErrorCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug (_log,"SSL Error occured: [%d] %s ",error.error(), QSTRING_CSTR(error.errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoredErrorCount == errors.size())
|
||||
{
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
}
|
||||
|
@@ -10,12 +10,13 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <QFile>
|
||||
#include <QBasicTimer>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 1000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 2000 };
|
||||
|
||||
//Set QNetworkReply timeout without external timer
|
||||
//https://stackoverflow.com/questions/37444539/how-to-set-qnetworkreply-timeout-without-external-timer
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
enum HandleMethod { Abort, Close };
|
||||
|
||||
ReplyTimeout(QNetworkReply* reply, const int timeout, HandleMethod method = Abort) :
|
||||
QObject(reply), m_method(method), m_timedout(false)
|
||||
QObject(reply), m_method(method), m_timedout(false)
|
||||
{
|
||||
Q_ASSERT(reply);
|
||||
if (reply && reply->isRunning()) {
|
||||
@@ -87,6 +88,10 @@ public:
|
||||
QJsonDocument getBody() const { return _responseBody; }
|
||||
void setBody(const QJsonDocument& body) { _responseBody = body; }
|
||||
|
||||
|
||||
QByteArray getHeader(const QByteArray header) const;
|
||||
void setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs);
|
||||
|
||||
QString getErrorReason() const { return _errorReason; }
|
||||
void setErrorReason(const QString& errorReason) { _errorReason = errorReason; }
|
||||
|
||||
@@ -99,6 +104,8 @@ public:
|
||||
private:
|
||||
|
||||
QJsonDocument _responseBody {};
|
||||
QHash<QByteArray, QByteArray> _responseHeaders;
|
||||
|
||||
bool _hasError = false;
|
||||
QString _errorReason;
|
||||
|
||||
@@ -131,6 +138,7 @@ class ProviderRestApi : public QObject
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
/// @brief Constructor of the REST-API wrapper
|
||||
///
|
||||
ProviderRestApi();
|
||||
@@ -176,6 +184,20 @@ public:
|
||||
///
|
||||
virtual ~ProviderRestApi() override;
|
||||
|
||||
///
|
||||
/// @brief Set the API's scheme
|
||||
///
|
||||
/// @param[in] scheme
|
||||
///
|
||||
void setScheme(const QString& scheme);
|
||||
|
||||
///
|
||||
/// @brief Get the API's scheme
|
||||
///
|
||||
/// return schme
|
||||
///
|
||||
QString getScheme() { return _apiUrl.scheme(); }
|
||||
|
||||
///
|
||||
/// @brief Set an API's host
|
||||
///
|
||||
@@ -190,6 +212,13 @@ public:
|
||||
///
|
||||
void setPort(const int port) { _apiUrl.setPort(port); }
|
||||
|
||||
///
|
||||
/// @brief Get the API's port
|
||||
///
|
||||
/// return port
|
||||
///
|
||||
int getPort() { return _apiUrl.port(); }
|
||||
|
||||
///
|
||||
/// @brief Set an API's url
|
||||
///
|
||||
@@ -204,6 +233,13 @@ public:
|
||||
///
|
||||
QUrl getUrl() const;
|
||||
|
||||
///
|
||||
/// @brief Set an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
/// @param[in] pathElements to form a path, e.g. (clip,v2,resource) results in "/clip/v2/resource"
|
||||
///
|
||||
void setBasePath(const QStringList& pathElements);
|
||||
|
||||
///
|
||||
/// @brief Set an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
@@ -211,6 +247,11 @@ public:
|
||||
///
|
||||
void setBasePath(const QString& basePath);
|
||||
|
||||
///
|
||||
/// @brief Clear an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
void clearBasePath();
|
||||
|
||||
///
|
||||
/// @brief Set an API's path to address resources
|
||||
///
|
||||
@@ -218,12 +259,18 @@ public:
|
||||
///
|
||||
void setPath(const QString& path);
|
||||
|
||||
///
|
||||
/// @brief Set an API's path to address resources
|
||||
///
|
||||
/// @param[in] pathElements to form a path, e.g. (lights,1,state) results in "/lights/1/state/"
|
||||
///
|
||||
void setPath(const QStringList& pathElements);
|
||||
|
||||
///
|
||||
/// @brief Clear an API's path
|
||||
///
|
||||
void clearPath();
|
||||
|
||||
///
|
||||
/// @brief Append an API's path element to path set before
|
||||
///
|
||||
@@ -252,6 +299,10 @@ public:
|
||||
///
|
||||
void setQuery(const QUrlQuery& query);
|
||||
|
||||
|
||||
QString getBasePath() {return _basePath;}
|
||||
QString getPath() {return _path;}
|
||||
|
||||
///
|
||||
/// @brief Execute GET request
|
||||
///
|
||||
@@ -359,6 +410,14 @@ public:
|
||||
/// @param[in] timeout in milliseconds.
|
||||
void setTransferTimeout(std::chrono::milliseconds timeout = DEFAULT_REST_TIMEOUT) { _requestTimeout = timeout; }
|
||||
|
||||
|
||||
bool setCaCertificate(const QString& caFileName);
|
||||
|
||||
void acceptSelfSignedCertificates(bool accept);
|
||||
|
||||
void setAlternateServerIdentity(const QString& serverIdentity);
|
||||
QString getAlternateServerIdentity() const;
|
||||
|
||||
///
|
||||
/// @brief Set the common logger for LED-devices.
|
||||
///
|
||||
@@ -366,6 +425,10 @@ public:
|
||||
///
|
||||
void setLogger(Logger* log) { _log = log; }
|
||||
|
||||
protected slots:
|
||||
/// Handle the SSLErrors
|
||||
void onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors);
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
@@ -379,9 +442,11 @@ private:
|
||||
|
||||
httpResponse executeOperation(QNetworkAccessManager::Operation op, const QUrl& url, const QByteArray& body = {});
|
||||
|
||||
bool checkServerIdentity(const QSslConfiguration& sslConfig) const;
|
||||
|
||||
Logger* _log;
|
||||
|
||||
// QNetworkAccessManager object for sending REST-requests.
|
||||
/// QNetworkAccessManager object for sending REST-requests.
|
||||
QNetworkAccessManager* _networkManager;
|
||||
std::chrono::milliseconds _requestTimeout;
|
||||
|
||||
@@ -394,6 +459,9 @@ private:
|
||||
QUrlQuery _query;
|
||||
|
||||
QNetworkRequest _networkRequestHeaders;
|
||||
|
||||
QString _serverIdentity;
|
||||
bool _isSeflSignedCertificateAccpeted;
|
||||
};
|
||||
|
||||
#endif // PROVIDERRESTKAPI_H
|
||||
|
@@ -150,6 +150,11 @@ const int *ProviderUdpSSL::getCiphersuites() const
|
||||
return mbedtls_ssl_list_ciphersuites();
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::setPSKidentity(const QString& pskIdentity)
|
||||
{
|
||||
_psk_identity = pskIdentity;
|
||||
}
|
||||
|
||||
bool ProviderUdpSSL::initNetwork()
|
||||
{
|
||||
if ((!_isDeviceReady || _streamPaused) && _streamReady)
|
||||
@@ -334,6 +339,11 @@ void ProviderUdpSSL::freeSSLConnection()
|
||||
}
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::writeBytes(QByteArray data, bool flush)
|
||||
{
|
||||
writeBytes(static_cast<uint>(data.size()), reinterpret_cast<unsigned char*>(data.data()), flush);
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::writeBytes(unsigned int size, const uint8_t* data, bool flush)
|
||||
{
|
||||
if (!_streamReady || _streamPaused)
|
||||
|
@@ -100,6 +100,14 @@ protected:
|
||||
///
|
||||
void stopConnection();
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
///
|
||||
/// @param[in] data The data
|
||||
///
|
||||
void writeBytes(QByteArray data, bool flush = false);
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
@@ -116,6 +124,8 @@ protected:
|
||||
///
|
||||
virtual const int * getCiphersuites() const;
|
||||
|
||||
void setPSKidentity(const QString& pskIdentity);
|
||||
|
||||
private:
|
||||
|
||||
bool initConnection();
|
||||
|
Reference in New Issue
Block a user