fixed state update

This commit is contained in:
Boernsman 2020-01-03 20:16:51 +01:00
parent d47214ac14
commit 5c28eaf871
5 changed files with 265 additions and 149 deletions

View File

@ -2,3 +2,13 @@
Let's you connect to your Tado account. All configured zones will appear in nymea automatically. Let's you connect to your Tado account. All configured zones will appear in nymea automatically.
You will get all relevant data and can set the mode and target temperature. You will get all relevant data and can set the mode and target temperature.
## Device Setup
nymea will connect to your Tado account. After the account is connected all associated devices
will appear automatically. The Tado app is required to create a Tado account and connect the
devices to it.
More about Tado:
https://www.tado.com

View File

@ -1,24 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * *
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> * * Copyright 2013 - 2020, nymea GmbH
* * * Contact: contact@nymea.io
* This file is part of nymea. * *
* * * This file is part of nymea.
* This library is free software; you can redistribute it and/or * * This project including source code and documentation is protected by copyright law, and
* modify it under the terms of the GNU Lesser General Public * * remains the property of nymea GmbH. All rights, including reproduction, publication,
* License as published by the Free Software Foundation; either * * editing and translation, are reserved. The use of this project is subject to the terms of a
* version 2.1 of the License, or (at your option) any later version. * * license agreement to be concluded with nymea GmbH in accordance with the terms
* * * of use of nymea GmbH, available under https://nymea.io/license
* This library is distributed in the hope that it will be useful, * *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * GNU Lesser General Public License Usage
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * This project may also contain libraries licensed under the open source software license GNU GPL v.3.
* Lesser General Public License for more details. * * 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.
* You should have received a copy of the GNU Lesser General Public * * this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* License along with this library; If not, see * * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* <http://www.gnu.org/licenses/>. * * 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 <https://www.gnu.org/licenses/>.
*
* 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 "deviceplugintado.h" #include "deviceplugintado.h"
#include "devices/device.h" #include "devices/device.h"
@ -34,11 +40,6 @@ DevicePluginTado::DevicePluginTado()
} }
DevicePluginTado::~DevicePluginTado()
{
}
void DevicePluginTado::startPairing(DevicePairingInfo *info) void DevicePluginTado::startPairing(DevicePairingInfo *info)
{ {
info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the login credentials.")); info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the login credentials."));
@ -53,6 +54,7 @@ void DevicePluginTado::confirmPairing(DevicePairingInfo *info, const QString &us
connect(tado, &Tado::homesReceived, this, &DevicePluginTado::onHomesReceived); connect(tado, &Tado::homesReceived, this, &DevicePluginTado::onHomesReceived);
connect(tado, &Tado::zonesReceived, this, &DevicePluginTado::onZonesReceived); connect(tado, &Tado::zonesReceived, this, &DevicePluginTado::onZonesReceived);
connect(tado, &Tado::zoneStateReceived, this, &DevicePluginTado::onZoneStateReceived); connect(tado, &Tado::zoneStateReceived, this, &DevicePluginTado::onZoneStateReceived);
connect(tado, &Tado::overlayReceived, this, &DevicePluginTado::onOverlayReceived);
m_unfinishedTadoAccounts.insert(info->deviceId(), tado); m_unfinishedTadoAccounts.insert(info->deviceId(), tado);
m_unfinishedDevicePairings.insert(info->deviceId(), info); m_unfinishedDevicePairings.insert(info->deviceId(), info);
tado->getToken(password); tado->getToken(password);
@ -88,6 +90,7 @@ void DevicePluginTado::setupDevice(DeviceSetupInfo *info)
connect(tado, &Tado::homesReceived, this, &DevicePluginTado::onHomesReceived); connect(tado, &Tado::homesReceived, this, &DevicePluginTado::onHomesReceived);
connect(tado, &Tado::zonesReceived, this, &DevicePluginTado::onZonesReceived); connect(tado, &Tado::zonesReceived, this, &DevicePluginTado::onZonesReceived);
connect(tado, &Tado::zoneStateReceived, this, &DevicePluginTado::onZoneStateReceived); connect(tado, &Tado::zoneStateReceived, this, &DevicePluginTado::onZoneStateReceived);
connect(tado, &Tado::overlayReceived, this, &DevicePluginTado::onOverlayReceived);
tado->getToken(password); tado->getToken(password);
m_tadoAccounts.insert(device->id(), tado); m_tadoAccounts.insert(device->id(), tado);
m_asyncDeviceSetup.insert(tado, info); m_asyncDeviceSetup.insert(tado, info);
@ -106,9 +109,6 @@ void DevicePluginTado::deviceRemoved(Device *device)
if (device->deviceClassId() == tadoConnectionDeviceClassId) { if (device->deviceClassId() == tadoConnectionDeviceClassId) {
Tado *tado = m_tadoAccounts.take(device->id()); Tado *tado = m_tadoAccounts.take(device->id());
tado->deleteLater(); tado->deleteLater();
} else if (device->deviceClassId() == zoneDeviceClassId) {
} }
if (myDevices().isEmpty() && m_pluginTimer) { if (myDevices().isEmpty() && m_pluginTimer) {
@ -129,7 +129,6 @@ void DevicePluginTado::postSetupDevice(Device *device)
device->setStateValue(tadoConnectionUserDisplayNameStateTypeId, tado->username()); device->setStateValue(tadoConnectionUserDisplayNameStateTypeId, tado->username());
device->setStateValue(tadoConnectionLoggedInStateTypeId, true); device->setStateValue(tadoConnectionLoggedInStateTypeId, true);
device->setStateValue(tadoConnectionConnectedStateTypeId, true); device->setStateValue(tadoConnectionConnectedStateTypeId, true);
tado->getHomes(); tado->getHomes();
} else if (device->deviceClassId() == zoneDeviceClassId) { } else if (device->deviceClassId() == zoneDeviceClassId) {
@ -169,7 +168,8 @@ void DevicePluginTado::executeAction(DeviceActionInfo *info)
double temperature = action.param(zoneTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble(); double temperature = action.param(zoneTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble();
if (temperature <= 0) { if (temperature <= 0) {
tado->setOverlay(homeId, zoneId, false, 0); QUuid requestId = tado->setOverlay(homeId, zoneId, false, 0);
m_asyncActions.insert(requestId, info);
} else { } else {
tado->setOverlay(homeId, zoneId, true, temperature); tado->setOverlay(homeId, zoneId, true, temperature);
} }
@ -210,7 +210,8 @@ void DevicePluginTado::onAuthenticationStatusChanged(bool authenticated)
Tado *tado = static_cast<Tado*>(sender()); Tado *tado = static_cast<Tado*>(sender());
if (m_unfinishedTadoAccounts.values().contains(tado) && !authenticated){ if (m_unfinishedTadoAccounts.values().contains(tado) && !authenticated){
DeviceId id = m_tadoAccounts.key(tado); DeviceId id = m_unfinishedTadoAccounts.key(tado);
m_unfinishedTadoAccounts.remove(id);
DevicePairingInfo *info = m_unfinishedDevicePairings.take(id); DevicePairingInfo *info = m_unfinishedDevicePairings.take(id);
info->finish(Device::DeviceErrorSetupFailed); info->finish(Device::DeviceErrorSetupFailed);
} }
@ -238,10 +239,6 @@ void DevicePluginTado::onTokenReceived(Tado::Token token)
DevicePairingInfo *info = m_unfinishedDevicePairings.take(id); DevicePairingInfo *info = m_unfinishedDevicePairings.take(id);
info->finish(Device::DeviceErrorNoError); info->finish(Device::DeviceErrorNoError);
} }
if (m_tadoAccounts.values().contains(tado)) {
}
} }
void DevicePluginTado::onHomesReceived(QList<Tado::Home> homes) void DevicePluginTado::onHomesReceived(QList<Tado::Home> homes)
@ -304,8 +301,6 @@ void DevicePluginTado::onZoneStateReceived(const QString &homeId, const QString
} }
device->setStateValue(zonePowerStateTypeId, (state.heatingPowerPercentage > 0)); device->setStateValue(zonePowerStateTypeId, (state.heatingPowerPercentage > 0));
device->setStateValue(zoneConnectedStateTypeId, state.connected); device->setStateValue(zoneConnectedStateTypeId, state.connected);
device->setStateValue(zoneTargetTemperatureStateTypeId, state.settingTemperature); device->setStateValue(zoneTargetTemperatureStateTypeId, state.settingTemperature);
device->setStateValue(zoneTemperatureStateTypeId, state.temperature); device->setStateValue(zoneTemperatureStateTypeId, state.temperature);
@ -313,3 +308,26 @@ void DevicePluginTado::onZoneStateReceived(const QString &homeId, const QString
device->setStateValue(zoneWindowOpenStateTypeId, state.windowOpen); device->setStateValue(zoneWindowOpenStateTypeId, state.windowOpen);
device->setStateValue(zoneTadoModeStateTypeId, state.tadoMode); device->setStateValue(zoneTadoModeStateTypeId, state.tadoMode);
} }
void DevicePluginTado::onOverlayReceived(const QString &homeId, const QString &zoneId, const Tado::Overlay &overlay)
{
Tado *tado = static_cast<Tado*>(sender());
DeviceId parentId = m_tadoAccounts.key(tado);
ParamList params;
params.append(Param(zoneDeviceHomeIdParamTypeId, homeId));
params.append(Param(zoneDeviceZoneIdParamTypeId, zoneId));
Device *device = myDevices().filterByParentDeviceId(parentId).findByParams(params);
if (!device)
return;
device->setStateValue(zoneTargetTemperatureStateTypeId, overlay.temperature);
if (overlay.tadoMode == "MANUAL") {
if (overlay.power) {
device->setStateValue(zoneModeStateTypeId, "Manual");
} else {
device->setStateValue(zoneModeStateTypeId, "Off");
}
} else {
device->setStateValue(zoneModeStateTypeId, "Tado");
}
}

