diff --git a/miele/integrationpluginmiele.cpp b/miele/integrationpluginmiele.cpp index b1dc8ec0..10bea6af 100644 --- a/miele/integrationpluginmiele.cpp +++ b/miele/integrationpluginmiele.cpp @@ -64,6 +64,7 @@ IntegrationPluginMiele::IntegrationPluginMiele() m_mieleDeviceTypeLabelToThingClassId.insert("coffee system", coffeeMakerThingClassId); m_mieleDeviceTypeLabelToThingClassId.insert("tumber dryer", dryerThingClassId); m_mieleDeviceTypeLabelToThingClassId.insert("refrigerator", fridgeThingClassId); + m_mieleDeviceTypeLabelToThingClassId.insert("hood", hoodThingClassId); } @@ -88,7 +89,9 @@ void IntegrationPluginMiele::startPairing(ThingPairingInfo *info) void IntegrationPluginMiele::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) { - Q_UNUSED(username); + //Q_UNUSED(username); + + qCDebug(dcMiele()) << "\n\n Confirm pairing for username: [" << username << "]"; if (info->thingClassId() == mieleAccountThingClassId) { qCDebug(dcMiele()) << "Confirm pairing Miele account"; @@ -166,6 +169,7 @@ void IntegrationPluginMiele::setupThing(ThingSetupInfo *info) connect(info, &ThingSetupInfo::aborted, miele, &Miele::deleteLater); connect(miele, &Miele::devicesFound, this, &IntegrationPluginMiele::onDevicesFound); connect(miele, &Miele::deviceStateReceived, this, &IntegrationPluginMiele::onDeviceStateReceived); + connect(miele, &Miele::deviceNotFound, this, &IntegrationPluginMiele::onDeviceNotFound); } } else if (m_idParamTypeIds.contains(thing->thingClassId())) { Thing *parentThing = myThings().findById(thing->parentId()); @@ -212,8 +216,7 @@ void IntegrationPluginMiele::postSetupThing(Thing *thing) miele->getDevicesShort(); //miele->connectEventStream(); thing->setStateValue(mieleAccountConnectedStateTypeId, true); - thing->setStateValue(mieleAccountLoggedInStateTypeId, true); - //TBD Set user name + thing->setStateValue(mieleAccountLoggedInStateTypeId, true); } else if (m_idParamTypeIds.contains(thing->thingClassId())) { Thing *parentThing = myThings().findById(thing->parentId()); if (!parentThing) @@ -245,10 +248,58 @@ void IntegrationPluginMiele::executeAction(ThingActionInfo *info) double targetTemp = action.param(fridgeTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble(); int iTargetTemp = floor(targetTemp); qCDebug(dcMiele()) << "Setting fridge temp: " << iTargetTemp; - miele->setTargetTemperature(deviceId, 1, iTargetTemp); + QUuid requestId = miele->setTargetTemperature(deviceId, 1, iTargetTemp); info->finish(Thing::ThingErrorNoError); thing->setStateValue(fridgeTargetTemperatureStateTypeId, iTargetTemp); + m_pendingActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); + } else if (action.actionTypeId() == fridgeStartSuperCoolingActionTypeId) { + QUuid requestId = miele->processAction(deviceId, Miele::ProcessActionsStartSupercooling); + m_pendingActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); + // don't set state, it should come as event once it successfully started on the actual device + //thing->setStateValue(fridgePowerStateTypeId, !currentSuperCoolingState); + } else if (action.actionTypeId() == fridgeStopSuperCoolingActionTypeId) { + QUuid requestId = miele->processAction(deviceId, Miele::ProcessActionsStopSupercooling); + m_pendingActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); } + } else if (thing->thingClassId() == hoodThingClassId) { + if (action.actionTypeId() == hoodStopHoodActionTypeId) { + // TODO: should check if current state of the hood allows the Stop action + QUuid requestId = miele->processAction(deviceId, Miele::ProcessActionsStop); + m_pendingActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); + } else if (action.actionTypeId() == hoodVentilationStepActionTypeId) { + int ventStep = action.param(hoodVentilationStepActionVentilationStepParamTypeId).value().toInt(); + QUuid requestId = miele->setVentilationStep(deviceId, ventStep); + m_pendingActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); + } else if (action.actionTypeId() == hoodLightColorActionTypeId) { + QString color = action.param(hoodLightColorActionLightColorParamTypeId).value().toString(); + QUuid requestId = miele->setColorsStr(deviceId, color); + m_pendingActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); + } else if (action.actionTypeId() == hoodLightActionTypeId) { + bool power = action.param(hoodLightActionLightParamTypeId).value().toBool(); + QUuid requestId = miele->setLight(deviceId, power); + connect(info, &ThingActionInfo::aborted, [requestId, this] { + m_pendingActions.remove(requestId); + }); + } + } else if (thing->thingClassId() == ovenThingClassId) { } else { @@ -358,7 +409,7 @@ void IntegrationPluginMiele::onDevicesFound(QList devices) if(m_mieleConnections.values().contains(miele)) { Thing *parentDevice = myThings().findById(m_mieleConnections.key(miele)->id()); - qCDebug(dcMiele()) << devices.size() << " devices received for prent: " << parentDevice->id(); + qCDebug(dcMiele()) << devices.size() << " devices received for parent: " << parentDevice->id(); ThingDescriptors descriptors; foreach(Miele::DeviceShort ds, devices) { @@ -420,6 +471,7 @@ void IntegrationPluginMiele::setThingState(Thing *thing, const QVariantMap &stat ? false : true; thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), connected); + thing->setStateValue(fridgeOperationStateStateTypeId, state.value("status").toMap().value("value_localized").toString()); // Fridges should have only one temperature zone QVariant fridgeTempZone = state.value("temperature").toList().first(); @@ -430,7 +482,60 @@ void IntegrationPluginMiele::setThingState(Thing *thing, const QVariantMap &stat double fridgeTargetTemp = fridgeTargetTempZone.toMap().value("value_raw").toDouble(); thing->setStateValue(fridgeTargetTemperatureStateTypeId, fridgeTargetTemp / 100); + thing->setStateValue(fridgeSignalDoorStateTypeId, state.value("signalDoor").toBool()); + thing->setStateValue(fridgeSignalInfoStateTypeId, state.value("signalInfo").toBool()); + thing->setStateValue(fridgeSignalFailureStateTypeId, state.value("signalFailure").toBool()); + + QVariantMap remoteEnabledModes = state.value("remoteEnable").toMap(); + thing->setStateValue(fridgeFullRemoteControlStateTypeId, remoteEnabledModes.value("fullRemoteControl").toBool()); + thing->setStateValue(fridgeSmartGridStateTypeId, remoteEnabledModes.value("smartGrid").toBool()); + thing->setStateValue(fridgeMobileStartStateTypeId, remoteEnabledModes.value("mobileStart").toBool()); + + } else if (thing->thingClassId() == hoodThingClassId) { + qCDebug(dcMiele()) << "Setting state for hood!"; + + int status = state.value("status").toMap().value("value_raw").toInt(); + bool connected = (status == Miele::StatusNotConnected) ? false : true; + bool poweredOn = (status == Miele::StatusOff) ? false : true; + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), connected); + thing->setStateValue(hoodPowerStateTypeId, poweredOn); + thing->setStateValue(hoodOperationStateStateTypeId, state.value("status").toMap().value("value_localized").toString()); + + thing->setStateValue(hoodSignalInfoStateTypeId, state.value("signalInfo").toBool()); + thing->setStateValue(hoodSignalFailureStateTypeId, state.value("signalFailure").toBool()); + thing->setStateValue(hoodLightStateTypeId, state.value("light").toInt() != 0 ? true : false); + + QVariantMap remoteEnabledModes = state.value("remoteEnable").toMap(); + thing->setStateValue(hoodFullRemoteControlStateTypeId, remoteEnabledModes.value("fullRemoteControl").toBool()); + thing->setStateValue(hoodSmartGridStateTypeId, remoteEnabledModes.value("smartGrid").toBool()); + thing->setStateValue(hoodMobileStartStateTypeId, remoteEnabledModes.value("mobileStart").toBool()); + + int ventilationStep = state.value("ventilationStep").toMap().value("value_raw").toInt(); + thing->setStateValue(hoodVentilationStepStateTypeId, ventilationStep); } else { qCWarning(dcMiele()) << "Device " << thing->name() << " is not yet supported!"; } } + +void IntegrationPluginMiele::onDeviceNotFound(const QString &deviceId) +{ + Miele *miele = static_cast(sender()); + if (!m_mieleConnections.values().contains(miele)) { + qCWarning(dcMiele()) << "Can't find Miele connection!"; + return; + } + + Thing *parentDevice = myThings().findById(m_mieleConnections.key(miele)->id()); + if (!parentDevice) { + qCWarning(dcMiele()) << "Can't find parent thing!"; + return; + } + + foreach(Thing *thing, myThings().filterByParentId(parentDevice->id())) { + if (thing->paramValue(m_idParamTypeIds.value(thing->thingClassId())).toString() == deviceId) { + qCDebug(dcMiele()) << "Device not found anymore: " << thing->name(); + emit autoThingDisappeared(thing->id()); + break; + } + } +} diff --git a/miele/integrationpluginmiele.h b/miele/integrationpluginmiele.h index 03449e9b..303b44ba 100644 --- a/miele/integrationpluginmiele.h +++ b/miele/integrationpluginmiele.h @@ -79,6 +79,7 @@ private slots: void onDevicesFound(QList devices); void onDeviceStateReceived(const QString &deviceId, const QVariantMap &deviceState); + void onDeviceNotFound(const QString &deviceId); }; #endif // INTEGRATIONPLUGINMIELE_H diff --git a/miele/integrationpluginmiele.json b/miele/integrationpluginmiele.json index 43cb6e0b..da288900 100644 --- a/miele/integrationpluginmiele.json +++ b/miele/integrationpluginmiele.json @@ -51,14 +51,6 @@ "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" } ] }, @@ -198,6 +190,38 @@ "defaultValue": true, "cached": false, "type": "bool" + }, + { + "id": "2f2039bb-b0af-4272-a8ba-558589f49299", + "name": "operationState", + "displayName": "Operation state", + "displayNameEvent": "Operation state changed", + "defaultValue": "Not Connected", + "type": "QString" + }, + { + "id": "bafefd33-cfdb-4715-a1c1-fb3d4215f84c", + "name": "fullRemoteControl", + "displayName": "Full remote control", + "displayNameEvent": "Full remote control changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "89081d4a-5e46-4569-b0f9-52347491a013", + "name": "smartGrid", + "displayName": "Smart grid enabled", + "displayNameEvent": "Smart grid enabled changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "a878082e-a530-439a-9d71-dfe430fb4988", + "name": "mobileStart", + "displayName": "Mobile start enabled", + "displayNameEvent": "Mobile start enabled changed", + "defaultValue": false, + "type": "bool" }, { "id": "ef656321-e2c8-4339-be18-aa3cdb53dea6", @@ -225,15 +249,40 @@ "ioType": "analogInput" }, { - "id": "60a46368-febc-4cff-bbfb-99064dbb93a9", - "name": "power", - "displayName": "On/off", - "displayNameEvent": "Turned on/off", - "displayNameAction": "Turn on/off", - "type": "bool", + "id": "6954a425-e02e-4930-b073-1cc70805305a", + "name": "signalDoor", + "displayName": "Door open", + "displayNameEvent": "Door open changed", "defaultValue": false, - "writable": true, - "ioType": "digitalInput" + "type": "bool" + }, + { + "id": "0469a4b9-ec7d-444c-9aa3-410518ec41ef", + "name": "signalInfo", + "displayName": "Unread notification", + "displayNameEvent": "Unread notification changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "cbc573bd-ad3f-4d58-a796-b81bea14622d", + "name": "signalFailure", + "displayName": "Failure notification", + "displayNameEvent": "Failure notification changed", + "defaultValue": false, + "type": "bool" + } + ], + "actionTypes": [ + { + "id": "0b206df2-47aa-4f8a-9c14-ff483c539826", + "name": "startSuperCooling", + "displayName": "Start SuperCooling" + }, + { + "id": "293568c2-5f8c-42f9-b150-769e181a8cbd", + "name": "stopSuperCooling", + "displayName": "Stop SuperCooling" } ] }, @@ -296,7 +345,7 @@ "id": "707e46a5-9ca0-4375-8249-2de19ec669ec", "name": "hood", "displayName": "Hood", - "interfaces": ["connectable"], + "interfaces": ["power", "connectable"], "createMethods": ["auto"], "paramTypes": [ { @@ -316,6 +365,115 @@ "defaultValue": true, "cached": false, "type": "bool" + }, + { + "id": "2d5c88b4-34fd-4f15-b731-332dc905ef27", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Powered on/off", + "displayNameAction": "Power on/off", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "db566fc5-383f-4726-a730-9dadac76e2d0", + "name": "operationState", + "displayName": "Operation state", + "displayNameEvent": "Operation state changed", + "defaultValue": "Not Connected", + "type": "QString" + }, + { + "id": "d64b646c-8405-4384-8aa3-02e288dc0603", + "name": "signalInfo", + "displayName": "Unread notification", + "displayNameEvent": "Unread notification changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "8da242c3-117c-4cb7-91f9-2bd34835e427", + "name": "signalFailure", + "displayName": "Failure notification", + "displayNameEvent": "Failure notification changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "dd97c6dd-94fd-4dd3-b2ce-812c40e6247b", + "name": "fullRemoteControl", + "displayName": "Full remote control", + "displayNameEvent": "Full remote control changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "0b1a6594-fda6-4c48-a898-7b54b30538f7", + "name": "smartGrid", + "displayName": "Smart grid enabled", + "displayNameEvent": "Smart grid enabled changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "363d5770-6f11-41c8-981c-9f1d49278b3c", + "name": "mobileStart", + "displayName": "Mobile start enabled", + "displayNameEvent": "Mobile start enabled changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "ae7044b9-3a46-4c98-bd02-2abded425c35", + "name": "ventilationStep", + "displayName": "Ventilation step", + "displayNameEvent": "Ventilation step changed", + "displayNameAction": "Set ventilation step", + "defaultValue": 0, + "type": "int", + "possibleValues": [ + 0, 1, 2, 3, 4 + ], + "writable": true + }, + { + "id": "29d4fb9f-0ad3-4911-89c1-68789d58e00c", + "name": "light", + "displayName": "Light", + "displayNameEvent": "Light on/off changed", + "displayNameAction": "Set light on/off", + "defaultValue": false, + "type": "bool", + "writable": true + }, + { + "id": "d5953bee-eb5a-4c3d-a7a4-3f24562f585d", + "name": "lightColor", + "displayName": "Light color", + "displayNameEvent": "Light color changed", + "displayNameAction": "Set light color", + "defaultValue": "white", + "type": "QString", + "writable": true, + "possibleValues": [ + "white", + "blue", + "red", + "yellow", + "orange", + "green", + "pink", + "purple", + "turquoise" + ] + } + ], + "actionTypes": [ + { + "id": "0aae84bb-5502-437c-b821-46ef26209e09", + "name": "stopHood", + "displayName": "Stop" } ] }, diff --git a/miele/miele.cpp b/miele/miele.cpp index 633d7312..76b0e4ca 100644 --- a/miele/miele.cpp +++ b/miele/miele.cpp @@ -92,11 +92,7 @@ void Miele::getAccessTokenFromRefreshToken(const QByteArray &refreshToken) qCWarning(dcMiele()) << "No refresh token given!"; setAuthenticated(false); return; - } - - //delete me - qCDebug(dcMiele()) << "Refresh token is not empty: " << refreshToken; - qCDebug(dcMiele()) << "Client: " << m_clientId << " secret: " << m_clientSecret; + } QUrl url(m_tokenUrl); QUrlQuery query; @@ -302,6 +298,10 @@ void Miele::getDeviceState(const QString &deviceId) { QByteArray rawData = reply->readAll(); if (!checkStatusCode(reply, rawData)) { + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (status == 404) { + emit deviceNotFound(deviceId); + } return; } QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap(); @@ -387,6 +387,15 @@ QUuid Miele::setColors(const QString &deviceId, Miele::Color color) return putAction(deviceId, doc); } +QUuid Miele::setColorsStr(const QString &deviceId, const QString &color) +{ + QJsonDocument doc; + QJsonObject object; + object.insert("colors", color.toLower()); + doc.setObject(object); + return putAction(deviceId, doc); +} + QUuid Miele::setModes(const QString &deviceId, Miele::Mode mode) { QJsonDocument doc; diff --git a/miele/miele.h b/miele/miele.h index 603609ef..20afe653 100644 --- a/miele/miele.h +++ b/miele/miele.h @@ -122,9 +122,10 @@ public: QUuid setLight(const QString &deviceId, bool power); QUuid setTargetTemperature(const QString &deviceId, int zone, int targetTemperature); QUuid setColors(const QString &deviceId, Color color); + QUuid setColorsStr(const QString &deviceId, const QString &color); QUuid setModes(const QString &deviceId, Mode mode); QUuid setVentilationStep(const QString &deviceId, int step); - QUuid setStartTime(const QString &deviceId, int seconds); + QUuid setStartTime(const QString &deviceId, int seconds); // EVENTS void getAllEvents(); @@ -162,6 +163,7 @@ signals: void commandExecuted(const QUuid &commandId, bool success); void devicesFound(QList devices); void deviceStateReceived(const QString &deviceId, const QVariantMap &deviceState); + void deviceNotFound(const QString &deviceId); private slots: void onRefreshTimeout();