From 0f79c1d3a1a09f3c37c8f07b03f0045c45586c73 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 6 Jul 2023 19:37:39 +0200 Subject: [PATCH] Easee: Various fixes after beta testing --- easee/integrationplugineasee.cpp | 157 ++++++++++++++++++++----------- easee/integrationplugineasee.h | 16 +++- easee/signalrconnection.cpp | 48 +++++++--- easee/signalrconnection.h | 2 + 4 files changed, 151 insertions(+), 72 deletions(-) diff --git a/easee/integrationplugineasee.cpp b/easee/integrationplugineasee.cpp index 891bd227..2c55e962 100644 --- a/easee/integrationplugineasee.cpp +++ b/easee/integrationplugineasee.cpp @@ -40,6 +40,9 @@ #include #include +QString apiEndpoint = "https://api.easee.com/api"; +QString streamEndpoint = "http://streams.easee.com/hubs/chargers"; + IntegrationPluginEasee::IntegrationPluginEasee() { @@ -51,7 +54,7 @@ IntegrationPluginEasee::~IntegrationPluginEasee() void IntegrationPluginEasee::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) { - QNetworkRequest request(QUrl(QString("https://api.easee.cloud/api/accounts/login"))); + QNetworkRequest request(QUrl(QString("%1/%2").arg(apiEndpoint).arg("accounts/login"))); request.setRawHeader("accept", "application/json"); request.setRawHeader("content-type", "application/*+json"); QVariantMap body; @@ -63,10 +66,12 @@ void IntegrationPluginEasee::confirmPairing(ThingPairingInfo *info, const QStrin qCDebug(dcEasee) << "auth reply finished" << reply->error(); if (reply->error() == QNetworkReply::ProtocolInvalidOperationError) { + qCWarning(dcEasee) << "Authentication failed. Looks like a wrong password"; info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Authentication failed. Please try again.")); return; } if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcEasee) << "Unable to connect to the Easee server"; info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to contact the easee server. Please try again later.")); return; } @@ -153,10 +158,11 @@ void IntegrationPluginEasee::setupThing(ThingSetupInfo *info) } if (thing->thingClassId() == chargerThingClassId) { - refreshCurrentState(thing); + // We'll need a cache of the maxChargingCurrent as sometimes need that before a executeAction is finished + // initializing it to make sure there's always a value in it. + m_desiredMax[info->thing()] = thing->stateValue(chargerMaxChargingCurrentStateTypeId).toUInt(); } - info->finish(Thing::ThingErrorNoError); } @@ -181,16 +187,6 @@ void IntegrationPluginEasee::postSetupThing(Thing *thing) // Refreshing the products refreshProducts(t); - - if (!m_signalRConnections.value(t)->connected()) { - // If the SignalR connection fails for whatever reason, let's poll - foreach (Thing *child, myThings().filterByParentId(t->id())) { - refreshCurrentState(child); - } - } - } else if (t->thingClassId() == chargerThingClassId) { - // We'll be using the SignalR connection instead for updates. - //refreshCurrentState(t); } } }); @@ -206,14 +202,15 @@ void IntegrationPluginEasee::postSetupThing(Thing *thing) qCDebug(dcEasee()) << "Access token:" << accessToken; qCDebug(dcEasee()) << "Token expiry:" << expiry; - SignalRConnection *signalR = new SignalRConnection(QUrl("http://streams.easee.com/hubs/chargers"), accessToken, hardwareManager()->networkManager(), thing); + SignalRConnection *signalR = new SignalRConnection(QUrl(streamEndpoint), accessToken, hardwareManager()->networkManager(), thing); m_signalRConnections.insert(thing, signalR); connect(signalR, &SignalRConnection::connectionStateChanged, thing, [=](bool connected){ foreach (Thing *charger, myThings().filterByParentId(thing->id())) { - charger->setStateValue(chargerConnectedStateTypeId, true); if (connected) { signalR->subscribe(charger->paramValue(chargerThingIdParamTypeId).toString()); + } else { + charger->setStateValue(chargerConnectedStateTypeId, false); } } }); @@ -256,12 +253,11 @@ void IntegrationPluginEasee::postSetupThing(Thing *thing) charger->setStateValue(chargerPluggedInStateTypeId, false); } else if (mode == "B" || mode == "C") { charger->setStateValue(chargerPluggedInStateTypeId, true); - } else { } break; } case ObservationPointOutputPhase: - charger->setStateValue(chargerPhaseCountStateTypeId, value.toUInt() > 10 ? 3 : 1); + charger->setStateValue(chargerPhaseCountStateTypeId, value.toUInt() == 30 ? 3 : 1); break; case ObservationPointChargerOpMode: // 2: charging disabled, 3: enabled and charging, 4: enabled but not charging @@ -269,11 +265,25 @@ void IntegrationPluginEasee::postSetupThing(Thing *thing) charger->setStateValue(chargerPowerStateTypeId, value.toUInt() >= 3); break; case ObservationPointDynamicChargerCurrent: - charger->setStateValue(chargerMaxChargingCurrentStateTypeId, value.toUInt()); + // May give us 0 when pausing charging etc, ignoring that. + if (value.toUInt() > 0) { + charger->setStateValue(chargerMaxChargingCurrentStateTypeId, value.toUInt()); + // Updating the desired value when it is changed by the wallbox (e.g. through app) + m_desiredMax[charger] = value.toUInt(); + } break; case ObservationPointMaxChargerCurrent: - charger->setStateMaxValue(chargerMaxChargingCurrentStateTypeId, value.toUInt()); + m_wallboxMax[chargerId] = value.toUInt(); + charger->setStateMinMaxValues(chargerMaxChargingCurrentStateTypeId, 6, qMin(m_wallboxMax.value(chargerId), m_cableRating.value(chargerId, 32))); break; + case ObservationPointCableRating: + m_cableRating[chargerId] = value.toUInt(); + charger->setStateMinMaxValues(chargerMaxChargingCurrentStateTypeId, 6, qMin(m_wallboxMax.value(chargerId, 32), value.toUInt())); + break; + case ObservationPointConnectedToCloud: + charger->setStateValue(chargerConnectedStateTypeId, value.toString() == "True" || value.toString() == "1"); + break; + default: break; @@ -293,6 +303,10 @@ void IntegrationPluginEasee::thingRemoved(Thing *thing) hardwareManager()->pluginTimerManager()->unregisterTimer(m_timer); m_timer = nullptr; } + + if (thing->thingClassId() == chargerThingClassId) { + m_desiredMax.take(thing); + } } void IntegrationPluginEasee::executeAction(ThingActionInfo *info) @@ -303,22 +317,34 @@ void IntegrationPluginEasee::executeAction(ThingActionInfo *info) QString chargerId = thing->paramValue(chargerThingIdParamTypeId).toString(); if (info->action().actionTypeId() == chargerPowerActionTypeId) { bool power = info->action().paramValue(chargerPowerActionPowerParamTypeId).toBool(); - QString actionPath = power ? "start_charging" : "stop_charging"; + QString actionPath = power ? "start_charging" : "pause_charging"; QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/commands/%2").arg(chargerId).arg(actionPath)); qCDebug(dcEasee()) << "Setting power:" << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QByteArray()); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, info, [reply, info, power](){ + connect(reply, &QNetworkReply::finished, info, [=](){ qCDebug(dcEasee()) << "Reply" << reply->error(); if (reply->error() == QNetworkReply::NoError) { info->thing()->setStateValue(chargerPowerStateTypeId, power); } info->finish(reply->error() == QNetworkReply::NoError ? Thing::ThingErrorNoError : Thing::ThingErrorHardwareFailure); + + // resume/start charging will for some reason reset the dynamicChargerCurrent... We'll have to re-write ours. + if (power) { + uint maxChargingCurrent = m_desiredMax[info->thing()]; + QVariantMap data; + data.insert("dynamicChargerCurrent", maxChargingCurrent); + QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/settings").arg(chargerId)); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(data).toJson(QJsonDocument::Compact)); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + } }); return; } if (info->action().actionTypeId() == chargerMaxChargingCurrentActionTypeId) { uint maxChargingCurrent = info->action().paramValue(chargerMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt(); + // We'll need this for resume_charging as that call will for some reason reset it to the max of 32A, so we'll need to immediately write this one again. + m_desiredMax[info->thing()] = maxChargingCurrent; QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/settings").arg(chargerId)); QVariantMap data; data.insert("dynamicChargerCurrent", maxChargingCurrent); @@ -326,7 +352,7 @@ void IntegrationPluginEasee::executeAction(ThingActionInfo *info) QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(data).toJson(QJsonDocument::Compact)); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, info, [reply, info, maxChargingCurrent](){ - qCDebug(dcEasee()) << "Reply" << reply->error(); + qCDebug(dcEasee()) << "Set dynamicaChargerCurrent reply" << reply->error(); if (reply->error() == QNetworkReply::NoError) { info->thing()->setStateValue(chargerMaxChargingCurrentStateTypeId, maxChargingCurrent); } @@ -335,20 +361,54 @@ void IntegrationPluginEasee::executeAction(ThingActionInfo *info) return; } if (info->action().actionTypeId() == chargerDesiredPhaseCountActionTypeId) { - uint desiredPhaseCount = info->action().paramValue(chargerMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt(); - QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/settings").arg(chargerId)); - QVariantMap data; - data.insert("lockToSinglePhaseCharging", desiredPhaseCount == 1); - qCDebug(dcEasee()) << "Setting single phase charging:" << request.url().toString() << QJsonDocument::fromVariant(data).toJson(); - QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(data).toJson(QJsonDocument::Compact)); + uint desiredPhaseCount = info->action().paramValue(chargerDesiredPhaseCountActionDesiredPhaseCountParamTypeId).toUInt(); + bool wasOn = thing->stateValue(chargerPowerStateTypeId).toBool(); + bool oldMaxCurrent = m_desiredMax.value(info->thing()); + if (desiredPhaseCount == thing->stateValue(chargerPhaseCountStateTypeId)) { + qCInfo(dcEasee()) << "effective phases already equals desired ones..."; + info->finish(Thing::ThingErrorNoError); + return; + } + qCDebug(dcEasee()) << "Pausing charging"; + QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/commands/pause_charging").arg(chargerId)); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QByteArray()); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, info, [reply, info, desiredPhaseCount](){ - qCDebug(dcEasee()) << "Reply" << reply->error(); - if (reply->error() == QNetworkReply::NoError) { - info->thing()->setStateValue(chargerDesiredPhaseCountStateTypeId, desiredPhaseCount); - } - info->finish(reply->error() == QNetworkReply::NoError ? Thing::ThingErrorNoError : Thing::ThingErrorHardwareFailure); + connect(reply, &QNetworkReply::finished, info, [=](){ + + QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/settings").arg(chargerId)); + + QVariantMap data; + data.insert("phaseMode", desiredPhaseCount == 1 ? PhaseModeLockedTo1Phase : PhaseModeLockedTo3Phase); + qCDebug(dcEasee()) << "Setting single phase charging:" << request.url().toString() << QJsonDocument::fromVariant(data).toJson(); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(data).toJson(QJsonDocument::Compact)); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [=](){ + qCDebug(dcEasee()) << "Set phaseMode reply" << reply->error(); + if (reply->error() == QNetworkReply::NoError) { + info->thing()->setStateValue(chargerDesiredPhaseCountStateTypeId, desiredPhaseCount); + } + info->finish(reply->error() == QNetworkReply::NoError ? Thing::ThingErrorNoError : Thing::ThingErrorHardwareFailure); + + if (wasOn) { + qCDebug(dcEasee()) << "Resuming charging"; + QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/commands/resume_charging").arg(chargerId)); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QByteArray()); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [=](){ + qCDebug(dcEasee()) << "Restoring max charger current"; + QNetworkRequest request = createRequest(parentThing, QString("chargers/%1/settings").arg(chargerId)); + QVariantMap data; + data.insert("dynamicChargerCurrent", oldMaxCurrent); + qCDebug(dcEasee()) << "Setting max current:" << request.url().toString() << QJsonDocument::fromVariant(data).toJson(); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(data).toJson(QJsonDocument::Compact)); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + }); + + + } + }); }); + return; } @@ -361,7 +421,7 @@ QNetworkRequest IntegrationPluginEasee::createRequest(Thing *thing, const QStrin pluginStorage()->beginGroup(thing->id().toString()); QByteArray accessToken = pluginStorage()->value("accessToken").toByteArray(); pluginStorage()->endGroup(); - QNetworkRequest request(QUrl(QString("https://api.easee.cloud/api/%1").arg(endpoint))); + QNetworkRequest request(QUrl(QString("%1/%2").arg(apiEndpoint).arg(endpoint))); request.setRawHeader("Authorization", "Bearer " + accessToken); request.setRawHeader("accept", "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/*+json"); @@ -379,20 +439,21 @@ QNetworkReply *IntegrationPluginEasee::refreshToken(Thing *thing) // FIXME: Ideally we should use the refresh_token API and not store user/pass in the config, but it seems to not work -// QNetworkRequest request(QUrl(QString("https://api.easee.cloud/api/accounts/refresh_token"))); +// QNetworkRequest request(QUrl(QString("%1/%2").arg(apiEndpoint).arg("accounts/refresh_token"))); // request.setRawHeader("accept", "application/json"); // request.setRawHeader("content-type", "application/*+json"); // QVariantMap body; // body.insert("refreshToken", refreshToken); // body.insert("accessToken", accessToken); - QNetworkRequest request(QUrl(QString("https://api.easee.cloud/api/accounts/login"))); + QNetworkRequest request(QUrl(QString("%1/%2").arg(apiEndpoint).arg("accounts/login"))); request.setRawHeader("accept", "application/json"); request.setRawHeader("content-type", "application/*+json"); QVariantMap body; body.insert("userName", username); body.insert("password", password); + qCDebug(dcEasee()) << "Fetching:" << request.url().toString(); QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(body).toJson(QJsonDocument::Compact)); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); @@ -424,7 +485,9 @@ QNetworkReply *IntegrationPluginEasee::refreshToken(Thing *thing) pluginStorage()->setValue("refreshToken", refreshToken); pluginStorage()->endGroup(); - m_signalRConnections.value(thing)->updateToken(accessToken); + if (m_signalRConnections.contains(thing)) { + m_signalRConnections.value(thing)->updateToken(accessToken); + } }); @@ -506,24 +569,6 @@ void IntegrationPluginEasee::refreshCurrentState(Thing *charger) QVariantMap map = jsonDoc.toVariant().toMap(); qCDebug(dcEasee) << "Charger state reply:" << qUtf8Printable(jsonDoc.toJson()); charger->setStateValue(chargerConnectedStateTypeId, map.value("isOnline").toBool()); - charger->setStateValue(chargerSignalStrengthStateTypeId, qMax(0, qMin(100, (map.value("wiFiRSSI").toInt() + 100) * 2))); - charger->setStateValue(chargerCurrentPowerStateTypeId, map.value("totalPower").toDouble() * 1000); - charger->setStateValue(chargerPhaseCountStateTypeId, map.value("outputPhase").toUInt() > 10 ? 3 : 1); - charger->setStateValue(chargerChargingStateTypeId, map.value("chargerOpMode").toUInt() == 3); - - // 1: unplugged, 2: charging disabled, 3: enabled and charging, 4: enabled but not charging - uint chargerOpMode = map.value("chargerOpMode").toUInt(); - charger->setStateValue(chargerPluggedInStateTypeId, chargerOpMode >= 2); - charger->setStateValue(chargerChargingStateTypeId, chargerOpMode == 3); - charger->setStateValue(chargerPowerStateTypeId, chargerOpMode >= 3); - - charger->setStateValue(chargerMaxChargingCurrentStateTypeId, map.value("dynamicChargerCurrent").toUInt()); - charger->setStateMaxValue(chargerMaxChargingCurrentStateTypeId, 6); // Fixme: where to get this from? - charger->setStateMaxValue(chargerMaxChargingCurrentStateTypeId, 32); // Fixme: where to get this from? - - charger->setStateValue(chargerTotalEnergyConsumedStateTypeId, map.value("lifetimeEnergy").toDouble()); - charger->setStateValue(chargerSessionEnergyStateTypeId, map.value("sessionEnergy").toDouble()); - }); diff --git a/easee/integrationplugineasee.h b/easee/integrationplugineasee.h index cca914fb..be9d23e8 100644 --- a/easee/integrationplugineasee.h +++ b/easee/integrationplugineasee.h @@ -212,10 +212,20 @@ public: ObservationPointLTERSRQ = 222, ObservationPointEQAvailableCurrentP1 = 230, ObservationPointEQAvailableCurrentP2 = 231, - ObservationPointEQAvailableCurrentP3 = 232 + ObservationPointEQAvailableCurrentP3 = 232, + ObservationPointConnectedToCloud = 250 }; Q_ENUM(ObservationPoint) + + enum PhaseMode { + PhaseModeIgnore = 0, + PhaseModeLockedTo1Phase = 1, + PhaseModeAuto = 2, + PhaseModeLockedTo3Phase = 3 + }; + Q_ENUM(PhaseMode) + explicit IntegrationPluginEasee(); ~IntegrationPluginEasee(); @@ -237,6 +247,10 @@ private: QHash m_siteIds; // chargerId, siteId PluginTimer *m_timer = nullptr; + + QHash m_cableRating; + QHash m_wallboxMax; + QHash m_desiredMax; }; #endif // INTEGRATIONPLUGINEASEE_H diff --git a/easee/signalrconnection.cpp b/easee/signalrconnection.cpp index 886741c1..a6ecab1a 100644 --- a/easee/signalrconnection.cpp +++ b/easee/signalrconnection.cpp @@ -18,13 +18,13 @@ SignalRConnection::SignalRConnection(const QUrl &url, const QByteArray &accessTo m_socket = new QWebSocket(); typedef void (QWebSocket:: *errorSignal)(QAbstractSocket::SocketError); connect(m_socket, static_cast(&QWebSocket::error), this, [](QAbstractSocket::SocketError error){ - qCWarning(dcEasee) << "Error in websocket:" << error; + qCWarning(dcEasee) << "SingalR: Error in websocket:" << error; }); connect(m_socket, &QWebSocket::stateChanged, this, [=](QAbstractSocket::SocketState state){ - qCDebug(dcEasee) << "Websocket state changed" << state; + qCDebug(dcEasee) << "SingalR: Websocket state changed" << state; if (state == QAbstractSocket::ConnectedState) { - qCDebug(dcEasee) << "Websocket connected"; + qCDebug(dcEasee) << "SingalR: Websocket connected"; QVariantMap handshake; handshake.insert("protocol", "json"); @@ -32,7 +32,9 @@ SignalRConnection::SignalRConnection(const QUrl &url, const QByteArray &accessTo QByteArray data = encode(handshake); qCDebug(dcEasee) << "Sending handshake" << data; m_socket->sendTextMessage(data); + m_watchdog->start(); } else if (QAbstractSocket::UnconnectedState) { + m_watchdog->stop(); QTimer::singleShot(5000, this, [=](){ connectToHost(); }); @@ -40,9 +42,11 @@ SignalRConnection::SignalRConnection(const QUrl &url, const QByteArray &accessTo }); connect(m_socket, &QWebSocket::binaryMessageReceived, this, [](const QByteArray &message){ - qCDebug(dcEasee) << "Binary message received" << message; + qCDebug(dcEasee) << "SingalR: Binary message received" << message; }); connect(m_socket, &QWebSocket::textMessageReceived, this, [=](const QString &message){ + qCDebug(dcEasee) << "SingalR: Text message received" << message; + QStringList messages = message.split(QByteArray::fromHex("1E")); foreach (const QString &msg, messages) { @@ -54,13 +58,13 @@ SignalRConnection::SignalRConnection(const QUrl &url, const QByteArray &accessTo QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(msg.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { - qCWarning(dcEasee()) << "Unable to parse message from SignalR socket" << error.errorString() << msg; + qCWarning(dcEasee()) << "SingalR: Unable to parse message from SignalR socket" << error.errorString() << msg; continue; } if (m_waitingForHandshakeReply && jsonDoc.toVariant().toMap().isEmpty()) { m_waitingForHandshakeReply = false; - qCDebug(dcEasee()) << "Handshake reply received."; + qCDebug(dcEasee()) << "SingalR: Handshake reply received."; emit connectionStateChanged(true); return; } @@ -72,17 +76,27 @@ SignalRConnection::SignalRConnection(const QUrl &url, const QByteArray &accessTo break; case 3: // Silencing acks to our requests - qCDebug(dcEasee()) << "Message ACK received:" << map; + qCDebug(dcEasee()) << "SingalR: Message ACK received:" << map; + break; case 6: - // Silencing pings + // Resetting watchdog + m_watchdog->start(); break; default: - qCWarning(dcEasee()) << "Unhandled signalr message type" << map; + qCWarning(dcEasee()) << "SingalR: Unhandled SingalR message type" << map; } } }); connectToHost(); + + m_watchdog = new QTimer(this); + m_watchdog->setInterval(30000); + connect(m_watchdog, &QTimer::timeout, this, [=](){ + qCWarning(dcEasee()) << "SingalR: Watchdog triggered! Reconnecting web socket stream..."; + m_socket->close(); + connectToHost(); + }); } void SignalRConnection::subscribe(const QString &chargerId) @@ -92,7 +106,7 @@ void SignalRConnection::subscribe(const QString &chargerId) map.insert("invocationId", QUuid::createUuid()); map.insert("target", "SubscribeWithCurrentState"); map.insert("arguments", QVariantList{chargerId, true}); - qCDebug(dcEasee) << "subscribing to" << chargerId; + qCDebug(dcEasee) << "SingalR: subscribing to" << chargerId; m_socket->sendTextMessage(encode(map)); } @@ -104,6 +118,8 @@ bool SignalRConnection::connected() const void SignalRConnection::updateToken(const QByteArray &accessToken) { m_accessToken = accessToken; + m_socket->close(); + connectToHost(); } QByteArray SignalRConnection::encode(const QVariantMap &message) @@ -118,19 +134,21 @@ void SignalRConnection::connectToHost() negotiationUrl.setPath(negotiationUrl.path() + "/negotiate"); QNetworkRequest negotiateRequest(negotiationUrl); negotiateRequest.setRawHeader("Authorization", "Bearer " + m_accessToken); - qCDebug(dcEasee()) << "Negotiating:" << negotiationUrl << negotiateRequest.rawHeader("Authorization"); + qCDebug(dcEasee()) << "SingalR: Negotiating:" << negotiationUrl << negotiateRequest.rawHeader("Authorization"); QNetworkReply *negotiantionReply = m_nam->post(negotiateRequest, QByteArray()); connect(negotiantionReply, &QNetworkReply::finished, this, [=](){ if (negotiantionReply->error() != QNetworkReply::NoError) { - qCWarning(dcEasee()) << "Unable to neotiate SignalR channel:" << negotiantionReply->error(); + qCWarning(dcEasee()) << "SingalR: Unable to neotiate SignalR channel:" << negotiantionReply->error(); + QTimer::singleShot(5000, this, [=](){connectToHost();}); return; } QByteArray data = negotiantionReply->readAll(); - qCDebug(dcEasee) << "Negotiation reply" << data; + qCDebug(dcEasee) << "SingalR: Negotiation reply" << data; QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { - qCWarning(dcEasee()) << "Unable to parse json from negoatiate endpoint" << error.errorString() << data; + qCWarning(dcEasee()) << "SingalR: Unable to parse json from negoatiate endpoint" << error.errorString() << data; + QTimer::singleShot(5000, this, [=](){connectToHost();}); return; } @@ -145,7 +163,7 @@ void SignalRConnection::connectToHost() wsUrl.setQuery(query); QNetworkRequest request(wsUrl); request.setRawHeader("Authorization", "Bearer " + m_accessToken); - qCDebug(dcEasee()) << "Connecting websocket:" << wsUrl.toString(); + qCDebug(dcEasee()) << "SingalR: Connecting websocket:" << wsUrl.toString(); m_waitingForHandshakeReply = true; #if QT_VERSION >= QT_VERSION_CHECK(5,6,0) m_socket->open(request); diff --git a/easee/signalrconnection.h b/easee/signalrconnection.h index 19ef1c11..a81c9cc2 100644 --- a/easee/signalrconnection.h +++ b/easee/signalrconnection.h @@ -4,6 +4,7 @@ #include #include #include +#include #include class SignalRConnection : public QObject @@ -32,6 +33,7 @@ private: QByteArray m_accessToken; NetworkAccessManager *m_nam = nullptr; QWebSocket *m_socket = nullptr; + QTimer *m_watchdog = nullptr; bool m_waitingForHandshakeReply = false; };