View File

@ -1,24 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@nymea.io> * * Copyright 2013 - 2020, nymea GmbH
* * * Contact: contact@nymea.io
* This file is part of nymea. * *
* * * This file is part of nymea.
* This library is free software; you can redistribute it and/or * * This project including source code and documentation is protected by copyright law, and
* modify it under the terms of the GNU Lesser General Public * * remains the property of nymea GmbH. All rights, including reproduction, publication,
* License as published by the Free Software Foundation; either * * editing and translation, are reserved. The use of this project is subject to the terms of a
* version 2.1 of the License, or (at your option) any later version. * * license agreement to be concluded with nymea GmbH in accordance with the terms
* * * of use of nymea GmbH, available under https://nymea.io/license
* This library is distributed in the hope that it will be useful, * *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * GNU Lesser General Public License Usage
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * This project may also contain libraries licensed under the open source software license GNU GPL v.3.
* Lesser General Public License for more details. * * 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.
* You should have received a copy of the GNU Lesser General Public * * this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* License along with this library; If not, see * * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* <http://www.gnu.org/licenses/>. * * 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 <https://www.gnu.org/licenses/>.
*
* 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 DEVICEPLUGINTADO_H #ifndef DEVICEPLUGINTADO_H
#define DEVICEPLUGINTADO_H #define DEVICEPLUGINTADO_H
@ -39,8 +45,6 @@ class DevicePluginTado : public DevicePlugin
public: public:
explicit DevicePluginTado(); explicit DevicePluginTado();
~DevicePluginTado();
void startPairing(DevicePairingInfo *info) override; void startPairing(DevicePairingInfo *info) override;
void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override; void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override;
void setupDevice(DeviceSetupInfo *info) override; void setupDevice(DeviceSetupInfo *info) override;
@ -55,6 +59,7 @@ private:
QHash<DeviceId, Tado*> m_tadoAccounts; QHash<DeviceId, Tado*> m_tadoAccounts;
QHash<Tado *, DeviceSetupInfo *> m_asyncDeviceSetup; QHash<Tado *, DeviceSetupInfo *> m_asyncDeviceSetup;
QHash<QUuid, DeviceActionInfo *> m_asyncActions;
private slots: private slots:
void onPluginTimer(); void onPluginTimer();
@ -65,6 +70,7 @@ private slots:
void onHomesReceived(QList<Tado::Home> homes); void onHomesReceived(QList<Tado::Home> homes);
void onZonesReceived(const QString &homeId, QList<Tado::Zone> zones); void onZonesReceived(const QString &homeId, QList<Tado::Zone> zones);
void onZoneStateReceived(const QString &homeId,const QString &zoneId, Tado::ZoneState sate); void onZoneStateReceived(const QString &homeId,const QString &zoneId, Tado::ZoneState sate);
void onOverlayReceived(const QString &homeId, const QString &zoneId, const Tado::Overlay &overlay);
}; };
#endif // DEVICEPLUGINTADO_H #endif // DEVICEPLUGINTADO_H

