mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-07-18 00:43:48 +02:00
added setup
This commit is contained in:
parent
37587e6590
commit
0fc16e66f8
@ -1,5 +1,32 @@
|
||||
# Miele
|
||||
|
||||
Connects your Miele home appliances to nymea.
|
||||
|
||||
## Supported Things
|
||||
|
||||
Miele appliances, like:
|
||||
* Oven
|
||||
* Dishwasher
|
||||
* Coffee maker
|
||||
* Dryer
|
||||
* Fridge
|
||||
* Washer
|
||||
* Cook Top
|
||||
* Hood
|
||||
|
||||
## Requirements
|
||||
|
||||
* The package “nymea-plugin-miele” must be installed
|
||||
* Internet connection
|
||||
* Miele account
|
||||
|
||||
## Plug-In Settings
|
||||
|
||||
**Custom client key and secret**
|
||||
You can register as developer on https://miele.com/developer, where you can obtain you own client credentials.
|
||||
The default client credentials are made available through the nymea community API key provider.
|
||||
|
||||
## More
|
||||
|
||||
https://www.miele.com/developer/index.html
|
||||
|
||||
|
||||
@ -29,34 +29,45 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "integrationpluginmiele.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
|
||||
IntegrationPluginMiele::IntegrationPluginMiele()
|
||||
{
|
||||
|
||||
m_idParamTypeIds.insert(ovenThingClassId, ovenThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(fridgeThingClassId, fridgeThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(dryerThingClassId, dryerThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(coffeeMakerThingClassId, coffeeMakerThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(dishwasherThingClassId, dishwasherThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(washerThingClassId, washerThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(cookTopThingClassId, cookTopThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(hoodThingClassId, hoodThingIdParamTypeId);
|
||||
m_idParamTypeIds.insert(cleaningRobotThingClassId, cleaningRobotThingIdParamTypeId);
|
||||
}
|
||||
|
||||
void IntegrationPluginMiele::startPairing(ThingPairingInfo *info)
|
||||
{
|
||||
if (info->thingClassId() == mieleAccountThingClassId) {
|
||||
|
||||
QByteArray clientKey = configValue(mielePluginCustomClientKeyParamTypeId).toByteArray();
|
||||
QByteArray clientSecret = configValue(mielePluginCustomClientSecretParamTypeId).toByteArray();
|
||||
if (clientKey.isEmpty() || clientSecret.isEmpty()) {
|
||||
clientKey = apiKeyStorage()->requestKey("miele").data("clientKey");
|
||||
clientSecret = apiKeyStorage()->requestKey("miele").data("clientSecret");
|
||||
QByteArray clientId = configValue(mielePluginCustomClientIdParamTypeId).toByteArray();
|
||||
if (clientId.isEmpty()) {
|
||||
clientId = apiKeyStorage()->requestKey("miele").data("clientKey");
|
||||
}
|
||||
if (clientKey.isEmpty() || clientSecret.isEmpty()) {
|
||||
if (clientId.isEmpty()) {
|
||||
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Client key and/or seceret is not available."));
|
||||
return;
|
||||
}
|
||||
Miele *miele = new Miele(hardwareManager()->networkManager(), clientKey, clientSecret, this);
|
||||
QUrl url = miele->getLoginUrl(QUrl("https://127.0.0.1:8888"), scope);
|
||||
Miele *miele = new Miele(hardwareManager()->networkManager(), clientId, this);
|
||||
QUrl url = miele->getLoginUrl(QUrl("https://127.0.0.1:8888"));
|
||||
qCDebug(dcMiele()) << "HomeConnect url:" << url;
|
||||
m_setupHomeConnectConnections.insert(info->thingId(), miele);
|
||||
m_setupMieleConnections.insert(info->thingId(), miele);
|
||||
info->setOAuthUrl(url);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
} else {
|
||||
qCWarning(dMiele()) << "Unhandled pairing metod!";
|
||||
qCWarning(dcMiele()) << "Unhandled pairing metod!";
|
||||
info->finish(Thing::ThingErrorCreationMethodNotSupported);
|
||||
}
|
||||
}
|
||||
@ -96,6 +107,51 @@ void IntegrationPluginMiele::confirmPairing(ThingPairingInfo *info, const QStrin
|
||||
|
||||
void IntegrationPluginMiele::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
if (thing->thingClassId() == mieleAccountThingClassId) {
|
||||
Miele *miele;
|
||||
if (m_setupMieleConnections.keys().contains(thing->id())) {
|
||||
//Fresh device setup, has already a fresh access token
|
||||
qCDebug(dcMiele()) << "Miele OAuth setup complete";
|
||||
miele = m_setupMieleConnections.take(thing->id());
|
||||
m_mieleConnections.insert(thing, miele);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
} else {
|
||||
//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();
|
||||
if (refreshToken.isEmpty()) {
|
||||
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");
|
||||
}
|
||||
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);
|
||||
}
|
||||
} else if (m_idParamTypeIds.contains(thing->thingClassId())) {
|
||||
Thing *parentThing = myThings().findById(thing->parentId());
|
||||
if (parentThing->setupComplete()) {
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
} else {
|
||||
connect(parentThing, &Thing::setupStatusChanged, info, [parentThing, info]{
|
||||
if (parentThing->setupComplete()) {
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -118,6 +174,28 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginMiele::executeAction(ThingActionInfo *info)
|
||||
|
||||
@ -5,15 +5,8 @@
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "6ebf0bdb-012d-4277-9603-2a2e68e23c33",
|
||||
"name": "customClientKey",
|
||||
"displayName": "Custom client key",
|
||||
"defaultValue": "",
|
||||
"type": "QString"
|
||||
},
|
||||
{
|
||||
"id": "37230edb-22ed-4f1d-8849-7fd72f192d89",
|
||||
"name": "customClientSecret",
|
||||
"displayName": "Custom client secret",
|
||||
"name": "customClientId",
|
||||
"displayName": "Custom client id",
|
||||
"defaultValue": "",
|
||||
"type": "QString"
|
||||
}
|
||||
@ -27,7 +20,7 @@
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "bea89711-7f41-4add-ba27-32dd69515c1c",
|
||||
"name": "miele",
|
||||
"name": "mieleAccount",
|
||||
"displayName": "Miele",
|
||||
"interfaces": ["account"],
|
||||
"createMethods": ["user"],
|
||||
|
||||
@ -37,11 +37,59 @@
|
||||
#include <QJsonObject>
|
||||
#include <QUrlQuery>
|
||||
|
||||
|
||||
Miele::Miele(NetworkAccessManager *networkmanager, const QByteArray &clientId, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_clientId(clientId),
|
||||
m_networkManager(networkmanager)
|
||||
{
|
||||
m_tokenRefreshTimer = new QTimer(this);
|
||||
m_tokenRefreshTimer->setSingleShot(true);
|
||||
connect(m_tokenRefreshTimer, &QTimer::timeout, this, &Miele::onRefreshTimeout);
|
||||
}
|
||||
|
||||
QByteArray Miele::accessToken()
|
||||
{
|
||||
return m_accessToken;
|
||||
}
|
||||
|
||||
QByteArray Miele::refreshToken()
|
||||
{
|
||||
return m_refreshToken;
|
||||
}
|
||||
|
||||
QUrl Miele::getLoginUrl(const QUrl &redirectUrl)
|
||||
{
|
||||
if (m_clientId.isEmpty()) {
|
||||
qWarning(dcMiele) << "Client key not defined!";
|
||||
return QUrl("");
|
||||
}
|
||||
|
||||
if (redirectUrl.isEmpty()){
|
||||
qWarning(dcMiele()) << "No redirect uri defined!";
|
||||
}
|
||||
m_redirectUri = QUrl::toPercentEncoding(redirectUrl.toString());
|
||||
|
||||
QUrl url(m_authorizationUrl);
|
||||
QUrlQuery queryParams;
|
||||
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);
|
||||
url.setQuery(queryParams);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Miele::getDevices()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
QUuid Miele::processAction(const QString &deviceId, Miele::ProcessAction action)
|
||||
{
|
||||
QJsonDocument doc;
|
||||
@ -107,7 +155,7 @@ QUuid Miele::putAction(const QString &deviceId, const QJsonDocument &action)
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setRawHeader("Authorization", "Bearer "+m_accessToken);
|
||||
// request.setRawHeader("Accept-Language", "en-US");
|
||||
// request.setRawHeader("Accept-Language", "en-US");
|
||||
request.setRawHeader("accept", "application/json; charset=utf-8");
|
||||
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
|
||||
|
||||
|
||||
@ -89,12 +89,12 @@ public:
|
||||
StatusNotConnected = 255
|
||||
};
|
||||
|
||||
Miele(NetworkAccessManager *networkmanager, const QByteArray &clientKey, const QByteArray &clientSecret, bool simulationMode = false, QObject *parent = nullptr);
|
||||
Miele(NetworkAccessManager *networkmanager, const QByteArray &clientId, QObject *parent = nullptr);
|
||||
QByteArray accessToken();
|
||||
QByteArray refreshToken();
|
||||
void setSimulationMode(bool simulation);
|
||||
|
||||
QUrl getLoginUrl(const QUrl &redirectUrl, const QString &scope);
|
||||
QUrl getLoginUrl(const QUrl &redirectUrl);
|
||||
|
||||
void getAccessTokenFromRefreshToken(const QByteArray &refreshToken);
|
||||
void getAccessTokenFromAuthorizationCode(const QByteArray &authorizationCode);
|
||||
@ -127,6 +127,20 @@ 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;
|
||||
|
||||
bool checkStatusCode(QNetworkReply *reply, const QByteArray &rawData);
|
||||
signals:
|
||||
@ -135,5 +149,8 @@ signals:
|
||||
void receivedRefreshToken(const QByteArray &refreshToken);
|
||||
void receivedAccessToken(const QByteArray &accessToken);
|
||||
void commandExecuted(const QUuid &commandId, bool success);
|
||||
|
||||
private slots:
|
||||
void onRefreshTimeout();
|
||||
};
|
||||
#endif // MIELE
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user