added authentication

This commit is contained in:
Boernsman 2020-12-01 09:31:59 +01:00
parent 0fc16e66f8
commit c0e53c6669
5 changed files with 463 additions and 85 deletions

View File

@ -29,7 +29,7 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginmiele.h"
#include "extern-plugininfo.h"
#include "plugininfo.h"
#include <QJsonDocument>
#include <QUrl>
@ -46,21 +46,23 @@ IntegrationPluginMiele::IntegrationPluginMiele()
m_idParamTypeIds.insert(cookTopThingClassId, cookTopThingIdParamTypeId);
m_idParamTypeIds.insert(hoodThingClassId, hoodThingIdParamTypeId);
m_idParamTypeIds.insert(cleaningRobotThingClassId, cleaningRobotThingIdParamTypeId);
m_connectedStateTypeIds.insert(ovenThingClassId, ovenConnectedStateTypeId);
m_connectedStateTypeIds.insert(fridgeThingClassId, fridgeConnectedStateTypeId);
m_connectedStateTypeIds.insert(dryerThingClassId, dryerConnectedStateTypeId);
m_connectedStateTypeIds.insert(coffeeMakerThingClassId, coffeeMakerConnectedStateTypeId);
m_connectedStateTypeIds.insert(dishwasherThingClassId, dishwasherConnectedStateTypeId);
m_connectedStateTypeIds.insert(washerThingClassId, washerConnectedStateTypeId);
m_connectedStateTypeIds.insert(cookTopThingClassId, cookTopConnectedStateTypeId);
m_connectedStateTypeIds.insert(cleaningRobotThingClassId, cleaningRobotConnectedStateTypeId);
m_connectedStateTypeIds.insert(hoodThingClassId, hoodConnectedStateTypeId);
}
void IntegrationPluginMiele::startPairing(ThingPairingInfo *info)
{
if (info->thingClassId() == mieleAccountThingClassId) {
QByteArray clientId = configValue(mielePluginCustomClientIdParamTypeId).toByteArray();
if (clientId.isEmpty()) {
clientId = apiKeyStorage()->requestKey("miele").data("clientKey");
}
if (clientId.isEmpty()) {
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Client key and/or seceret is not available."));
return;
}
Miele *miele = new Miele(hardwareManager()->networkManager(), clientId, this);
Miele *miele = createMieleConnection();
QUrl url = miele->getLoginUrl(QUrl("https://127.0.0.1:8888"));
qCDebug(dcMiele()) << "HomeConnect url:" << url;
m_setupMieleConnections.insert(info->thingId(), miele);
@ -84,7 +86,7 @@ void IntegrationPluginMiele::confirmPairing(ThingPairingInfo *info, const QStrin
Miele *miele = m_setupMieleConnections.value(info->thingId());
if (!miele) {
qWarning(dcMiele()) << "No Miele connection found for device:" << info->thingName();
qWarning(dcMiele()) << "No Miele connection found for thing:" << info->thingName();
m_setupMieleConnections.remove(info->thingId());
info->finish(Thing::ThingErrorHardwareFailure);
return;
@ -118,7 +120,7 @@ void IntegrationPluginMiele::setupThing(ThingSetupInfo *info)
m_mieleConnections.insert(thing, miele);
info->finish(Thing::ThingErrorNoError);
} else {
//device loaded from the device database, needs a new access token;
// device loaded from the device database, needs a new access token;
pluginStorage()->beginGroup(thing->id().toString());
QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray();
pluginStorage()->endGroup();
@ -126,15 +128,10 @@ void IntegrationPluginMiele::setupThing(ThingSetupInfo *info)
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Refresh token is not available."));
return;
}
QByteArray clientId = configValue(mielePluginCustomClientIdParamTypeId).toByteArray();
if (clientId.isEmpty()) {
clientId = apiKeyStorage()->requestKey("miele").data("clientId");
miele = createMieleConnection();
if (!miele) {
return info->finish(Thing::ThingErrorSetupFailed);
}
if (clientId.isEmpty()) {
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Client id is not available."));
return;
}
miele = new Miele(hardwareManager()->networkManager(), clientId, this);
miele->getAccessTokenFromRefreshToken(refreshToken);
m_asyncSetup.insert(miele, info);
}
@ -176,26 +173,26 @@ void IntegrationPluginMiele::postSetupThing(Thing *thing)
}
if (thing->thingClassId() == mieleAccountThingClassId) {
Miele *miele = m_mieleConnections.value(thing);
miele->getDevices();
//miele->connectEventStream();
thing->setStateValue(mieleAccountConnectedStateTypeId, true);
thing->setStateValue(mieleAccountLoggedInStateTypeId, true);
//TBD Set user name
} else if (m_idParamTypeIds.contains(thing->thingClassId())) {
Thing *parentThing = myThings().findById(thing->parentId());
if (!parentThing)
qCWarning(dcMiele()) << "Could not find parent with Id" << thing->parentId().toString();
Miele *miele = m_mieleConnections.value(parentThing);
QString deviceId = thing->paramValue(m_idParamTypeIds.value(thing->thingClassId())).toString();
if (!miele) {
qCWarning(dcMiele()) << "Could not find HomeConnect connection for thing" << thing->name();
} else {
miele->getDevice(deviceId);
}
} else {
Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
Miele *miele = m_mieleConnections.value(thing);
miele->getDevices();
//miele->connectEventStream();
thing->setStateValue(mieleAccountConnectedStateTypeId, true);
thing->setStateValue(mieleAccountLoggedInStateTypeId, true);
//TBD Set user name
} else if (m_idParamTypeIds.contains(thing->thingClassId())) {
Thing *parentThing = myThings().findById(thing->parentId());
if (!parentThing)
qCWarning(dcMiele()) << "Could not find parent with Id" << thing->parentId().toString();
Miele *miele = m_mieleConnections.value(parentThing);
QString deviceId = thing->paramValue(m_idParamTypeIds.value(thing->thingClassId())).toString();
if (!miele) {
qCWarning(dcMiele()) << "Could not find HomeConnect connection for thing" << thing->name();
} else {
miele->getDevice(deviceId);
}
} else {
Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginMiele::executeAction(ThingActionInfo *info)
@ -233,4 +230,76 @@ void IntegrationPluginMiele::thingRemoved(Thing *thing)
}
}
Miele *IntegrationPluginMiele::createMieleConnection()
{
QByteArray clientId = configValue(mielePluginCustomClientIdParamTypeId).toByteArray();
QByteArray clientSecret = configValue(mielePluginCustomClientSecretParamTypeId).toByteArray();
if (clientId.isEmpty() || clientSecret.isEmpty()) {
clientId = apiKeyStorage()->requestKey("miele").data("clientId");
clientSecret = apiKeyStorage()->requestKey("miele").data("clientSecret");
} else {
qCDebug(dcMiele()) << "Using custom api credentials";
}
if (clientId.isEmpty() || clientSecret.isEmpty()) {
return nullptr;
} else {
qCDebug(dcMiele()) << "Using api credentials from API key provider";
}
return new Miele(hardwareManager()->networkManager(), clientId, clientSecret, "en", this);
}
void IntegrationPluginMiele::onConnectionChanged(bool connected)
{
Miele *miele = static_cast<Miele *>(sender());
Thing *thing = m_mieleConnections.key(miele);
if (!thing)
return;
thing->setStateValue(mieleAccountConnectedStateTypeId, connected);
if (!connected) {
Q_FOREACH(Thing *child, myThings().filterByParentId(thing->id())) {
child->setStateValue(m_connectedStateTypeIds.value(child->thingClassId()), connected);
}
}
}
void IntegrationPluginMiele::onAuthenticationStatusChanged(bool authenticated)
{
Miele *mieleConnection = static_cast<Miele *>(sender());
if (m_asyncSetup.contains(mieleConnection)) {
ThingSetupInfo *info = m_asyncSetup.take(mieleConnection);
if (authenticated) {
m_mieleConnections.insert(info->thing(), mieleConnection);
info->finish(Thing::ThingErrorNoError);
} else {
mieleConnection->deleteLater();
info->finish(Thing::ThingErrorHardwareFailure);
}
} else {
Thing *thing = m_mieleConnections.key(mieleConnection);
if (!thing)
return;
thing->setStateValue(mieleAccountLoggedInStateTypeId, authenticated);
if (!authenticated) {
//refresh access token needs to be refreshed
pluginStorage()->beginGroup(thing->id().toString());
QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray();
pluginStorage()->endGroup();
mieleConnection->getAccessTokenFromRefreshToken(refreshToken);
}
}
}
void IntegrationPluginMiele::onRequestExecuted(QUuid requestId, bool success)
{
if (m_pendingActions.contains(requestId)) {
ThingActionInfo *info = m_pendingActions.value(requestId);
if (success) {
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareNotAvailable);
}
}
}

