diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..569386d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pro.user +.crossbuilder/* diff --git a/config.pri b/config.pri new file mode 100644 index 0000000..faa15ab --- /dev/null +++ b/config.pri @@ -0,0 +1,10 @@ +gcc { + COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion") + COMPILER_MAJOR_VERSION = $$str_member($$COMPILER_VERSION) + greaterThan(COMPILER_MAJOR_VERSION, 7): QMAKE_CXXFLAGS += -Wno-deprecated-copy +} + +top_srcdir = $$PWD +top_builddir = $$shadowed($$PWD) + +CONFIG += c++11 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..fbd66ef --- /dev/null +++ b/debian/changelog @@ -0,0 +1,3 @@ +nymea-experience-plugin-energy (0.3) xenial; urgency=medium + + -- Jenkins Tue, 05 Oct 2021 08:33:51 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..af488e6 --- /dev/null +++ b/debian/control @@ -0,0 +1,42 @@ +Source: nymea-experience-plugin-energy +Section: utils +Priority: optional +Maintainer: Michael Zanetti +Standards-Version: 3.9.7 +Homepage: https://nymea.io +Build-Depends: debhelper (>= 9.0.0), + dpkg-dev (>= 1.16.1~), + libnymea-dev (>= 0.17), + nymea-dev-tools:native, + qt5-qmake, + qtbase5-dev, + +Package: nymea-experience-plugin-energy +Section: libs +Architecture: any +Multi-Arch: same +Depends: ${shlibs:Depends}, + ${misc:Depends}, +Description: nymea experiece plugin for energy related use cases + This nymea experience adds the support energy related use + caes to nymea. + +Package: libnymea-energy +Section: libs +Architecture: any +Multi-Arch: same +Depends: ${shlibs:Depends}, + ${misc:Depends}, +Description: Library for nymea energy experience plugins + This library is used by plugins loaded by the nymea + energy experience. + +Package: libnymea-energy-dev +Section: libdevel +Architecture: any +Multi-Arch: same +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-energy (= ${binary:Version}), +Description: Library for nymea expergy experience plugins - Development files + diff --git a/debian/libnymea-energy-dev.install.in b/debian/libnymea-energy-dev.install.in new file mode 100644 index 0000000..2760d48 --- /dev/null +++ b/debian/libnymea-energy-dev.install.in @@ -0,0 +1,3 @@ +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-energy.so +usr/include/nymea-energy/*.h +usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea-energy.pc diff --git a/debian/libnymea-energy.install.in b/debian/libnymea-energy.install.in new file mode 100644 index 0000000..62c885e --- /dev/null +++ b/debian/libnymea-energy.install.in @@ -0,0 +1,3 @@ +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-energy.so.1 +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-energy.so.1.0 +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-energy.so.1.0.0 diff --git a/debian/nymea-experience-plugin-energy.install.in b/debian/nymea-experience-plugin-energy.install.in new file mode 100644 index 0000000..e81a9ca --- /dev/null +++ b/debian/nymea-experience-plugin-energy.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/experiences/libnymea_experiencepluginenergy.so diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..f063fd2 --- /dev/null +++ b/debian/rules @@ -0,0 +1,27 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +export DH_VERBOSE=1 +export QT_QPA_PLATFORM=minimal + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +PREPROCESS_FILES := $(wildcard debian/*.in) + +$(PREPROCESS_FILES:.in=): %: %.in + sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@ + +override_dh_install: $(PREPROCESS_FILES:.in=) + dh_install + +override_dh_auto_clean: + dh_auto_clean + rm -rf $(PREPROCESS_FILES:.in=) + +override_dh_auto_test: + +override_dh_install: $(PREPROCESS_FILES:.in=) + dh_install --fail-missing + +%: + dh $@ --buildsystem=qmake --parallel diff --git a/libnymea-energy/energymanager.cpp b/libnymea-energy/energymanager.cpp new file mode 100644 index 0000000..01e7a2c --- /dev/null +++ b/libnymea-energy/energymanager.cpp @@ -0,0 +1,37 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#include "energymanager.h" + +EnergyManager::EnergyManager(QObject *parent) : QObject(parent) +{ + +} diff --git a/libnymea-energy/energymanager.h b/libnymea-energy/energymanager.h new file mode 100644 index 0000000..83ce535 --- /dev/null +++ b/libnymea-energy/energymanager.h @@ -0,0 +1,65 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#ifndef ENERGYMANAGER_H +#define ENERGYMANAGER_H + +#include + +#include + +class EnergyManager : public QObject +{ + Q_OBJECT +public: + enum EnergyError { + EnergyErrorNoError, + EnergyErrorMissingParameter, + EnergyErrorInvalidParameter, + }; + Q_ENUM(EnergyError) + + explicit EnergyManager(QObject *parent = nullptr); + virtual ~EnergyManager() = default; + + virtual EnergyError setRootMeter(const ThingId &rootMeterId) = 0; + virtual Thing *rootMeter() const = 0; + + virtual double currentPowerConsumption() const = 0; + virtual double currentPowerProduction() const = 0; + virtual double currentPowerAcquisition() const = 0; + +signals: + void rootMeterChanged(); + void powerBalanceChanged(); +}; + +#endif // ENERGYMANAGER_H diff --git a/libnymea-energy/energyplugin.cpp b/libnymea-energy/energyplugin.cpp new file mode 100644 index 0000000..75d8f99 --- /dev/null +++ b/libnymea-energy/energyplugin.cpp @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "energyplugin.h" + +EnergyPlugin::EnergyPlugin(QObject *parent) : QObject(parent) +{ + +} + +EnergyManager *EnergyPlugin::energyManager() const +{ + return m_energyManager; +} + +ThingManager *EnergyPlugin::thingManager() const +{ + return m_thingManager; +} + +JsonRPCServer *EnergyPlugin::jsonRpcServer() const +{ + return m_jsonRpcServer; +} + +void EnergyPlugin::initPlugin(EnergyManager *energyManager, ThingManager *thingManager, JsonRPCServer *jsonRPCServer) +{ + m_energyManager = energyManager; + m_thingManager = thingManager; + m_jsonRpcServer = jsonRPCServer; + + init(); +} + diff --git a/libnymea-energy/energyplugin.h b/libnymea-energy/energyplugin.h new file mode 100644 index 0000000..9202c58 --- /dev/null +++ b/libnymea-energy/energyplugin.h @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ENERGYPLUGIN_H +#define ENERGYPLUGIN_H + +#include + +#include +#include + +#include "energymanager.h" + +class EnergyPlugin : public QObject +{ + Q_OBJECT +public: + explicit EnergyPlugin(QObject *parent = nullptr); + virtual ~EnergyPlugin() = default; + + virtual void init() = 0; + +protected: + EnergyManager* energyManager() const; + ThingManager* thingManager() const; + JsonRPCServer* jsonRpcServer() const; + +private: + friend class ExperiencePluginEnergy; + void initPlugin(EnergyManager *energyManager, ThingManager *thingManager, JsonRPCServer *jsonRPCServer); + + EnergyManager *m_energyManager = nullptr; + ThingManager *m_thingManager = nullptr; + JsonRPCServer *m_jsonRpcServer = nullptr; +}; + +Q_DECLARE_INTERFACE(EnergyPlugin, "io.nymea.EnergyPlugin") + +#endif // ENERGYPLUGIN_H diff --git a/libnymea-energy/libnymea-energy.pro b/libnymea-energy/libnymea-energy.pro new file mode 100644 index 0000000..fb31ade --- /dev/null +++ b/libnymea-energy/libnymea-energy.pro @@ -0,0 +1,38 @@ +TEMPLATE = lib +TARGET = $$qtLibraryTarget(nymea-energy) + +include(../config.pri) +NYMEA_ENERGY_VERSION_STRING = "0.0.1" + +CONFIG += link_pkgconfig +PKGCONFIG += nymea + + +HEADERS += \ + energymanager.h \ + energyplugin.h + +SOURCES += \ + energymanager.cpp \ + energyplugin.cpp + +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target + +for(header, HEADERS) { + path = $$[QT_INSTALL_PREFIX]/include/nymea-energy/$${dirname(header)} + eval(headers_$${path}.files += $${header}) + eval(headers_$${path}.path = $${path}) + eval(INSTALLS *= headers_$${path}) +} + +CONFIG += create_pc create_prl no_install_prl +QMAKE_PKGCONFIG_NAME = libnymea-energy +QMAKE_PKGCONFIG_DESCRIPTION = nymea energy library +QMAKE_PKGCONFIG_PREFIX = $$[QT_INSTALL_PREFIX] +QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/nymea-energy/ +QMAKE_PKGCONFIG_LIBDIR = $$target.path +QMAKE_PKGCONFIG_VERSION = $$NYMEA_ENERGY_VERSION_STRING +QMAKE_PKGCONFIG_FILE = nymea-energy +QMAKE_PKGCONFIG_DESTDIR = pkgconfig + diff --git a/nymea-experience-plugin-energy.pro b/nymea-experience-plugin-energy.pro new file mode 100644 index 0000000..7c4fc4b --- /dev/null +++ b/nymea-experience-plugin-energy.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +SUBDIRS += libnymea-energy plugin + +plugin.depends = libnymea-energy + + diff --git a/plugin/energyjsonhandler.cpp b/plugin/energyjsonhandler.cpp new file mode 100644 index 0000000..c6ee7d9 --- /dev/null +++ b/plugin/energyjsonhandler.cpp @@ -0,0 +1,98 @@ +#include "energyjsonhandler.h" +#include "energymanagerimpl.h" + +Q_DECLARE_LOGGING_CATEGORY(dcEnergyExperience) + +EnergyJsonHandler::EnergyJsonHandler(EnergyManager *energyManager, QObject *parent): + JsonHandler(parent), + m_energyManager(energyManager) +{ + registerEnum(); + + QVariantMap params, returns; + QString description; + + params.clear(); returns.clear(); + description = "Get the root meter ID. If there is no root meter set, the params will be empty."; + returns.insert("o:rootMeterThingId", enumValueName(Uuid)); + registerMethod("GetRootMeter", description, params, returns); + + params.clear(); returns.clear(); + description = "Set the root meter."; + params.insert("rootMeterThingId", enumValueName(Uuid)); + returns.insert("energyError", enumRef()); + registerMethod("SetRootMeter", description, params, returns); + + params.clear(); returns.clear(); + description = "Get the current power balance. That is, production, consumption and acquisition."; + returns.insert("currentPowerConsumption", enumValueName(Double)); + returns.insert("currentPowerProduction", enumValueName(Double)); + returns.insert("currentPowerAcquisition", enumValueName(Double)); + registerMethod("GetPowerBalance", description, params, returns); + + params.clear(); + description = "Emitted whenever the root meter id changes. If the root meter has been unset, the params will be empty."; + params.insert("o:rootMeterThingId", enumValueName(Uuid)); + registerNotification("RootMeterChanged", description, params); + + params.clear(); + description = "Emitted whenever the energy balance changes. That is, when the current consumption, production or acquisition changes. Typically they will all change at the same time."; + params.insert("currentPowerConsumption", enumValueName(Double)); + params.insert("currentPowerProduction", enumValueName(Double)); + params.insert("currentPowerAcquisition", enumValueName(Double)); + registerNotification("PowerBalanceChanged", description, params); + + connect(m_energyManager, &EnergyManager::rootMeterChanged, this, [=](){ + QVariantMap params; + if (m_energyManager->rootMeter()) { + params.insert("rootMeterThingId", m_energyManager->rootMeter()->id()); + } + emit RootMeterChanged(params); + }); + + connect(m_energyManager, &EnergyManager::powerBalanceChanged, this, [=](){ + QVariantMap params; + params.insert("currentPowerConsumption", m_energyManager->currentPowerConsumption()); + params.insert("currentPowerProduction", m_energyManager->currentPowerProduction()); + params.insert("currentPowerAcquisition", m_energyManager->currentPowerAcquisition()); + emit PowerBalanceChanged(params); + }); +} + +QString EnergyJsonHandler::name() const +{ + return "Energy"; +} + +JsonReply *EnergyJsonHandler::GetRootMeter(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + QVariantMap ret; + if (m_energyManager->rootMeter()) { + ret.insert("rootMeterThingId", m_energyManager->rootMeter()->id()); + } + return createReply(ret); +} + +JsonReply *EnergyJsonHandler::SetRootMeter(const QVariantMap ¶ms) +{ + QVariantMap returns; + + if (!params.contains("rootMeterThingId")) { + returns.insert("energyError", enumValueName(EnergyManager::EnergyErrorMissingParameter)); + return createReply(returns); + } + EnergyManager::EnergyError status = m_energyManager->setRootMeter(params.value("rootMeterThingId").toUuid()); + returns.insert("energyError", enumValueName(status)); + return createReply(returns); +} + +JsonReply *EnergyJsonHandler::GetPowerBalance(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + QVariantMap ret; + ret.insert("currentPowerConsumption", m_energyManager->currentPowerConsumption()); + ret.insert("currentPowerProduction", m_energyManager->currentPowerProduction()); + ret.insert("currentPowerAcquisition", m_energyManager->currentPowerAcquisition()); + return createReply(ret); +} diff --git a/plugin/energyjsonhandler.h b/plugin/energyjsonhandler.h new file mode 100644 index 0000000..4f7fed6 --- /dev/null +++ b/plugin/energyjsonhandler.h @@ -0,0 +1,31 @@ +#ifndef ENERGYJSONHANDLER_H +#define ENERGYJSONHANDLER_H + +#include +#include "jsonrpc/jsonhandler.h" + +class EnergyManager; + +class EnergyJsonHandler : public JsonHandler +{ + Q_OBJECT +public: + explicit EnergyJsonHandler(EnergyManager *energyManager, QObject *parent = nullptr); + + QString name() const override; + + Q_INVOKABLE JsonReply* GetRootMeter(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* SetRootMeter(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* GetPowerBalance(const QVariantMap ¶ms); + +signals: + void RootMeterChanged(const QVariantMap ¶ms); + + void PowerBalanceChanged(const QVariantMap ¶ms); + +private: + EnergyManager *m_energyManager = nullptr; + +}; + +#endif // ENERGYJSONHANDLER_H diff --git a/plugin/energymanagerimpl.cpp b/plugin/energymanagerimpl.cpp new file mode 100644 index 0000000..ac450fb --- /dev/null +++ b/plugin/energymanagerimpl.cpp @@ -0,0 +1,135 @@ +#include "energymanagerimpl.h" +#include "nymeasettings.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(dcEnergyExperience) + +EnergyManagerImpl::EnergyManagerImpl(ThingManager *thingManager, QObject *parent): + EnergyManager(parent), + m_thingManager(thingManager) +{ + // Most of the time we get a bunch of signals at the same time (root meter, producers, consumers etc) + // In order to decrease some load on the system, we'll wait for wee bit until we actually update to + // accumulate those changes and calculate the change in one go. + m_balanceUpdateTimer.setInterval(50); + m_balanceUpdateTimer.setSingleShot(true); + connect(&m_balanceUpdateTimer, &QTimer::timeout, this, &EnergyManagerImpl::updatePowerBalance); + + QSettings settings(NymeaSettings::settingsPath() + "/energy.conf", QSettings::IniFormat); + ThingId rootMeterThingId = settings.value("rootMeterThingId").toUuid(); + EnergyManagerImpl::setRootMeter(rootMeterThingId); + qCDebug(dcEnergyExperience()) << "Loaded root meter" << rootMeterThingId; + + foreach (Thing *thing, m_thingManager->configuredThings()) { + watchThing(thing); + } + connect(thingManager, &ThingManager::thingAdded, this, &EnergyManagerImpl::watchThing); + connect(thingManager, &ThingManager::thingRemoved, this, &EnergyManagerImpl::unwatchThing); +} + +Thing *EnergyManagerImpl::rootMeter() const +{ + return m_rootMeter; +} + +EnergyManager::EnergyError EnergyManagerImpl::setRootMeter(const ThingId &rootMeterId) +{ + Thing *rootMeter = m_thingManager->findConfiguredThing(rootMeterId); + if (!rootMeter || !rootMeter->thingClass().interfaces().contains("energymeter")) { + return EnergyErrorInvalidParameter; + } + + if (m_rootMeter != rootMeter) { + qCDebug(dcEnergyExperience()) << "Setting root meter to" << rootMeter->name(); + m_rootMeter = rootMeter; + + QSettings settings(NymeaSettings::settingsPath() + "/energy.conf", QSettings::IniFormat); + settings.setValue("rootMeterThingId", rootMeter->id()); + + emit rootMeterChanged(); + } + return EnergyErrorNoError; +} + +double EnergyManagerImpl::currentPowerConsumption() const +{ + return m_currentPowerConsumption; +} + +double EnergyManagerImpl::currentPowerProduction() const +{ + return m_currentPowerProduction; +} + +double EnergyManagerImpl::currentPowerAcquisition() const +{ + return m_currentPowerAcquisition; +} + +void EnergyManagerImpl::watchThing(Thing *thing) +{ + // If we don't have a root meter yet, we'll be auto-setting the first energymeter that appears. + // It may be changed by the user through an API call later. + if (!m_rootMeter && thing->thingClass().interfaces().contains("energymeter")) { + setRootMeter(thing->id()); + } + + qCDebug(dcEnergyExperience()) << "Wathing thing:" << thing->name(); + if (thing->thingClass().interfaces().contains("smartmeterproducer") + || thing->thingClass().interfaces().contains("energymeter") + || thing->thingClass().interfaces().contains("energystorage")) { + connect(thing, &Thing::stateValueChanged, this, [=](const StateTypeId &stateTypeId){ + if (thing->thingClass().getStateType(stateTypeId).name() == "currentPower") { + m_balanceUpdateTimer.start(); + } + }); + } +} + +void EnergyManagerImpl::unwatchThing(const ThingId &thingId) +{ + if (m_rootMeter && m_rootMeter->id() == thingId) { + m_rootMeter = nullptr; + emit rootMeterChanged(); + } +} + +void EnergyManagerImpl::updatePowerBalance() +{ + double currentPowerAcquisition = 0; + if (m_rootMeter) { + currentPowerAcquisition = m_rootMeter->stateValue("currentPower").toDouble(); + } + + double currentPowerProduction = 0; + foreach (Thing* thing, m_thingManager->configuredThings().filterByInterface("smartmeterproducer")) { + currentPowerProduction += thing->stateValue("currentPower").toDouble(); + } + + double currentBatteryBalance = 0; + foreach (Thing *thing, m_thingManager->configuredThings().filterByInterface("energystorage")) { + currentBatteryBalance += thing->stateValue("currentPower").toDouble(); + } + + double currentPowerConsumption = -currentPowerProduction + currentPowerAcquisition - currentBatteryBalance; + + qCDebug(dcEnergyExperience()) << "Consumption:" << currentPowerConsumption << "Production:" << currentPowerProduction << "Acquisition:" << currentPowerAcquisition << "Battery:" << currentBatteryBalance; + if (currentPowerAcquisition != m_currentPowerAcquisition + || currentPowerConsumption != m_currentPowerConsumption + || currentPowerProduction != m_currentPowerProduction) { + m_currentPowerAcquisition = currentPowerAcquisition; + m_currentPowerProduction = currentPowerProduction; + m_currentPowerConsumption = currentPowerConsumption; + emit powerBalanceChanged(); + } +} + +void EnergyManagerImpl::logDumpConsumers() +{ + foreach (Thing *consumer, m_thingManager->configuredThings().filterByInterface("smartmeterconsumer")) { + qCDebug(dcEnergyExperience()).nospace().noquote() << consumer->name() << ": " << (consumer->stateValue("currentPower").toDouble() / 230) << "A (" << consumer->stateValue("currentPower").toDouble() << "W)"; + } +} + + diff --git a/plugin/energymanagerimpl.h b/plugin/energymanagerimpl.h new file mode 100644 index 0000000..1a70d90 --- /dev/null +++ b/plugin/energymanagerimpl.h @@ -0,0 +1,45 @@ +#ifndef ENERGYMANAGERIMPL_H +#define ENERGYMANAGERIMPL_H + +#include +#include +#include + +#include "integrations/thingmanager.h" + +#include "energymanager.h" + +class EnergyManagerImpl : public EnergyManager +{ + Q_OBJECT +public: + explicit EnergyManagerImpl(ThingManager *thingManager, QObject *parent = nullptr); + + Thing *rootMeter() const override; + EnergyError setRootMeter(const ThingId &rootMeterId) override; + + double currentPowerConsumption() const override; + double currentPowerProduction() const override; + double currentPowerAcquisition() const override; + +private: + void watchThing(Thing *thing); + void unwatchThing(const ThingId &thingId); + + void updatePowerBalance(); + +private slots: + void logDumpConsumers(); + +private: + ThingManager *m_thingManager = nullptr; + + Thing *m_rootMeter = nullptr; + + QTimer m_balanceUpdateTimer; + double m_currentPowerConsumption; + double m_currentPowerProduction; + double m_currentPowerAcquisition; +}; + +#endif // ENERGYMANAGERIMPL_H diff --git a/plugin/experiencepluginenergy.cpp b/plugin/experiencepluginenergy.cpp new file mode 100644 index 0000000..301281e --- /dev/null +++ b/plugin/experiencepluginenergy.cpp @@ -0,0 +1,119 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "experiencepluginenergy.h" + +#include "energymanagerimpl.h" +#include "energyjsonhandler.h" + +#include "energyplugin.h" + +#include +#include + +#include +#include +#include + +NYMEA_LOGGING_CATEGORY(dcEnergyExperience, "EnergyExperience") + +ExperiencePluginEnergy::ExperiencePluginEnergy() +{ + +} + +void ExperiencePluginEnergy::init() +{ + qCDebug(dcEnergyExperience()) << "Initializing energy experience"; + + m_energyManager = new EnergyManagerImpl(thingManager(), this); + jsonRpcServer()->registerExperienceHandler(new EnergyJsonHandler(m_energyManager, this), 1, 0); + + loadPlugins(); +} + +void ExperiencePluginEnergy::loadPlugins() +{ + foreach (const QString &path, pluginSearchDirs()) { + QDir dir(path); + qCDebug(dcEnergyExperience()) << "Loading energy plugins from:" << dir.absolutePath(); + foreach (const QString &entry, dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot)) { + QFileInfo fi(path + "/" + entry); + if (fi.isFile()) { + if (entry.startsWith("libnymea_energyplugin") && entry.endsWith(".so")) { + loadEnergyPlugin(path + "/" + entry); + } + } else if (fi.isDir()) { + if (QFileInfo::exists(path + "/" + entry + "/libnymea_energyplugin" + entry + ".so")) { + loadEnergyPlugin(path + "/" + entry + "/libnymea_energyplugin" + entry + ".so"); + } + } + } + } +} + +QStringList ExperiencePluginEnergy::pluginSearchDirs() const +{ + QStringList searchDirs; + QByteArray envPath = qgetenv("NYMEA_ENERGY_PLUGINS_PATH"); + if (!envPath.isEmpty()) { + searchDirs << QString(envPath).split(':'); + } + + foreach (QString libraryPath, QCoreApplication::libraryPaths()) { + searchDirs << libraryPath.replace("qt5", "nymea").replace("plugins", "energy"); + } + searchDirs << QCoreApplication::applicationDirPath() + "/../lib/nymea/energy"; + searchDirs << QCoreApplication::applicationDirPath() + "/../energy/"; + searchDirs << QCoreApplication::applicationDirPath() + "/../../../energy/"; + return searchDirs; +} + +void ExperiencePluginEnergy::loadEnergyPlugin(const QString &file) +{ + QPluginLoader loader; + loader.setFileName(file); + loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); + if (!loader.load()) { + qCWarning(dcExperiences()) << loader.errorString(); + return; + } + EnergyPlugin *plugin = qobject_cast(loader.instance()); + if (!plugin) { + qCWarning(dcEnergyExperience()) << "Could not get plugin instance of" << loader.fileName(); + loader.unload(); + return; + } + qCDebug(dcEnergyExperience()) << "Loaded energy plugin:" << loader.fileName(); + m_plugins.append(plugin); + plugin->setParent(this); + plugin->initPlugin(m_energyManager, thingManager(), jsonRpcServer()); + +} diff --git a/plugin/experiencepluginenergy.h b/plugin/experiencepluginenergy.h new file mode 100644 index 0000000..cd0d911 --- /dev/null +++ b/plugin/experiencepluginenergy.h @@ -0,0 +1,64 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef EXPERIENCEPLUGINENERGY_H +#define EXPERIENCEPLUGINENERGY_H + +#include + +#include "energyplugin.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(dcEnergyExperience) + +class EnergyManagerImpl; + +class ExperiencePluginEnergy: public ExperiencePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.nymea.ExperiencePlugin") + Q_INTERFACES(ExperiencePlugin) + +public: + ExperiencePluginEnergy(); + void init() override; + +private: + QStringList pluginSearchDirs() const; + void loadPlugins(); + void loadEnergyPlugin(const QString &file); + + QList m_plugins; + + EnergyManagerImpl *m_energyManager = nullptr; +}; + +#endif // EXPERIENCEPLUGINENERGY_H diff --git a/plugin/plugin.pro b/plugin/plugin.pro new file mode 100644 index 0000000..68acd49 --- /dev/null +++ b/plugin/plugin.pro @@ -0,0 +1,42 @@ +TEMPLATE = lib +TARGET = $$qtLibraryTarget(nymea_experiencepluginenergy) + +CONFIG += plugin link_pkgconfig c++11 +PKGCONFIG += nymea + +QT -= gui +QT += network + +include(../config.pri) + +INCLUDEPATH += $$top_srcdir/libnymea-energy +LIBS += -L$$top_builddir/libnymea-energy -lnymea-energy + +HEADERS += experiencepluginenergy.h \ + energyjsonhandler.h \ + energymanagerimpl.h + +SOURCES += experiencepluginenergy.cpp \ + energyjsonhandler.cpp \ + energymanagerimpl.cpp + +target.path = $$[QT_INSTALL_LIBS]/nymea/experiences/ +INSTALLS += target + +# Install translation files +TRANSLATIONS *= $$files($${_PRO_FILE_PWD_}/translations/*ts, true) +lupdate.depends = FORCE +lupdate.depends += qmake_all +lupdate.commands = lupdate -recursive -no-obsolete $${_PRO_FILE_PWD_}/experience.pro +QMAKE_EXTRA_TARGETS += lupdate + +# make lrelease to build .qm from .ts +lrelease.depends = FORCE +lrelease.commands += lrelease $$files($$_PRO_FILE_PWD_/translations/*.ts, true); +QMAKE_EXTRA_TARGETS += lrelease + +translations.depends += lrelease +translations.path = /usr/share/nymea/translations +translations.files = $$[QT_SOURCE_TREE]/translations/*.qm +INSTALLS += translations +