diff --git a/debian/control b/debian/control
index 0e628d8..eadbad7 100644
--- a/debian/control
+++ b/debian/control
@@ -45,6 +45,23 @@ Description: nymea.io plugin for iDM heat pumps
This package will install the nymea.io plugin for iDM heat pumps
+Package: nymea-plugin-energymeters
+Architecture: any
+Multi-Arch: same
+Section: libs
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+ nymea-plugins-modbus-translations
+Description: nymea.io plugin for Modbus based energy meters
+ The nymea daemon is a plugin based IoT (Internet of Things) server. The
+ server works like a translator for devices, things and services and
+ allows them to interact.
+ With the powerful rule engine you are able to connect any device available
+ in the system and create individual scenes and behaviors for your environment.
+ .
+ This package will install the nymea.io plugin for energy meters
+
+
Package: nymea-plugin-modbuscommander
Architecture: any
Section: libs
diff --git a/debian/nymea-plugin-energymeters.install.in b/debian/nymea-plugin-energymeters.install.in
new file mode 100644
index 0000000..2f08f1f
--- /dev/null
+++ b/debian/nymea-plugin-energymeters.install.in
@@ -0,0 +1 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginenergymeters.so
diff --git a/energymeters/README.md b/energymeters/README.md
new file mode 100644
index 0000000..b8b1fb8
--- /dev/null
+++ b/energymeters/README.md
@@ -0,0 +1,9 @@
+# B+G e-tech
+
+## Supported Things
+
+
+## Requirements
+
+
+## More
diff --git a/energymeters/bg-etechmodbusregister.h b/energymeters/bg-etechmodbusregister.h
new file mode 100644
index 0000000..368c5eb
--- /dev/null
+++ b/energymeters/bg-etechmodbusregister.h
@@ -0,0 +1,59 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 BGETECHMODBUSREGISTER_H
+#define BGETECHMODBUSREGISTER_H
+
+#include "registerdescriptor.h"
+
+class BgEtechModbusRegisers
+{
+
+private:
+ BgEtechModbusRegisers() {}
+ static void init () {
+ m_registerMap.insert(ModbusRegisterType::Voltage, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));
+ /* m_registerMap.insert(ModbusRegisterType::Current, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));
+ m_registerMap.insert(ModbusRegisterType::ActivePower, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));
+ m_registerMap.insert(ModbusRegisterType::Frequency, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));
+ m_registerMap.insert(ModbusRegisterType::PowerFactor, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));
+ m_registerMap.insert(ModbusRegisterType::EnergyConsumed, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));
+ m_registerMap.insert(ModbusRegisterType::EnergyProduced, ModbusRegisterDescriptor(1, 3, 2, "V", "float"));*/
+ }
+protected:
+ static QHash m_registerMap;
+public:
+ static QHash map()
+ { BgEtechModbusRegisers();
+ init();
+ return m_registerMap;}
+};
+
+#endif // BGETECHMODBUSREGISTER_H
diff --git a/energymeters/energymeter.cpp b/energymeters/energymeter.cpp
new file mode 100644
index 0000000..65c6f63
--- /dev/null
+++ b/energymeters/energymeter.cpp
@@ -0,0 +1,148 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 "energymeter.h"
+#include "hardware/modbus/modbusrtureply.h"
+
+EnergyMeter::EnergyMeter(ModbusRtuMaster *modbusMaster, int slaveAddress, const QHash &modbusRegisters, QObject *parent) :
+ QObject(parent),
+ m_modbusRtuMaster(modbusMaster),
+ m_slaveAddress(slaveAddress),
+ m_modbusRegisters(modbusRegisters)
+{
+
+}
+
+bool EnergyMeter::init()
+{
+ return true;
+}
+
+bool EnergyMeter::connected()
+{
+ return m_connected;
+}
+
+bool EnergyMeter::getVoltage()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::Voltage))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::Voltage);
+ getRegister(ModbusRegisterType::Voltage, descriptor);
+ return true;
+}
+
+bool EnergyMeter::getCurrent()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::Current))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::Current);
+ getRegister(ModbusRegisterType::Current, descriptor);
+ return true;
+}
+
+bool EnergyMeter::getFrequency()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::Frequency))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::Frequency);
+ getRegister(ModbusRegisterType::Frequency, descriptor);
+ return true;
+}
+
+bool EnergyMeter::getPowerFactor()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::PowerFactor))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::PowerFactor);
+ getRegister(ModbusRegisterType::PowerFactor, descriptor);
+ return true;
+}
+
+bool EnergyMeter::getActivePower()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::ActivePower))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::ActivePower);
+ getRegister(ModbusRegisterType::ActivePower, descriptor);
+ return true;
+}
+
+bool EnergyMeter::getEnergyProduced()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::EnergyProduced))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::EnergyProduced);
+ getRegister(ModbusRegisterType::EnergyProduced, descriptor);
+ return true;
+}
+
+bool EnergyMeter::getEnergyConsumed()
+{
+ if (!m_modbusRegisters.contains(ModbusRegisterType::EnergyConsumed))
+ return false;
+
+ ModbusRegisterDescriptor descriptor = m_modbusRegisters.value(ModbusRegisterType::EnergyConsumed);
+ getRegister(ModbusRegisterType::EnergyConsumed, descriptor);
+ return true;
+}
+
+void EnergyMeter::getRegister(ModbusRegisterType type, ModbusRegisterDescriptor descriptor)
+{
+
+ ModbusRtuReply *reply;
+ if (descriptor.functionCode() == 1){
+
+ } else if (descriptor.functionCode() == 2){
+
+ } else if (descriptor.functionCode() == 3){
+ reply = m_modbusRtuMaster->readHoldingRegister(m_slaveAddress, descriptor.address(), descriptor.length());
+ } else if (descriptor.functionCode() == 4){
+ }
+ connect(reply, &ModbusRtuReply::finished, reply, &ModbusRtuReply::deleteLater);
+ connect(reply, &ModbusRtuReply::finished, this, [reply, type, this] {
+ if (reply->error() != ModbusRtuReply::NoError) {
+ return;
+ }
+ double value = 0;
+ if (reply->result().length() == 2) {
+ value = static_cast(reply->result().at(0) << 16 | reply->result().at(1));
+ }
+
+ emit valueReceived(type, value);
+ });
+}
+
diff --git a/energymeters/energymeter.h b/energymeters/energymeter.h
new file mode 100644
index 0000000..d5e4fe9
--- /dev/null
+++ b/energymeters/energymeter.h
@@ -0,0 +1,81 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 ENERGYMETER_H
+#define ENERGYMETER_H
+
+#include
+
+#include "registerdescriptor.h"
+#include "hardware/modbus/modbusrtumaster.h"
+
+class EnergyMeter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit EnergyMeter(ModbusRtuMaster *modbusMaster, int slaveAddress, const QHash &modbusRegisters, QObject *parent = nullptr);
+ bool init();
+
+ bool connected();
+ bool getVoltage();
+ bool getCurrent();
+ bool getFrequency();
+ bool getPowerFactor();
+ bool getActivePower();
+ bool getEnergyProduced();
+ bool getEnergyConsumed();
+
+private:
+ bool m_connected = false;
+
+ ModbusRtuMaster *m_modbusRtuMaster = nullptr;
+ int m_slaveAddress;
+
+ QHash m_modbusRegisters;
+
+ void getRegister(ModbusRegisterType type, ModbusRegisterDescriptor descriptor);
+
+signals:
+ void connectedChanged(bool connected);
+ void valueReceived(ModbusRegisterType type, double value);
+
+ void voltageReceived(double voltage);
+ void currentReceived(double current);
+ void frequencyReceived(double freqeuncy);
+ void activePowerReceived(double power);
+ void powerFactorReceived(double powerFactor);
+ void producedEnergyReceived(double energy);
+ void consumedEnergyReceived(double energy);
+
+//private slot:
+// void onRegisterReceived();
+};
+
+#endif // ENERGYMETER_H
diff --git a/energymeters/energymeters.pro b/energymeters/energymeters.pro
new file mode 100644
index 0000000..17d879f
--- /dev/null
+++ b/energymeters/energymeters.pro
@@ -0,0 +1,16 @@
+include(../plugins.pri)
+
+QT += \
+ serialport \
+ serialbus \
+
+SOURCES += \
+ energymeter.cpp \
+ integrationpluginenergymeters.cpp
+
+HEADERS += \
+ energymeter.h \
+ integrationpluginenergymeters.h \
+ inepromodbusregister.h \
+ bg-etechmodbusregister.h \
+ registerdescriptor.h
diff --git a/energymeters/inepromodbusregister.h b/energymeters/inepromodbusregister.h
new file mode 100644
index 0000000..370a4ed
--- /dev/null
+++ b/energymeters/inepromodbusregister.h
@@ -0,0 +1,45 @@
+#ifndef INEPROMODBUSREGISTER_H
+#define INEPROMODBUSREGISTER_H
+
+enum InputRegisters {
+ Phase1ToNeutralVolts = 1,
+ Phase2ToNeutralVolts = 3,
+ Phase3ToNeutralVolts = 5,
+ Phase1Current = 7,
+ Phase2Current = 9,
+ Phase3Current = 11,
+ Phase1Power = 13,
+ Phase2Power = 15,
+ Phase3Power = 17,
+ Phase1ApparentPower = 19,
+ Phase2ApparentPower = 21,
+ Phase3ApparentPower = 23,
+ Phase1ReactivePower = 25,
+ Phase2ReactivePower = 27,
+ Phase3ReactivePower = 29,
+ Phase1PowerFactor = 31,
+ Phase2PowerFactor = 33,
+ Phase3PowerFactor = 35,
+ Phase1Angle = 37,
+ Phase2Angle = 39,
+ Phase3Angle = 41,
+ Frequency = 71,
+ ImportActiveEnergy = 73,
+ ExportActiveEnergy = 75,
+ ImportReactiveEnergy = 77,
+ ExportReactiveEnergy = 79,
+ TotalActiveEnergy = 343,
+ TotalReactiveEnergy = 345
+};
+
+enum HoldingRegisters {
+ RelayPulseWidth = 13,
+ NetworkParityStop = 19,
+ NetworkPortNode = 21,
+ NetworkBaudRate = 29,
+ Pulse1Output = 87,
+ Pulse1Constant = 63761,
+ MeasurementMode = 63776
+};
+
+#endif // BGETECHMODBUSREGISTER_H
diff --git a/energymeters/integrationpluginenergymeters.cpp b/energymeters/integrationpluginenergymeters.cpp
new file mode 100644
index 0000000..e7b5743
--- /dev/null
+++ b/energymeters/integrationpluginenergymeters.cpp
@@ -0,0 +1,291 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 "integrationpluginenergymeters.h"
+#include "plugininfo.h"
+
+#include "bg-etechmodbusregister.h"
+
+IntegrationPluginEnergyMeters::IntegrationPluginEnergyMeters()
+{
+ /*
+ * NOTE:
+ * To add an new device model, the integrationplugin json file must be extended with the new model and vendor.
+ * Then add the new states and params to the lists here, also add the modbus register configuration file
+*/
+ m_slaveIdParamTypeIds.insert(pro380ThingClassId, pro380ThingSlaveAddressParamTypeId);
+ m_slaveIdParamTypeIds.insert(sdm630ThingClassId, sdm630ThingSlaveAddressParamTypeId);
+
+ m_modbusUuidParamTypeIds.insert(pro380ThingClassId, pro380ThingModbusMasterUuidParamTypeId);
+ m_modbusUuidParamTypeIds.insert(sdm630ThingClassId, sdm630ThingModbusMasterUuidParamTypeId);
+
+ m_connectionStateTypeIds.insert(pro380ThingClassId, pro380ConnectedStateTypeId);
+ m_connectionStateTypeIds.insert(sdm630ThingClassId, sdm630ConnectedStateTypeId);
+
+ m_voltageStateTypeIds.insert(pro380ThingClassId, pro380VoltageStateTypeId);
+ m_voltageStateTypeIds.insert(sdm630ThingClassId, sdm630VoltageStateTypeId);
+
+ m_currentStateTypeIds.insert(pro380ThingClassId, pro380CurrentStateTypeId);
+ m_currentStateTypeIds.insert(sdm630ThingClassId, sdm630CurrentStateTypeId);
+
+ m_activePowerStateTypeIds.insert(pro380ThingClassId, pro380CurrentPowerEventTypeId);
+ m_activePowerStateTypeIds.insert(sdm630ThingClassId, sdm630CurrentPowerStateTypeId);
+
+ m_frequencyStateTypeIds.insert(pro380ThingClassId, pro380FrequencyStateTypeId);
+ m_frequencyStateTypeIds.insert(sdm630ThingClassId, sdm630FrequencyStateTypeId);
+
+ m_powerFactorStateTypeIds.insert(pro380ThingClassId, pro380PowerFactorStateTypeId);
+ m_powerFactorStateTypeIds.insert(sdm630ThingClassId, sdm630PowerFactorStateTypeId);
+
+ m_discoverySlaveAddressParamTypeIds.insert(pro380ThingClassId, pro380DiscoverySlaveAddressParamTypeId);
+ m_discoverySlaveAddressParamTypeIds.insert(sdm630ThingClassId, sdm630DiscoverySlaveAddressParamTypeId);
+
+ // Modbus RTU hardware resource
+ connect(hardwareManager()->modbusRtuResource(), &ModbusRtuHardwareResource::modbusRtuMasterRemoved, this, [=](const QUuid &modbusUuid){
+ qCDebug(dcEnergyMeters()) << "Modbus RTU master has been removed" << modbusUuid.toString();
+
+ // Check if there is any device using this resource
+ foreach (Thing *thing, m_modbusRtuMasters.keys()) {
+ if (m_modbusRtuMasters.value(thing)->modbusUuid() == modbusUuid) {
+ qCWarning(dcEnergyMeters()) << "Hardware resource removed for" << thing << ". The thing will not be functional any more until a new resource has been configured for it.";
+ m_modbusRtuMasters.remove(thing);
+ thing->setStateValue(m_connectionStateTypeIds[thing->thingClassId()], false);
+
+ // Set all child things disconnected
+ foreach (Thing *childThing, myThings()) {
+ if (childThing->parentId() == thing->id()) {
+ thing->setStateValue(m_connectionStateTypeIds[childThing->thingClassId()], false);
+ }
+ }
+ }
+ }
+ });
+}
+
+void IntegrationPluginEnergyMeters::discoverThings(ThingDiscoveryInfo *info)
+{
+ qCDebug(dcEnergyMeters()) << "Discover things";
+ QList thingDescriptors;
+
+ if (hardwareManager()->modbusRtuResource()->modbusRtuMasters().isEmpty()) {
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No Modbus RTU interface available."));
+ return;
+ }
+
+ if (m_connectionStateTypeIds.contains(info->thingClassId())) {
+ int slaveAddress = info->params().paramValue(m_discoverySlaveAddressParamTypeIds.value(info->thingClassId())).toInt();
+ if (slaveAddress > 254 || slaveAddress == 0) {
+ info->finish(Thing::ThingErrorInvalidParameter, tr("Modbus slave address must be between 1 and 254"));
+ return;
+ }
+ Q_FOREACH(ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
+ qCDebug(dcEnergyMeters()) << "Found RTU master resource" << modbusMaster << "connected" << modbusMaster->connected();
+ if (!modbusMaster->connected()) {
+ continue;
+ }
+ ThingDescriptor descriptor(info->thingClassId(), "Modbus interface "+modbusMaster->serialPort(), modbusMaster->modbusUuid().toString());
+ ParamList params;
+ params << Param(m_slaveIdParamTypeIds.value(info->thingClassId()), slaveAddress);
+ params << Param(m_modbusUuidParamTypeIds.value(info->thingClassId()), modbusMaster->modbusUuid());
+ descriptor.setParams(params);
+ info->addThingDescriptor(descriptor);
+ }
+ info->finish(Thing::ThingErrorNoError);
+ return;
+ } else {
+ Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
+ }
+}
+
+void IntegrationPluginEnergyMeters::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ qCDebug(dcEnergyMeters()) << "Setup thing" << thing->name();
+
+ if (m_connectionStateTypeIds.contains(thing->thingClassId())) {
+
+ if (m_energyMeters.contains(thing)) {
+ qCDebug(dcEnergyMeters()) << "Setup after rediscovery, cleaning up ...";
+ m_energyMeters.take(thing)->deleteLater();
+ }
+ int address = thing->paramValue(m_slaveIdParamTypeIds.value(thing->thingClassId())).toInt();
+ if (address > 254 || address == 0) {
+ qCWarning(dcEnergyMeters()) << "Setup failed, slave address is not valid" << address;
+ info->finish(Thing::ThingErrorSetupFailed, tr("Slave address not valid, must be between 1 and 254"));
+ return;
+ }
+ QUuid uuid = thing->paramValue(m_modbusUuidParamTypeIds.value(thing->thingClassId())).toString();
+ if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
+ qCWarning(dcEnergyMeters()) << "Setup failed, hardware manager not available";
+ info->finish(Thing::ThingErrorSetupFailed, tr("Modbus RTU resource not available."));
+ return;
+ }
+
+ EnergyMeter *meter = new EnergyMeter(hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, BgEtechModbusRegisers::map(), this);
+ connect(info, &ThingSetupInfo::aborted, meter, &EnergyMeter::deleteLater);
+ connect(meter, &EnergyMeter::consumedEnergyReceived, info, [this, info, meter] {
+ qCDebug(dcEnergyMeters()) << "Reply received, setup finished";
+ connect(meter, &EnergyMeter::connectedChanged, this, &IntegrationPluginEnergyMeters::onConnectionStateChanged);
+ connect(meter, &EnergyMeter::voltageReceived, this, &IntegrationPluginEnergyMeters::onVoltageReceived);
+ connect(meter, &EnergyMeter::currentReceived, this, &IntegrationPluginEnergyMeters::onCurrentReceived);
+ connect(meter, &EnergyMeter::activePowerReceived, this, &IntegrationPluginEnergyMeters::onActivePowerReceived);
+ connect(meter, &EnergyMeter::powerFactorReceived, this, &IntegrationPluginEnergyMeters::onPowerFactorReceived);
+ connect(meter, &EnergyMeter::frequencyReceived, this, &IntegrationPluginEnergyMeters::onActivePowerReceived);
+ connect(meter, &EnergyMeter::producedEnergyReceived, this, &IntegrationPluginEnergyMeters::onProducedEnergyReceived);
+ connect(meter, &EnergyMeter::consumedEnergyReceived, this, &IntegrationPluginEnergyMeters::onConsumedEnergyReceived);
+
+ m_energyMeters.insert(info->thing(), meter);
+ info->finish(Thing::ThingErrorNoError);
+ });
+ return;
+ } else {
+ Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
+ }
+}
+
+void IntegrationPluginEnergyMeters::postSetupThing(Thing *thing)
+{
+ qCDebug(dcEnergyMeters) << "Post setup thing" << thing->name();
+ if (m_connectionStateTypeIds.contains(thing->thingClassId())) {
+ thing->setStateValue(m_connectionStateTypeIds.value(thing->thingClassId()), true);
+ }
+
+ if (!m_updateTimer) {
+ qCDebug(dcEnergyMeters()) << "Creating update timer";
+ m_updateTimer = new QTimer(this);
+ m_updateTimer->start(configValue(energyMetersPluginUpdateIntervalParamTypeId).toInt());
+ connect(m_updateTimer, &QTimer::timeout, this, [this] {
+ foreach (EnergyMeter *meter, m_energyMeters) {
+ meter->getVoltage();
+ }
+ });
+ connect(this, &IntegrationPlugin::configValueChanged, [this] (const ParamTypeId ¶mTypeId, const QVariant value) {
+ if (m_updateTimer && (paramTypeId == energyMetersPluginUpdateIntervalParamTypeId)) {
+ qCDebug(dcEnergyMeters()) << "Updating update interval to" << value;
+ m_updateTimer->setInterval(value.toInt());
+ }
+ });
+ }
+}
+
+void IntegrationPluginEnergyMeters::thingRemoved(Thing *thing)
+{
+ qCDebug(dcEnergyMeters()) << "Thing removed" << thing->name();
+
+ if (m_energyMeters.contains(thing)) {
+ m_energyMeters.take(thing)->deleteLater();
+ }
+
+ if (myThings().isEmpty() && !m_updateTimer) {
+ qCDebug(dcEnergyMeters()) << "Deleting update timer";
+ m_updateTimer->deleteLater();
+ m_updateTimer = nullptr;
+ }
+}
+
+void IntegrationPluginEnergyMeters::onConnectionStateChanged(bool status)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_connectionStateTypeIds.value(thing->thingClassId()), status);
+}
+
+void IntegrationPluginEnergyMeters::onVoltageReceived(double voltage)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_voltageStateTypeIds.value(thing->thingClassId()), voltage);
+}
+
+void IntegrationPluginEnergyMeters::onCurrentReceived(double current)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_currentStateTypeIds.value(thing->thingClassId()), current);
+}
+
+void IntegrationPluginEnergyMeters::onActivePowerReceived(double power)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_activePowerStateTypeIds.value(thing->thingClassId()), power);
+}
+
+void IntegrationPluginEnergyMeters::onFrequencyReceived(double frequency)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_frequencyStateTypeIds.value(thing->thingClassId()), frequency);
+}
+
+void IntegrationPluginEnergyMeters::onPowerFactorReceived(double powerFactor)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_powerFactorStateTypeIds.value(thing->thingClassId()), powerFactor);
+}
+
+void IntegrationPluginEnergyMeters::onProducedEnergyReceived(double energy)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_totalEnergyProducedStateTypeIds.value(thing->thingClassId()), energy);
+}
+
+void IntegrationPluginEnergyMeters::onConsumedEnergyReceived(double energy)
+{
+ EnergyMeter *meter = static_cast(sender());
+ Thing *thing = m_energyMeters.key(meter);
+ if (!thing)
+ return;
+
+ thing->setStateValue(m_totalEnergyConsumedStateTypeIds.value(thing->thingClassId()), energy);
+}
diff --git a/energymeters/integrationpluginenergymeters.h b/energymeters/integrationpluginenergymeters.h
new file mode 100644
index 0000000..9dfbf10
--- /dev/null
+++ b/energymeters/integrationpluginenergymeters.h
@@ -0,0 +1,89 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 INTEGRATIONPLUGINENERGYMETERS_H
+#define INTEGRATIONPLUGINENERGYMETERS_H
+
+#include "integrations/integrationplugin.h"
+#include "hardware/modbus/modbusrtuhardwareresource.h"
+#include "plugintimer.h"
+
+#include "energymeter.h"
+
+#include
+#include
+
+class IntegrationPluginEnergyMeters : public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginenergymeters.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginEnergyMeters();
+
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ void thingRemoved(Thing *thing) override;
+
+private:
+ QTimer *m_updateTimer = nullptr;
+ QHash m_connectionStateTypeIds;
+ QHash m_voltageStateTypeIds;
+ QHash m_currentStateTypeIds;
+ QHash m_activePowerStateTypeIds;
+ QHash m_frequencyStateTypeIds;
+ QHash m_powerFactorStateTypeIds;
+ QHash m_totalEnergyConsumedStateTypeIds;
+ QHash m_totalEnergyProducedStateTypeIds;
+
+ QHash m_discoverySlaveAddressParamTypeIds;
+ QHash m_slaveIdParamTypeIds;
+ QHash m_modbusUuidParamTypeIds;
+
+ QHash m_energyMeters;
+ QHash m_modbusRtuMasters;
+ PluginTimer *m_pluginTimer = nullptr;
+ QHash m_asyncActions;
+
+private slots:
+ void onConnectionStateChanged(bool status);
+ void onVoltageReceived(double voltage);
+ void onCurrentReceived(double current);
+ void onActivePowerReceived(double power);
+ void onFrequencyReceived(double frequency);
+ void onPowerFactorReceived(double powerFactor);
+ void onProducedEnergyReceived(double energy);
+ void onConsumedEnergyReceived(double energy);
+};
+
+#endif // INTEGRATIONPLUGINENERGYMETERS_H
diff --git a/energymeters/integrationpluginenergymeters.json b/energymeters/integrationpluginenergymeters.json
new file mode 100644
index 0000000..56016eb
--- /dev/null
+++ b/energymeters/integrationpluginenergymeters.json
@@ -0,0 +1,244 @@
+{
+ "name": "EnergyMeters",
+ "displayName": "EnergyMeters",
+ "id": "56e95111-fb6b-4f63-9a0a-a5ee001e89ed",
+ "paramTypes":[
+ {
+ "id": "eaa84c3c-06b8-4642-a40b-c2efbe6aae66",
+ "name": "updateInterval",
+ "displayName": "Update interval",
+ "type": "uint",
+ "unit": "MilliSeconds",
+ "defaultValue": 300,
+ "minValue": 200
+ }
+ ],
+ "vendors": [
+ {
+ "name": "ineproMetering",
+ "displayName": "inepro Metering",
+ "id": "64f4df0f-18ce-409c-bf32-84a086c691ca",
+ "thingClasses": [
+ {
+ "name": "pro380",
+ "displayName": "PRO380-Mod",
+ "id": "d7c6440b-54f9-4cc0-a96b-9bb7304b3e77",
+ "createMethods": ["discovery"],
+ "interfaces": ["extendedsmartmeterconsumer"],
+ "discoveryParamTypes": [
+ {
+ "id": "a29f37f6-b344-4628-8ab4-8f4c18fada4a",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "c75b2c31-6ec3-49ab-8c8f-5231d0a7e941",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "int",
+ "defaultValue": 1
+ },
+ {
+ "id": "6cdbec8c-21b9-42dc-b1ab-8901ac609482",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "7f9bc504-0882-4b86-83b1-42fa345acfd9",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "04dba21a-7447-46b9-b9ae-095e5769e511",
+ "name": "voltage",
+ "displayName": "Voltage",
+ "displayNameEvent": "Voltage changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "1e077a3b-2dab-4ec4-ae96-ab49a564fe31",
+ "name": "current",
+ "displayName": "Current",
+ "displayNameEvent": "Current changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "464eff60-11c2-46b7-98f5-1aa8172e5a2d",
+ "name": "currentPower",
+ "displayName": "Active power",
+ "displayNameEvent": "Active power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "cdb34487-3d9b-492a-8c33-802f32a2e90e",
+ "name": "powerFactor",
+ "displayName": "Power factor",
+ "displayNameEvent": "Power factor changed",
+ "type": "double",
+ "defaultValue": 0.00
+ },
+ {
+ "id": "bb6fd00c-3bbb-4977-bb8a-96787bb6f5c5",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0.00
+ },
+ {
+ "id": "f18fd596-b47f-44be-a0f0-6ca44369ebf5",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0.00
+ },
+ {
+ "id": "112911c9-14e0-4c83-ac92-f2ceb3bdecdf",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0.00
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "bgetech",
+ "displayName": "B+G e-tech",
+ "id": "215035fe-95e8-43d8-a52e-0a31b787d902",
+ "thingClasses": [
+ {
+ "name": "sdm630",
+ "displayName": "SDM630Modbus",
+ "id": "f37597bb-35fe-48f2-9617-343dd54c0903",
+ "createMethods": ["discovery"],
+ "interfaces": ["extendedsmartmeterconsumer"],
+ "discoveryParamTypes": [
+ {
+ "id": "6ab43559-53ec-47ba-b8a0-8d3b7f8d90c2",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "ac77ea98-b006-486e-a3e8-b30a483f26c1",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "int",
+ "defaultValue": 1
+ },
+ {
+ "id": "d90e9292-d03c-4f2a-957e-5d965018c9c9",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "8050bd0b-1dad-4a7e-b632-c71ead3c9f8b",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "4636ec5c-fcb9-45b7-ad68-2818cb615ce1",
+ "name": "voltage",
+ "displayName": "Voltage",
+ "displayNameEvent": "Voltage changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "96bc65ce-5bde-4a69-9ebf-711d65c6501c",
+ "name": "current",
+ "displayName": "Current",
+ "displayNameEvent": "Current changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "c824e97b-a6d1-4030-9d7a-00af6fb8e1c3",
+ "name": "currentPower",
+ "displayName": "Active power",
+ "displayNameEvent": "Active power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "31b9032f-f994-472b-94bd-44f9fb094801",
+ "name": "powerFactor",
+ "displayName": "Power factor",
+ "displayNameEvent": "Power factor changed",
+ "type": "double",
+ "defaultValue": 0.00
+ },
+ {
+ "id": "ab24f26c-dc15-4ec3-8d76-06a48285440b",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0.00
+ },
+ {
+ "id": "98d858a8-22e8-4262-b5c7-25bb027942ad",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0.00
+ },
+ {
+ "id": "e469b3ff-a4c2-42da-af35-ccafaef214af",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0.00
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/energymeters/meta.json b/energymeters/meta.json
new file mode 100644
index 0000000..e69de29
diff --git a/energymeters/registerdescriptor.h b/energymeters/registerdescriptor.h
new file mode 100644
index 0000000..5e53807
--- /dev/null
+++ b/energymeters/registerdescriptor.h
@@ -0,0 +1,83 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 REGISTERDESCRIPTOR_H
+#define REGISTERDESCRIPTOR_H
+
+#include
+#include
+
+enum ModbusRegisterType {
+ Voltage,
+ Current,
+ ActivePower,
+ Frequency,
+ PowerFactor,
+ EnergyProduced,
+ EnergyConsumed
+};
+
+class ModbusRegisterDescriptor
+{
+
+public:
+ ModbusRegisterDescriptor() {}
+ ModbusRegisterDescriptor(int address, int functionCode, int length, QString unit, QString dataType) :
+ m_address(address),
+ m_functionCode(functionCode),
+ m_length(length),
+ m_unit(unit),
+ m_dataType(dataType)
+ {}
+
+ int address() const
+ { return m_address;}
+
+ int functionCode() const
+ { return m_functionCode;}
+
+ int length() const
+ { return m_length;}
+
+ QString unit() const
+ { return m_unit;}
+
+ QString dataType() const
+ { return m_dataType;}
+
+private:
+ int m_address;
+ int m_functionCode;
+ int m_length;
+ QString m_unit;
+ QString m_dataType;
+};
+
+#endif // REGISTERDESCRIPTOR_H
diff --git a/nymea-plugins-modbus.pro b/nymea-plugins-modbus.pro
index 207d8bc..2515df5 100644
--- a/nymea-plugins-modbus.pro
+++ b/nymea-plugins-modbus.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
PLUGIN_DIRS = \
drexelundweiss \
+ energymeters \
modbuscommander \
mypv \
sunspec \