View File

@ -55,10 +55,6 @@ public:
void executeAction(ThingActionInfo *info) override;
void thingRemoved(Thing *thing) override;
void browseThing(BrowseResult *result) override;
void browserItem(BrowserItemResult *result) override;
void executeBrowserItem(BrowserActionInfo *info) override;
private:
PluginTimer *m_pluginTimer15min = nullptr;
@ -68,31 +64,11 @@ private:
QHash<Thing *, Miele *> m_mieleConnections;
QHash<QUuid, ThingActionInfo *> m_pendingActions;
QHash<Thing *, QString> m_selectedProgram;
QHash<ThingClassId, ParamTypeId> m_idParamTypeIds;
QHash<ThingClassId, StateTypeId> m_connectedStateTypeIds;
QHash<ThingClassId, StateTypeId> m_doorStateStateTypeIds;
QHash<ThingClassId, StateTypeId> m_localControlStateTypeIds;
QHash<ThingClassId, StateTypeId> m_remoteControlActivationStateTypeIds;
QHash<ThingClassId, StateTypeId> m_remoteStartAllowanceStateTypeIds;
QHash<ThingClassId, StateTypeId> m_operationStateTypeIds;
QHash<ThingClassId, StateTypeId> m_doorStateTypeIds;
QHash<ThingClassId, StateTypeId> m_selectedProgramStateTypeIds;
QHash<ThingClassId, StateTypeId> m_progressStateTypeIds;
QHash<ThingClassId, StateTypeId> m_endTimerStateTypeIds;
QHash<ThingClassId, ActionTypeId> m_startActionTypeIds;
QHash<ThingClassId, ActionTypeId> m_stopActionTypeIds;
QHash<ThingClassId, EventTypeId> m_programFinishedEventTypeIds;
QHash<QString, QString> m_coffeeStrengthTypes;
void parseKey(Thing *thing, const QString &key, const QVariant &value);
void parseSettingKey(Thing *thing, const QString &key, const QVariant &value);
bool checkIfActionIsPossible(ThingActionInfo *info);
Miele *createMieleConnection();
private slots:
void onConnectionChanged(bool connected);

