diff --git a/tado/integrationplugintado.cpp b/tado/integrationplugintado.cpp index 380dc406..178e15a5 100644 --- a/tado/integrationplugintado.cpp +++ b/tado/integrationplugintado.cpp @@ -63,13 +63,6 @@ void IntegrationPluginTado::confirmPairing(ThingPairingInfo *info, const QString { qCDebug(dcTado()) << "Confirm pairing" << username << "Network manager available" << hardwareManager()->networkManager()->available(); Tado *tado = new Tado(hardwareManager()->networkManager(), username, this); - connect(tado, &Tado::authenticationStatusChanged, this, &IntegrationPluginTado::onAuthenticationStatusChanged); - connect(tado, &Tado::requestExecuted, this, &IntegrationPluginTado::onRequestExecuted); - connect(tado, &Tado::connectionChanged, this, &IntegrationPluginTado::onConnectionChanged); - connect(tado, &Tado::homesReceived, this, &IntegrationPluginTado::onHomesReceived); - connect(tado, &Tado::zonesReceived, this, &IntegrationPluginTado::onZonesReceived); - connect(tado, &Tado::zoneStateReceived, this, &IntegrationPluginTado::onZoneStateReceived); - connect(tado, &Tado::overlayReceived, this, &IntegrationPluginTado::onOverlayReceived); m_unfinishedTadoAccounts.insert(info->thingId(), tado); connect(info, &ThingPairingInfo::aborted, this, [info, tado, this]() { @@ -78,13 +71,18 @@ void IntegrationPluginTado::confirmPairing(ThingPairingInfo *info, const QString tado->deleteLater(); }); - connect(tado, &Tado::connectionError, info, [this, info] (QNetworkReply::NetworkError error){ + connect(tado, &Tado::connectionError, info, [info] (QNetworkReply::NetworkError error){ if (error != QNetworkReply::NetworkError::NoError){ - info->finish(Thing::ThingErrorSetupFailed); + qCWarning(dcTado()) << "Confirm pairing failed" << error; + info->finish(Thing::ThingErrorSetupFailed, tr("Connection error")); } // info->finish(success) will be called after the token has been received }); + connect(tado, &Tado::apiCredentialsReceived, info, [password, tado] { + tado->getToken(password); + }); + connect(tado, &Tado::tokenReceived, info, [this, info, username, password](Tado::Token token) { Q_UNUSED(token) @@ -95,7 +93,7 @@ void IntegrationPluginTado::confirmPairing(ThingPairingInfo *info, const QString info->finish(Thing::ThingErrorNoError); }); - tado->getToken(password); + tado->getApiCredentials(); } void IntegrationPluginTado::setupThing(ThingSetupInfo *info) @@ -109,7 +107,7 @@ void IntegrationPluginTado::setupThing(ThingSetupInfo *info) if (m_unfinishedTadoAccounts.contains(thing->id())) { tado = m_unfinishedTadoAccounts.take(thing->id()); m_tadoAccounts.insert(thing->id(), tado); - return info->finish(Thing::ThingErrorNoError); + info->finish(Thing::ThingErrorNoError); } else { pluginStorage()->beginGroup(thing->id().toString()); QString username = pluginStorage()->value("username").toString(); @@ -117,14 +115,6 @@ void IntegrationPluginTado::setupThing(ThingSetupInfo *info) pluginStorage()->endGroup(); tado = new Tado(hardwareManager()->networkManager(), username, this); - connect(tado, &Tado::authenticationStatusChanged, this, &IntegrationPluginTado::onAuthenticationStatusChanged); - connect(tado, &Tado::requestExecuted, this, &IntegrationPluginTado::onRequestExecuted); - connect(tado, &Tado::connectionChanged, this, &IntegrationPluginTado::onConnectionChanged); - connect(tado, &Tado::homesReceived, this, &IntegrationPluginTado::onHomesReceived); - connect(tado, &Tado::zonesReceived, this, &IntegrationPluginTado::onZonesReceived); - connect(tado, &Tado::zoneStateReceived, this, &IntegrationPluginTado::onZoneStateReceived); - connect(tado, &Tado::overlayReceived, this, &IntegrationPluginTado::onOverlayReceived); - m_tadoAccounts.insert(thing->id(), tado); connect(info, &ThingSetupInfo::aborted, [info, this] { if (m_tadoAccounts.contains(info->thing()->id())) { @@ -133,7 +123,11 @@ void IntegrationPluginTado::setupThing(ThingSetupInfo *info) } }); - connect(tado, &Tado::tokenReceived, info, [this, info, tado](Tado::Token token) { + connect(tado, &Tado::apiCredentialsReceived, info, [password, tado] { + tado->getToken(password); + }); + + connect(tado, &Tado::tokenReceived, info, [ info](Tado::Token token) { Q_UNUSED(token) qCDebug(dcTado()) << "Token received, account setup successfull"; @@ -141,18 +135,7 @@ void IntegrationPluginTado::setupThing(ThingSetupInfo *info) }); connect(tado, &Tado::connectionError, info, [this, info] (QNetworkReply::NetworkError error){ - if (error == QNetworkReply::NetworkError::HostNotFoundError) { - QTimer::singleShot(2000, info, [info, this] { - - pluginStorage()->beginGroup(info->thing()->id().toString()); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); - if (m_tadoAccounts.contains(info->thing()->id())) { - Tado *tado = m_tadoAccounts.take(info->thing()->id()); - tado->getToken(password); - } - }); - } else if (error != QNetworkReply::NetworkError::NoError){ + if (error != QNetworkReply::NetworkError::NoError){ if (m_tadoAccounts.contains(info->thing()->id())) { Tado *tado = m_tadoAccounts.take(info->thing()->id()); tado->deleteLater(); @@ -160,15 +143,23 @@ void IntegrationPluginTado::setupThing(ThingSetupInfo *info) info->finish(Thing::ThingErrorSetupFailed); } }); - tado->getToken(password); + tado->getApiCredentials(); } + connect(tado, &Tado::authenticationStatusChanged, this, &IntegrationPluginTado::onAuthenticationStatusChanged); + connect(tado, &Tado::requestExecuted, this, &IntegrationPluginTado::onRequestExecuted); + connect(tado, &Tado::connectionChanged, this, &IntegrationPluginTado::onConnectionChanged); + connect(tado, &Tado::homesReceived, this, &IntegrationPluginTado::onHomesReceived); + connect(tado, &Tado::zonesReceived, this, &IntegrationPluginTado::onZonesReceived); + connect(tado, &Tado::zoneStateReceived, this, &IntegrationPluginTado::onZoneStateReceived); + connect(tado, &Tado::overlayReceived, this, &IntegrationPluginTado::onOverlayReceived); + return; } else if (thing->thingClassId() == zoneThingClassId) { qCDebug(dcTado) << "Setup tado thermostat" << thing->params(); return info->finish(Thing::ThingErrorNoError); } else { - return info->finish(Thing::ThingErrorThingClassNotFound); qCWarning(dcTado()) << "Unhandled thing class in setupDevice"; + return info->finish(Thing::ThingErrorThingClassNotFound); } } diff --git a/tado/tado.cpp b/tado/tado.cpp index 71338c39..c52a7bee 100644 --- a/tado/tado.cpp +++ b/tado/tado.cpp @@ -56,6 +56,11 @@ QString Tado::username() return m_username; } +bool Tado::apiAvailable() +{ + return m_apiAvailable; +} + bool Tado::authenticated() { return m_authenticationStatus; @@ -66,8 +71,61 @@ bool Tado::connected() return m_connectionStatus; } +void Tado::getApiCredentials(const QString &url) +{ + QNetworkRequest request; + request.setUrl(url); + QNetworkReply *reply = m_networkManager->get(request); + qCDebug(dcTado()) << "Sending request" << request.url(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, this, [reply, this] { + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + // Check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcTado()) << "Request error:" << status << reply->errorString(); + return; + } + QRegExp filter; + filter.setPatternSyntax(QRegExp::Wildcard); + filter.setPattern("*tgaRestApiV2Endpoint:*"); + + QStringList list = QString(reply->readAll()).split('\n'); + int index = list.indexOf(filter); + if (index == -1) + return; + m_baseControlUrl = list.value(index).split(": ").last().remove(QRegExp("[,']"));; + qCDebug(dcTado()) << "Received control url" << m_baseControlUrl; + filter.setPattern("*apiEndpoint*"); + index = list.indexOf(filter); + if (index == -1) + return; + m_baseAuthorizationUrl = list.value(index).split(": ").last().remove(QRegExp("[,']"))+"/token"; + qCDebug(dcTado()) << "Received auth url" << m_baseAuthorizationUrl; + filter.setPattern("*clientId*"); + index = list.indexOf(filter); + if (index == -1) + return; + m_clientId = list.value(index).split(": ").last().remove(QRegExp("[,']")); + qCDebug(dcTado()) << "Received client id" << m_clientId; + filter.setPattern("*clientSecret*"); + index = list.indexOf(filter); + if (index == -1) + return; + m_clientSecret = list.value(index).split(": ").last().remove(QRegExp("[,']")); + qCDebug(dcTado()) << "Received client secret" << m_clientSecret; + m_apiAvailable = true; + emit apiCredentialsReceived(); + }); +} + void Tado::getToken(const QString &password) { + if (!m_apiAvailable) { + qCWarning(dcTado()) << "Not sending request, get API credentials firs"; + return; + } + QNetworkRequest request; request.setUrl(QUrl(m_baseAuthorizationUrl)); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -102,7 +160,7 @@ void Tado::getToken(const QString &password) QJsonParseError error; QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error); if (error.error != QJsonParseError::NoError) { - qDebug(dcTado()) << "Get Token: Received invalide JSON object"; + qDebug(dcTado()) << "Get Token: Received invalide JSON object:" << data; return; } if (data.isObject()) { @@ -137,6 +195,11 @@ void Tado::getToken(const QString &password) void Tado::getHomes() { + if (!m_apiAvailable) { + qCWarning(dcTado()) << "Not sending request, get API credentials firs"; + return; + } + if(m_accessToken.isEmpty()) { qCWarning(dcTado()) << "Not sending request, get the access token first"; return; @@ -188,6 +251,16 @@ void Tado::getHomes() void Tado::getZones(const QString &homeId) { + if (!m_apiAvailable) { + qCWarning(dcTado()) << "Not sending request, get API credentials firs"; + return; + } + + if(m_accessToken.isEmpty()) { + qCWarning(dcTado()) << "Not sending request, get the access token first"; + return; + } + QNetworkRequest request; request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones")); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -235,10 +308,16 @@ void Tado::getZones(const QString &homeId) void Tado::getZoneState(const QString &homeId, const QString &zoneId) { + if (!m_apiAvailable) { + qCWarning(dcTado()) << "Not sending request, get API credentials firs"; + return; + } + if(m_accessToken.isEmpty()) { qCWarning(dcTado()) << "Not sending request, get the access token first"; return; } + QNetworkRequest request; request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/state")); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -307,10 +386,16 @@ void Tado::getZoneState(const QString &homeId, const QString &zoneId) QUuid Tado::setOverlay(const QString &homeId, const QString &zoneId, bool power, double targetTemperature) { + if (!m_apiAvailable) { + qCWarning(dcTado()) << "Not sending request, get API credentials firs"; + return ""; + } + if(m_accessToken.isEmpty()) { qCWarning(dcTado()) << "Not sending request, get the access token first"; return ""; } + QUuid requestId = QUuid::createUuid(); QNetworkRequest request; request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/overlay")); @@ -376,10 +461,16 @@ QUuid Tado::setOverlay(const QString &homeId, const QString &zoneId, bool power, QUuid Tado::deleteOverlay(const QString &homeId, const QString &zoneId) { + if (!m_apiAvailable) { + qCWarning(dcTado()) << "Not sending request, get API credentials firs"; + return ""; + } + if(m_accessToken.isEmpty()) { qCWarning(dcTado()) << "Not sending request, get the access token first"; return ""; } + QUuid requestId = QUuid::createUuid(); QNetworkRequest request; request.setUrl(QUrl(m_baseControlUrl+"/homes/"+homeId+"/zones/"+zoneId+"/overlay")); diff --git a/tado/tado.h b/tado/tado.h index 1e813130..3fe7fb78 100644 --- a/tado/tado.h +++ b/tado/tado.h @@ -93,9 +93,11 @@ public: void setUsername(const QString &username); QString username(); + bool apiAvailable(); bool authenticated(); bool connected(); + void getApiCredentials(const QString &url = "https://my.tado.com/webapp/env.js"); void getToken(const QString &password); void getHomes(); void getZones(const QString &homeId); @@ -105,10 +107,11 @@ public: QUuid deleteOverlay(const QString &homeId, const QString &zoneId); private: - QByteArray m_baseAuthorizationUrl = "https://auth.tado.com/oauth/token"; - QByteArray m_baseControlUrl = "https://my.tado.com/api/v2"; - QByteArray m_clientSecret = "4HJGRffVR8xb3XdEUQpjgZ1VplJi6Xgw"; - QByteArray m_clientId = "public-api-preview"; + bool m_apiAvailable = false; + QString m_baseAuthorizationUrl; + QString m_baseControlUrl; + QString m_clientSecret; + QString m_clientId; NetworkAccessManager *m_networkManager = nullptr; QString m_username; @@ -123,6 +126,7 @@ private: signals: void connectionChanged(bool connected); + void apiCredentialsReceived(); void authenticationStatusChanged(bool authenticated); void requestExecuted(QUuid requestId, bool success);