diff --git a/plugin/energymanagerdbusclient.cpp b/plugin/energymanagerdbusclient.cpp new file mode 100644 index 0000000..48d1368 --- /dev/null +++ b/plugin/energymanagerdbusclient.cpp @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright (C) 2013 - 2024, nymea GmbH +* Copyright (C) 2024 - 2025, chargebyte austria GmbH +* +* This file is part of nymea-energy-plugin-nymea. +* +* nymea-energy-plugin-nymea.s free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* nymea-energy-plugin-nymea.s distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with nymea-energy-plugin-nymea. If not, see . +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "energymanagerdbusclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const QString kDbusService = QStringLiteral("io.nymea.energymanager"); +static const QString kDbusPath = QStringLiteral("/io/nymea/energymanager"); +static const QString kDbusInterface = QStringLiteral("io.nymea.energymanager"); + +EnergyManagerDbusClient::EnergyManagerDbusClient(QObject *parent) : + QObject(parent), + m_connection(QDBusConnection::systemBus()) +{ + if (!m_connection.isConnected()) { + emit errorOccurred(QStringLiteral("DBus system bus not connected")); + return; + } + + m_serviceWatcher = new QDBusServiceWatcher(kDbusService, + m_connection, + QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, + this); + connect(m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &EnergyManagerDbusClient::onServiceRegistered); + connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &EnergyManagerDbusClient::onServiceUnregistered); + + QDBusConnectionInterface *bus = m_connection.interface(); + if (bus && bus->isServiceRegistered(kDbusService)) { + onServiceRegistered(kDbusService); + } +} + +EnergyManagerDbusClient::~EnergyManagerDbusClient() +{ +} + +QVariantList EnergyManagerDbusClient::chargingInfos() const +{ + return m_chargingInfos; +} + +void EnergyManagerDbusClient::refreshChargingInfos() +{ + if (!m_interface || !m_interface->isValid()) { + if (!setupInterface()) { + emit errorOccurred(QStringLiteral("EnergyManager DBus interface is not available")); + return; + } + } + + if (!m_interface) { + emit errorOccurred(QStringLiteral("EnergyManager DBus interface is not available")); + return; + } + + QDBusReply reply = m_interface->call(QStringLiteral("chargingInfos")); + if (!reply.isValid()) { + emit errorOccurred(reply.error().message()); + return; + } + + QVariantList convertedInfos; + const QVariantList infos = reply.value(); + for (const QVariant &value : infos) { + if (value.canConvert()) { + convertedInfos.append(value.toMap()); + continue; + } + + const QDBusArgument arg = value.value(); + convertedInfos.append(qdbus_cast(arg)); + } + + m_chargingInfos = convertedInfos; + emit chargingInfosUpdated(m_chargingInfos); +} + +void EnergyManagerDbusClient::onChargingInfoAdded(const QVariantMap &chargingInfo) +{ + replaceOrAdd(chargingInfo); + emit chargingInfoAdded(chargingInfo); + emit chargingInfosUpdated(m_chargingInfos); +} + +void EnergyManagerDbusClient::onChargingInfoRemoved(const QString &evChargerId) +{ + int index = indexOfInfo(evChargerId); + if (index >= 0) { + m_chargingInfos.removeAt(index); + emit chargingInfoRemoved(evChargerId); + emit chargingInfosUpdated(m_chargingInfos); + } +} + +void EnergyManagerDbusClient::onChargingInfoChanged(const QVariantMap &chargingInfo) +{ + replaceOrAdd(chargingInfo); + emit chargingInfoChanged(chargingInfo); + emit chargingInfosUpdated(m_chargingInfos); +} + +int EnergyManagerDbusClient::indexOfInfo(const QString &evChargerId) const +{ + for (int i = 0; i < m_chargingInfos.count(); ++i) { + const QVariantMap map = m_chargingInfos.at(i).toMap(); + if (map.value(QStringLiteral("evChargerId")).toString() == evChargerId) { + return i; + } + } + return -1; +} + +void EnergyManagerDbusClient::replaceOrAdd(const QVariantMap &chargingInfo) +{ + const QString evChargerId = chargingInfo.value(QStringLiteral("evChargerId")).toString(); + int index = indexOfInfo(evChargerId); + if (index >= 0) { + m_chargingInfos[index] = chargingInfo; + } else { + m_chargingInfos.append(chargingInfo); + } +} + +bool EnergyManagerDbusClient::setupInterface() +{ + if (!m_connection.isConnected()) { + return false; + } + + QDBusConnectionInterface *bus = m_connection.interface(); + if (!bus || !bus->isServiceRegistered(kDbusService)) { + return false; + } + + if (m_interface && m_interface->isValid()) { + return true; + } + + delete m_interface; + m_interface = new QDBusInterface(kDbusService, kDbusPath, kDbusInterface, m_connection, this); + + if (!m_interface->isValid()) { + emit errorOccurred(QStringLiteral("EnergyManager DBus interface is not available")); + delete m_interface; + m_interface = nullptr; + return false; + } + + connect(m_interface, SIGNAL(chargingInfoAdded(QVariantMap)), this, SLOT(onChargingInfoAdded(QVariantMap)), Qt::UniqueConnection); + connect(m_interface, SIGNAL(chargingInfoRemoved(QString)), this, SLOT(onChargingInfoRemoved(QString)), Qt::UniqueConnection); + connect(m_interface, SIGNAL(chargingInfoChanged(QVariantMap)), this, SLOT(onChargingInfoChanged(QVariantMap)), Qt::UniqueConnection); + + return true; +} + +void EnergyManagerDbusClient::onServiceRegistered(const QString &service) +{ + if (service != kDbusService) { + return; + } + + if (setupInterface()) { + refreshChargingInfos(); + } +} + +void EnergyManagerDbusClient::onServiceUnregistered(const QString &service) +{ + if (service != kDbusService) { + return; + } + + delete m_interface; + m_interface = nullptr; + m_chargingInfos.clear(); + emit chargingInfosUpdated(m_chargingInfos); +} diff --git a/plugin/energymanagerdbusclient.h b/plugin/energymanagerdbusclient.h new file mode 100644 index 0000000..2d29828 --- /dev/null +++ b/plugin/energymanagerdbusclient.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright (C) 2013 - 2024, nymea GmbH +* Copyright (C) 2024 - 2025, chargebyte austria GmbH +* +* This file is part of nymea-energy-plugin-nymea. +* +* nymea-energy-plugin-nymea.s free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* nymea-energy-plugin-nymea.s distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with nymea-energy-plugin-nymea. If not, see . +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ENERGYMANAGERDBUSCLIENT_H +#define ENERGYMANAGERDBUSCLIENT_H + +#include +#include +#include + +class QDBusInterface; +class QDBusPendingCallWatcher; +class QDBusServiceWatcher; + +class EnergyManagerDbusClient : public QObject +{ + Q_OBJECT +public: + explicit EnergyManagerDbusClient(QObject *parent = nullptr); + ~EnergyManagerDbusClient(); + + QVariantList chargingInfos() const; + +public slots: + void refreshChargingInfos(); + +signals: + void chargingInfosUpdated(const QVariantList &chargingInfos); + void chargingInfoAdded(const QVariantMap &chargingInfo); + void chargingInfoRemoved(const QString &evChargerId); + void chargingInfoChanged(const QVariantMap &chargingInfo); + void errorOccurred(const QString &message); + +private slots: + void onChargingInfoAdded(const QVariantMap &chargingInfo); + void onChargingInfoRemoved(const QString &evChargerId); + void onChargingInfoChanged(const QVariantMap &chargingInfo); + void onServiceRegistered(const QString &service); + void onServiceUnregistered(const QString &service); + +private: + int indexOfInfo(const QString &evChargerId) const; + void replaceOrAdd(const QVariantMap &chargingInfo); + bool setupInterface(); + + QDBusConnection m_connection; + QDBusInterface *m_interface = nullptr; + QDBusServiceWatcher *m_serviceWatcher = nullptr; + QVariantList m_chargingInfos; +}; + +#endif // ENERGYMANAGERDBUSCLIENT_H diff --git a/plugin/evdashengine.cpp b/plugin/evdashengine.cpp index d9c8ab2..efe0021 100644 --- a/plugin/evdashengine.cpp +++ b/plugin/evdashengine.cpp @@ -31,6 +31,7 @@ #include "evdashengine.h" #include "evdashsettings.h" #include "evdashwebserverresource.h" +#include "energymanagerdbusclient.h" #include @@ -101,6 +102,44 @@ EvDashEngine::EvDashEngine(ThingManager *thingManager, EvDashWebServerResource * bool enabled = settings.value("enabled", false).toBool(); settings.endGroup(); + + m_energyManagerClient = new EnergyManagerDbusClient(this); + connect(m_energyManagerClient, &EnergyManagerDbusClient::chargingInfosUpdated, this, [](const QVariantList &chargingInfos){ + qCDebug(dcEvDashExperience()) << "ChargingInfos:"; + foreach (const QVariant &ciVariant, chargingInfos) { + qCDebug(dcEvDashExperience()) << "-->" << ciVariant.toMap(); + } + }); + + connect(m_energyManagerClient, &EnergyManagerDbusClient::chargingInfoAdded, this, [this](const QVariantMap &chargingInfo){ + qCDebug(dcEvDashExperience()) << "ChargingInfo added:" << chargingInfo; + Thing *charger = m_thingManager->findConfiguredThing(chargingInfo.value("evChargerId").toUuid()); + if (charger) { + onThingChanged(charger); + } + }); + + connect(m_energyManagerClient, &EnergyManagerDbusClient::chargingInfoChanged, this, [this](const QVariantMap &chargingInfo){ + qCDebug(dcEvDashExperience()) << "ChargingInfo changed:" << chargingInfo; + Thing *charger = m_thingManager->findConfiguredThing(chargingInfo.value("evChargerId").toUuid()); + if (charger) { + onThingChanged(charger); + } + }); + + connect(m_energyManagerClient, &EnergyManagerDbusClient::chargingInfoRemoved, this, [](const QString &evChargerId){ + qCDebug(dcEvDashExperience()) << "ChargingInfo removed:" << evChargerId; + }); + + connect(m_energyManagerClient, &EnergyManagerDbusClient::errorOccurred, this, [](const QString &errorMessage){ + qCWarning(dcEvDashExperience()) << "Energy manager DBus client error:" << errorMessage; + }); + + qCDebug(dcEvDashExperience()) << "ChargingInfos:" << m_energyManagerClient->chargingInfos(); + foreach (const QVariant &ciVariant, m_energyManagerClient->chargingInfos()) { + qCDebug(dcEvDashExperience()) << "-->" << ciVariant.toMap(); + } + // Start the service if enabled setEnabled(enabled); } @@ -367,11 +406,27 @@ QJsonObject EvDashEngine::packCharger(Thing *charger) const QJsonObject chargerObject; chargerObject.insert("id", charger->id().toString(QUuid::WithoutBraces)); chargerObject.insert("name", charger->name()); + + foreach (const QVariant &chargingInfoVariant, m_energyManagerClient->chargingInfos()) { + QVariantMap chargingInfo = chargingInfoVariant.toMap(); + if (chargingInfo.value("evChargerId").toUuid() == charger->id()) { + if (chargingInfo.value("assignedCarId").toString().isEmpty()) { + chargerObject.insert("assignedCar", ""); + } else { + Thing *car = m_thingManager->findConfiguredThing(chargingInfo.value("assignedCarId").toUuid()); + if (car) { + chargerObject.insert("assignedCar", car->name()); + } else { + chargerObject.insert("assignedCar", ""); + } + } + } + } + chargerObject.insert("connected", charger->stateValue("connected").toBool()); + chargerObject.insert("status", charger->stateValue("status").toString()); chargerObject.insert("chargingCurrent", charger->stateValue("maxChargingCurrent").toDouble()); - chargerObject.insert("chargingAllowed", charger->stateValue("power").toBool()); chargerObject.insert("currentPower", charger->stateValue("currentPower").toDouble()); - chargerObject.insert("pluggedIn", charger->stateValue("pluggedIn").toBool()); if (charger->hasState("currentVersion")) chargerObject.insert("version", charger->stateValue("currentVersion").toDouble()); diff --git a/plugin/evdashengine.h b/plugin/evdashengine.h index df6fc47..0c9e436 100644 --- a/plugin/evdashengine.h +++ b/plugin/evdashengine.h @@ -42,6 +42,7 @@ class QWebSocketServer; class Thing; class ThingManager; +class EnergyManagerDbusClient; class EvDashWebServerResource; class EvDashEngine : public QObject @@ -75,9 +76,10 @@ private slots: private: ThingManager *m_thingManager = nullptr; EvDashWebServerResource *m_webServerResource = nullptr; - bool m_enabled = false; + EnergyManagerDbusClient *m_energyManagerClient = nullptr; + QWebSocketServer *m_webSocketServer = nullptr; quint16 m_webSocketPort = 4449; diff --git a/plugin/plugin.pro b/plugin/plugin.pro index 673d588..e235dc6 100644 --- a/plugin/plugin.pro +++ b/plugin/plugin.pro @@ -9,15 +9,17 @@ PKGCONFIG += nymea RESOURCES += ../dashboard.qrc QT -= gui -QT += network sql websockets +QT += network sql websockets dbus HEADERS += experiencepluginevdash.h \ + energymanagerdbusclient.h \ evdashengine.h \ evdashjsonhandler.h \ evdashsettings.h \ evdashwebserverresource.h SOURCES += experiencepluginevdash.cpp \ + energymanagerdbusclient.cpp \ evdashengine.cpp \ evdashjsonhandler.cpp \ evdashsettings.cpp \