View File

@ -9,6 +9,13 @@
"displayName": "Custom client id",
"defaultValue": "",
"type": "QString"
},
{
"id": "cd4709fb-adc2-4403-99b5-93dc9cc7f0d8",
"name": "customClientSecret",
"displayName": "Custom client secret",
"defaultValue": "",
"type": "QString"
}
],
"apiKeys": ["miele"],
@ -56,7 +63,7 @@
]
},
{
"id": "cf542b5e-2903-497c-95a0-042df6535450",
"id": "98491bd0-9ba7-4060-924d-153900f470b5",
"name": "oven",
"displayName": "Oven",
"interfaces": ["connectable"],
@ -64,7 +71,7 @@
"browsable": true,
"paramTypes": [
{
"id": "1e287843-41dd-49c8-a250-a21ed0e4215c",
"id": "5be2f375-8068-40b8-9a88-fc5a07508d82",
"name": "id",
"displayName": "ID",
"defaultValue": "-",
@ -73,7 +80,7 @@
],
"stateTypes": [
{
"id": "9c2c9360-0a4a-43ff-8d15-9cefa80380ec",
"id": "eba13efe-cc1c-4954-bcaf-beed1d490f6d",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",

View File

@ -36,11 +36,13 @@
#include <QUrl>
#include <QJsonObject>
#include <QUrlQuery>
#include <QMetaEnum>
Miele::Miele(NetworkAccessManager *networkmanager, const QByteArray &clientId, QObject *parent) :
Miele::Miele(NetworkAccessManager *networkmanager, const QByteArray &clientId, const QByteArray &clientSecret, const QString &language, QObject *parent) :
QObject(parent),
m_language(language),
m_clientId(clientId),
m_clientSecret(clientSecret),
m_networkManager(networkmanager)
{
m_tokenRefreshTimer = new QTimer(this);
@ -58,15 +60,15 @@ QByteArray Miele::refreshToken()
return m_refreshToken;
}
QUrl Miele::getLoginUrl(const QUrl &redirectUrl)
QUrl Miele::getLoginUrl(const QUrl &redirectUrl, const QString &state)
{
if (m_clientId.isEmpty()) {
qWarning(dcMiele) << "Client key not defined!";
qCWarning(dcMiele) << "Client key not defined!";
return QUrl("");
}
if (redirectUrl.isEmpty()){
qWarning(dcMiele()) << "No redirect uri defined!";
qCWarning(dcMiele()) << "No redirect uri defined!";
}
m_redirectUri = QUrl::toPercentEncoding(redirectUrl.toString());
@ -75,18 +77,178 @@ QUrl Miele::getLoginUrl(const QUrl &redirectUrl)
queryParams.addQueryItem("client_id", m_clientId);
queryParams.addQueryItem("redirect_uri", m_redirectUri);
queryParams.addQueryItem("response_type", "code");
m_state = QUuid::createUuid().toString();
queryParams.addQueryItem("state", m_state);
if (!state.isEmpty())
queryParams.addQueryItem("state", state);
url.setQuery(queryParams);
return url;
}
void Miele::getAccessTokenFromRefreshToken(const QByteArray &refreshToken)
{
if (refreshToken.isEmpty()) {
qCWarning(dcMiele()) << "No refresh token given!";
setAuthenticated(false);
return;
}
QUrl url(m_tokenUrl);
QUrlQuery query;
query.clear();
query.addQueryItem("grant_type", "refresh_token");
query.addQueryItem("refresh_token", refreshToken);
query.addQueryItem("client_id", m_clientId);
query.addQueryItem("client_secret", m_clientSecret);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply *reply = m_networkManager->post(request, query.toString().toUtf8());
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply](){
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QJsonDocument data = QJsonDocument::fromJson(rawData);
if(!data.toVariant().toMap().contains("access_token")) {
setAuthenticated(false);
return;
}
m_accessToken = data.toVariant().toMap().value("access_token").toByteArray();
emit receivedAccessToken(m_accessToken);
if (data.toVariant().toMap().contains("expires_in")) {
int expireTime = data.toVariant().toMap().value("expires_in").toInt();
qCDebug(dcMiele) << "Access token expires int" << expireTime << "s, at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
if (!m_tokenRefreshTimer) {
qWarning(dcMiele()) << "Access token refresh timer not initialized";
return;
}
if (expireTime < 20) {
qCWarning(dcMiele()) << "Expire time too short";
return;
}
m_tokenRefreshTimer->start((expireTime - 20) * 1000);
}
});
}
void Miele::getAccessTokenFromAuthorizationCode(const QByteArray &authorizationCode)
{
if(authorizationCode.isEmpty())
qCWarning(dcMiele) << "No authorization code given!";
QUrl url = QUrl(m_tokenUrl);
QUrlQuery query;
query.clear();
query.addQueryItem("client_id", m_clientId);
query.addQueryItem("client_secret", m_clientSecret);
query.addQueryItem("vg", "de-DE");
query.addQueryItem("redirect_uri", m_redirectUri);
query.addQueryItem("grant_type", "authorization_code");
query.addQueryItem("code", authorizationCode);
url.setQuery(query);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply *reply = m_networkManager->post(request, query.toString().toUtf8());
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply](){
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);
if(!jsonDoc.toVariant().toMap().contains("access_token") || !jsonDoc.toVariant().toMap().contains("refresh_token") ) {
setAuthenticated(false);
return;
}
m_accessToken = jsonDoc.toVariant().toMap().value("access_token").toByteArray();
receivedAccessToken(m_accessToken);
m_refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toByteArray();
receivedRefreshToken(m_refreshToken);
if (jsonDoc.toVariant().toMap().contains("expires_in")) {
int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt();
qCDebug(dcMiele()) << "Token expires in" << expireTime << "s, at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
if (!m_tokenRefreshTimer) {
qWarning(dcMiele()) << "Token refresh timer not initialized";
setAuthenticated(false);
return;
}
if (expireTime < 20) {
qCWarning(dcMiele()) << "Expire time too short";
return;
}
m_tokenRefreshTimer->start((expireTime - 20) * 1000);
}
});
}
void Miele::getDevices()
{
QUrl url = m_apiUrl;
url.setPath("/v1/devices");
url.setQuery("language=en");
QNetworkRequest request(url);
request.setRawHeader("access_token", m_accessToken);
request.setRawHeader("language", "en");
request.setRawHeader("accept", "application/json; charset=utf-8");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply]{
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap();
qCDebug(dcMiele()) << "Get devices" << map;
if (map.contains("data")) {
QVariantMap dataMap = map.value("data").toMap();
qCDebug(dcMiele()) << "key" << dataMap.value("key").toString() << "value" << dataMap.value("value").toString() << dataMap.value("unit").toString();
} else if (map.contains("error")) {
qCWarning(dcMiele()) << "Send command" << map.value("error").toMap().value("description").toString();
}
});
}
void Miele::getDevice(const QString &deviceId)
{
QUrl url = m_apiUrl;
url.setPath("/v1/devices/"+deviceId);
url.setQuery("language=en");
QNetworkRequest request(url);
request.setRawHeader("access_token", m_accessToken);
request.setRawHeader("language", "en");
request.setRawHeader("accept", "application/json; charset=utf-8");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply]{
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap();
qCDebug(dcMiele()) << "Get device" << map;
});
}
void Miele::getActions(const QString &deviceId)
{
Q_UNUSED(deviceId)
}
@ -146,6 +308,65 @@ QUuid Miele::setTargetTemperature(const QString &deviceId, int zone, int targetT
return putAction(deviceId, doc);
}
QUuid Miele::setColors(const QString &deviceId, Miele::Color color)
{
QJsonDocument doc;
QJsonObject object;
QString colorString = QMetaEnum::fromType<Miele::Color>().valueToKey(color);
object.insert("color", colorString.remove("Color").toLower());
doc.setObject(object);
return putAction(deviceId, doc);
}
QUuid Miele::setModes(const QString &deviceId, Miele::Mode mode)
{
QJsonDocument doc;
QJsonObject object;
object.insert("modes", mode);
doc.setObject(object);
return putAction(deviceId, doc);
}
QUuid Miele::setVentilationStep(const QString &deviceId, int step)
{
QJsonDocument doc;
QJsonObject object;
object.insert("ventilationStep", step);
doc.setObject(object);
return putAction(deviceId, doc);
}
QUuid Miele::setStartTime(const QString &deviceId, int seconds)
{
QJsonDocument doc;
QJsonObject object;
object.insert("startTime", seconds);
doc.setObject(object);
return putAction(deviceId, doc);
}
void Miele::getAllEvents()
{
QUrl url = m_apiUrl;
url.setPath("/v1/devices/all/events");
QNetworkRequest request(url);
request.setRawHeader("authorization", m_accessToken);
request.setRawHeader("accept", "*/*");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished, [reply, this] {
reply->deleteLater();
QTimer::singleShot(5000, this, [this] {getAllEvents();}); //try to reconnect every 5 seconds
});
connect(reply, &QNetworkReply::readyRead, this, [this, reply]{
while (reply->canReadLine()) {
}
});
}
QUuid Miele::putAction(const QString &deviceId, const QJsonDocument &action)
{
QUuid commandId = QUuid::createUuid();
@ -179,3 +400,109 @@ QUuid Miele::putAction(const QString &deviceId, const QJsonDocument &action)
});
return commandId;
}
void Miele::setAuthenticated(bool state)
{
if (state != m_authenticated) {
m_authenticated = state;
emit authenticationStatusChanged(state);
}
}
void Miele::setConnected(bool state)
{
if (state != m_connected) {
m_connected = state;
emit connectionChanged(state);
}
}
bool Miele::checkStatusCode(QNetworkReply *reply, const QByteArray &rawData)
{
// Check for the internet connection
if (reply->error() == QNetworkReply::NetworkError::HostNotFoundError ||
reply->error() == QNetworkReply::NetworkError::UnknownNetworkError ||
reply->error() == QNetworkReply::NetworkError::TemporaryNetworkFailureError) {
qCWarning(dcMiele()) << "Connection error" << reply->errorString();
setConnected(false);
setAuthenticated(false);
return false;
} else {
setConnected(true);
}
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData, &error);
switch (status){
case 200: //The request was successful. Typically returned for successful GET requests.
case 204: //The request was successful. Typically returned for successful PUT/DELETE requests with no payload.
break;
case 400: //Error occurred (e.g. validation error - value is out of range)
if(!jsonDoc.toVariant().toMap().contains("error")) {
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_client") {
qWarning(dcMiele()) << "Client token provided doesnt correspond to client that generated auth code.";
}
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_redirect_uri") {
qWarning(dcMiele()) << "Missing redirect_uri parameter.";
}
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_code") {
qWarning(dcMiele()) << "Expired authorization code.";
}
}
setAuthenticated(false);
return false;
case 401:
qWarning(dcMiele()) << "Client does not have permission to use this API.";
setAuthenticated(false);
return false;
case 403:
qCWarning(dcMiele()) << "Forbidden, Scope has not been granted or home appliance is not assigned to HC account";
setAuthenticated(false);
return false;
case 404:
qCWarning(dcMiele()) << "Not Found. This resource is not available (e.g. no images on washing machine)";
return false;
case 405:
qWarning(dcMiele()) << "Wrong HTTP method used.";
setAuthenticated(false);
return false;
case 408:
qCWarning(dcMiele())<< "Request Timeout, API Server failed to produce an answer or has no connection to backend service";
return false;
case 409:
qCWarning(dcMiele()) << "Conflict - Command/Query cannot be executed for the home appliance, the error response contains the error details";
qCWarning(dcMiele()) << "Error" << jsonDoc.toVariant().toMap().value("error").toString();
return false;
case 415:
qCWarning(dcMiele())<< "Unsupported Media Type. The request's Content-Type is not supported";
return false;
case 429:
qCWarning(dcMiele())<< "Too Many Requests, the number of requests for a specific endpoint exceeded the quota of the client";
return false;
case 500:
qCWarning(dcMiele())<< "Internal Server Error, in case of a server configuration error or any errors in resource files";
return false;
case 503:
qCWarning(dcMiele())<< "Service Unavailable,if a required backend service is not available";
return false;
default:
break;
}
if (error.error != QJsonParseError::NoError) {
qCWarning(dcMiele()) << "Received invalide JSON object" << rawData;
qCWarning(dcMiele()) << "Status" << status;
setAuthenticated(false);
return false;
}
setAuthenticated(true);
return true;
}
void Miele::onRefreshTimeout()
{
qCDebug(dcMiele()) << "Refresh authentication token";
getAccessTokenFromRefreshToken(m_refreshToken);
}

