Easee: Various fixes after beta testing
parent
17489941cf
commit
0f79c1d3a1
|
|
@ -40,6 +40,9 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QWebSocket>
|
||||
|
||||
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());
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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<QString, uint> m_siteIds; // chargerId, siteId
|
||||
|
||||
PluginTimer *m_timer = nullptr;
|
||||
|
||||
QHash<QString,uint> m_cableRating;
|
||||
QHash<QString,uint> m_wallboxMax;
|
||||
QHash<Thing*,uint> m_desiredMax;
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINEASEE_H
|
||||
|
|
|
|||
|
|
@ -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<errorSignal>(&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);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QWebSocket>
|
||||
#include <QTimer>
|
||||
#include <network/networkaccessmanager.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue