diff --git a/debian/control b/debian/control
index a3c0e990..acecfdc0 100644
--- a/debian/control
+++ b/debian/control
@@ -494,6 +494,21 @@ Description: nymea.io plugin for a generic MQTT client
This package will install a generic MQTT client plugin for nymea.io
+Package: nymea-plugin-miele
+Architecture: any
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+ nymea-plugins-translations,
+Description: nymea.io plugin for Miele appliances
+ The nymea daemon is a plugin based IoT (Internet of Things) server. The
+ server works like a translator for devices, things and services and
+ allows them to interact.
+ With the powerful rule engine you are able to connect any device available
+ in the system and create individual scenes and behaviors for your environment.
+ .
+ This package will install an integration plugin for Miele appliances.
+
+
Package: nymea-plugin-netatmo
Architecture: any
Depends: ${shlibs:Depends},
diff --git a/debian/nymea-plugin-miele.install.in b/debian/nymea-plugin-miele.install.in
new file mode 100644
index 00000000..1275f706
--- /dev/null
+++ b/debian/nymea-plugin-miele.install.in
@@ -0,0 +1 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginmiele.so
diff --git a/miele/README.md b/miele/README.md
new file mode 100644
index 00000000..6bd4b562
--- /dev/null
+++ b/miele/README.md
@@ -0,0 +1,5 @@
+# Miele
+
+## Requirements
+
+## More
diff --git a/miele/integrationpluginmiele.cpp b/miele/integrationpluginmiele.cpp
new file mode 100644
index 00000000..f5803218
--- /dev/null
+++ b/miele/integrationpluginmiele.cpp
@@ -0,0 +1,158 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "integrationpluginmiele.h"
+
+IntegrationPluginMiele::IntegrationPluginMiele()
+{
+
+}
+
+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");
+ }
+ if (clientKey.isEmpty() || clientSecret.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);
+ qCDebug(dcMiele()) << "HomeConnect url:" << url;
+ m_setupHomeConnectConnections.insert(info->thingId(), miele);
+ info->setOAuthUrl(url);
+ info->finish(Thing::ThingErrorNoError);
+ } else {
+ qCWarning(dMiele()) << "Unhandled pairing metod!";
+ info->finish(Thing::ThingErrorCreationMethodNotSupported);
+ }
+}
+
+void IntegrationPluginMiele::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret)
+{
+ Q_UNUSED(username);
+
+ if (info->thingClassId() == mieleAccountThingClassId) {
+ qCDebug(dcMiele()) << "Redirect url is" << secret;
+ QUrl url(secret);
+ QUrlQuery query(url);
+ QByteArray authorizationCode = query.queryItemValue("code").toLocal8Bit();
+
+ Miele *miele = m_setupMieleConnections.value(info->thingId());
+ if (!miele) {
+ qWarning(dcMiele()) << "No Miele connection found for device:" << info->thingName();
+ m_setupMieleConnections.remove(info->thingId());
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+ qCDebug(dcMiele()) << "Authorization code" << authorizationCode;
+ miele->getAccessTokenFromAuthorizationCode(authorizationCode);
+ connect(miele, &Miele::receivedRefreshToken, info, [info, this](const QByteArray &refreshToken){
+ qCDebug(dcMiele()) << "Token:" << refreshToken;
+
+ pluginStorage()->beginGroup(info->thingId().toString());
+ pluginStorage()->setValue("refresh_token", refreshToken);
+ pluginStorage()->endGroup();
+
+ info->finish(Thing::ThingErrorNoError);
+ });
+ } else {
+ Q_ASSERT_X(false, "confirmPairing", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
+ }
+}
+
+void IntegrationPluginMiele::setupThing(ThingSetupInfo *info)
+{
+
+}
+
+void IntegrationPluginMiele::postSetupThing(Thing *thing)
+{
+ if (!m_pluginTimer15min) {
+ m_pluginTimer15min = hardwareManager()->pluginTimerManager()->registerTimer(60*15);
+ connect(m_pluginTimer15min, &PluginTimer::timeout, this, [this]() {
+ Q_FOREACH (Thing *thing, myThings().filterByThingClassId(mieleAccountThingClassId)) {
+ Miele *miele = m_mieleConnections.value(thing);
+ if (!miele) {
+ qWarning(dcMiele()) << "No Miele account found for" << thing->name();
+ continue;
+ }
+ miele->getDevices();
+ Q_FOREACH (Thing *childThing, myThings().filterByParentId(thing->id())) {
+ QString deviceId = childThing->paramValue(m_idParamTypeIds.value(childThing->thingClassId())).toString();
+ miele->getDevice(deviceId);
+ }
+ }
+ });
+ }
+}
+
+void IntegrationPluginMiele::executeAction(ThingActionInfo *info)
+{
+ Thing *thing = info->thing();
+ Action action = info->action();
+ Miele *miele = m_mieleConnections.value(myThings().findById(thing->parentId()));
+ if (!miele) {
+ return info->finish(Thing::ThingErrorHardwareNotAvailable);
+ }
+ QString haid = thing->paramValue(m_idParamTypeIds.value(thing->thingClassId())).toString();
+
+ if (thing->thingClassId() == ovenThingClassId) {
+
+ } else if (thing->thingClassId() == ovenThingClassId) {
+
+ } else {
+
+ }
+}
+
+void IntegrationPluginMiele::thingRemoved(Thing *thing)
+{
+ qCDebug(dcMiele) << "Delete " << thing->name();
+
+ if (thing->thingClassId() == mieleAccountThingClassId) {
+ m_mieleConnections.take(thing)->deleteLater();
+ }
+
+ if (myThings().empty()) {
+ if (m_pluginTimer15min) {
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer15min);
+ m_pluginTimer15min = nullptr;
+ }
+ }
+}
+
+
diff --git a/miele/integrationpluginmiele.h b/miele/integrationpluginmiele.h
new file mode 100644
index 00000000..b49a715a
--- /dev/null
+++ b/miele/integrationpluginmiele.h
@@ -0,0 +1,103 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef INTEGRATIONPLUGINMIELE_H
+#define INTEGRATIONPLUGINMIELE_H
+
+#include "integrations/integrationplugin.h"
+#include "plugintimer.h"
+#include "miele.h"
+
+#include
+#include
+
+class IntegrationPluginMiele : public IntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginmiele.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginMiele();
+
+ void startPairing(ThingPairingInfo *info) override;
+ void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override;
+
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ 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;
+
+ QHash m_asyncSetup;
+
+ QHash m_setupMieleConnections;
+ QHash m_mieleConnections;
+
+ QHash m_pendingActions;
+ QHash m_selectedProgram;
+
+ QHash m_idParamTypeIds;
+
+ QHash m_connectedStateTypeIds;
+ QHash m_doorStateStateTypeIds;
+ QHash m_localControlStateTypeIds;
+ QHash m_remoteControlActivationStateTypeIds;
+ QHash m_remoteStartAllowanceStateTypeIds;
+ QHash m_operationStateTypeIds;
+ QHash m_doorStateTypeIds;
+ QHash m_selectedProgramStateTypeIds;
+ QHash m_progressStateTypeIds;
+ QHash m_endTimerStateTypeIds;
+
+ QHash m_startActionTypeIds;
+ QHash m_stopActionTypeIds;
+
+ QHash m_programFinishedEventTypeIds;
+
+ QHash 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);
+
+private slots:
+ void onConnectionChanged(bool connected);
+ void onAuthenticationStatusChanged(bool authenticated);
+ void onRequestExecuted(QUuid requestId, bool success);
+};
+
+#endif // INTEGRATIONPLUGINMIELE_H
diff --git a/miele/integrationpluginmiele.json b/miele/integrationpluginmiele.json
new file mode 100644
index 00000000..8437f7ee
--- /dev/null
+++ b/miele/integrationpluginmiele.json
@@ -0,0 +1,343 @@
+{
+ "id": "d44fad27-668a-4540-9eee-505d0ff13c37",
+ "name": "miele",
+ "displayName": "Miele",
+ "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",
+ "defaultValue": "",
+ "type": "QString"
+ }
+ ],
+ "apiKeys": ["miele"],
+ "vendors": [
+ {
+ "id": "ec1e94c4-d649-4af5-90c2-6c50b1a48e9e",
+ "name": "miele",
+ "displayName": "Miele",
+ "thingClasses": [
+ {
+ "id": "bea89711-7f41-4add-ba27-32dd69515c1c",
+ "name": "miele",
+ "displayName": "Miele",
+ "interfaces": ["account"],
+ "createMethods": ["user"],
+ "setupMethod": "oauth",
+ "paramTypes": [
+ ],
+ "stateTypes": [
+ {
+ "id": "89f4554b-fad2-402e-a0f0-6a76b26d10ea",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ },
+ {
+ "id": "b38f3d86-6d54-442c-a0f3-78ece8887d93",
+ "name": "loggedIn",
+ "displayName": "Logged in",
+ "displayNameEvent": "Logged in changed",
+ "defaultValue": true,
+ "type": "bool"
+ },
+ {
+ "id": "a5b85688-d319-4f61-8907-7da8a2b16d64",
+ "name": "userDisplayName",
+ "displayName": "User name",
+ "displayNameEvent": "User name changed",
+ "defaultValue": "",
+ "type": "QString"
+ }
+ ]
+ },
+ {
+ "id": "cf542b5e-2903-497c-95a0-042df6535450",
+ "name": "oven",
+ "displayName": "Oven",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "browsable": true,
+ "paramTypes": [
+ {
+ "id": "1e287843-41dd-49c8-a250-a21ed0e4215c",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "9c2c9360-0a4a-43ff-8d15-9cefa80380ec",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "82c7cba0-63fc-400c-8c47-dbeee47a3459",
+ "name": "dishwasher",
+ "displayName": "Dishwasher",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "browsable": true,
+ "paramTypes": [
+ {
+ "id": "f61d0fef-b588-4547-a5b6-03dc1622d0c5",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "170a1bb2-7c8f-448e-acdc-61a4537f10ab",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "8af89a1f-c752-4452-a568-d3b987e4667d",
+ "name": "coffeeMaker",
+ "displayName": "Coffee Maker",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "browsable": true,
+ "paramTypes": [
+ {
+ "id": "438cb528-91f4-481f-9b51-16658fd0a7c8",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "4b999b92-def7-4192-9814-38adf9af14e3",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "ad9bc908-9f61-4cb3-a8df-db003c6bf171",
+ "name": "dryer",
+ "displayName": "Dryer",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "browsable": true,
+ "paramTypes": [
+ {
+ "id": "3de1300c-766f-4a0b-9bc8-e75aa2402a6a",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "4dfce9b8-940b-49cb-8961-b2504fc2f3a6",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "a03e17fd-8da8-4977-8ac3-c058ada9f547",
+ "name": "fridge",
+ "displayName": "Fridge Freezer",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "paramTypes": [
+ {
+ "id": "945c1fa3-c304-4fc5-8c6b-f88f1f08d47a",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "6b066033-a290-459f-b6a1-c63857e6b538",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "58a6feaf-b2f7-48e9-822e-6f9526671f04",
+ "name": "washer",
+ "displayName": "Washer",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "browsable": true,
+ "paramTypes": [
+ {
+ "id": "2681e31e-cb0a-486f-b5b7-8ae28f7c96f9",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "ba271347-04b6-40a6-86f7-9c260a2fb23f",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "bbc55426-f649-4d32-81fc-cebd8a2b46d0",
+ "name": "cookTop",
+ "displayName": "Cook top",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "paramTypes": [
+ {
+ "id": "39f094ee-b672-4d75-b106-a21eeec41a34",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "08b7960c-e244-4956-808f-53ca7e486dd9",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "707e46a5-9ca0-4375-8249-2de19ec669ec",
+ "name": "hood",
+ "displayName": "Hood",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "paramTypes": [
+ {
+ "id": "15c3cea9-16c2-4517-ae43-3a9b5f388b82",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "be91bef9-c957-4bb8-9c92-1d2a548e762f",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "5698a1cf-5b3d-4746-a4fe-e4412d2768ab",
+ "name": "cleaningRobot",
+ "displayName": "Cleaning robot",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "paramTypes": [
+ {
+ "id": "629ac68f-89a2-41cf-801b-20df9968d204",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "8abe70f4-f49f-4b13-8251-216bea230f04",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "id": "40d31505-d6ae-4c45-a2b2-d652e1b85c80",
+ "name": "cookProcessor",
+ "displayName": "Cook processor",
+ "interfaces": ["connectable"],
+ "createMethods": ["auto"],
+ "paramTypes": [
+ {
+ "id": "51750b9f-4fc3-4556-a651-c8efcf07abe7",
+ "name": "id",
+ "displayName": "ID",
+ "defaultValue": "-",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "9eee48ba-b2e2-40fe-81b5-3404b655a497",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "defaultValue": true,
+ "cached": false,
+ "type": "bool"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/miele/meta.json b/miele/meta.json
new file mode 100644
index 00000000..bbe5b9e8
--- /dev/null
+++ b/miele/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "Miele",
+ "tagline": "Connect to Miele appliances.",
+ "icon": "miele.png",
+ "stability": "consumer",
+ "offline": false,
+ "technologies": [
+ "network"
+ ],
+ "categories": [
+ "online-service"
+ ]
+}
diff --git a/miele/miele.cpp b/miele/miele.cpp
new file mode 100644
index 00000000..40f8c2c8
--- /dev/null
+++ b/miele/miele.cpp
@@ -0,0 +1,133 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "miele.h"
+#include "extern-plugininfo.h"
+
+#include
+#include
+#include
+#include
+#include
+
+void Miele::getDevices()
+{
+
+}
+
+QUuid Miele::processAction(const QString &deviceId, Miele::ProcessAction action)
+{
+ QJsonDocument doc;
+ QJsonObject object;
+ object.insert("processAction", action);
+ doc.setObject(object);
+ return putAction(deviceId, doc);
+}
+
+QUuid Miele::setPower(const QString &deviceId, bool power)
+{
+ QJsonDocument doc;
+ QJsonObject object;
+ if (power) {
+ object.insert("powerOn", true);
+ } else {
+ object.insert("powerOff", true);
+ }
+ doc.setObject(object);
+ return putAction(deviceId, doc);
+}
+
+QUuid Miele::setDeviceName(const QString &deviceId, const QString &deviceName)
+{
+ QJsonDocument doc;
+ QJsonObject object;
+ object.insert("description", deviceName);
+ doc.setObject(object);
+ return putAction(deviceId, doc);
+}
+
+QUuid Miele::setLight(const QString &deviceId, bool power)
+{
+ QJsonDocument doc;
+ QJsonObject object;
+ if (power) {
+ object.insert("light", 1);
+ } else {
+ object.insert("light", 2);
+ }
+ doc.setObject(object);
+ return putAction(deviceId, doc);
+}
+
+QUuid Miele::setTargetTemperature(const QString &deviceId, int zone, int targetTemperature)
+{
+ QJsonDocument doc;
+ QJsonObject object;
+ QJsonObject temperatureObj;
+ temperatureObj.insert("zone", zone);
+ temperatureObj.insert("value", targetTemperature);
+ object.insert("targetTemperature", temperatureObj);
+ doc.setObject(object);
+ return putAction(deviceId, doc);
+}
+
+QUuid Miele::putAction(const QString &deviceId, const QJsonDocument &action)
+{
+ QUuid commandId = QUuid::createUuid();
+ QUrl url = m_apiUrl;
+ url.setPath("/v1/devices/"+deviceId+"/actions");
+ url.setQuery("language=en");
+
+ QNetworkRequest request(url);
+ request.setRawHeader("Authorization", "Bearer "+m_accessToken);
+ // request.setRawHeader("Accept-Language", "en-US");
+ request.setRawHeader("accept", "application/json; charset=utf-8");
+ request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
+
+ QNetworkReply *reply = m_networkManager->put(request, action.toJson());
+ connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, this, [this, commandId, reply]{
+
+ QByteArray rawData = reply->readAll();
+ if (!checkStatusCode(reply, rawData)) {
+ return;
+ }
+ QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap();
+ qCDebug(dcMiele()) << "Send command" << 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();
+ }
+ emit commandExecuted(commandId, true);
+ });
+ return commandId;
+}
diff --git a/miele/miele.h b/miele/miele.h
new file mode 100644
index 00000000..0fb89c24
--- /dev/null
+++ b/miele/miele.h
@@ -0,0 +1,139 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef MIELE_H
+#define MIELE_H
+
+#include
+#include
+#include
+#include
+
+#include "network/networkaccessmanager.h"
+
+class Miele : public QObject
+{
+ Q_OBJECT
+public:
+ enum ProcessAction {
+ ProcessActionsStart = 0,
+ ProcessActionsStop,
+ ProcessActionsPause,
+ ProcessActionsStarSuperfreezing,
+ ProcessActionsStopSuperfreezing,
+ ProcessActionsStartSupercooling,
+ ProcessActionsStopSupercooling
+ };
+
+ enum Mode {
+ ModeNormal,
+ ModeSabbath
+ };
+
+ enum Color {
+ ColorWhite,
+ ColorBlue,
+ ColorRed,
+ ColorYellow,
+ ColorOrange,
+ ColorGreen,
+ ColorPink,
+ ColorPurple,
+ ColorTurquoise
+ };
+
+ enum Status {
+ StatusOff = 1,
+ StatusOn = 2,
+ StatusProgrammed = 3,
+ StatusProgrammedWaitingToStart = 4,
+ StatusRunnin = 5,
+ StatusPause = 6,
+ StatusEndProgrammed = 7,
+ StatusFailure = 8,
+ StatusProgrammInterrupted = 9,
+ StatusIdle = 10,
+ StatusRinseHold = 11,
+ StatusService = 12,
+ StatusSuperfreezing = 13,
+ StatusSupercooling = 14,
+ StatusSuperheating = 15,
+ StatusSupercoolingSuperfreezing = 146,
+ StatusNotConnected = 255
+ };
+
+ Miele(NetworkAccessManager *networkmanager, const QByteArray &clientKey, const QByteArray &clientSecret, bool simulationMode = false, QObject *parent = nullptr);
+ QByteArray accessToken();
+ QByteArray refreshToken();
+ void setSimulationMode(bool simulation);
+
+ QUrl getLoginUrl(const QUrl &redirectUrl, const QString &scope);
+
+ void getAccessTokenFromRefreshToken(const QByteArray &refreshToken);
+ void getAccessTokenFromAuthorizationCode(const QByteArray &authorizationCode);
+
+ // INFORMATION
+ void getDevices();
+ void getDevice(const QString &deviceId);
+
+ // ACTION
+ void getActions(const QString &deviceId); //The GET action is used to request a device to send the currently supported actions
+ QUuid processAction(const QString &deviceId, ProcessAction action);
+
+ QUuid setPower(const QString &deviceId, bool power);
+ QUuid setDeviceName(const QString &deviceId, const QString &deviceName);
+ QUuid setLight(const QString &deviceId, bool power);
+ QUuid setTargetTemperature(const QString &deviceId, int zone, int targetTemperature);
+ QUuid setColors(const QString &deviceId, Color color);
+ QUuid setModes(const QString &deviceId, Mode mode);
+ QUuid setVentilationStep(const QString &deviceId, int step);
+ QUuid setStartTime();
+
+ // EVENTS
+ void getAllEvents();
+private:
+
+ NetworkAccessManager *m_networkManager = nullptr;
+
+ QUuid putAction(const QString &deviceId, const QJsonDocument &action); //
+
+ 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/");
+
+ bool checkStatusCode(QNetworkReply *reply, const QByteArray &rawData);
+signals:
+ void connectionChanged(bool connected);
+ void authenticationStatusChanged(bool authenticated);
+ void receivedRefreshToken(const QByteArray &refreshToken);
+ void receivedAccessToken(const QByteArray &accessToken);
+ void commandExecuted(const QUuid &commandId, bool success);
+};
+#endif // MIELE
diff --git a/miele/miele.pro b/miele/miele.pro
new file mode 100644
index 00000000..f3e269d3
--- /dev/null
+++ b/miele/miele.pro
@@ -0,0 +1,11 @@
+include(../plugins.pri)
+
+QT += network
+
+SOURCES += \
+ integrationpluginmiele.cpp \
+ miele.cpp \
+
+HEADERS += \
+ integrationpluginmiele.h \
+ miele.h \
diff --git a/miele/translations/d44fad27-668a-4540-9eee-505d0ff13c37-en_US.ts b/miele/translations/d44fad27-668a-4540-9eee-505d0ff13c37-en_US.ts
new file mode 100644
index 00000000..345b9c8e
--- /dev/null
+++ b/miele/translations/d44fad27-668a-4540-9eee-505d0ff13c37-en_US.ts
@@ -0,0 +1,12 @@
+
+
+
+
+ IntegrationPluginMiele
+
+
+ Client key and/or seceret is not available.
+
+
+
+
diff --git a/nymea-plugins.pro b/nymea-plugins.pro
index 30260ca7..ab627f6e 100644
--- a/nymea-plugins.pro
+++ b/nymea-plugins.pro
@@ -33,6 +33,7 @@ PLUGIN_DIRS = \
lifx \
mailnotification \
mqttclient \
+ miele \
nanoleaf \
netatmo \
networkdetector \