View File

@ -68,6 +68,7 @@ public:
ColorPurple,
ColorTurquoise
};
Q_ENUM(Color)
enum Status {
StatusOff = 1,
@ -89,12 +90,11 @@ public:
StatusNotConnected = 255
};
Miele(NetworkAccessManager *networkmanager, const QByteArray &clientId, QObject *parent = nullptr);
Miele(NetworkAccessManager *networkmanager, const QByteArray &clientId, const QByteArray &clientSecret, const QString &language = "en", QObject *parent = nullptr);
QByteArray accessToken();
QByteArray refreshToken();
void setSimulationMode(bool simulation);
QUrl getLoginUrl(const QUrl &redirectUrl);
QUrl getLoginUrl(const QUrl &redirectUrl, const QString &state = "");
void getAccessTokenFromRefreshToken(const QByteArray &refreshToken);
void getAccessTokenFromAuthorizationCode(const QByteArray &authorizationCode);
@ -114,12 +114,14 @@ public:
QUuid setColors(const QString &deviceId, Color color);
QUuid setModes(const QString &deviceId, Mode mode);
QUuid setVentilationStep(const QString &deviceId, int step);
QUuid setStartTime();
QUuid setStartTime(const QString &deviceId, int seconds);
// EVENTS
void getAllEvents();
private:
QString m_language;
QByteArray m_clientId;
QByteArray m_clientSecret;
NetworkAccessManager *m_networkManager = nullptr;
QUuid putAction(const QString &deviceId, const QJsonDocument &action); //
@ -127,21 +129,18 @@ private:
QUrl m_authorizationUrl = QUrl("https://api.mcs3.miele.com/thirdparty/login/");
QUrl m_tokenUrl = QUrl("https://api.mcs3.miele.com/thirdparty/token/");
QUrl m_apiUrl = QUrl("https://api.mcs3.miele.com/");
QByteArray m_clientId;
QByteArray m_accessToken;
QByteArray m_refreshToken;
QByteArray m_redirectUri = "https://127.0.0.1:8888";
QString m_state;
QTimer *m_tokenRefreshTimer = nullptr;
void setAuthenticated(bool state);
void setConnected(bool state);
bool m_authenticated = false;
bool m_connected = false;
void setAuthenticated(bool state);
void setConnected(bool state);
bool checkStatusCode(QNetworkReply *reply, const QByteArray &rawData);
signals:
void connectionChanged(bool connected);