View File

@ -1,24 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * *
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> * * Copyright 2013 - 2020, nymea GmbH
* * * Contact: contact@nymea.io
* This file is part of nymea. * *
* * * This file is part of nymea.
* This library is free software; you can redistribute it and/or * * This project including source code and documentation is protected by copyright law, and
* modify it under the terms of the GNU Lesser General Public * * remains the property of nymea GmbH. All rights, including reproduction, publication,
* License as published by the Free Software Foundation; either * * editing and translation, are reserved. The use of this project is subject to the terms of a
* version 2.1 of the License, or (at your option) any later version. * * license agreement to be concluded with nymea GmbH in accordance with the terms
* * * of use of nymea GmbH, available under https://nymea.io/license
* This library is distributed in the hope that it will be useful, * *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * GNU Lesser General Public License Usage
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * This project may also contain libraries licensed under the open source software license GNU GPL v.3.
* Lesser General Public License for more details. * * 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.
* You should have received a copy of the GNU Lesser General Public * * this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* License along with this library; If not, see * * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* <http://www.gnu.org/licenses/>. * * 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 <https://www.gnu.org/licenses/>.
*
* 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 "tado.h" #include "tado.h"
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
@ -72,33 +78,47 @@ void Tado::getToken(const QString &password)
if (reply->error() == QNetworkReply::HostNotFoundError) { if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false); emit connectionChanged(false);
} }
if (status == 400 || status == 401) { if (status == 401) {
emit authenticationStatusChanged(false); emit authenticationStatusChanged(false);
} }
qCWarning(dcTado()) << "Request error:" << status << reply->errorString(); qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
return; return;
} }
emit connectionChanged(true); emit connectionChanged(true);
emit authenticationStatusChanged(true);
QJsonParseError error; QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error); QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qDebug(dcTado()) << "Get Token: Recieved invalide JSON object"; qDebug(dcTado()) << "Get Token: Received invalide JSON object";
return; return;
} }
Token token; if (data.isObject()) {
QVariantMap obj = data.toVariant().toMap(); Token token;
token.accesToken = obj["access_token"].toString(); QVariantMap obj = data.toVariant().toMap();
m_accessToken = token.accesToken; if (obj.contains("access_token")) {
token.tokenType = obj["token_type"].toString(); token.accesToken = obj["access_token"].toString();
token.refreshToken = obj["refresh_token"].toString(); m_accessToken = token.accesToken;
m_refreshToken = token.refreshToken; } else {
token.expires = obj["expires_in"].toInt(); qCWarning(dcTado()) << "Received response doesnt contain an access token";
m_refreshTimer->start((token.expires - 10)*1000); }
token.scope = obj["scope"].toString();
token.jti = obj["jti"].toString(); token.tokenType = obj["token_type"].toString();
emit tokenReceived(token); token.refreshToken = obj["refresh_token"].toString();
m_refreshToken = token.refreshToken;
if (obj.contains("expires_in")) {
token.expires = obj["expires_in"].toInt();
m_refreshTimer->start((token.expires - 10)*1000);
} else {
qCWarning(dcTado()) << "Received response doesn't contain an expire time";
}
token.scope = obj["scope"].toString();
token.jti = obj["jti"].toString();
emit authenticationStatusChanged(true);
emit tokenReceived(token);
} else {
qCWarning(dcTado()) << "Received response isn't an object" << data.toJson();
emit authenticationStatusChanged(false);
}
}); });
} }
@ -119,12 +139,14 @@ void Tado::getHomes()
if (reply->error() == QNetworkReply::HostNotFoundError) { if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false); emit connectionChanged(false);
} }
if (status == 400 || status == 401) { if (status == 401) {
emit authenticationStatusChanged(false); emit authenticationStatusChanged(false);
} }
qCWarning(dcTado()) << "Request error:" << status << reply->errorString(); qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
return; return;
} }
emit connectionChanged(true);
emit authenticationStatusChanged(true);
QJsonParseError error; QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error); QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
@ -163,12 +185,14 @@ void Tado::getZones(const QString &homeId)
if (reply->error() == QNetworkReply::HostNotFoundError) { if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false); emit connectionChanged(false);
} }
if (status == 400 || status == 401) { if (status == 401) {
emit authenticationStatusChanged(false); emit authenticationStatusChanged(false);
} }
qCWarning(dcTado()) << "Request error:" << status << reply->errorString(); qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
return; return;
} }
emit connectionChanged(true);
emit authenticationStatusChanged(true);
QJsonParseError error; QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error); QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
@ -207,19 +231,24 @@ void Tado::getZoneState(const QString &homeId, const QString &zoneId)
if (reply->error() == QNetworkReply::HostNotFoundError) { if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false); emit connectionChanged(false);
} }
if (status == 400 || status == 401) { if (status == 401) {
emit authenticationStatusChanged(false); emit authenticationStatusChanged(false);
} }
qCWarning(dcTado()) << "Request error:" << status << reply->errorString(); qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
return; return;
} }
emit connectionChanged(true);
emit authenticationStatusChanged(true);
QJsonParseError error; QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error); QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qDebug(dcTado()) << "Get Token: Recieved invalide JSON object"; qDebug(dcTado()) << "Get Token: Recieved invalide JSON object";
return; return;
} }
qDebug(dcTado()) << "Get zone stat" << data;
ZoneState state; ZoneState state;
QVariantMap map = data.toVariant().toMap(); QVariantMap map = data.toVariant().toMap();
state.tadoMode = map["tadoMode"].toString(); state.tadoMode = map["tadoMode"].toString();
@ -244,8 +273,9 @@ void Tado::getZoneState(const QString &homeId, const QString &zoneId)
state.overlayIsSet = true; state.overlayIsSet = true;
QVariantMap overlayMap = map["overlay"].toMap(); QVariantMap overlayMap = map["overlay"].toMap();
state.overlayType = map["overlayType"].toString(); state.overlayType = map["overlayType"].toString();
state.overlaySettingPower = (overlayMap["settings"].toMap().value("power").toString() == "ON"); qCDebug(dcTado()) << "Overlay power" << overlayMap["setting"].toMap().value("power").toString() << overlayMap["setting"].toMap().value("temperature").toDouble();
state.overlaySettingTemperature = overlayMap["settings"].toMap().value("temperature").toDouble(); state.overlaySettingPower = (overlayMap["setting"].toMap().value("power").toString() == "ON");
state.overlaySettingTemperature = overlayMap["setting"].toMap().value("temperature").toDouble();
} else { } else {
state.overlayIsSet = false; state.overlayIsSet = false;
} }
@ -253,26 +283,13 @@ void Tado::getZoneState(const QString &homeId, const QString &zoneId)
}); });
} }
void Tado::setOverlay(const QString &homeId, const QString &zoneId, bool power, double targetTemperature) QUuid Tado::setOverlay(const QString &homeId, const QString &zoneId, bool power, double targetTemperature)
{ {
QUuid requestId = QUuid::createUuid();
QNetworkRequest request; QNetworkRequest request;
request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/overlay")); request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/overlay"));
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json;charset=utf-8"); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json;charset=utf-8");
request.setRawHeader("Authorization", "Bearer " + m_accessToken.toLocal8Bit()); request.setRawHeader("Authorization", "Bearer " + m_accessToken.toLocal8Bit());
/*QJsonDocument doc;
QJsonObject obj;
QJsonObject setting;
setting.insert("power", "ON");
QJsonObject temperature;
temperature.insert("celsius", targetTemperature);
//temperature.insert("fahrenheit", (targetTemperature * (9.0/5.0)) + 32.0);
setting.insert("type", "HEATING");
setting.insert("temperature", temperature);
obj.insert("setting", setting);
QJsonObject termination;
termination.insert("type", "MANUAL");
obj.insert("termination", termination);
doc.setObject(obj);*/
QByteArray body; QByteArray body;
QByteArray powerString; QByteArray powerString;
@ -282,23 +299,30 @@ void Tado::setOverlay(const QString &homeId, const QString &zoneId, bool power,
powerString = "OFF"; powerString = "OFF";
body.append("{\"setting\":{\"type\":\"HEATING\",\"power\":\""+ powerString + "\",\"temperature\":{\"celsius\":" + QVariant(targetTemperature).toByteArray() + "}},\"termination\":{\"type\":\"MANUAL\"}}"); body.append("{\"setting\":{\"type\":\"HEATING\",\"power\":\""+ powerString + "\",\"temperature\":{\"celsius\":" + QVariant(targetTemperature).toByteArray() + "}},\"termination\":{\"type\":\"MANUAL\"}}");
qCDebug(dcTado()) << "Sending request" << body;
QNetworkReply *reply = m_networkManager->put(request, body); QNetworkReply *reply = m_networkManager->put(request, body);
connect(reply, &QNetworkReply::finished, this, [reply, this] { connect(reply, &QNetworkReply::finished, this, [homeId, zoneId, requestId, reply, this] {
reply->deleteLater(); reply->deleteLater();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// Check HTTP status code // Check HTTP status code
if (status != 200 || reply->error() != QNetworkReply::NoError) { if (status != 200 || reply->error() != QNetworkReply::NoError) {
emit requestExecuted(requestId, false);
if (reply->error() == QNetworkReply::HostNotFoundError) { if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false); emit connectionChanged(false);
} }
if (status == 400 || status == 401) { if (status == 401) { //Unauthorized
emit authenticationStatusChanged(false); emit authenticationStatusChanged(false);
} else if (status == 422) { //Unprocessable Entity
qCWarning(dcTado()) << "Unprocessable Entity, probably a value out of range";
} else {
qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
} }
qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
return; return;
} }
emit authenticationStatusChanged(true);
emit connectionChanged(true);
emit requestExecuted(requestId, true);
QJsonParseError error; QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error); QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
@ -307,32 +331,72 @@ void Tado::setOverlay(const QString &homeId, const QString &zoneId, bool power,
return; return;
} }
QVariantMap map = data.toVariant().toMap(); QVariantMap map = data.toVariant().toMap();
qCDebug(dcTado()) << map["type"].toString();
Overlay overlay;
QVariantMap settingsMap = map["setting"].toMap();
overlay.zoneType = settingsMap["type"].toString();
overlay.power = (settingsMap["power"].toString() == "ON");
overlay.temperature = settingsMap["temperature"].toMap().value("celsius").toDouble();
QVariantMap terminationMap = map["termination"].toMap();
overlay.terminationType = terminationMap["type"].toString();
overlay.tadoMode = map["type"].toString();
emit overlayReceived(homeId, zoneId, overlay);
}); });
return requestId;
} }
void Tado::deleteOverlay(const QString &homeId, const QString &zoneId) QUuid Tado::deleteOverlay(const QString &homeId, const QString &zoneId)
{ {
QUuid requestId = QUuid::createUuid();
QNetworkRequest request; QNetworkRequest request;
request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/overlay")); request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/overlay"));
request.setRawHeader("Authorization", "Bearer " + m_accessToken.toLocal8Bit()); request.setRawHeader("Authorization", "Bearer " + m_accessToken.toLocal8Bit());
QNetworkReply *reply = m_networkManager->deleteResource(request); QNetworkReply *reply = m_networkManager->deleteResource(request);
connect(reply, &QNetworkReply::finished, this, [reply, this] { connect(reply, &QNetworkReply::finished, this, [homeId, zoneId, requestId, reply, this] {
reply->deleteLater(); reply->deleteLater();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// Check HTTP status code // Check HTTP status code
if (status < 200 || status > 210 || reply->error() != QNetworkReply::NoError) { if (status < 200 || status > 210 || reply->error() != QNetworkReply::NoError) {
emit requestExecuted(requestId ,false);
if (reply->error() == QNetworkReply::HostNotFoundError) { if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false); emit connectionChanged(false);
} }
if (status == 400 || status == 401) { if (status == 401) { //Unauthorized
emit authenticationStatusChanged(false); emit authenticationStatusChanged(false);
} else if (status == 422) { //Unprocessable Entity
qCWarning(dcTado()) << "Unprocessable Entity, probably a value out of range";
} else {
qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
} }
qCWarning(dcTado()) << "Request error:" << status << reply->errorString(); qCWarning(dcTado()) << "Request error:" << status << reply->errorString();
return; return;
} }
emit authenticationStatusChanged(true);
emit connectionChanged(true);
emit requestExecuted(requestId, true);
QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qDebug(dcTado()) << "Get Token: Recieved invalide JSON object";
return;
}
QVariantMap map = data.toVariant().toMap();
Overlay overlay;
QVariantMap settingsMap = map["setting"].toMap();
overlay.zoneType = settingsMap["type"].toString();
overlay.power = (settingsMap["power"].toString() == "ON");
overlay.temperature = settingsMap["temperature"].toMap().value("celsius").toDouble();
QVariantMap terminationMap = map["termination"].toMap();
overlay.terminationType = terminationMap["type"].toString();
overlay.tadoMode = map["type"].toString();
emit overlayReceived(homeId, zoneId, overlay);
}); });
return requestId;
} }
void Tado::onRefreshTimer() void Tado::onRefreshTimer()

View File

@ -1,24 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * *
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> * * Copyright 2013 - 2020, nymea GmbH
* * * Contact: contact@nymea.io
* This file is part of nymea. * *
* * * This file is part of nymea.
* This library is free software; you can redistribute it and/or * * This project including source code and documentation is protected by copyright law, and
* modify it under the terms of the GNU Lesser General Public * * remains the property of nymea GmbH. All rights, including reproduction, publication,
* License as published by the Free Software Foundation; either * * editing and translation, are reserved. The use of this project is subject to the terms of a
* version 2.1 of the License, or (at your option) any later version. * * license agreement to be concluded with nymea GmbH in accordance with the terms
* * * of use of nymea GmbH, available under https://nymea.io/license
* This library is distributed in the hope that it will be useful, * *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * GNU Lesser General Public License Usage
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * This project may also contain libraries licensed under the open source software license GNU GPL v.3.
* Lesser General Public License for more details. * * 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.
* You should have received a copy of the GNU Lesser General Public * * this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* License along with this library; If not, see * * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* <http://www.gnu.org/licenses/>. * * 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 <https://www.gnu.org/licenses/>.
*
* 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 TADO_H #ifndef TADO_H
#define TADO_H #define TADO_H
@ -28,6 +34,7 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QUuid>
class Tado : public QObject class Tado : public QObject
{ {
@ -48,6 +55,15 @@ public:
QString type; QString type;
}; };
struct Overlay {
bool power;
double temperature;
QString zoneType;
QString terminationType;
QString tadoMode;
};
struct ZoneState { struct ZoneState {
bool connected; bool connected;
bool power; bool power;
@ -81,8 +97,8 @@ public:
void getZones(const QString &homeId); void getZones(const QString &homeId);
void getZoneState(const QString &homeId, const QString &zoneId); void getZoneState(const QString &homeId, const QString &zoneId);
void setOverlay(const QString &homeId, const QString &zoneId, bool power, double targetTemperature); QUuid setOverlay(const QString &homeId, const QString &zoneId, bool power, double targetTemperature);
void deleteOverlay(const QString &homeId, const QString &zoneId); QUuid deleteOverlay(const QString &homeId, const QString &zoneId);
private: private:
QByteArray m_baseAuthorizationUrl = "https://auth.tado.com/oauth/token"; QByteArray m_baseAuthorizationUrl = "https://auth.tado.com/oauth/token";
@ -99,12 +115,14 @@ private:
signals: signals:
void connectionChanged(bool connected); void connectionChanged(bool connected);
void authenticationStatusChanged(bool authenticated); void authenticationStatusChanged(bool authenticated);
void requestExecuted(QUuid requestId, bool success);
void tokenReceived(Token token); void tokenReceived(Token token);
void homesReceived(QList<Home> homes); void homesReceived(QList<Home> homes);
void zonesReceived(const QString &homeId, QList<Zone> zones); void zonesReceived(const QString &homeId, QList<Zone> zones);
void zoneStateReceived(const QString &homeId,const QString &zoneId, ZoneState sate); void zoneStateReceived(const QString &homeId,const QString &zoneId, ZoneState sate);
void overlayReceived(const QString &homeId, const QString &zoneId, const Overlay &overlay);
private slots: private slots:
void onRefreshTimer(); void onRefreshTimer();