+ + + History + Charging sessions + + + Charger + + All chargers + + Fetch sessions + + + Select a charger to filter by its assigned car and fetch sessions from nymea. + No charging sessions fetched yet. +
diff --git a/plugin/chargingsessionsdbusinterfaceclient.cpp b/plugin/chargingsessionsdbusinterfaceclient.cpp index 03dd5be..6d93c1d 100644 --- a/plugin/chargingsessionsdbusinterfaceclient.cpp +++ b/plugin/chargingsessionsdbusinterfaceclient.cpp @@ -26,14 +26,13 @@ #include #include +#include #include #include #include #include #include -#include - -Q_DECLARE_LOGGING_CATEGORY(dcChargingSessions) +#include static const QString kDbusService = QStringLiteral("io.nymea.energy.chargingsessions"); static const QString kDbusPath = QStringLiteral("/io/nymea/energy/chargingsessions"); @@ -43,8 +42,16 @@ ChargingSessionsDBusInterfaceClient::ChargingSessionsDBusInterfaceClient(QObject QObject(parent), m_connection(QDBusConnection::systemBus()) { - if (!m_connection.isConnected()) { - qCWarning(dcChargingSessions()) << "DBus system bus not connected"; + m_serviceWatcher = new QDBusServiceWatcher(kDbusService, + m_connection, + QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, + this); + connect(m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &ChargingSessionsDBusInterfaceClient::onServiceRegistered); + connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &ChargingSessionsDBusInterfaceClient::onServiceUnregistered); + + QDBusConnectionInterface *bus = m_connection.interface(); + if (bus && bus->isServiceRegistered(kDbusService)) { + onServiceRegistered(kDbusService); } } @@ -76,7 +83,6 @@ void ChargingSessionsDBusInterfaceClient::onCallFinished(QDBusPendingCallWatcher watcher->deleteLater(); if (reply.isError()) { - qCWarning(dcChargingSessions()) << "GetSessions DBus call failed:" << reply.error().message(); emit errorOccurred(reply.error().message()); return; } @@ -107,13 +113,11 @@ bool ChargingSessionsDBusInterfaceClient::ensureInterface() m_interface = nullptr; if (!m_connection.isConnected()) { - qCWarning(dcChargingSessions()) << "DBus system bus not connected"; return false; } m_interface = new QDBusInterface(kDbusService, kDbusPath, kDbusInterface, m_connection, this); if (!m_interface->isValid()) { - qCWarning(dcChargingSessions()) << "Charging sessions DBus interface is not available:" << m_connection.lastError().message(); delete m_interface; m_interface = nullptr; return false; @@ -121,3 +125,18 @@ bool ChargingSessionsDBusInterfaceClient::ensureInterface() return true; } + +void ChargingSessionsDBusInterfaceClient::onServiceRegistered(const QString &service) +{ + Q_UNUSED(service) + ensureInterface(); +} + +void ChargingSessionsDBusInterfaceClient::onServiceUnregistered(const QString &service) +{ + Q_UNUSED(service) + if (m_interface) { + m_interface->deleteLater(); + } + m_interface = nullptr; +} diff --git a/plugin/chargingsessionsdbusinterfaceclient.h b/plugin/chargingsessionsdbusinterfaceclient.h index aacd161..de1d994 100644 --- a/plugin/chargingsessionsdbusinterfaceclient.h +++ b/plugin/chargingsessionsdbusinterfaceclient.h @@ -29,9 +29,11 @@ #include #include #include +#include class QDBusInterface; class QDBusPendingCallWatcher; +class QDBusServiceWatcher; class ChargingSessionsDBusInterfaceClient : public QObject { @@ -51,11 +53,14 @@ signals: private slots: void onCallFinished(QDBusPendingCallWatcher *watcher); + void onServiceRegistered(const QString &service); + void onServiceUnregistered(const QString &service); private: bool ensureInterface(); QDBusConnection m_connection; QDBusInterface *m_interface = nullptr; + QDBusServiceWatcher *m_serviceWatcher = nullptr; QList m_sessions; }; diff --git a/plugin/evdashengine.cpp b/plugin/evdashengine.cpp index 4f90df4..c965f15 100644 --- a/plugin/evdashengine.cpp +++ b/plugin/evdashengine.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include Q_DECLARE_LOGGING_CATEGORY(dcEvDashExperience) @@ -105,16 +106,11 @@ EvDashEngine::EvDashEngine(ThingManager *thingManager, EvDashWebServerResource * // ChargingSessions client for fetching charging sessions m_chargingSessionsClient = new ChargingSessionsDBusInterfaceClient(this); - connect(m_chargingSessionsClient, &ChargingSessionsDBusInterfaceClient::sessionsReceived, this, [](const QList &chargingSessions){ - qCDebug(dcEvDashExperience()) << "ChargingSessions :"; - foreach (const QVariant &ciVariant, chargingSessions) { - qCDebug(dcEvDashExperience()) << "-->" << ciVariant.toMap(); - } - }); + connect(m_chargingSessionsClient, &ChargingSessionsDBusInterfaceClient::sessionsReceived, + this, &EvDashEngine::onSessionsReceived); - connect(m_chargingSessionsClient, &ChargingSessionsDBusInterfaceClient::errorOccurred, this, [](const QString &errorMessage){ - qCWarning(dcEvDashExperience()) << "Charging sessions DBus client error occurred:" << errorMessage; - }); + connect(m_chargingSessionsClient, &ChargingSessionsDBusInterfaceClient::errorOccurred, + this, &EvDashEngine::onSessionsError); // Energy manager client for associated cars and current mode @@ -307,11 +303,13 @@ void EvDashEngine::processTextMessage(QWebSocket *socket, const QString &message } QJsonObject response = handleApiRequest(socket, requestObject); - sendReply(socket, response); + if (!response.isEmpty()) { + sendReply(socket, response); - if (isAuthenticateAction && !response.value(QStringLiteral("success")).toBool()) { - socket->close(QWebSocketProtocol::CloseCodePolicyViolated, QStringLiteral("Authentication failed")); - m_authenticatedClients.remove(socket); + if (isAuthenticateAction && !response.value(QStringLiteral("success")).toBool()) { + socket->close(QWebSocketProtocol::CloseCodePolicyViolated, QStringLiteral("Authentication failed")); + m_authenticatedClients.remove(socket); + } } } @@ -366,6 +364,19 @@ QJsonObject EvDashEngine::handleApiRequest(QWebSocket *socket, const QJsonObject return createSuccessResponse(requestId, payload); } + if (action.compare(QStringLiteral("GetChargingSessions"), Qt::CaseInsensitive) == 0) { + if (!m_chargingSessionsClient) + return createErrorResponse(requestId, QStringLiteral("chargingSessionsUnavailable")); + + const QJsonObject payload = request.value(QStringLiteral("payload")).toObject(); + const QString chargerId = payload.value(QStringLiteral("chargerId")).toString(); + const QStringList carThingIds = carThingIdsForCharger(chargerId); + + m_pendingChargingSessionsRequests.insert(requestId, QPointer(socket)); + m_chargingSessionsClient->getSessions(carThingIds); + return {}; + } + return createErrorResponse(requestId, QStringLiteral("unknownAction")); } @@ -384,6 +395,9 @@ void EvDashEngine::sendNotification(const QString ¬ification, QJsonObject pay // Send to all active clients for (QWebSocket *client : qAsConst(m_clients)) { + if (m_authenticatedClients.value(client).isEmpty()) + continue; + QJsonObject notificationObject; notificationObject.insert(QStringLiteral("requestId"), QUuid::createUuid().toString(QUuid::WithoutBraces)); notificationObject.insert("event", notification); @@ -465,3 +479,62 @@ QJsonObject EvDashEngine::packCharger(Thing *charger) const return chargerObject; } + +QStringList EvDashEngine::carThingIdsForCharger(const QString &chargerId) const +{ + QStringList carThingIds; + if (!m_energyManagerClient || chargerId.isEmpty()) + return carThingIds; + + const QUuid chargerUuid = QUuid::fromString(chargerId); + if (chargerUuid.isNull()) + return carThingIds; + + for (const QVariant &ciVariant : m_energyManagerClient->chargingInfos()) { + const QVariantMap chargingInfo = ciVariant.toMap(); + if (chargingInfo.value(QStringLiteral("evChargerId")).toUuid() != chargerUuid) + continue; + + const QString assignedCarId = chargingInfo.value(QStringLiteral("assignedCarId")).toString(); + if (!assignedCarId.isEmpty()) + carThingIds.append(assignedCarId); + break; + } + + return carThingIds; +} + +void EvDashEngine::onSessionsReceived(const QList &sessions) +{ + qCDebug(dcEvDashExperience()) << "ChargingSessions received:" << sessions.count(); + + QJsonArray sessionArray; + for (const QVariantMap &session : sessions) + sessionArray.append(QJsonObject::fromVariantMap(session)); + + QJsonObject payload; + payload.insert(QStringLiteral("sessions"), sessionArray); + + const QList pendingRequestIds = m_pendingChargingSessionsRequests.keys(); + for (const QString &requestId : pendingRequestIds) { + QPointer socket = m_pendingChargingSessionsRequests.take(requestId); + if (!socket) + continue; + sendReply(socket, createSuccessResponse(requestId, payload)); + } + + sendNotification(QStringLiteral("chargingSessionsUpdated"), payload); +} + +void EvDashEngine::onSessionsError(const QString &errorMessage) +{ + qCWarning(dcEvDashExperience()) << "Charging sessions DBus client error occurred:" << errorMessage; + + const QList pendingRequestIds = m_pendingChargingSessionsRequests.keys(); + for (const QString &requestId : pendingRequestIds) { + QPointer socket = m_pendingChargingSessionsRequests.take(requestId); + if (!socket) + continue; + sendReply(socket, createErrorResponse(requestId, errorMessage)); + } +} diff --git a/plugin/evdashengine.h b/plugin/evdashengine.h index 9366528..89b8585 100644 --- a/plugin/evdashengine.h +++ b/plugin/evdashengine.h @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include @@ -91,6 +93,10 @@ private: QList m_chargers; void monitorChargerThing(Thing *thing); + // Pending requests waiting for charging sessions data to return + QHash> m_pendingChargingSessionsRequests; + QStringList carThingIdsForCharger(const QString &chargerId) const; + // Websocket server bool startWebSocketServer(quint16 port = 0); void stopWebSocketServer(); @@ -105,6 +111,8 @@ private: QJsonObject createErrorResponse(const QString &requestId, const QString &errorMessage) const; QJsonObject packCharger(Thing *charger) const; + void onSessionsReceived(const QList &sessions); + void onSessionsError(const QString &errorMessage); };