diff --git a/alphainnotec/README.md b/alphainnotec/README.md
new file mode 100644
index 0000000..9cc8557
--- /dev/null
+++ b/alphainnotec/README.md
@@ -0,0 +1,23 @@
+# alpha innotec
+
+Connect nymea to alpha innotec heat pumps.
+
+In order to use the modbus interface, it is important to enable modbus on the heatpump using the Windows tools from Alpha Innotec.
+The instructions for that can be found in the user manual of the heat pump.
+
+Please also make sure all values are configured as readable and writable for modbus, otherwise the heapump can only be monitored, but
+not optimized for smart heating.
+
+## Supported Things
+
+* alpha connect
+
+## Requirements
+
+* The package 'nymea-plugin-alphainnotec' must be installed
+* Both devices must be in the same local area network.
+* Modbus enabled and all values are readable and writable.
+
+## More
+
+https://www.alpha-innotec.de
diff --git a/alphainnotec/alpha-innotec.png b/alphainnotec/alpha-innotec.png
new file mode 100644
index 0000000..3fd2fcc
Binary files /dev/null and b/alphainnotec/alpha-innotec.png differ
diff --git a/alphainnotec/alphaconnectmodbustcpconnection.cpp b/alphainnotec/alphaconnectmodbustcpconnection.cpp
new file mode 100644
index 0000000..9f6dd9b
--- /dev/null
+++ b/alphainnotec/alphaconnectmodbustcpconnection.cpp
@@ -0,0 +1,1273 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2021, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This fileDescriptor 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 "alphaconnectmodbustcpconnection.h"
+#include "loggingcategories.h"
+
+NYMEA_LOGGING_CATEGORY(dcAlphaConnectModbusTcpConnection, "AlphaConnectModbusTcpConnection")
+
+AlphaConnectModbusTcpConnection::AlphaConnectModbusTcpConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :
+ ModbusTCPMaster(hostAddress, port, parent),
+ m_slaveId(slaveId)
+{
+
+}
+
+float AlphaConnectModbusTcpConnection::flowTemperature() const
+{
+ return m_flowTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::returnTemperature() const
+{
+ return m_returnTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::externalReturnTemperature() const
+{
+ return m_externalReturnTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::hotWaterTemperature() const
+{
+ return m_hotWaterTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::hotGasTemperature() const
+{
+ return m_hotGasTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::heatSourceInletTemperature() const
+{
+ return m_heatSourceInletTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::heatSourceOutletTemperature() const
+{
+ return m_heatSourceOutletTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::roomTemperature1() const
+{
+ return m_roomTemperature1;
+}
+
+float AlphaConnectModbusTcpConnection::roomTemperature2() const
+{
+ return m_roomTemperature2;
+}
+
+float AlphaConnectModbusTcpConnection::roomTemperature3() const
+{
+ return m_roomTemperature3;
+}
+
+float AlphaConnectModbusTcpConnection::solarCollectorTemperature() const
+{
+ return m_solarCollectorTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::solarStorageTankTemperature() const
+{
+ return m_solarStorageTankTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::externalEnergySourceTemperature() const
+{
+ return m_externalEnergySourceTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::supplyAirTemperature() const
+{
+ return m_supplyAirTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::externalAirTemperature() const
+{
+ return m_externalAirTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::rbeRoomActualTemperature() const
+{
+ return m_rbeRoomActualTemperature;
+}
+
+float AlphaConnectModbusTcpConnection::rbeRoomSetpointTemperature() const
+{
+ return m_rbeRoomSetpointTemperature;
+}
+
+quint16 AlphaConnectModbusTcpConnection::heatingPumpOperatingHours() const
+{
+ return m_heatingPumpOperatingHours;
+}
+
+AlphaConnectModbusTcpConnection::SystemStatus AlphaConnectModbusTcpConnection::systemStatus() const
+{
+ return m_systemStatus;
+}
+
+float AlphaConnectModbusTcpConnection::heatingEnergy() const
+{
+ return m_heatingEnergy;
+}
+
+float AlphaConnectModbusTcpConnection::waterHeatEnergy() const
+{
+ return m_waterHeatEnergy;
+}
+
+float AlphaConnectModbusTcpConnection::totalHeatEnergy() const
+{
+ return m_totalHeatEnergy;
+}
+
+float AlphaConnectModbusTcpConnection::outdoorTemperature() const
+{
+ return m_outdoorTemperature;
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::setOutdoorTemperature(float outdoorTemperature)
+{
+ QVector values = ModbusDataUtils::convertFromUInt16(static_cast(outdoorTemperature * 1.0 / pow(10, -1)));
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Outdoor temperature\" register:" << 0 << "size:" << 1 << values;
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 0, values.count());
+ request.setValues(values);
+ return sendWriteRequest(request, m_slaveId);
+}
+
+float AlphaConnectModbusTcpConnection::returnSetpointTemperature() const
+{
+ return m_returnSetpointTemperature;
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::setReturnSetpointTemperature(float returnSetpointTemperature)
+{
+ QVector values = ModbusDataUtils::convertFromUInt16(static_cast(returnSetpointTemperature * 1.0 / pow(10, -1)));
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Return setpoint temperature\" register:" << 1 << "size:" << 1 << values;
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 1, values.count());
+ request.setValues(values);
+ return sendWriteRequest(request, m_slaveId);
+}
+
+float AlphaConnectModbusTcpConnection::hotWaterSetpointTemperature() const
+{
+ return m_hotWaterSetpointTemperature;
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::setHotWaterSetpointTemperature(float hotWaterSetpointTemperature)
+{
+ QVector values = ModbusDataUtils::convertFromUInt16(static_cast(hotWaterSetpointTemperature * 1.0 / pow(10, -1)));
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Hot water setpoint temperature\" register:" << 5 << "size:" << 1 << values;
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 5, values.count());
+ request.setValues(values);
+ return sendWriteRequest(request, m_slaveId);
+}
+
+AlphaConnectModbusTcpConnection::SmartGridState AlphaConnectModbusTcpConnection::smartGrid() const
+{
+ return m_smartGrid;
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::setSmartGrid(SmartGridState smartGrid)
+{
+ QVector values = ModbusDataUtils::convertFromUInt16(static_cast(smartGrid));
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Smart grid control\" register:" << 14 << "size:" << 1 << values;
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 14, values.count());
+ request.setValues(values);
+ return sendWriteRequest(request, m_slaveId);
+}
+
+void AlphaConnectModbusTcpConnection::initialize()
+{
+ // No init registers defined. Nothing to be done and we are finished.
+ emit initializationFinished();
+}
+
+void AlphaConnectModbusTcpConnection::update()
+{
+ updateFlowTemperature();
+ updateReturnTemperature();
+ updateExternalReturnTemperature();
+ updateHotWaterTemperature();
+ updateHotGasTemperature();
+ updateHeatSourceInletTemperature();
+ updateHeatSourceOutletTemperature();
+ updateRoomTemperature1();
+ updateRoomTemperature2();
+ updateRoomTemperature3();
+ updateSolarCollectorTemperature();
+ updateSolarStorageTankTemperature();
+ updateExternalEnergySourceTemperature();
+ updateSupplyAirTemperature();
+ updateExternalAirTemperature();
+ updateRbeRoomActualTemperature();
+ updateRbeRoomSetpointTemperature();
+ updateHeatingPumpOperatingHours();
+ updateSystemStatus();
+ updateHeatingEnergy();
+ updateWaterHeatEnergy();
+ updateTotalHeatEnergy();
+ updateOutdoorTemperature();
+ updateReturnSetpointTemperature();
+ updateHotWaterSetpointTemperature();
+ updateSmartGrid();
+}
+
+void AlphaConnectModbusTcpConnection::updateFlowTemperature()
+{
+ // Update registers from Flow
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Flow\" register:" << 1 << "size:" << 1;
+ QModbusReply *reply = readFlowTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Flow\" register" << 1 << "size:" << 1 << unit.values();
+ float receivedFlowTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_flowTemperature != receivedFlowTemperature) {
+ m_flowTemperature = receivedFlowTemperature;
+ emit flowTemperatureChanged(m_flowTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Flow\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Flow\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateReturnTemperature()
+{
+ // Update registers from Return
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Return\" register:" << 2 << "size:" << 1;
+ QModbusReply *reply = readReturnTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Return\" register" << 2 << "size:" << 1 << unit.values();
+ float receivedReturnTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_returnTemperature != receivedReturnTemperature) {
+ m_returnTemperature = receivedReturnTemperature;
+ emit returnTemperatureChanged(m_returnTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Return\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Return\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateExternalReturnTemperature()
+{
+ // Update registers from External return
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"External return\" register:" << 3 << "size:" << 1;
+ QModbusReply *reply = readExternalReturnTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"External return\" register" << 3 << "size:" << 1 << unit.values();
+ float receivedExternalReturnTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_externalReturnTemperature != receivedExternalReturnTemperature) {
+ m_externalReturnTemperature = receivedExternalReturnTemperature;
+ emit externalReturnTemperatureChanged(m_externalReturnTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"External return\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"External return\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHotWaterTemperature()
+{
+ // Update registers from Hot water temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Hot water temperature\" register:" << 4 << "size:" << 1;
+ QModbusReply *reply = readHotWaterTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Hot water temperature\" register" << 4 << "size:" << 1 << unit.values();
+ float receivedHotWaterTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_hotWaterTemperature != receivedHotWaterTemperature) {
+ m_hotWaterTemperature = receivedHotWaterTemperature;
+ emit hotWaterTemperatureChanged(m_hotWaterTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Hot water temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Hot water temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHotGasTemperature()
+{
+ // Update registers from Hot gas temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Hot gas temperature\" register:" << 8 << "size:" << 1;
+ QModbusReply *reply = readHotGasTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Hot gas temperature\" register" << 8 << "size:" << 1 << unit.values();
+ float receivedHotGasTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_hotGasTemperature != receivedHotGasTemperature) {
+ m_hotGasTemperature = receivedHotGasTemperature;
+ emit hotGasTemperatureChanged(m_hotGasTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Hot gas temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Hot gas temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHeatSourceInletTemperature()
+{
+ // Update registers from Heat source inlet temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heat source inlet temperature\" register:" << 9 << "size:" << 1;
+ QModbusReply *reply = readHeatSourceInletTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heat source inlet temperature\" register" << 9 << "size:" << 1 << unit.values();
+ float receivedHeatSourceInletTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_heatSourceInletTemperature != receivedHeatSourceInletTemperature) {
+ m_heatSourceInletTemperature = receivedHeatSourceInletTemperature;
+ emit heatSourceInletTemperatureChanged(m_heatSourceInletTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heat source inlet temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heat source inlet temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHeatSourceOutletTemperature()
+{
+ // Update registers from Heat source outlet temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heat source outlet temperature\" register:" << 10 << "size:" << 1;
+ QModbusReply *reply = readHeatSourceOutletTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heat source outlet temperature\" register" << 10 << "size:" << 1 << unit.values();
+ float receivedHeatSourceOutletTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_heatSourceOutletTemperature != receivedHeatSourceOutletTemperature) {
+ m_heatSourceOutletTemperature = receivedHeatSourceOutletTemperature;
+ emit heatSourceOutletTemperatureChanged(m_heatSourceOutletTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heat source outlet temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heat source outlet temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateRoomTemperature1()
+{
+ // Update registers from Room remote adjuster 1 temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Room remote adjuster 1 temperature\" register:" << 11 << "size:" << 1;
+ QModbusReply *reply = readRoomTemperature1();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Room remote adjuster 1 temperature\" register" << 11 << "size:" << 1 << unit.values();
+ float receivedRoomTemperature1 = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_roomTemperature1 != receivedRoomTemperature1) {
+ m_roomTemperature1 = receivedRoomTemperature1;
+ emit roomTemperature1Changed(m_roomTemperature1);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Room remote adjuster 1 temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Room remote adjuster 1 temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateRoomTemperature2()
+{
+ // Update registers from Room remote adjuster 2 temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Room remote adjuster 2 temperature\" register:" << 12 << "size:" << 1;
+ QModbusReply *reply = readRoomTemperature2();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Room remote adjuster 2 temperature\" register" << 12 << "size:" << 1 << unit.values();
+ float receivedRoomTemperature2 = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_roomTemperature2 != receivedRoomTemperature2) {
+ m_roomTemperature2 = receivedRoomTemperature2;
+ emit roomTemperature2Changed(m_roomTemperature2);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Room remote adjuster 2 temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Room remote adjuster 2 temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateRoomTemperature3()
+{
+ // Update registers from Room remote adjuster 3 temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Room remote adjuster 3 temperature\" register:" << 13 << "size:" << 1;
+ QModbusReply *reply = readRoomTemperature3();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Room remote adjuster 3 temperature\" register" << 13 << "size:" << 1 << unit.values();
+ float receivedRoomTemperature3 = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_roomTemperature3 != receivedRoomTemperature3) {
+ m_roomTemperature3 = receivedRoomTemperature3;
+ emit roomTemperature3Changed(m_roomTemperature3);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Room remote adjuster 3 temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Room remote adjuster 3 temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateSolarCollectorTemperature()
+{
+ // Update registers from Solar collector temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Solar collector temperature\" register:" << 14 << "size:" << 1;
+ QModbusReply *reply = readSolarCollectorTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Solar collector temperature\" register" << 14 << "size:" << 1 << unit.values();
+ float receivedSolarCollectorTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_solarCollectorTemperature != receivedSolarCollectorTemperature) {
+ m_solarCollectorTemperature = receivedSolarCollectorTemperature;
+ emit solarCollectorTemperatureChanged(m_solarCollectorTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Solar collector temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Solar collector temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateSolarStorageTankTemperature()
+{
+ // Update registers from Solar storage tank temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Solar storage tank temperature\" register:" << 15 << "size:" << 1;
+ QModbusReply *reply = readSolarStorageTankTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Solar storage tank temperature\" register" << 15 << "size:" << 1 << unit.values();
+ float receivedSolarStorageTankTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_solarStorageTankTemperature != receivedSolarStorageTankTemperature) {
+ m_solarStorageTankTemperature = receivedSolarStorageTankTemperature;
+ emit solarStorageTankTemperatureChanged(m_solarStorageTankTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Solar storage tank temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Solar storage tank temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateExternalEnergySourceTemperature()
+{
+ // Update registers from External energy source temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"External energy source temperature\" register:" << 16 << "size:" << 1;
+ QModbusReply *reply = readExternalEnergySourceTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"External energy source temperature\" register" << 16 << "size:" << 1 << unit.values();
+ float receivedExternalEnergySourceTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_externalEnergySourceTemperature != receivedExternalEnergySourceTemperature) {
+ m_externalEnergySourceTemperature = receivedExternalEnergySourceTemperature;
+ emit externalEnergySourceTemperatureChanged(m_externalEnergySourceTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"External energy source temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"External energy source temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateSupplyAirTemperature()
+{
+ // Update registers from Supply air temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Supply air temperature\" register:" << 17 << "size:" << 1;
+ QModbusReply *reply = readSupplyAirTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Supply air temperature\" register" << 17 << "size:" << 1 << unit.values();
+ float receivedSupplyAirTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_supplyAirTemperature != receivedSupplyAirTemperature) {
+ m_supplyAirTemperature = receivedSupplyAirTemperature;
+ emit supplyAirTemperatureChanged(m_supplyAirTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Supply air temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Supply air temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateExternalAirTemperature()
+{
+ // Update registers from External air temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"External air temperature\" register:" << 18 << "size:" << 1;
+ QModbusReply *reply = readExternalAirTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"External air temperature\" register" << 18 << "size:" << 1 << unit.values();
+ float receivedExternalAirTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_externalAirTemperature != receivedExternalAirTemperature) {
+ m_externalAirTemperature = receivedExternalAirTemperature;
+ emit externalAirTemperatureChanged(m_externalAirTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"External air temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"External air temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateRbeRoomActualTemperature()
+{
+ // Update registers from RBE actual room temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"RBE actual room temperature\" register:" << 24 << "size:" << 1;
+ QModbusReply *reply = readRbeRoomActualTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"RBE actual room temperature\" register" << 24 << "size:" << 1 << unit.values();
+ float receivedRbeRoomActualTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_rbeRoomActualTemperature != receivedRbeRoomActualTemperature) {
+ m_rbeRoomActualTemperature = receivedRbeRoomActualTemperature;
+ emit rbeRoomActualTemperatureChanged(m_rbeRoomActualTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"RBE actual room temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"RBE actual room temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateRbeRoomSetpointTemperature()
+{
+ // Update registers from RBE room temperature setpoint
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"RBE room temperature setpoint\" register:" << 24 << "size:" << 1;
+ QModbusReply *reply = readRbeRoomSetpointTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"RBE room temperature setpoint\" register" << 24 << "size:" << 1 << unit.values();
+ float receivedRbeRoomSetpointTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_rbeRoomSetpointTemperature != receivedRbeRoomSetpointTemperature) {
+ m_rbeRoomSetpointTemperature = receivedRbeRoomSetpointTemperature;
+ emit rbeRoomSetpointTemperatureChanged(m_rbeRoomSetpointTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"RBE room temperature setpoint\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"RBE room temperature setpoint\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHeatingPumpOperatingHours()
+{
+ // Update registers from Heating pump operating hours
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heating pump operating hours\" register:" << 33 << "size:" << 1;
+ QModbusReply *reply = readHeatingPumpOperatingHours();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heating pump operating hours\" register" << 33 << "size:" << 1 << unit.values();
+ quint16 receivedHeatingPumpOperatingHours = ModbusDataUtils::convertToUInt16(unit.values());
+ if (m_heatingPumpOperatingHours != receivedHeatingPumpOperatingHours) {
+ m_heatingPumpOperatingHours = receivedHeatingPumpOperatingHours;
+ emit heatingPumpOperatingHoursChanged(m_heatingPumpOperatingHours);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heating pump operating hours\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heating pump operating hours\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateSystemStatus()
+{
+ // Update registers from System status
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"System status\" register:" << 37 << "size:" << 1;
+ QModbusReply *reply = readSystemStatus();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"System status\" register" << 37 << "size:" << 1 << unit.values();
+ SystemStatus receivedSystemStatus = static_cast(ModbusDataUtils::convertToUInt16(unit.values()));
+ if (m_systemStatus != receivedSystemStatus) {
+ m_systemStatus = receivedSystemStatus;
+ emit systemStatusChanged(m_systemStatus);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"System status\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"System status\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHeatingEnergy()
+{
+ // Update registers from Heating energy
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heating energy\" register:" << 38 << "size:" << 2;
+ QModbusReply *reply = readHeatingEnergy();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heating energy\" register" << 38 << "size:" << 2 << unit.values();
+ float receivedHeatingEnergy = ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -1);
+ if (m_heatingEnergy != receivedHeatingEnergy) {
+ m_heatingEnergy = receivedHeatingEnergy;
+ emit heatingEnergyChanged(m_heatingEnergy);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heating energy\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heating energy\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateWaterHeatEnergy()
+{
+ // Update registers from Water heat energy
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Water heat energy\" register:" << 40 << "size:" << 2;
+ QModbusReply *reply = readWaterHeatEnergy();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Water heat energy\" register" << 40 << "size:" << 2 << unit.values();
+ float receivedWaterHeatEnergy = ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -1);
+ if (m_waterHeatEnergy != receivedWaterHeatEnergy) {
+ m_waterHeatEnergy = receivedWaterHeatEnergy;
+ emit waterHeatEnergyChanged(m_waterHeatEnergy);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Water heat energy\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Water heat energy\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateTotalHeatEnergy()
+{
+ // Update registers from Total energy
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Total energy\" register:" << 44 << "size:" << 2;
+ QModbusReply *reply = readTotalHeatEnergy();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Total energy\" register" << 44 << "size:" << 2 << unit.values();
+ float receivedTotalHeatEnergy = ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -1);
+ if (m_totalHeatEnergy != receivedTotalHeatEnergy) {
+ m_totalHeatEnergy = receivedTotalHeatEnergy;
+ emit totalHeatEnergyChanged(m_totalHeatEnergy);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Total energy\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Total energy\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateOutdoorTemperature()
+{
+ // Update registers from Outdoor temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Outdoor temperature\" register:" << 0 << "size:" << 1;
+ QModbusReply *reply = readOutdoorTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Outdoor temperature\" register" << 0 << "size:" << 1 << unit.values();
+ float receivedOutdoorTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_outdoorTemperature != receivedOutdoorTemperature) {
+ m_outdoorTemperature = receivedOutdoorTemperature;
+ emit outdoorTemperatureChanged(m_outdoorTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Outdoor temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Outdoor temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateReturnSetpointTemperature()
+{
+ // Update registers from Return setpoint temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Return setpoint temperature\" register:" << 1 << "size:" << 1;
+ QModbusReply *reply = readReturnSetpointTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Return setpoint temperature\" register" << 1 << "size:" << 1 << unit.values();
+ float receivedReturnSetpointTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_returnSetpointTemperature != receivedReturnSetpointTemperature) {
+ m_returnSetpointTemperature = receivedReturnSetpointTemperature;
+ emit returnSetpointTemperatureChanged(m_returnSetpointTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Return setpoint temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Return setpoint temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateHotWaterSetpointTemperature()
+{
+ // Update registers from Hot water setpoint temperature
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Hot water setpoint temperature\" register:" << 5 << "size:" << 1;
+ QModbusReply *reply = readHotWaterSetpointTemperature();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Hot water setpoint temperature\" register" << 5 << "size:" << 1 << unit.values();
+ float receivedHotWaterSetpointTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1);
+ if (m_hotWaterSetpointTemperature != receivedHotWaterSetpointTemperature) {
+ m_hotWaterSetpointTemperature = receivedHotWaterSetpointTemperature;
+ emit hotWaterSetpointTemperatureChanged(m_hotWaterSetpointTemperature);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Hot water setpoint temperature\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Hot water setpoint temperature\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+void AlphaConnectModbusTcpConnection::updateSmartGrid()
+{
+ // Update registers from Smart grid control
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Smart grid control\" register:" << 14 << "size:" << 1;
+ QModbusReply *reply = readSmartGrid();
+ if (reply) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, this, [this, reply](){
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Smart grid control\" register" << 14 << "size:" << 1 << unit.values();
+ SmartGridState receivedSmartGrid = static_cast(ModbusDataUtils::convertToUInt16(unit.values()));
+ if (m_smartGrid != receivedSmartGrid) {
+ m_smartGrid = receivedSmartGrid;
+ emit smartGridChanged(m_smartGrid);
+ }
+ }
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Smart grid control\" registers from" << hostAddress().toString() << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else {
+ delete reply; // Broadcast reply returns immediatly
+ }
+ } else {
+ qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Smart grid control\" registers from" << hostAddress().toString() << errorString();
+ }
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readFlowTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 1, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readReturnTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 2, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readExternalReturnTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 3, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHotWaterTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 4, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHotGasTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 8, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHeatSourceInletTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 9, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHeatSourceOutletTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 10, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readRoomTemperature1()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 11, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readRoomTemperature2()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 12, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readRoomTemperature3()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 13, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readSolarCollectorTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 14, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readSolarStorageTankTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 15, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readExternalEnergySourceTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 16, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readSupplyAirTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 17, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readExternalAirTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 18, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readRbeRoomActualTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 24, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readRbeRoomSetpointTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 24, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHeatingPumpOperatingHours()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 33, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readSystemStatus()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 37, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHeatingEnergy()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 38, 2);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readWaterHeatEnergy()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 40, 2);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readTotalHeatEnergy()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 44, 2);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readOutdoorTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 0, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readReturnSetpointTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 1, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readHotWaterSetpointTemperature()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 5, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+QModbusReply *AlphaConnectModbusTcpConnection::readSmartGrid()
+{
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 14, 1);
+ return sendReadRequest(request, m_slaveId);
+}
+
+void AlphaConnectModbusTcpConnection::verifyInitFinished()
+{
+ if (m_pendingInitReplies.isEmpty()) {
+ qCDebug(dcAlphaConnectModbusTcpConnection()) << "Initialization finished of AlphaConnectModbusTcpConnection" << hostAddress().toString();
+ emit initializationFinished();
+ }
+}
+
+QDebug operator<<(QDebug debug, AlphaConnectModbusTcpConnection *alphaConnectModbusTcpConnection)
+{
+ debug.nospace().noquote() << "AlphaConnectModbusTcpConnection(" << alphaConnectModbusTcpConnection->hostAddress().toString() << ":" << alphaConnectModbusTcpConnection->port() << ")" << "\n";
+ debug.nospace().noquote() << " - Flow:" << alphaConnectModbusTcpConnection->flowTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Return:" << alphaConnectModbusTcpConnection->returnTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - External return:" << alphaConnectModbusTcpConnection->externalReturnTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Hot water temperature:" << alphaConnectModbusTcpConnection->hotWaterTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Hot gas temperature:" << alphaConnectModbusTcpConnection->hotGasTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Heat source inlet temperature:" << alphaConnectModbusTcpConnection->heatSourceInletTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Heat source outlet temperature:" << alphaConnectModbusTcpConnection->heatSourceOutletTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Room remote adjuster 1 temperature:" << alphaConnectModbusTcpConnection->roomTemperature1() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Room remote adjuster 2 temperature:" << alphaConnectModbusTcpConnection->roomTemperature2() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Room remote adjuster 3 temperature:" << alphaConnectModbusTcpConnection->roomTemperature3() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Solar collector temperature:" << alphaConnectModbusTcpConnection->solarCollectorTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Solar storage tank temperature:" << alphaConnectModbusTcpConnection->solarStorageTankTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - External energy source temperature:" << alphaConnectModbusTcpConnection->externalEnergySourceTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Supply air temperature:" << alphaConnectModbusTcpConnection->supplyAirTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - External air temperature:" << alphaConnectModbusTcpConnection->externalAirTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - RBE actual room temperature:" << alphaConnectModbusTcpConnection->rbeRoomActualTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - RBE room temperature setpoint:" << alphaConnectModbusTcpConnection->rbeRoomSetpointTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Heating pump operating hours:" << alphaConnectModbusTcpConnection->heatingPumpOperatingHours() << " [h]" << "\n";
+ debug.nospace().noquote() << " - System status:" << alphaConnectModbusTcpConnection->systemStatus() << "\n";
+ debug.nospace().noquote() << " - Heating energy:" << alphaConnectModbusTcpConnection->heatingEnergy() << " [kWh]" << "\n";
+ debug.nospace().noquote() << " - Water heat energy:" << alphaConnectModbusTcpConnection->waterHeatEnergy() << " [kWh]" << "\n";
+ debug.nospace().noquote() << " - Total energy:" << alphaConnectModbusTcpConnection->totalHeatEnergy() << " [kWh]" << "\n";
+ debug.nospace().noquote() << " - Outdoor temperature:" << alphaConnectModbusTcpConnection->outdoorTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Return setpoint temperature:" << alphaConnectModbusTcpConnection->returnSetpointTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Hot water setpoint temperature:" << alphaConnectModbusTcpConnection->hotWaterSetpointTemperature() << " [°C]" << "\n";
+ debug.nospace().noquote() << " - Smart grid control:" << alphaConnectModbusTcpConnection->smartGrid() << "\n";
+ return debug.quote().space();
+}
+
diff --git a/alphainnotec/alphaconnectmodbustcpconnection.h b/alphainnotec/alphaconnectmodbustcpconnection.h
new file mode 100644
index 0000000..4b27fd1
--- /dev/null
+++ b/alphainnotec/alphaconnectmodbustcpconnection.h
@@ -0,0 +1,273 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2021, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This fileDescriptor 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 ALPHACONNECTMODBUSTCPCONNECTION_H
+#define ALPHACONNECTMODBUSTCPCONNECTION_H
+
+#include
+
+#include "../modbus/modbusdatautils.h"
+#include "../modbus/modbustcpmaster.h"
+
+class AlphaConnectModbusTcpConnection : public ModbusTCPMaster
+{
+ Q_OBJECT
+public:
+ enum SystemStatus {
+ SystemStatusHeatingMode = 0,
+ SystemStatusDomesticHotWater = 1,
+ SystemStatusSwimmingPool = 2,
+ SystemStatusEVUOff = 3,
+ SystemStatusDefrost = 4,
+ SystemStatusOff = 5,
+ SystemStatusExternalEnergySource = 6,
+ SystemStatusCoolingMode = 7
+ };
+ Q_ENUM(SystemStatus)
+
+ enum SmartGridState {
+ SmartGridStateOff = 0,
+ SmartGridStateLow = 1,
+ SmartGridStateStandard = 2,
+ SmartGridStateHigh = 3
+ };
+ Q_ENUM(SmartGridState)
+
+ explicit AlphaConnectModbusTcpConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);
+ ~AlphaConnectModbusTcpConnection() = default;
+
+ /* Flow [°C] - Address: 1, Size: 1 */
+ float flowTemperature() const;
+
+ /* Return [°C] - Address: 2, Size: 1 */
+ float returnTemperature() const;
+
+ /* External return [°C] - Address: 3, Size: 1 */
+ float externalReturnTemperature() const;
+
+ /* Hot water temperature [°C] - Address: 4, Size: 1 */
+ float hotWaterTemperature() const;
+
+ /* Hot gas temperature [°C] - Address: 8, Size: 1 */
+ float hotGasTemperature() const;
+
+ /* Heat source inlet temperature [°C] - Address: 9, Size: 1 */
+ float heatSourceInletTemperature() const;
+
+ /* Heat source outlet temperature [°C] - Address: 10, Size: 1 */
+ float heatSourceOutletTemperature() const;
+
+ /* Room remote adjuster 1 temperature [°C] - Address: 11, Size: 1 */
+ float roomTemperature1() const;
+
+ /* Room remote adjuster 2 temperature [°C] - Address: 12, Size: 1 */
+ float roomTemperature2() const;
+
+ /* Room remote adjuster 3 temperature [°C] - Address: 13, Size: 1 */
+ float roomTemperature3() const;
+
+ /* Solar collector temperature [°C] - Address: 14, Size: 1 */
+ float solarCollectorTemperature() const;
+
+ /* Solar storage tank temperature [°C] - Address: 15, Size: 1 */
+ float solarStorageTankTemperature() const;
+
+ /* External energy source temperature [°C] - Address: 16, Size: 1 */
+ float externalEnergySourceTemperature() const;
+
+ /* Supply air temperature [°C] - Address: 17, Size: 1 */
+ float supplyAirTemperature() const;
+
+ /* External air temperature [°C] - Address: 18, Size: 1 */
+ float externalAirTemperature() const;
+
+ /* RBE actual room temperature [°C] - Address: 24, Size: 1 */
+ float rbeRoomActualTemperature() const;
+
+ /* RBE room temperature setpoint [°C] - Address: 24, Size: 1 */
+ float rbeRoomSetpointTemperature() const;
+
+ /* Heating pump operating hours [h] - Address: 33, Size: 1 */
+ quint16 heatingPumpOperatingHours() const;
+
+ /* System status - Address: 37, Size: 1 */
+ SystemStatus systemStatus() const;
+
+ /* Heating energy [kWh] - Address: 38, Size: 2 */
+ float heatingEnergy() const;
+
+ /* Water heat energy [kWh] - Address: 40, Size: 2 */
+ float waterHeatEnergy() const;
+
+ /* Total energy [kWh] - Address: 44, Size: 2 */
+ float totalHeatEnergy() const;
+
+ /* Outdoor temperature [°C] - Address: 0, Size: 1 */
+ float outdoorTemperature() const;
+ QModbusReply *setOutdoorTemperature(float outdoorTemperature);
+
+ /* Return setpoint temperature [°C] - Address: 1, Size: 1 */
+ float returnSetpointTemperature() const;
+ QModbusReply *setReturnSetpointTemperature(float returnSetpointTemperature);
+
+ /* Hot water setpoint temperature [°C] - Address: 5, Size: 1 */
+ float hotWaterSetpointTemperature() const;
+ QModbusReply *setHotWaterSetpointTemperature(float hotWaterSetpointTemperature);
+
+ /* Smart grid control - Address: 14, Size: 1 */
+ SmartGridState smartGrid() const;
+ QModbusReply *setSmartGrid(SmartGridState smartGrid);
+
+ virtual void initialize();
+ virtual void update();
+
+ void updateFlowTemperature();
+ void updateReturnTemperature();
+ void updateExternalReturnTemperature();
+ void updateHotWaterTemperature();
+ void updateHotGasTemperature();
+ void updateHeatSourceInletTemperature();
+ void updateHeatSourceOutletTemperature();
+ void updateRoomTemperature1();
+ void updateRoomTemperature2();
+ void updateRoomTemperature3();
+ void updateSolarCollectorTemperature();
+ void updateSolarStorageTankTemperature();
+ void updateExternalEnergySourceTemperature();
+ void updateSupplyAirTemperature();
+ void updateExternalAirTemperature();
+ void updateRbeRoomActualTemperature();
+ void updateRbeRoomSetpointTemperature();
+ void updateHeatingPumpOperatingHours();
+ void updateSystemStatus();
+ void updateHeatingEnergy();
+ void updateWaterHeatEnergy();
+ void updateTotalHeatEnergy();
+ void updateOutdoorTemperature();
+ void updateReturnSetpointTemperature();
+ void updateHotWaterSetpointTemperature();
+ void updateSmartGrid();
+
+signals:
+ void initializationFinished();
+
+ void flowTemperatureChanged(float flowTemperature);
+ void returnTemperatureChanged(float returnTemperature);
+ void externalReturnTemperatureChanged(float externalReturnTemperature);
+ void hotWaterTemperatureChanged(float hotWaterTemperature);
+ void hotGasTemperatureChanged(float hotGasTemperature);
+ void heatSourceInletTemperatureChanged(float heatSourceInletTemperature);
+ void heatSourceOutletTemperatureChanged(float heatSourceOutletTemperature);
+ void roomTemperature1Changed(float roomTemperature1);
+ void roomTemperature2Changed(float roomTemperature2);
+ void roomTemperature3Changed(float roomTemperature3);
+ void solarCollectorTemperatureChanged(float solarCollectorTemperature);
+ void solarStorageTankTemperatureChanged(float solarStorageTankTemperature);
+ void externalEnergySourceTemperatureChanged(float externalEnergySourceTemperature);
+ void supplyAirTemperatureChanged(float supplyAirTemperature);
+ void externalAirTemperatureChanged(float externalAirTemperature);
+ void rbeRoomActualTemperatureChanged(float rbeRoomActualTemperature);
+ void rbeRoomSetpointTemperatureChanged(float rbeRoomSetpointTemperature);
+ void heatingPumpOperatingHoursChanged(quint16 heatingPumpOperatingHours);
+ void systemStatusChanged(SystemStatus systemStatus);
+ void heatingEnergyChanged(float heatingEnergy);
+ void waterHeatEnergyChanged(float waterHeatEnergy);
+ void totalHeatEnergyChanged(float totalHeatEnergy);
+ void outdoorTemperatureChanged(float outdoorTemperature);
+ void returnSetpointTemperatureChanged(float returnSetpointTemperature);
+ void hotWaterSetpointTemperatureChanged(float hotWaterSetpointTemperature);
+ void smartGridChanged(SmartGridState smartGrid);
+
+private:
+ quint16 m_slaveId = 1;
+ QVector m_pendingInitReplies;
+
+ float m_flowTemperature = 0;
+ float m_returnTemperature = 0;
+ float m_externalReturnTemperature = 0;
+ float m_hotWaterTemperature = 0;
+ float m_hotGasTemperature = 0;
+ float m_heatSourceInletTemperature = 0;
+ float m_heatSourceOutletTemperature = 0;
+ float m_roomTemperature1 = 0;
+ float m_roomTemperature2 = 0;
+ float m_roomTemperature3 = 0;
+ float m_solarCollectorTemperature = 0;
+ float m_solarStorageTankTemperature = 0;
+ float m_externalEnergySourceTemperature = 0;
+ float m_supplyAirTemperature = 0;
+ float m_externalAirTemperature = 0;
+ float m_rbeRoomActualTemperature = 0;
+ float m_rbeRoomSetpointTemperature = 0;
+ quint16 m_heatingPumpOperatingHours = 0;
+ SystemStatus m_systemStatus = SystemStatusHeatingMode;
+ float m_heatingEnergy = 0;
+ float m_waterHeatEnergy = 0;
+ float m_totalHeatEnergy = 0;
+ float m_outdoorTemperature = 0;
+ float m_returnSetpointTemperature = 0;
+ float m_hotWaterSetpointTemperature = 0;
+ SmartGridState m_smartGrid = SmartGridStateStandard;
+
+ void verifyInitFinished();
+
+ QModbusReply *readFlowTemperature();
+ QModbusReply *readReturnTemperature();
+ QModbusReply *readExternalReturnTemperature();
+ QModbusReply *readHotWaterTemperature();
+ QModbusReply *readHotGasTemperature();
+ QModbusReply *readHeatSourceInletTemperature();
+ QModbusReply *readHeatSourceOutletTemperature();
+ QModbusReply *readRoomTemperature1();
+ QModbusReply *readRoomTemperature2();
+ QModbusReply *readRoomTemperature3();
+ QModbusReply *readSolarCollectorTemperature();
+ QModbusReply *readSolarStorageTankTemperature();
+ QModbusReply *readExternalEnergySourceTemperature();
+ QModbusReply *readSupplyAirTemperature();
+ QModbusReply *readExternalAirTemperature();
+ QModbusReply *readRbeRoomActualTemperature();
+ QModbusReply *readRbeRoomSetpointTemperature();
+ QModbusReply *readHeatingPumpOperatingHours();
+ QModbusReply *readSystemStatus();
+ QModbusReply *readHeatingEnergy();
+ QModbusReply *readWaterHeatEnergy();
+ QModbusReply *readTotalHeatEnergy();
+ QModbusReply *readOutdoorTemperature();
+ QModbusReply *readReturnSetpointTemperature();
+ QModbusReply *readHotWaterSetpointTemperature();
+ QModbusReply *readSmartGrid();
+
+
+};
+
+QDebug operator<<(QDebug debug, AlphaConnectModbusTcpConnection *alphaConnectModbusTcpConnection);
+
+#endif // ALPHACONNECTMODBUSTCPCONNECTION_H
diff --git a/alphainnotec/alphainnotec-registers.json b/alphainnotec/alphainnotec-registers.json
new file mode 100644
index 0000000..1908e0d
--- /dev/null
+++ b/alphainnotec/alphainnotec-registers.json
@@ -0,0 +1,401 @@
+{
+ "endianness": "BigEndian",
+ "enums": [
+ {
+ "name": "SystemStatus",
+ "values": [
+ {
+ "key": "HeatingMode",
+ "value": 0
+ },
+ {
+ "key": "DomesticHotWater",
+ "value": 1
+ },
+ {
+ "key": "SwimmingPool",
+ "value": 2
+ },
+ {
+ "key": "EVUOff",
+ "value": 3
+ },
+ {
+ "key": "Defrost",
+ "value": 4
+ },
+ {
+ "key": "Off",
+ "value": 5
+ },
+ {
+ "key": "ExternalEnergySource",
+ "value": 6
+ },
+ {
+ "key": "CoolingMode",
+ "value": 7
+ }
+ ]
+ },
+ {
+ "name": "SmartGridState",
+ "values": [
+ {
+ "key": "Off",
+ "value": 0
+ },
+ {
+ "key": "Low",
+ "value": 1
+ },
+ {
+ "key": "Standard",
+ "value": 2
+ },
+ {
+ "key": "High",
+ "value": 3
+ }
+ ]
+ }
+ ],
+ "registers": [
+ {
+ "id": "flowTemperature",
+ "address": 1,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Flow",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "returnTemperature",
+ "address": 2,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Return",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "externalReturnTemperature",
+ "address": 3,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "External return",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "hotWaterTemperature",
+ "address": 4,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Hot water temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "hotGasTemperature",
+ "address": 8,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Hot gas temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "heatSourceInletTemperature",
+ "address": 9,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Heat source inlet temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "heatSourceOutletTemperature",
+ "address": 10,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Heat source outlet temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "roomTemperature1",
+ "address": 11,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Room remote adjuster 1 temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "roomTemperature2",
+ "address": 12,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Room remote adjuster 2 temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "roomTemperature3",
+ "address": 13,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Room remote adjuster 3 temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "solarCollectorTemperature",
+ "address": 14,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Solar collector temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "solarStorageTankTemperature",
+ "address": 15,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Solar storage tank temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "externalEnergySourceTemperature",
+ "address": 16,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "External energy source temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "supplyAirTemperature",
+ "address": 17,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Supply air temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "externalAirTemperature",
+ "address": 18,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "External air temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "rbeRoomActualTemperature",
+ "address": 24,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "RBE actual room temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "rbeRoomSetpointTemperature",
+ "address": 24,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "RBE room temperature setpoint",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RO"
+ },
+ {
+ "id": "heatingPumpOperatingHours",
+ "address": 33,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Heating pump operating hours",
+ "defaultValue": "0",
+ "unit": "h",
+ "access": "RO"
+ },
+ {
+ "id": "systemStatus",
+ "address": 37,
+ "size": 1,
+ "type": "uint16",
+ "enum": "SystemStatus",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "System status",
+ "defaultValue": "SystemStatusHeatingMode",
+ "access": "RO"
+ },
+ {
+ "id": "heatingEnergy",
+ "address": 38,
+ "size": 2,
+ "type": "uint32",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "kWh",
+ "description": "Heating energy",
+ "access": "RO"
+ },
+ {
+ "id": "waterHeatEnergy",
+ "address": 40,
+ "size": 2,
+ "type": "uint32",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "kWh",
+ "description": "Water heat energy",
+ "access": "RO"
+ },
+ {
+ "id": "totalHeatEnergy",
+ "address": 44,
+ "size": 2,
+ "type": "uint32",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "kWh",
+ "description": "Total energy",
+ "access": "RO"
+ },
+
+ {
+ "id": "outdoorTemperature",
+ "address": 0,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Outdoor temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RW"
+ },
+ {
+ "id": "returnSetpointTemperature",
+ "address": 1,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Return setpoint temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RW"
+ },
+ {
+ "id": "hotWaterSetpointTemperature",
+ "address": 5,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Hot water setpoint temperature",
+ "staticScaleFactor": -1,
+ "defaultValue": "0",
+ "unit": "°C",
+ "access": "RW"
+ },
+ {
+ "id": "smartGrid",
+ "address": 14,
+ "size": 1,
+ "type": "uint16",
+ "enum": "SmartGridState",
+ "registerType": "holdingRegister",
+ "readSchedule": "update",
+ "description": "Smart grid control",
+ "defaultValue": "SmartGridStateStandard",
+ "access": "RW"
+ }
+ ]
+}
diff --git a/alphainnotec/alphainnotec.pro b/alphainnotec/alphainnotec.pro
new file mode 100644
index 0000000..b88c856
--- /dev/null
+++ b/alphainnotec/alphainnotec.pro
@@ -0,0 +1,15 @@
+include(../plugins.pri)
+
+QT += network serialbus
+
+SOURCES += \
+ integrationpluginalphainnotec.cpp \
+ alphaconnectmodbustcpconnection.cpp \
+ ../modbus/modbustcpmaster.cpp \
+ ../modbus/modbusdatautils.cpp
+
+HEADERS += \
+ integrationpluginalphainnotec.h \
+ alphaconnectmodbustcpconnection.h \
+ ../modbus/modbustcpmaster.h \
+ ../modbus/modbusdatautils.h
diff --git a/alphainnotec/integrationpluginalphainnotec.cpp b/alphainnotec/integrationpluginalphainnotec.cpp
new file mode 100644
index 0000000..153d6b3
--- /dev/null
+++ b/alphainnotec/integrationpluginalphainnotec.cpp
@@ -0,0 +1,474 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 "integrationpluginalphainnotec.h"
+
+#include "network/networkdevicediscovery.h"
+#include "hardwaremanager.h"
+#include "plugininfo.h"
+
+IntegrationPluginAlphaInnotec::IntegrationPluginAlphaInnotec()
+{
+
+}
+
+void IntegrationPluginAlphaInnotec::discoverThings(ThingDiscoveryInfo *info)
+{
+ if (!hardwareManager()->networkDeviceDiscovery()->available()) {
+ qCWarning(dcAlphaInnotec()) << "The network discovery is not available on this platform.";
+ info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
+ return;
+ }
+
+ NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
+ connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
+ foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
+
+ qCDebug(dcAlphaInnotec()) << "Found" << networkDeviceInfo;
+
+ QString title;
+ if (networkDeviceInfo.hostName().isEmpty()) {
+ title = networkDeviceInfo.address().toString();
+ } else {
+ title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")";
+ }
+
+ QString description;
+ if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
+ description = networkDeviceInfo.macAddress();
+ } else {
+ description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")";
+ }
+
+ ThingDescriptor descriptor(alphaConnectThingClassId, title, description);
+ ParamList params;
+ params << Param(alphaConnectThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
+ params << Param(alphaConnectThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
+ descriptor.setParams(params);
+
+ // Check if we already have set up this device
+ Things existingThings = myThings().filterByParam(alphaConnectThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
+ if (existingThings.count() == 1) {
+ qCDebug(dcAlphaInnotec()) << "This connection already exists in the system:" << networkDeviceInfo;
+ descriptor.setThingId(existingThings.first()->id());
+ }
+
+ info->addThingDescriptor(descriptor);
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+ });
+}
+
+void IntegrationPluginAlphaInnotec::startMonitoringAutoThings()
+{
+
+}
+
+void IntegrationPluginAlphaInnotec::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ qCDebug(dcAlphaInnotec()) << "Setup" << thing << thing->params();
+
+ if (thing->thingClassId() == alphaConnectThingClassId) {
+ QHostAddress hostAddress = QHostAddress(thing->paramValue(alphaConnectThingIpAddressParamTypeId).toString());
+ if (hostAddress.isNull()) {
+ info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("No IP address given"));
+ return;
+ }
+
+ uint port = thing->paramValue(alphaConnectThingPortParamTypeId).toUInt();
+ quint16 slaveId = thing->paramValue(alphaConnectThingSlaveIdParamTypeId).toUInt();
+
+ AlphaConnectModbusTcpConnection *alphaConnectTcpConnection = new AlphaConnectModbusTcpConnection(hostAddress, port, slaveId, this);
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::connectionStateChanged, this, [thing, alphaConnectTcpConnection](bool status){
+ qCDebug(dcAlphaInnotec()) << "Connected changed to" << status << "for" << thing;
+ if (status) {
+ alphaConnectTcpConnection->update();
+ }
+
+ thing->setStateValue(alphaConnectConnectedStateTypeId, status);
+ });
+
+
+ // Input registers
+// connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::meanTemperatureChanged, this, [thing](float meanTemperature){
+// qCDebug(dcAlphaInnotec()) << thing << "mean temperature changed" << meanTemperature << "°C";
+// thing->setStateValue(alphaConnectMeanTemperatureStateTypeId, meanTemperature);
+// });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::flowTemperatureChanged, this, [thing](float flowTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "flow temperature changed" << flowTemperature << "°C";
+ thing->setStateValue(alphaConnectFlowTemperatureStateTypeId, flowTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::returnTemperatureChanged, this, [thing](float returnTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "return temperature changed" << returnTemperature << "°C";
+ thing->setStateValue(alphaConnectReturnTemperatureStateTypeId, returnTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::externalReturnTemperatureChanged, this, [thing](float externalReturnTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "external return temperature changed" << externalReturnTemperature << "°C";
+ thing->setStateValue(alphaConnectExternalReturnTemperatureStateTypeId, externalReturnTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::hotWaterTemperatureChanged, this, [thing](float hotWaterTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "hot water temperature changed" << hotWaterTemperature << "°C";
+ thing->setStateValue(alphaConnectHotWaterTemperatureStateTypeId, hotWaterTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::hotGasTemperatureChanged, this, [thing](float hotGasTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "hot gas temperature changed" << hotGasTemperature << "°C";
+ thing->setStateValue(alphaConnectHotGasTemperatureStateTypeId, hotGasTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatSourceInletTemperatureChanged, this, [thing](float heatSourceInletTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "heat source inlet temperature changed" << heatSourceInletTemperature << "°C";
+ thing->setStateValue(alphaConnectHeatSourceInletTemperatureStateTypeId, heatSourceInletTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatSourceOutletTemperatureChanged, this, [thing](float heatSourceOutletTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "heat source outlet temperature changed" << heatSourceOutletTemperature << "°C";
+ thing->setStateValue(alphaConnectHeatSourceOutletTemperatureStateTypeId, heatSourceOutletTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::roomTemperature1Changed, this, [thing](float roomTemperature1){
+ qCDebug(dcAlphaInnotec()) << thing << "room remote adjuster 1 temperature changed" << roomTemperature1 << "°C";
+ thing->setStateValue(alphaConnectRoomTemperature1StateTypeId, roomTemperature1);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::roomTemperature2Changed, this, [thing](float roomTemperature2){
+ qCDebug(dcAlphaInnotec()) << thing << "room remote adjuster 2 temperature changed" << roomTemperature2 << "°C";
+ thing->setStateValue(alphaConnectRoomTemperature2StateTypeId, roomTemperature2);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::roomTemperature3Changed, this, [thing](float roomTemperature3){
+ qCDebug(dcAlphaInnotec()) << thing << "room remote adjuster 3 temperature changed" << roomTemperature3 << "°C";
+ thing->setStateValue(alphaConnectRoomTemperature2StateTypeId, roomTemperature3);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::solarCollectorTemperatureChanged, this, [thing](float solarCollectorTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "solar collector temperature changed" << solarCollectorTemperature << "°C";
+ thing->setStateValue(alphaConnectSolarCollectorTemperatureStateTypeId, solarCollectorTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::solarStorageTankTemperatureChanged, this, [thing](float solarStorageTankTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "solar storage tank temperature changed" << solarStorageTankTemperature << "°C";
+ thing->setStateValue(alphaConnectSolarCollectorTemperatureStateTypeId, solarStorageTankTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::externalEnergySourceTemperatureChanged, this, [thing](float externalEnergySourceTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "external energy source temperature changed" << externalEnergySourceTemperature << "°C";
+ thing->setStateValue(alphaConnectExternalEnergySourceTemperatureStateTypeId, externalEnergySourceTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::supplyAirTemperatureChanged, this, [thing](float supplyAirTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "supply air temperature changed" << supplyAirTemperature << "°C";
+ thing->setStateValue(alphaConnectSupplyAirTemperatureStateTypeId, supplyAirTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::externalAirTemperatureChanged, this, [thing](float externalAirTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "external air temperature changed" << externalAirTemperature << "°C";
+ thing->setStateValue(alphaConnectExternalAirTemperatureStateTypeId, externalAirTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatingPumpOperatingHoursChanged, this, [thing](quint16 heatingPumpOperatingHours){
+ qCDebug(dcAlphaInnotec()) << thing << "heating pump operating hours changed" << heatingPumpOperatingHours;
+ thing->setStateValue(alphaConnectHeatingPumpOperatingHoursStateTypeId, heatingPumpOperatingHours);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::systemStatusChanged, this, [thing](AlphaConnectModbusTcpConnection::SystemStatus systemStatus){
+ qCDebug(dcAlphaInnotec()) << thing << "system status changed" << systemStatus;
+ switch (systemStatus) {
+ case AlphaConnectModbusTcpConnection::SystemStatusHeatingMode:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Heating mode");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusDomesticHotWater:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Domestic hot water");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusSwimmingPool:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Swimming pool");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusEVUOff:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "EUV off");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusDefrost:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Defrost");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusOff:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Off");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusExternalEnergySource:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "External energy source");
+ break;
+ case AlphaConnectModbusTcpConnection::SystemStatusCoolingMode:
+ thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Cooling mode");
+ break;
+ }
+
+ // Set heating and cooling states according to the system state
+ thing->setStateValue(alphaConnectHeatingOnStateTypeId, systemStatus == AlphaConnectModbusTcpConnection::SystemStatusHeatingMode);
+ thing->setStateValue(alphaConnectCoolingOnStateTypeId, systemStatus == AlphaConnectModbusTcpConnection::SystemStatusCoolingMode);
+ });
+
+ // Energy
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::totalHeatEnergyChanged, this, [thing](float totalHeatEnergy){
+ qCDebug(dcAlphaInnotec()) << thing << "total heating energy changed" << totalHeatEnergy << "kWh";
+ thing->setStateValue(alphaConnectTotalEnergyStateTypeId, totalHeatEnergy);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatingEnergyChanged, this, [thing](float heatingEnergy){
+ qCDebug(dcAlphaInnotec()) << thing << "heating energy changed" << heatingEnergy << "kWh";
+ thing->setStateValue(alphaConnectHeatingEnergyStateTypeId, heatingEnergy);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::waterHeatEnergyChanged, this, [thing](float waterHeatEnergy){
+ qCDebug(dcAlphaInnotec()) << thing << "water heat energy changed" << waterHeatEnergy << "kWh";
+ thing->setStateValue(alphaConnectHotWaterEnergyStateTypeId, waterHeatEnergy);
+ });
+
+// connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::swimmingPoolHeatEnergyChanged, this, [thing](float swimmingPoolHeatEnergy){
+// qCDebug(dcAlphaInnotec()) << thing << "swimming pool heat energy changed" << swimmingPoolHeatEnergy << "kWh";
+// thing->setStateValue(alphaConnectSwimmingPoolEnergyStateTypeId, swimmingPoolHeatEnergy);
+// });
+
+ // Holding registers
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::outdoorTemperatureChanged, this, [thing](float outdoorTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "outdoor temperature changed" << outdoorTemperature << "°C";
+ thing->setStateValue(alphaConnectOutdoorTemperatureStateTypeId, outdoorTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::returnSetpointTemperatureChanged, this, [thing](float returnSetpointTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "return setpoint temperature changed" << returnSetpointTemperature << "°C";
+ thing->setStateValue(alphaConnectReturnSetpointTemperatureStateTypeId, returnSetpointTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::hotWaterSetpointTemperatureChanged, this, [thing](float hotWaterSetpointTemperature){
+ qCDebug(dcAlphaInnotec()) << thing << "hot water setpoint temperature changed" << hotWaterSetpointTemperature << "°C";
+ thing->setStateValue(alphaConnectHotWaterSetpointTemperatureStateTypeId, hotWaterSetpointTemperature);
+ });
+
+ connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::smartGridChanged, this, [thing](AlphaConnectModbusTcpConnection::SmartGridState smartGridState){
+ qCDebug(dcAlphaInnotec()) << thing << "smart grid state changed" << smartGridState;
+ switch (smartGridState) {
+ case AlphaConnectModbusTcpConnection::SmartGridStateOff:
+ thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "Off");
+ break;
+ case AlphaConnectModbusTcpConnection::SmartGridStateLow:
+ thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "Low");
+ break;
+ case AlphaConnectModbusTcpConnection::SmartGridStateStandard:
+ thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "Standard");
+ break;
+ case AlphaConnectModbusTcpConnection::SmartGridStateHigh:
+ thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "High");
+ break;
+ }
+ });
+
+ m_alpaConnectTcpThings.insert(thing, alphaConnectTcpConnection);
+ alphaConnectTcpConnection->connectDevice();
+
+ // FIXME: make async and check if this is really an alpha connect
+ info->finish(Thing::ThingErrorNoError);
+ }
+}
+
+void IntegrationPluginAlphaInnotec::postSetupThing(Thing *thing)
+{
+ if (thing->thingClassId() == alphaConnectThingClassId) {
+ if (!m_pluginTimer) {
+ qCDebug(dcAlphaInnotec()) << "Starting plugin timer...";
+ m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+ connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
+ foreach (AlphaConnectModbusTcpConnection *connection, m_alpaConnectTcpThings) {
+ if (connection->connected()) {
+ connection->update();
+ }
+ }
+ });
+
+ m_pluginTimer->start();
+ }
+ }
+}
+
+void IntegrationPluginAlphaInnotec::thingRemoved(Thing *thing)
+{
+ if (thing->thingClassId() == alphaConnectThingClassId && m_alpaConnectTcpThings.contains(thing)) {
+ AlphaConnectModbusTcpConnection *connection = m_alpaConnectTcpThings.take(thing);
+ delete connection;
+ }
+
+ if (myThings().isEmpty() && m_pluginTimer) {
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+ m_pluginTimer = nullptr;
+ }
+}
+
+void IntegrationPluginAlphaInnotec::executeAction(ThingActionInfo *info)
+{
+ Thing *thing = info->thing();
+ AlphaConnectModbusTcpConnection *connection = m_alpaConnectTcpThings.value(thing);
+
+ if (!connection->connected()) {
+ qCWarning(dcAlphaInnotec()) << "Could not execute action. The modbus connection is currently not available.";
+ info->finish(Thing::ThingErrorHardwareNotAvailable);
+ return;
+ }
+
+ if (thing->thingClassId() == alphaConnectThingClassId) {
+ /* if (info->action().actionTypeId() == alphaConnectOutdoorTemperatureActionTypeId) {
+ double outdoorTemperature = info->action().paramValue(alphaConnectOutdoorTemperatureActionOutdoorTemperatureParamTypeId).toDouble();
+ qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params();
+ QModbusReply *reply = connection->setOutdoorTemperature(outdoorTemperature);
+ if (!reply) {
+ qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created.";
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, info, [info, reply, outdoorTemperature]{
+ if (reply->error() != QModbusDevice::NoError) {
+ info->finish(Thing::ThingErrorHardwareFailure);
+ qCWarning(dcAlphaInnotec()) << "Set outdoor temperature finished with error" << reply->errorString();
+ return;
+ }
+
+ qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params();
+ info->thing()->setStateValue(alphaConnectOutdoorTemperatureStateTypeId, outdoorTemperature);
+ info->finish(Thing::ThingErrorNoError);
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else */
+ if (info->action().actionTypeId() == alphaConnectHotWaterSetpointTemperatureActionTypeId) {
+ double temperature = info->action().paramValue(alphaConnectHotWaterSetpointTemperatureActionHotWaterSetpointTemperatureParamTypeId).toDouble();
+ qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params();
+ QModbusReply *reply = connection->setHotWaterSetpointTemperature(temperature);
+ if (!reply) {
+ qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created.";
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, info, [info, reply, temperature]{
+ if (reply->error() != QModbusDevice::NoError) {
+ qCWarning(dcAlphaInnotec()) << "Set hot water setpoint temperature finished with error" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params();
+ info->thing()->setStateValue(alphaConnectHotWaterSetpointTemperatureStateTypeId, temperature);
+ info->finish(Thing::ThingErrorNoError);
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else if (info->action().actionTypeId() == alphaConnectReturnSetpointTemperatureActionTypeId) {
+ double temperature = info->action().paramValue(alphaConnectReturnSetpointTemperatureActionReturnSetpointTemperatureParamTypeId).toDouble();
+ qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params();
+ QModbusReply *reply = connection->setReturnSetpointTemperature(temperature);
+ if (!reply) {
+ qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created.";
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, info, [info, reply, temperature]{
+ if (reply->error() != QModbusDevice::NoError) {
+ qCWarning(dcAlphaInnotec()) << "Set return setpoint temperature finished with error" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params();
+ info->thing()->setStateValue(alphaConnectReturnSetpointTemperatureStateTypeId, temperature);
+ info->finish(Thing::ThingErrorNoError);
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ } else if (info->action().actionTypeId() == alphaConnectSgReadyModeActionTypeId) {
+ QString sgReadyModeString = info->action().paramValue(alphaConnectSgReadyModeActionSgReadyModeParamTypeId).toString();
+ qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params();
+ AlphaConnectModbusTcpConnection::SmartGridState sgReadyState;
+ if (sgReadyModeString == "Off") {
+ sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateOff;
+ } else if (sgReadyModeString == "Low") {
+ sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateLow;
+ } else if (sgReadyModeString == "High") {
+ sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateHigh;
+ } else {
+ sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateStandard;
+ }
+
+ QModbusReply *reply = connection->setSmartGrid(sgReadyState);
+ if (!reply) {
+ qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created.";
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
+ connect(reply, &QModbusReply::finished, info, [info, reply, sgReadyModeString]{
+ if (reply->error() != QModbusDevice::NoError) {
+ qCWarning(dcAlphaInnotec()) << "Set SG ready mode finished with error" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params();
+ info->thing()->setStateValue(alphaConnectSgReadyModeStateTypeId, sgReadyModeString);
+ info->finish(Thing::ThingErrorNoError);
+ });
+
+ connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){
+ qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString();
+ emit reply->finished(); // To make sure it will be deleted
+ });
+ }
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+}
+
+
diff --git a/alphainnotec/integrationpluginalphainnotec.h b/alphainnotec/integrationpluginalphainnotec.h
new file mode 100644
index 0000000..0f6a4cc
--- /dev/null
+++ b/alphainnotec/integrationpluginalphainnotec.h
@@ -0,0 +1,62 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 INTEGRATIONPLUGINALPHAINNOTEC_H
+#define INTEGRATIONPLUGINALPHAINNOTEC_H
+
+#include "plugintimer.h"
+#include "alphaconnectmodbustcpconnection.h"
+#include "integrations/integrationplugin.h"
+
+class IntegrationPluginAlphaInnotec: public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginalphainnotec.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginAlphaInnotec();
+
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void startMonitoringAutoThings() override;
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ void thingRemoved(Thing *thing) override;
+ void executeAction(ThingActionInfo *info) override;
+
+private:
+ PluginTimer *m_pluginTimer = nullptr;
+ QHash m_alpaConnectTcpThings;
+};
+
+#endif // INTEGRATIONPLUGINALPHAINNOTEC_H
+
+
diff --git a/alphainnotec/integrationpluginalphainnotec.json b/alphainnotec/integrationpluginalphainnotec.json
new file mode 100644
index 0000000..14990a4
--- /dev/null
+++ b/alphainnotec/integrationpluginalphainnotec.json
@@ -0,0 +1,343 @@
+{
+ "name": "AlphaInnotec",
+ "displayName": "alpha innotec",
+ "id": "b3225a18-e9b0-443e-8c88-802d3132f94d",
+ "vendors": [
+ {
+ "name": "alphaInnotec",
+ "displayName": "alpha Innotec",
+ "id": "2c2101ae-7bb8-4012-97ba-cedf4cecd924",
+ "thingClasses": [
+ {
+ "name": "alphaConnect",
+ "displayName": "alpha connect",
+ "id": "c5437b68-cfd2-4ec8-bad6-006fb5e8a8da",
+ "createMethods": ["discovery", "user"],
+ "interfaces": ["smartgridheatpump", "connectable"],
+ "paramTypes": [
+ {
+ "id": "64a18910-9111-4eaf-986d-f7b64b03b99a",
+ "name": "ipAddress",
+ "displayName": "IP address",
+ "type": "QString",
+ "inputType": "IPv4Address",
+ "defaultValue": "127.0.0.1"
+ },
+ {
+ "id": "f791c219-98a5-41ee-8e5f-1bfb5136dc9c",
+ "name":"macAddress",
+ "displayName": "MAC address",
+ "type": "QString",
+ "inputType": "MacAddress",
+ "defaultValue": ""
+ },
+ {
+ "id": "b92025c1-8978-4d47-bd3c-4df749dbfd0f",
+ "name":"port",
+ "displayName": "Port",
+ "type": "int",
+ "defaultValue": 502
+ },
+ {
+ "id": "1d9517ca-680c-49e2-a8d1-320743c27559",
+ "name":"slaveId",
+ "displayName": "Modbus slave ID",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "01ce0d61-3813-4c1b-a18b-555913d689a2",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "defaultValue": false,
+ "cached": false
+ },
+ {
+ "id": "07465fbb-6949-4bd1-90d5-acf2d80c161d",
+ "name": "heatingOn",
+ "displayName": "Heating on",
+ "displayNameEvent": "Heating turned on/off",
+ "type": "bool",
+ "defaultValue": false,
+ "suggestLogging": true
+ },
+ {
+ "id": "8b407c1d-b84f-48d4-9961-b29bc58fff0e",
+ "name": "coolingOn",
+ "displayName": "Cooling on",
+ "displayNameEvent": "Cooling turned on/off",
+ "type": "bool",
+ "defaultValue": false,
+ "suggestLogging": true
+ },
+ {
+ "id": "fb448191-69d7-4f2e-87bf-6e4fb61e403f",
+ "name": "flowTemperature",
+ "displayName": "Flow temperature",
+ "displayNameEvent": "Flow temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d",
+ "name": "returnTemperature",
+ "displayName": "Return temperature",
+ "displayNameEvent": "Return temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "b6702fc5-889b-48b6-9257-64c5ee6e5e95",
+ "name": "externalReturnTemperature",
+ "displayName": "External return temperature",
+ "displayNameEvent": "External return temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "da7e6bc9-95fd-4c90-931f-5b1cd69d864a",
+ "name": "hotWaterTemperature",
+ "displayName": "Hot water temperature",
+ "displayNameEvent": "Hot water temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "bea6658e-64bf-4476-b75d-38c34a5fed85",
+ "name": "hotGasTemperature",
+ "displayName": "Hot gas temperature",
+ "displayNameEvent": "Hot gas temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "e9facc5d-f41c-4256-85aa-b0f777431975",
+ "name": "heatSourceInletTemperature",
+ "displayName": "Heat source inlet temperature",
+ "displayNameEvent": "Heat source inlet temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "48424285-4bc4-4110-8089-0486292d7371",
+ "name": "heatSourceOutletTemperature",
+ "displayName": "Heat source outlet temperature",
+ "displayNameEvent": "Heat source outlet temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "52f8a581-7b9c-4c15-8aaa-c083763e7820",
+ "name": "roomTemperature1",
+ "displayName": "Room remote adjuster 1 temperature",
+ "displayNameEvent": "Room remote adjuster 1 temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "91b57690-5daa-4766-86ae-f205fa0c805a",
+ "name": "roomTemperature2",
+ "displayName": "Room remote adjuster 2 temperature",
+ "displayNameEvent": "Room remote adjuster 2 temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "a8c7e706-57ba-4572-b6e6-5eec91065ea8",
+ "name": "roomTemperature3",
+ "displayName": "Room remote adjuster 3 temperature",
+ "displayNameEvent": "Room remote adjuster 3 temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "d4484305-eb0f-418b-bb15-f71b08e6fce1",
+ "name": "solarCollectorTemperature",
+ "displayName": "Solar collector temperature",
+ "displayNameEvent": "Solar collector temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "75089369-c224-4fe5-91d1-9bdc5578f0a5",
+ "name": "solarStorageTankTemperature",
+ "displayName": "Solar storage tank temperature",
+ "displayNameEvent": "Solar storage tank temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "519d7d45-1f3d-4118-ade9-3d7508451005",
+ "name": "externalEnergySourceTemperature",
+ "displayName": "External energy source temperature",
+ "displayNameEvent": "External energy source temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "90d88164-3083-47c7-a396-b338bd9ea54c",
+ "name": "supplyAirTemperature",
+ "displayName": "Supply air temperature",
+ "displayNameEvent": "Supply air temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "92594f19-8e4c-485b-bfd5-5371a0454267",
+ "name": "externalAirTemperature",
+ "displayName": "External air temperature",
+ "displayNameEvent": "External air temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "5a21a131-f3a0-4ebd-9b2e-12eb25c84853",
+ "name": "systemStatus",
+ "displayName": "System status",
+ "displayNameEvent": "System status changed",
+ "type": "QString",
+ "possibleValues": [
+ "Heating mode",
+ "Domestic hot water",
+ "Swimming pool",
+ "EUV off",
+ "Defrost",
+ "Off",
+ "External energy source",
+ "Cooling mode"
+ ],
+ "defaultValue": "Heating mode",
+ "suggestLogging": true
+ },
+ {
+ "id": "ef378ce6-f112-4022-8535-78cbd6ccfeeb",
+ "name": "heatingPumpOperatingHours",
+ "displayName": "Heating pump operating hours",
+ "displayNameEvent": "Heating pump operating hours changed",
+ "unit": "Hours",
+ "type": "uint",
+ "defaultValue": 0
+ },
+ {
+ "id": "d0c8f168-49b5-47ca-9988-c9922be38dd5",
+ "name": "outdoorTemperature",
+ "displayName": "Outdoor temperature",
+ "displayNameEvent": "Outdoor temperature changed",
+ "displayNameAction": "Set outdoor temperature",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "1ee6b189-ceff-4912-a577-2e3320307c3f",
+ "name": "returnSetpointTemperature",
+ "displayName": "Return temperature setpoint",
+ "displayNameEvent": "Return temperature setpoint changed",
+ "displayNameAction": "Set return temperature setpoint",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "writable": true,
+ "suggestLogging": true
+ },
+ {
+ "id": "a7f1a688-51d3-4f9e-a323-86117c5542a8",
+ "name": "hotWaterSetpointTemperature",
+ "displayName": "Hot water temperature setpoint",
+ "displayNameEvent": "Hot water temperature setpoint changed",
+ "displayNameAction": "Set hot water temperature setpoint",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0,
+ "writable": true,
+ "suggestLogging": true
+ },
+ {
+ "id": "8cc9761f-b30e-4f54-aee7-b64190df57e1",
+ "name": "sgReadyMode",
+ "displayName": "Smart grid mode",
+ "displayNameEvent": "Smart grid mode changed",
+ "displayNameAction": "Set smart grid mode",
+ "type": "QString",
+ "possibleValues": [
+ "Off",
+ "Low",
+ "Standard",
+ "High"
+ ],
+ "writable": true,
+ "defaultValue": "Standard",
+ "suggestLogging": true
+ },
+ {
+ "id": "c7948cf6-fd1a-44fa-a91b-98f2474ecc62",
+ "name": "totalEnergy",
+ "displayName": "Total energy",
+ "displayNameEvent": "Total energy changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "24367fba-2883-40da-9b92-5d4db3c71c2f",
+ "name": "heatingEnergy",
+ "displayName": "Heating energy",
+ "displayNameEvent": "Heating energy changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0,
+ "suggestLogging": true
+ },
+ {
+ "id": "be2430c3-54ef-441e-b684-9aabc1d984d6",
+ "name": "hotWaterEnergy",
+ "displayName": "Hot water energy",
+ "displayNameEvent": "Hot water energy changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0,
+ "suggestLogging": true
+ }
+ ],
+ "actionTypes": [ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/alphainnotec/meta.json b/alphainnotec/meta.json
new file mode 100644
index 0000000..09832c9
--- /dev/null
+++ b/alphainnotec/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "alpha innotec",
+ "tagline": "Integrate alpha innotec heat pumps into nymea.",
+ "icon": "alpha-innotec.png",
+ "stability": "community",
+ "offline": true,
+ "technologies": [
+ "network"
+ ],
+ "categories": [
+ "heating"
+ ]
+}
diff --git a/alphainnotec/translations/b3225a18-e9b0-443e-8c88-802d3132f94d-en_US.ts b/alphainnotec/translations/b3225a18-e9b0-443e-8c88-802d3132f94d-en_US.ts
new file mode 100644
index 0000000..563414f
--- /dev/null
+++ b/alphainnotec/translations/b3225a18-e9b0-443e-8c88-802d3132f94d-en_US.ts
@@ -0,0 +1,431 @@
+
+
+
+
+ AlphaInnotec
+
+
+
+ Connected
+ The name of the ParamType (ThingClass: alphaConnect, EventType: connected, ID: {01ce0d61-3813-4c1b-a18b-555913d689a2})
+----------
+The name of the StateType ({01ce0d61-3813-4c1b-a18b-555913d689a2}) of ThingClass alphaConnect
+
+
+
+
+ Connected changed
+ The name of the EventType ({01ce0d61-3813-4c1b-a18b-555913d689a2}) of ThingClass alphaConnect
+
+
+
+
+
+ Cooling on
+ The name of the ParamType (ThingClass: alphaConnect, EventType: coolingOn, ID: {8b407c1d-b84f-48d4-9961-b29bc58fff0e})
+----------
+The name of the StateType ({8b407c1d-b84f-48d4-9961-b29bc58fff0e}) of ThingClass alphaConnect
+
+
+
+
+ Cooling turned on/off
+ The name of the EventType ({8b407c1d-b84f-48d4-9961-b29bc58fff0e}) of ThingClass alphaConnect
+
+
+
+
+
+ External air temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: externalAirTemperature, ID: {92594f19-8e4c-485b-bfd5-5371a0454267})
+----------
+The name of the StateType ({92594f19-8e4c-485b-bfd5-5371a0454267}) of ThingClass alphaConnect
+
+
+
+
+ External air temperature changed
+ The name of the EventType ({92594f19-8e4c-485b-bfd5-5371a0454267}) of ThingClass alphaConnect
+
+
+
+
+
+ External energy source temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: externalEnergySourceTemperature, ID: {519d7d45-1f3d-4118-ade9-3d7508451005})
+----------
+The name of the StateType ({519d7d45-1f3d-4118-ade9-3d7508451005}) of ThingClass alphaConnect
+
+
+
+
+ External energy source temperature changed
+ The name of the EventType ({519d7d45-1f3d-4118-ade9-3d7508451005}) of ThingClass alphaConnect
+
+
+
+
+
+ External return temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: externalReturnTemperature, ID: {b6702fc5-889b-48b6-9257-64c5ee6e5e95})
+----------
+The name of the StateType ({b6702fc5-889b-48b6-9257-64c5ee6e5e95}) of ThingClass alphaConnect
+
+
+
+
+ External return temperature changed
+ The name of the EventType ({b6702fc5-889b-48b6-9257-64c5ee6e5e95}) of ThingClass alphaConnect
+
+
+
+
+
+ Flow temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: flowTemperature, ID: {fb448191-69d7-4f2e-87bf-6e4fb61e403f})
+----------
+The name of the StateType ({fb448191-69d7-4f2e-87bf-6e4fb61e403f}) of ThingClass alphaConnect
+
+
+
+
+ Flow temperature changed
+ The name of the EventType ({fb448191-69d7-4f2e-87bf-6e4fb61e403f}) of ThingClass alphaConnect
+
+
+
+
+
+ Heat source inlet temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: heatSourceInletTemperature, ID: {e9facc5d-f41c-4256-85aa-b0f777431975})
+----------
+The name of the StateType ({e9facc5d-f41c-4256-85aa-b0f777431975}) of ThingClass alphaConnect
+
+
+
+
+ Heat source inlet temperature changed
+ The name of the EventType ({e9facc5d-f41c-4256-85aa-b0f777431975}) of ThingClass alphaConnect
+
+
+
+
+
+ Heat source outlet temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: heatSourceOutletTemperature, ID: {48424285-4bc4-4110-8089-0486292d7371})
+----------
+The name of the StateType ({48424285-4bc4-4110-8089-0486292d7371}) of ThingClass alphaConnect
+
+
+
+
+ Heat source outlet temperature changed
+ The name of the EventType ({48424285-4bc4-4110-8089-0486292d7371}) of ThingClass alphaConnect
+
+
+
+
+
+ Heating on
+ The name of the ParamType (ThingClass: alphaConnect, EventType: heatingOn, ID: {07465fbb-6949-4bd1-90d5-acf2d80c161d})
+----------
+The name of the StateType ({07465fbb-6949-4bd1-90d5-acf2d80c161d}) of ThingClass alphaConnect
+
+
+
+
+
+ Heating pump operating hours
+ The name of the ParamType (ThingClass: alphaConnect, EventType: heatingPumpOperatingHours, ID: {ef378ce6-f112-4022-8535-78cbd6ccfeeb})
+----------
+The name of the StateType ({ef378ce6-f112-4022-8535-78cbd6ccfeeb}) of ThingClass alphaConnect
+
+
+
+
+ Heating pump operating hours changed
+ The name of the EventType ({ef378ce6-f112-4022-8535-78cbd6ccfeeb}) of ThingClass alphaConnect
+
+
+
+
+ Heating turned on/off
+ The name of the EventType ({07465fbb-6949-4bd1-90d5-acf2d80c161d}) of ThingClass alphaConnect
+
+
+
+
+
+ Hot gas temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: hotGasTemperature, ID: {bea6658e-64bf-4476-b75d-38c34a5fed85})
+----------
+The name of the StateType ({bea6658e-64bf-4476-b75d-38c34a5fed85}) of ThingClass alphaConnect
+
+
+
+
+ Hot gas temperature changed
+ The name of the EventType ({bea6658e-64bf-4476-b75d-38c34a5fed85}) of ThingClass alphaConnect
+
+
+
+
+
+ Hot water temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: hotWaterTemperature, ID: {da7e6bc9-95fd-4c90-931f-5b1cd69d864a})
+----------
+The name of the StateType ({da7e6bc9-95fd-4c90-931f-5b1cd69d864a}) of ThingClass alphaConnect
+
+
+
+
+ Hot water temperature changed
+ The name of the EventType ({da7e6bc9-95fd-4c90-931f-5b1cd69d864a}) of ThingClass alphaConnect
+
+
+
+
+ IP address
+ The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {64a18910-9111-4eaf-986d-f7b64b03b99a})
+
+
+
+
+ MAC address
+ The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {f791c219-98a5-41ee-8e5f-1bfb5136dc9c})
+
+
+
+
+
+ Mean temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: meanTemperature, ID: {956051a6-8677-4b25-b899-71aa3590d4e5})
+----------
+The name of the StateType ({956051a6-8677-4b25-b899-71aa3590d4e5}) of ThingClass alphaConnect
+
+
+
+
+ Mean temperature changed
+ The name of the EventType ({956051a6-8677-4b25-b899-71aa3590d4e5}) of ThingClass alphaConnect
+
+
+
+
+ Modbus slave ID
+ The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {1d9517ca-680c-49e2-a8d1-320743c27559})
+
+
+
+
+
+ Outdoor temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: outdoorTemperature, ID: {d0c8f168-49b5-47ca-9988-c9922be38dd5})
+----------
+The name of the StateType ({d0c8f168-49b5-47ca-9988-c9922be38dd5}) of ThingClass alphaConnect
+
+
+
+
+ Outdoor temperature changed
+ The name of the EventType ({d0c8f168-49b5-47ca-9988-c9922be38dd5}) of ThingClass alphaConnect
+
+
+
+
+ Port
+ The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {b92025c1-8978-4d47-bd3c-4df749dbfd0f})
+
+
+
+
+
+ Return temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: returnTemperature, ID: {6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d})
+----------
+The name of the StateType ({6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d}) of ThingClass alphaConnect
+
+
+
+
+ Return temperature changed
+ The name of the EventType ({6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d}) of ThingClass alphaConnect
+
+
+
+
+
+ Room remote adjuster 1 temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: roomTemperature1, ID: {52f8a581-7b9c-4c15-8aaa-c083763e7820})
+----------
+The name of the StateType ({52f8a581-7b9c-4c15-8aaa-c083763e7820}) of ThingClass alphaConnect
+
+
+
+
+ Room remote adjuster 1 temperature changed
+ The name of the EventType ({52f8a581-7b9c-4c15-8aaa-c083763e7820}) of ThingClass alphaConnect
+
+
+
+
+
+ Room remote adjuster 2 temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: roomTemperature2, ID: {91b57690-5daa-4766-86ae-f205fa0c805a})
+----------
+The name of the StateType ({91b57690-5daa-4766-86ae-f205fa0c805a}) of ThingClass alphaConnect
+
+
+
+
+ Room remote adjuster 2 temperature changed
+ The name of the EventType ({91b57690-5daa-4766-86ae-f205fa0c805a}) of ThingClass alphaConnect
+
+
+
+
+
+ Room remote adjuster 3 temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: roomTemperature3, ID: {a8c7e706-57ba-4572-b6e6-5eec91065ea8})
+----------
+The name of the StateType ({a8c7e706-57ba-4572-b6e6-5eec91065ea8}) of ThingClass alphaConnect
+
+
+
+
+ Room remote adjuster 3 temperature changed
+ The name of the EventType ({a8c7e706-57ba-4572-b6e6-5eec91065ea8}) of ThingClass alphaConnect
+
+
+
+
+
+ Room temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: temperature, ID: {0b978a61-4340-443a-8ea6-69217a593508})
+----------
+The name of the StateType ({0b978a61-4340-443a-8ea6-69217a593508}) of ThingClass alphaConnect
+
+
+
+
+ Room temperature changed
+ The name of the EventType ({0b978a61-4340-443a-8ea6-69217a593508}) of ThingClass alphaConnect
+
+
+
+
+ Set target room temperature
+ The name of the ActionType ({2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) of ThingClass alphaConnect
+
+
+
+
+
+ Solar collector temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: solarCollectorTemperature, ID: {d4484305-eb0f-418b-bb15-f71b08e6fce1})
+----------
+The name of the StateType ({d4484305-eb0f-418b-bb15-f71b08e6fce1}) of ThingClass alphaConnect
+
+
+
+
+ Solar collector temperature changed
+ The name of the EventType ({d4484305-eb0f-418b-bb15-f71b08e6fce1}) of ThingClass alphaConnect
+
+
+
+
+
+ Solar storage tank temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: solarStorageTankTemperature, ID: {75089369-c224-4fe5-91d1-9bdc5578f0a5})
+----------
+The name of the StateType ({75089369-c224-4fe5-91d1-9bdc5578f0a5}) of ThingClass alphaConnect
+
+
+
+
+ Solar storage tank temperature changed
+ The name of the EventType ({75089369-c224-4fe5-91d1-9bdc5578f0a5}) of ThingClass alphaConnect
+
+
+
+
+
+ Supply air temperature
+ The name of the ParamType (ThingClass: alphaConnect, EventType: supplyAirTemperature, ID: {90d88164-3083-47c7-a396-b338bd9ea54c})
+----------
+The name of the StateType ({90d88164-3083-47c7-a396-b338bd9ea54c}) of ThingClass alphaConnect
+
+
+
+
+ Supply air temperature changed
+ The name of the EventType ({90d88164-3083-47c7-a396-b338bd9ea54c}) of ThingClass alphaConnect
+
+
+
+
+
+ System status
+ The name of the ParamType (ThingClass: alphaConnect, EventType: systemStatus, ID: {5a21a131-f3a0-4ebd-9b2e-12eb25c84853})
+----------
+The name of the StateType ({5a21a131-f3a0-4ebd-9b2e-12eb25c84853}) of ThingClass alphaConnect
+
+
+
+
+ System status changed
+ The name of the EventType ({5a21a131-f3a0-4ebd-9b2e-12eb25c84853}) of ThingClass alphaConnect
+
+
+
+
+
+
+ Target room temperature
+ The name of the ParamType (ThingClass: alphaConnect, ActionType: targetTemperature, ID: {2e8f44c5-c4e3-4539-b85b-45898bbbf6c2})
+----------
+The name of the ParamType (ThingClass: alphaConnect, EventType: targetTemperature, ID: {2e8f44c5-c4e3-4539-b85b-45898bbbf6c2})
+----------
+The name of the StateType ({2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) of ThingClass alphaConnect
+
+
+
+
+ Target room temperature changed
+ The name of the EventType ({2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) of ThingClass alphaConnect
+
+
+
+
+ alpha Innotec
+ The name of the vendor ({2c2101ae-7bb8-4012-97ba-cedf4cecd924})
+
+
+
+
+ alpha connect
+ The name of the ThingClass ({c5437b68-cfd2-4ec8-bad6-006fb5e8a8da})
+
+
+
+
+ alpha innotec
+ The name of the plugin AlphaInnotec ({b3225a18-e9b0-443e-8c88-802d3132f94d})
+
+
+
+
+ IntegrationPluginAlphaInnotec
+
+
+ The network device discovery is not available.
+
+
+
+
+ No IP address given
+
+
+
+
diff --git a/debian/control b/debian/control
index b5d0f21..e11bcbf 100644
--- a/debian/control
+++ b/debian/control
@@ -46,6 +46,21 @@ Description: The main libraries and header files for developing with nymea sunsp
.
This package will install the development files for nymea sunspec.
+Package: nymea-plugin-alphainnotec
+Architecture: any
+Section: libs
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+ nymea-plugins-modbus-translations
+Description: nymea.io plugin for alpha innotec heat pumps
+ 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 alpha innotec heat pumps
+
Package: nymea-plugin-drexelundweiss
Architecture: any
diff --git a/debian/nymea-plugin-alphainnotec.install.in b/debian/nymea-plugin-alphainnotec.install.in
new file mode 100644
index 0000000..ed59ad5
--- /dev/null
+++ b/debian/nymea-plugin-alphainnotec.install.in
@@ -0,0 +1 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginalphainnotec.so
diff --git a/modbus/modbusdatautils.cpp b/modbus/modbusdatautils.cpp
new file mode 100644
index 0000000..d3fea27
--- /dev/null
+++ b/modbus/modbusdatautils.cpp
@@ -0,0 +1,268 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 "modbusdatautils.h"
+
+#include
+
+ModbusDataUtils::ModbusDataUtils()
+{
+
+}
+
+quint16 ModbusDataUtils::convertToUInt16(const QVector ®isters)
+{
+ Q_ASSERT_X(registers.count() == 1, "ModbusDataUtils", "invalid raw data size for converting value to quint16");
+ return registers.at(0);
+}
+
+qint16 ModbusDataUtils::convertToInt16(const QVector ®isters)
+{
+ Q_ASSERT_X(registers.count() == 1, "ModbusDataUtils", "invalid raw data size for converting value to qint16");
+ return static_cast(registers.at(0));
+}
+
+quint32 ModbusDataUtils::convertToUInt32(const QVector ®isters, ByteOrder byteOrder)
+{
+ Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to quint32");
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ if (byteOrder == ByteOrderBigEndian) {
+ inputStream << registers.at(0);
+ inputStream << registers.at(1);
+ } else {
+ inputStream << registers.at(1);
+ inputStream << registers.at(0);
+ }
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ quint32 result = 0;
+ outputStream >> result;
+ return result;
+}
+
+qint32 ModbusDataUtils::convertToInt32(const QVector ®isters, ByteOrder byteOrder)
+{
+ Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to quint32");
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ if (byteOrder == ByteOrderBigEndian) {
+ inputStream << registers.at(0);
+ inputStream << registers.at(1);
+ } else {
+ inputStream << registers.at(1);
+ inputStream << registers.at(0);
+ }
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ qint32 result = 0;
+ outputStream >> result;
+ return result;
+}
+
+quint64 ModbusDataUtils::convertToUInt64(const QVector ®isters, ByteOrder byteOrder)
+{
+ Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to quint64");
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ if (byteOrder == ByteOrderBigEndian) {
+ inputStream << registers.at(0);
+ inputStream << registers.at(1);
+ inputStream << registers.at(2);
+ inputStream << registers.at(3);
+ } else {
+ inputStream << registers.at(3);
+ inputStream << registers.at(2);
+ inputStream << registers.at(1);
+ inputStream << registers.at(0);
+ }
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ quint64 result = 0;
+ outputStream >> result;
+ return result;
+}
+
+qint64 ModbusDataUtils::convertToInt64(const QVector ®isters, ByteOrder byteOrder)
+{
+ Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to qint64");
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ if (byteOrder == ByteOrderBigEndian) {
+ inputStream << registers.at(0);
+ inputStream << registers.at(1);
+ inputStream << registers.at(2);
+ inputStream << registers.at(3);
+ } else {
+ inputStream << registers.at(3);
+ inputStream << registers.at(2);
+ inputStream << registers.at(1);
+ inputStream << registers.at(0);
+ }
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ qint64 result = 0;
+ outputStream >> result;
+ return result;
+}
+
+QString ModbusDataUtils::convertToString(const QVector ®isters)
+{
+ QByteArray bytes;
+ QDataStream stream(&bytes, QIODevice::WriteOnly);
+ for (int i = 0; i < registers.count(); i++) {
+ stream << registers.at(i);
+ }
+
+ return QString::fromUtf8(bytes).trimmed();
+}
+
+float ModbusDataUtils::convertToFloat32(const QVector ®isters, ByteOrder byteOrder)
+{
+ Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to float32");
+ quint32 rawValue = ModbusDataUtils::convertToUInt32(registers, byteOrder);
+ float value = 0;
+ memcpy(&value, &rawValue, sizeof(quint32));
+ return value;
+}
+
+double ModbusDataUtils::convertToFloat64(const QVector ®isters, ByteOrder byteOrder)
+{
+
+ Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to float64");
+ quint64 rawValue = ModbusDataUtils::convertToUInt64(registers, byteOrder);
+ double value = 0;
+ memcpy(&value, &rawValue, sizeof(quint64));
+ return value;
+}
+
+QVector ModbusDataUtils::convertFromUInt16(quint16 value)
+{
+ return QVector() << value;
+}
+
+QVector ModbusDataUtils::convertFromInt16(qint16 value)
+{
+ return ModbusDataUtils::convertFromUInt16(static_cast(value));
+}
+
+QVector ModbusDataUtils::convertFromUInt32(quint32 value, ByteOrder byteOrder)
+{
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ inputStream << value;
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ QVector values;
+ for (int i = 0; i < 2; i++) {
+ quint16 registerValue = 0;
+ outputStream >> registerValue;
+ if (byteOrder == ByteOrderBigEndian) {
+ values.append(registerValue);
+ } else {
+ values.prepend(registerValue);
+ }
+ }
+
+ return values;
+}
+
+QVector ModbusDataUtils::convertFromInt32(qint32 value, ByteOrder byteOrder)
+{
+ return ModbusDataUtils::convertFromUInt32(static_cast(value), byteOrder);
+}
+
+QVector ModbusDataUtils::convertFromUInt64(quint64 value, ByteOrder byteOrder)
+{
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ inputStream << value;
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ QVector values;
+ for (int i = 0; i < 4; i++) {
+ quint16 registerValue = 0;
+ outputStream >> registerValue;
+ if (byteOrder == ByteOrderBigEndian) {
+ values.append(registerValue);
+ } else {
+ values.prepend(registerValue);
+ }
+ }
+ return values;
+}
+
+QVector ModbusDataUtils::convertFromInt64(qint64 value, ByteOrder byteOrder)
+{
+ QByteArray data;
+ QDataStream inputStream(&data, QIODevice::WriteOnly);
+ inputStream << value;
+
+ QDataStream outputStream(&data, QIODevice::ReadOnly);
+ QVector values;
+ for (int i = 0; i < 4; i++) {
+ quint16 registerValue = 0;
+ outputStream >> registerValue;
+ if (byteOrder == ByteOrderBigEndian) {
+ values.append(registerValue);
+ } else {
+ values.prepend(registerValue);
+ }
+ }
+ return values;
+}
+
+QVector ModbusDataUtils::convertFromString(const QString &value, quint16 stringLength)
+{
+ Q_ASSERT_X(value.length() <= stringLength, "ModbusDataUtils", "cannot convert a string which is bigger than the desired register vector.");
+ QByteArray data = value.toLatin1() + QByteArray('\0', stringLength - value.count());
+ QDataStream stream(&data, QIODevice::ReadOnly);
+ QVector values;
+ for (int i = 0; i < stringLength; i++) {
+ quint16 registerValue = 0;
+ stream >> registerValue;
+ values.append(registerValue);
+ }
+ return values;
+}
+
+QVector ModbusDataUtils::convertFromFloat32(float value, ByteOrder byteOrder)
+{
+ quint32 rawValue = 0;
+ memcpy(&rawValue, &value, sizeof(float));
+ return ModbusDataUtils::convertFromUInt32(rawValue, byteOrder);
+}
+
+QVector ModbusDataUtils::convertFromFloat64(double value, ByteOrder byteOrder)
+{
+ quint64 rawValue = 0;
+ memcpy(&rawValue, &value, sizeof(double));
+ return ModbusDataUtils::convertFromUInt64(rawValue, byteOrder);
+}
diff --git a/modbus/modbusdatautils.h b/modbus/modbusdatautils.h
new file mode 100644
index 0000000..f7a4bc6
--- /dev/null
+++ b/modbus/modbusdatautils.h
@@ -0,0 +1,107 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 MODBUSDATAUTILS_H
+#define MODBUSDATAUTILS_H
+
+#include
+#include
+
+class ModbusDataUtils
+{
+ Q_GADGET
+public:
+ enum Access {
+ AccessReadOnly,
+ AccessWriteOnly,
+ AccessReadWrite
+ };
+ Q_ENUM(Access)
+
+ enum ByteOrder {
+ ByteOrderLittleEndian,
+ ByteOrderBigEndian
+ };
+ Q_ENUM(ByteOrder)
+
+ enum DataType {
+ UInt8,
+ UInt16,
+ Uint32,
+ Uint64,
+ Int8,
+ Int16,
+ Int32,
+ Int64,
+ Float,
+ Float64,
+ String,
+ Bool
+ };
+ Q_ENUM(DataType)
+
+ typedef struct ModbusRegister {
+ quint16 address;
+ quint16 size;
+ DataType dataType;
+ Access access;
+ QString description;
+ QString unit;
+ QVector rawData;
+ } ModbusRegister;
+
+ typedef QVector ModbusRegisters;
+
+ explicit ModbusDataUtils();
+
+ // Convert to
+ static quint16 convertToUInt16(const QVector ®isters);
+ static qint16 convertToInt16(const QVector ®isters);
+ static quint32 convertToUInt32(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static qint32 convertToInt32(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static quint64 convertToUInt64(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static qint64 convertToInt64(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static QString convertToString(const QVector ®isters);
+ static float convertToFloat32(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static double convertToFloat64(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian);
+
+ // Convert from
+ static QVector convertFromUInt16(quint16 value);
+ static QVector convertFromInt16(qint16 value);
+ static QVector convertFromUInt32(quint32 value, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static QVector convertFromInt32(qint32 value, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static QVector convertFromUInt64(quint64 value, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static QVector convertFromInt64(qint64 value, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static QVector convertFromString(const QString &value, quint16 stringLength);
+ static QVector convertFromFloat32(float value, ByteOrder byteOrder = ByteOrderLittleEndian);
+ static QVector convertFromFloat64(double value, ByteOrder byteOrder = ByteOrderLittleEndian);
+};
+
+#endif // MODBUSDATAUTILS_H
diff --git a/modbus/tools/README.md b/modbus/tools/README.md
new file mode 100644
index 0000000..400b082
--- /dev/null
+++ b/modbus/tools/README.md
@@ -0,0 +1,139 @@
+# Generate a modbus read class
+
+In order to make the plugin development for modbus TCP devices much easier and faster, a small tool has been developed to generate a modbus TCP master based class providing get and set methods for the registers and property changed signals.
+
+The workflow looks like this:
+
+* Write the `registers.json` file containing all register information you are interested to.
+* Run the script and provide the class name, output directory and the path to the JSON file
+* Include the generated class in your plugin, connect the `Changed()` signal and update the thing state within the plugin.
+
+
+The class will provide 2 main methods for fetching information from the modbus device:
+
+* `initialize()` will read all registers with `"readSchedule": "init"` and emits the signal `initializationFinished()` once all replies returned.
+* `update()` can be used to update all registers with `"readSchedule": "update"`. The class will then fetch each register and update the specified value internally. If the value has changed, the `Changed()` signal will be emitted.
+
+The reulting class will inhert from the `ModbusTCPMaster` class, providing easy access to all possible modbus operations and inform about the connected state.
+
+
+# JSON format
+
+The basic structure of the modbus register JSON looks like following example:
+
+```
+{
+ "endianness": "BigEndian",
+ "enums": [
+ {
+ "name": "NameOfEnum",
+ "values": [
+ {
+ "key": "EnumValue1",
+ "value": 0
+ },
+ {
+ "key": "EnumValue2",
+ "value": 1
+ },
+ ....
+ ]
+ }
+ ],
+ "registers": [
+ {
+ "id": "registerPropertyName",
+ "address": 4,
+ "size": 1,
+ "type": "uint16",
+ "readSchedule": "init",
+ "description": "Description of the register",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "registerWithEnumValues",
+ "address": 5,
+ "size": 1,
+ "type": "uint16",
+ "readSchedule": "update",
+ "enum": "NameOfEnum",
+ "defaultValue": "NameOfEnumEnumValue1",
+ "description": "Description of the enum register like states",
+ "access": "RO"
+ },
+ ...
+ ]
+}
+
+```
+
+## Endianness
+
+When converting multiple registers to one data type (i.e. 2 registers uint16 values to one uint32), the order of the registers are important to align with the endiness of the data receiving.
+
+There are 2 possibilities:
+
+* `BigEndian`: default if not specified: register bytes come in following order `[0, 1, 2, 3]`: `ABCD`
+* `LittleEndian`: register bytes come in following order `[0, 1, 2, 3]`: `CDAB`
+
+## Enums
+
+Many modbus devices provide inforation using `Enums`, indicating a special state trough a defined list of values. If a register implements an enum, you can define it in the `enums` section. The `name` property defines the name of the enum, and the script will generate a c++ enum definition from this section. Each enum value will then be generated using ` = `.
+
+If a register represets an enum, you simply add the property `"enum": "NameOfEnum"` in the register map and the property will be defined using the resulting enum type. All convertion between enum and resulting modbus register value will be done automatically.
+
+## Registers
+
+Earch register will be defined as a property in the resulting class modbus TCP class providing easy access to the register data.
+
+* `id`: Mandatory. The id defines the name of the property used in the resulting class.
+* `address`: Mandatory. The modbus address of the register.
+* `size`: Mandatory. The amount of registers to read for the property.
+* `type`: Mandatory. The data type of this property. Available data types are:
+ * `uint16` : will be converted to `quint16`
+ * `int16` : will be converted to `qint16`
+ * `uint32` : will be converted to `quint32`
+ * `int32` : will be converted to `qint32`
+ * `uint64` : will be converted to `quint64`
+ * `int64` : will be converted to `qint64`
+ * `float`: will be converted to `float`
+ * `float64`: will be converted to `double`
+ * `string` : will be converted to `QString`
+* `readSchedule`: Optional. Defines when the register needs to be fetched. If no read schedule has been defined, the class will provide only the update methods, but will not read the value during `initialize()` or `update()` calls. Possible values are:
+ * `init`: The register will be fetched during initialization. Once all `init `registers have been fetched, the `initializationFinished()` signal will be emitted.
+ * `update`: The register will be feched each time the `update()` method will be called.
+* `enum`: Optional: If the given data type represents an enum value, this propery can be set to the name of the used enum from the `enum` definition. The class will take care internally about the data convertion from and to the enum values.
+* `description`: Mandatory. A clear description of the register.
+* `unit`: Optional. Represents the unit of this register value.
+* `registerType`: Optional. Represents the type of the register and how to read/write it. Default is `holdingRegister`. Possible values are:
+ * `holdingRegister`
+ * `inputRegister`
+ * `coils`
+ * `discreteInputs`
+* `access`: Mandatory. Describes the access to this register. Possible valies are:
+ * `RO`: Read only access. Only the get method and the changed singal will be defined.
+ * `RW`: Read and write access. Also a set mehtod will be defined.
+ * `WO`: Write only. Only the set method will be defined.
+* `scaleFactor`: Optional. The name of the scale factor register to convert this value to float. `floatValue = intValue * 10^scaleFactor value`. The scale factor value is normally a `int16` value, i.e. -10 or 10
+* `staticScaleFactor`: Optional. Use this static scale factor to convert this register value to float. `floatValue = registerValue * 10^staticScaleFactor`. The scale factor value is normally a `int16` value, i.e. -10 or 10
+* `defaultValue`: Optional. The value for initializing the property.
+
+# Example
+
+Change into your plugin sub directory.
+Assuming you wrote the registers.json file you can run now following command to generate your modbus class:
+
+`$ python3 ../modbus/tools/generate-connection.py -j registers.json -o . -c MyModbusConnection`
+
+You the result will be a header and a source file called:
+
+* `mymodbusconnection.h`
+* `mymodbusconnection.cpp`
+
+You can include this class in your project and provide one connection per thing.
+
+
+
+
diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py
new file mode 100644
index 0000000..83f3214
--- /dev/null
+++ b/modbus/tools/generate-connection.py
@@ -0,0 +1,686 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2021 nymea GmbH
+#
+# This program is 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# To lazy to type all those register plugins, let's make live much easier and generate code from a json register definition
+
+import os
+import re
+import sys
+import json
+import shutil
+import argparse
+import datetime
+
+def convertToAlphaNumeric(text):
+ finalText = ''
+ for character in text:
+ if character.isalnum():
+ finalText += character
+ else:
+ finalText += ' '
+ return finalText
+
+
+def splitCamelCase(text):
+ return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', text)).split()
+
+
+def convertToCamelCase(text, capitalize = False):
+ s = convertToAlphaNumeric(text)
+ s = s.replace("-", " ").replace("_", " ")
+ words = s.split()
+ #print('--> words', words)
+ finalWords = []
+
+ for i in range(len(words)):
+ camelCaseSplit = splitCamelCase(words[i])
+ if len(camelCaseSplit) == 0:
+ finalWords.append(words[i])
+ else:
+ #print('--> camel split words', camelCaseSplit)
+ for j in range(len(camelCaseSplit)):
+ finalWords.append(camelCaseSplit[j])
+
+ if len(finalWords) == 0:
+ return text
+
+ finalText = ''
+ if capitalize:
+ finalText = finalWords[0].capitalize() + ''.join(i.capitalize() for i in finalWords[1:])
+ else:
+ finalText = finalWords[0].lower() + ''.join(i.capitalize() for i in finalWords[1:])
+ #print('Convert camel case:', text, '-->', finalText)
+ return finalText
+
+
+def loadJsonFile(filePath):
+ print('--> Loading JSON file', filePath)
+ jsonFile = open(filePath, 'r')
+ return json.load(jsonFile)
+
+
+def writeLine(fileDescriptor, line = ''):
+ fileDescriptor.write(line + '\n')
+
+
+def writeLicenseHeader(fileDescriptor):
+ writeLine(fileDescriptor, '/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *')
+ writeLine(fileDescriptor, '*')
+ writeLine(fileDescriptor, '* Copyright 2013 - %s, nymea GmbH' % datetime.datetime.now().year)
+ writeLine(fileDescriptor, '* Contact: contact@nymea.io')
+ writeLine(fileDescriptor, '*')
+ writeLine(fileDescriptor, '* This fileDescriptor is part of nymea.')
+ writeLine(fileDescriptor, '* This project including source code and documentation is protected by')
+ writeLine(fileDescriptor, '* copyright law, and remains the property of nymea GmbH. All rights, including')
+ writeLine(fileDescriptor, '* reproduction, publication, editing and translation, are reserved. The use of')
+ writeLine(fileDescriptor, '* this project is subject to the terms of a license agreement to be concluded')
+ writeLine(fileDescriptor, '* with nymea GmbH in accordance with the terms of use of nymea GmbH, available')
+ writeLine(fileDescriptor, '* under https://nymea.io/license')
+ writeLine(fileDescriptor, '*')
+ writeLine(fileDescriptor, '* GNU Lesser General Public License Usage')
+ writeLine(fileDescriptor, '* Alternatively, this project may be redistributed and/or modified under the')
+ writeLine(fileDescriptor, '* terms of the GNU Lesser General Public License as published by the Free')
+ writeLine(fileDescriptor, '* Software Foundation; version 3. This project is distributed in the hope that')
+ writeLine(fileDescriptor, '* it will be useful, but WITHOUT ANY WARRANTY; without even the implied')
+ writeLine(fileDescriptor, '* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU')
+ writeLine(fileDescriptor, '* Lesser General Public License for more details.')
+ writeLine(fileDescriptor, '*')
+ writeLine(fileDescriptor, '* You should have received a copy of the GNU Lesser General Public License')
+ writeLine(fileDescriptor, '* along with this project. If not, see .')
+ writeLine(fileDescriptor, '*')
+ writeLine(fileDescriptor, '* For any further details and any questions please contact us under')
+ writeLine(fileDescriptor, '* contact@nymea.io or see our FAQ/Licensing Information on')
+ writeLine(fileDescriptor, '* https://nymea.io/license/faq')
+ writeLine(fileDescriptor, '*')
+ writeLine(fileDescriptor, '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */')
+ writeLine(fileDescriptor)
+
+
+def writeEnumDefinition(fileDescriptor, enumDefinition):
+ print('Writing enum', enumDefinition)
+ enumName = enumDefinition['name']
+ enumValues = enumDefinition['values']
+ writeLine(fileDescriptor, ' enum %s {' % enumName)
+ for i in range(len(enumValues)):
+ enumData = enumValues[i]
+ line = (' %s%s = %s' % (enumName, enumData['key'], enumData['value']))
+ if i < (len(enumValues) - 1):
+ line += ','
+
+ writeLine(fileDescriptor, line)
+
+ writeLine(fileDescriptor, ' };')
+ writeLine(fileDescriptor, ' Q_ENUM(%s)' % enumName)
+ writeLine(fileDescriptor)
+
+
+def getCppDataType(registerDefinition, rawType = False):
+ if not rawType:
+ if 'enum' in registerDefinition:
+ return registerDefinition['enum']
+
+ if 'scaleFactor' in registerDefinition or 'staticScaleFactor' in registerDefinition:
+ return 'float'
+
+ if registerDefinition['type'] == 'uint16':
+ return 'quint16'
+
+ if registerDefinition['type'] == 'int16':
+ return 'qint16'
+
+ if registerDefinition['type'] == 'uint32':
+ return 'quint32'
+
+ if registerDefinition['type'] == 'int32':
+ return 'qint32'
+
+ if registerDefinition['type'] == 'uint64':
+ return 'quint64'
+
+ if registerDefinition['type'] == 'int64':
+ return 'qint64'
+
+ if registerDefinition['type'] == 'float':
+ return 'float'
+
+ if registerDefinition['type'] == 'float64':
+ return 'double'
+
+ if registerDefinition['type'] == 'string':
+ return 'QString'
+
+
+def getConversionToValueMethod(registerDefinition):
+ # Handle enums
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition, True)
+
+ if 'enum' in registerDefinition:
+ enumName = registerDefinition['enum']
+ if registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName))
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName))
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, endianness))
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, endianness))
+
+ # Handle scale factors
+ if 'scaleFactor' in registerDefinition:
+ scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor']
+ if registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactorProperty))
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactorProperty))
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactorProperty, endianness))
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactorProperty, endianness))
+
+ elif 'staticScaleFactor' in registerDefinition:
+ scaleFactor = registerDefinition['staticScaleFactor']
+ if registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactor))
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactor))
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactor, endianness))
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactor, endianness))
+
+ # Handle default types
+ elif registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertFromUInt16(%s)' % propertyName)
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertFromInt16(%s)' % propertyName)
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertFromUInt32(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness))
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertFromInt32(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness))
+ elif registerDefinition['type'] == 'uint64':
+ return ('ModbusDataUtils::convertFromUInt64(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness))
+ elif registerDefinition['type'] == 'int64':
+ return ('ModbusDataUtils::convertFromInt64(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness))
+ elif registerDefinition['type'] == 'float':
+ return ('ModbusDataUtils::convertFromFloat32(%s, ModbusDataUtils::ByteOrder%s)' % propertyName, endianness)
+ elif registerDefinition['type'] == 'float64':
+ return ('ModbusDataUtils::convertFromFloat64(%s, ModbusDataUtils::ByteOrder%s)' % propertyName, endianness)
+ elif registerDefinition['type'] == 'string':
+ return ('ModbusDataUtils::convertFromString(%s)' % propertyName)
+
+
+def getValueConversionMethod(registerDefinition):
+ # Handle enums
+ if 'enum' in registerDefinition:
+ enumName = registerDefinition['enum']
+ if registerDefinition['type'] == 'uint16':
+ return ('static_cast<%s>(ModbusDataUtils::convertToUInt16(unit.values()))' % (enumName))
+ elif registerDefinition['type'] == 'int16':
+ return ('static_cast<%s>(ModbusDataUtils::convertToInt16(unit.values()))' % (enumName))
+ elif registerDefinition['type'] == 'uint32':
+ return ('static_cast<%s>(ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s))' % (enumName, endianness))
+ elif registerDefinition['type'] == 'int32':
+ return ('static_cast<%s>(ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s))' % (enumName, endianness))
+
+ # Handle scale factors
+ if 'scaleFactor' in registerDefinition:
+ scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor']
+ if registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertToInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactorProperty))
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactorProperty))
+
+ elif 'staticScaleFactor' in registerDefinition:
+ scaleFactor = registerDefinition['staticScaleFactor']
+ if registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactor))
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertToInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactor))
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactor))
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactor))
+
+ # Handle default types
+ elif registerDefinition['type'] == 'uint16':
+ return ('ModbusDataUtils::convertToUInt16(unit.values())')
+ elif registerDefinition['type'] == 'int16':
+ return ('ModbusDataUtils::convertToInt16(unit.values())')
+ elif registerDefinition['type'] == 'uint32':
+ return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness)
+ elif registerDefinition['type'] == 'int32':
+ return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness)
+ elif registerDefinition['type'] == 'uint64':
+ return ('ModbusDataUtils::convertToUInt64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness)
+ elif registerDefinition['type'] == 'int64':
+ return ('ModbusDataUtils::convertToInt64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness)
+ elif registerDefinition['type'] == 'float':
+ return ('ModbusDataUtils::convertToFloat32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness)
+ elif registerDefinition['type'] == 'float64':
+ return ('ModbusDataUtils::convertToFloat64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness)
+ elif registerDefinition['type'] == 'string':
+ return ('ModbusDataUtils::convertToString(unit.values())')
+
+
+def writePropertyGetSetMethodDeclarations(fileDescriptor, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ if 'unit' in registerDefinition and registerDefinition['unit'] != '':
+ writeLine(fileDescriptor, ' /* %s [%s] - Address: %s, Size: %s */' % (registerDefinition['description'], registerDefinition['unit'], registerDefinition['address'], registerDefinition['size']))
+ else:
+ writeLine(fileDescriptor, ' /* %s - Address: %s, Size: %s */' % (registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
+
+ writeLine(fileDescriptor, ' %s %s() const;' % (propertyTyp, propertyName))
+
+ # Check if we require a set method
+ if registerDefinition['access'] == 'RW' or registerDefinition['access'] == 'WO':
+ writeLine(fileDescriptor, ' QModbusReply *set%s(%s %s);' % (propertyName[0].upper() + propertyName[1:], propertyTyp, propertyName))
+
+ writeLine(fileDescriptor)
+
+
+def writePropertyGetSetMethodImplementations(fileDescriptor, className, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ # Get
+ if 'enum' in registerDefinition:
+ writeLine(fileDescriptor, '%s::%s %s::%s() const' % (className, propertyTyp, className, propertyName))
+ else:
+ writeLine(fileDescriptor, '%s %s::%s() const' % (propertyTyp, className, propertyName))
+
+ writeLine(fileDescriptor, '{')
+ writeLine(fileDescriptor, ' return m_%s;' % propertyName)
+ writeLine(fileDescriptor, '}')
+ writeLine(fileDescriptor)
+
+ # Check if we require a set method
+ if registerDefinition['access'] == 'RW' or registerDefinition['access'] == 'WO':
+ writeLine(fileDescriptor, 'QModbusReply *%s::set%s(%s %s)' % (className, propertyName[0].upper() + propertyName[1:], propertyTyp, propertyName))
+ writeLine(fileDescriptor, '{')
+
+ writeLine(fileDescriptor, ' QVector values = %s;' % getConversionToValueMethod(registerDefinition))
+ writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Write \\"%s\\" register:" << %s << "size:" << %s << values;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
+ if registerDefinition['registerType'] == 'holdingRegister':
+ writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, values.count());' % (registerDefinition['address']))
+
+ # TODO: other write methods
+
+ writeLine(fileDescriptor, ' request.setValues(values);')
+ writeLine(fileDescriptor, ' return sendWriteRequest(request, m_slaveId);')
+ writeLine(fileDescriptor, '}')
+ writeLine(fileDescriptor)
+
+
+
+def writePropertyUpdateMethodDeclarations(fileDescriptor, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
+ continue
+
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ writeLine(fileDescriptor, ' void update%s();' % (propertyName[0].upper() + propertyName[1:]))
+
+
+def writePropertyUpdateMethodImplementations(fileDescriptor, className, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
+ continue
+
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ writeLine(fileDescriptor, 'void %s::update%s()' % (className, propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, '{')
+ writeLine(fileDescriptor, ' // Update registers from %s' % registerDefinition['description'])
+ writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Read \\"%s\\" register:" << %s << "size:" << %s;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
+ writeLine(fileDescriptor, ' QModbusReply *reply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, ' if (reply) {')
+ writeLine(fileDescriptor, ' if (!reply->isFinished()) {')
+ writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);')
+ writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){')
+ writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {')
+ writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();')
+ writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from \\"%s\\" register" << %s << "size:" << %s << unit.values();' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
+
+ # FIXME: introduce bool and check register type for parsing
+ writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition)))
+ writeLine(fileDescriptor, ' if (m_%s != received%s) {' % (propertyName, propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, ' m_%s = received%s;' % (propertyName, propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName))
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor, ' });')
+ writeLine(fileDescriptor)
+ writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){')
+ writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while updating \\"%s\\" registers from" << hostAddress().toString() << error << reply->errorString();' % (className, registerDefinition['description']))
+ writeLine(fileDescriptor, ' emit reply->finished(); // To make sure it will be deleted')
+ writeLine(fileDescriptor, ' });')
+ writeLine(fileDescriptor, ' } else {')
+ writeLine(fileDescriptor, ' delete reply; // Broadcast reply returns immediatly')
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor, ' } else {')
+ writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << errorString();' % (className, registerDefinition['description']))
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor, '}')
+ writeLine(fileDescriptor)
+
+
+def writeInternalPropertyReadMethodDeclarations(fileDescriptor, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ writeLine(fileDescriptor, ' QModbusReply *read%s();' % (propertyName[0].upper() + propertyName[1:]))
+
+
+def writeInternalPropertyReadMethodImplementations(fileDescriptor, className, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ writeLine(fileDescriptor, 'QModbusReply *%s::read%s()' % (className, propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, '{')
+
+ # Build request depending on the register type
+ if registerDefinition['registerType'] == 'inputRegister':
+ writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, %s, %s);' % (registerDefinition['address'], registerDefinition['size']))
+ elif registerDefinition['registerType'] == 'discreteInputs':
+ writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, %s, %s);' % (registerDefinition['address'], registerDefinition['size']))
+ elif registerDefinition['registerType'] == 'coils':
+ writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, %s, %s);' % (registerDefinition['address'], registerDefinition['size']))
+ else:
+ #Default to holdingRegister
+ writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, %s);' % (registerDefinition['address'], registerDefinition['size']))
+
+ writeLine(fileDescriptor, ' return sendReadRequest(request, m_slaveId);')
+ writeLine(fileDescriptor, '}')
+ writeLine(fileDescriptor)
+
+
+def writePropertyChangedSignals(fileDescriptor, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ if propertyTyp == 'QString':
+ writeLine(fileDescriptor, ' void %sChanged(const %s &%s);' % (propertyName, propertyTyp, propertyName))
+ else:
+ writeLine(fileDescriptor, ' void %sChanged(%s %s);' % (propertyName, propertyTyp, propertyName))
+
+
+def writePrivatePropertyMembers(fileDescriptor, registerDefinitions):
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ if 'defaultValue' in registerDefinition:
+ writeLine(fileDescriptor, ' %s m_%s = %s;' % (propertyTyp, propertyName, registerDefinition['defaultValue']))
+ else:
+ writeLine(fileDescriptor, ' %s m_%s;' % (propertyTyp, propertyName))
+
+
+def writeInitializeMethod(fileDescriptor, className, registerDefinitions):
+ writeLine(fileDescriptor, 'void %s::initialize()' % (className))
+ writeLine(fileDescriptor, '{')
+
+ # First check if there are any init registers
+ initRequired = False
+ for registerDefinition in registerDefinitions:
+ if registerDefinition['readSchedule'] == 'init':
+ initRequired = True
+ break
+
+ if initRequired:
+ writeLine(fileDescriptor, ' QModbusReply *reply = nullptr;')
+ writeLine(fileDescriptor)
+ writeLine(fileDescriptor, ' if (!m_pendingInitReplies.isEmpty()) {')
+ writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Tried to initialize but there are still some init replies pending.";' % className)
+ writeLine(fileDescriptor, ' return;')
+ writeLine(fileDescriptor, ' }')
+
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+
+ if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
+ writeLine(fileDescriptor)
+ writeLine(fileDescriptor, ' // Read %s' % registerDefinition['description'])
+ writeLine(fileDescriptor, ' reply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, ' if (reply) {')
+ writeLine(fileDescriptor, ' if (!reply->isFinished()) {')
+ writeLine(fileDescriptor, ' m_pendingInitReplies.append(reply);')
+ writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);')
+ writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){')
+ writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {')
+ writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();')
+ writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition)))
+ writeLine(fileDescriptor, ' if (m_%s != received%s) {' % (propertyName, propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, ' m_%s = received%s;' % (propertyName, propertyName[0].upper() + propertyName[1:]))
+ writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName))
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor)
+ writeLine(fileDescriptor, ' m_pendingInitReplies.removeAll(reply);')
+ writeLine(fileDescriptor, ' verifyInitFinished();')
+ writeLine(fileDescriptor, ' });')
+ writeLine(fileDescriptor)
+ writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){')
+ writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << error << reply->errorString();' % (className, registerDefinition['description']))
+ writeLine(fileDescriptor, ' emit reply->finished(); // To make sure it will be deleted')
+ writeLine(fileDescriptor, ' });')
+ writeLine(fileDescriptor, ' } else {')
+ writeLine(fileDescriptor, ' delete reply; // Broadcast reply returns immediatly')
+ writeLine(fileDescriptor, ' }')
+ writeLine(fileDescriptor, ' } else {')
+ writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << errorString();' % (className, registerDefinition['description']))
+ writeLine(fileDescriptor, ' }')
+
+
+ else:
+ writeLine(fileDescriptor, ' // No init registers defined. Nothing to be done and we are finished.')
+ writeLine(fileDescriptor, ' emit initializationFinished();')
+
+ writeLine(fileDescriptor, '}')
+ writeLine(fileDescriptor)
+
+
+def writeUpdateMethod(fileDescriptor, className, registerDefinitions):
+ writeLine(fileDescriptor, 'void %s::update()' % (className))
+ writeLine(fileDescriptor, '{')
+ for registerDefinition in registerDefinitions:
+ propertyName = registerDefinition['id']
+ if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'update':
+ writeLine(fileDescriptor, ' update%s();' % (propertyName[0].upper() + propertyName[1:]))
+
+ writeLine(fileDescriptor, '}')
+ writeLine(fileDescriptor)
+
+
+############################################################################################
+# Main
+############################################################################################
+
+parser = argparse.ArgumentParser(description='Generate modbus tcp connection class from JSON register definitions file.')
+parser.add_argument('-j', '--json', metavar='', help='The JSON file containing the register definitions.')
+parser.add_argument('-o', '--output-directory', metavar='', help='The output directory for the resulting class.')
+parser.add_argument('-c', '--class-name', metavar='', help='The name of the resulting class.')
+args = parser.parse_args()
+
+registerJson = loadJsonFile(args.json)
+scriptPath = os.path.dirname(os.path.realpath(sys.argv[0]))
+outputDirectory = os.path.realpath(args.output_directory)
+className = args.class_name
+
+headerFileName = className.lower() + '.h'
+sourceFileName = className.lower() + '.cpp'
+
+headerFilePath = os.path.join(outputDirectory, headerFileName)
+sourceFilePath = os.path.join(outputDirectory, sourceFileName)
+
+print('Scrip path: %s' % scriptPath)
+print('Output directory: %s' % outputDirectory)
+print('Class name: %s' % className)
+print('Header file: %s' % headerFileName)
+print('Source file: %s' % sourceFileName)
+print('Header file path: %s' % headerFilePath)
+print('Source file path: %s' % sourceFilePath)
+
+endianness = 'BigEndian'
+if 'endianness' in registerJson:
+ endianness = registerJson['endianness']
+
+
+#############################################################################
+# Write header file
+#############################################################################
+
+headerFile = open(headerFilePath, 'w')
+
+writeLicenseHeader(headerFile)
+writeLine(headerFile, '#ifndef %s_H' % className.upper())
+writeLine(headerFile, '#define %s_H' % className.upper())
+writeLine(headerFile)
+writeLine(headerFile, '#include ')
+writeLine(headerFile)
+writeLine(headerFile, '#include "../modbus/modbusdatautils.h"')
+writeLine(headerFile, '#include "../modbus/modbustcpmaster.h"')
+
+writeLine(headerFile)
+
+# Begin of class
+writeLine(headerFile, 'class %s : public ModbusTCPMaster' % className)
+writeLine(headerFile, '{')
+writeLine(headerFile, ' Q_OBJECT')
+
+# Public members
+writeLine(headerFile, 'public:')
+
+# Enum declarations
+for enumDefinition in registerJson['enums']:
+ writeEnumDefinition(headerFile, enumDefinition)
+
+# Constructor
+writeLine(headerFile, ' explicit %s(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);' % className)
+writeLine(headerFile, ' ~%s() = default;' % className)
+writeLine(headerFile)
+
+# Write registers get method declarations
+writePropertyGetSetMethodDeclarations(headerFile, registerJson['registers'])
+
+# Write init and update method declarations
+writeLine(headerFile, ' virtual void initialize();')
+writeLine(headerFile, ' virtual void update();')
+writeLine(headerFile)
+
+writePropertyUpdateMethodDeclarations(headerFile, registerJson['registers'])
+writeLine(headerFile)
+
+# Write registers value changed signals
+writeLine(headerFile, 'signals:')
+writeLine(headerFile, ' void initializationFinished();')
+writeLine(headerFile)
+writePropertyChangedSignals(headerFile, registerJson['registers'])
+writeLine(headerFile)
+
+# Private members
+writeLine(headerFile, 'private:')
+writeLine(headerFile, ' quint16 m_slaveId = 1;')
+writeLine(headerFile, ' QVector m_pendingInitReplies;')
+writeLine(headerFile)
+writePrivatePropertyMembers(headerFile, registerJson['registers'])
+writeLine(headerFile)
+writeLine(headerFile, ' void verifyInitFinished();')
+writeLine(headerFile)
+writeInternalPropertyReadMethodDeclarations(headerFile, registerJson['registers'])
+writeLine(headerFile)
+
+# End of class
+writeLine(headerFile)
+writeLine(headerFile, '};')
+writeLine(headerFile)
+writeLine(headerFile, 'QDebug operator<<(QDebug debug, %s *%s);' % (className, className[0].lower() + className[1:]))
+writeLine(headerFile)
+writeLine(headerFile, '#endif // %s_H' % className.upper())
+
+headerFile.close()
+
+
+
+#############################################################################
+# Write source file
+#############################################################################
+
+sourceFile = open(sourceFilePath, 'w')
+writeLicenseHeader(sourceFile)
+writeLine(sourceFile)
+writeLine(sourceFile, '#include "%s"' % headerFileName)
+writeLine(sourceFile, '#include "loggingcategories.h"')
+writeLine(sourceFile)
+writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className))
+writeLine(sourceFile)
+
+# Constructor
+writeLine(sourceFile, '%s::%s(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :' % (className, className))
+writeLine(sourceFile, ' ModbusTCPMaster(hostAddress, port, parent),')
+writeLine(sourceFile, ' m_slaveId(slaveId)')
+writeLine(sourceFile, '{')
+writeLine(sourceFile, ' ')
+writeLine(sourceFile, '}')
+writeLine(sourceFile)
+
+# Property get methods
+writePropertyGetSetMethodImplementations(sourceFile, className, registerJson['registers'])
+
+# Write init and update method implementation
+writeInitializeMethod(sourceFile, className, registerJson['registers'])
+writeUpdateMethod(sourceFile, className, registerJson['registers'])
+
+# Write update methods
+writePropertyUpdateMethodImplementations(sourceFile, className, registerJson['registers'])
+
+# Write property read method implementations
+writeInternalPropertyReadMethodImplementations(sourceFile, className, registerJson['registers'])
+
+writeLine(sourceFile, 'void %s::verifyInitFinished()' % (className))
+writeLine(sourceFile, '{')
+writeLine(sourceFile, ' if (m_pendingInitReplies.isEmpty()) {')
+writeLine(sourceFile, ' qCDebug(dc%s()) << "Initialization finished of %s" << hostAddress().toString();' % (className, className))
+writeLine(sourceFile, ' emit initializationFinished();')
+writeLine(sourceFile, ' }')
+writeLine(sourceFile, '}')
+writeLine(sourceFile)
+
+# Write the debug print
+debugObjectParamName = className[0].lower() + className[1:]
+writeLine(sourceFile, 'QDebug operator<<(QDebug debug, %s *%s)' % (className, debugObjectParamName))
+writeLine(sourceFile, '{')
+writeLine(sourceFile, ' debug.nospace().noquote() << "%s(" << %s->hostAddress().toString() << ":" << %s->port() << ")" << "\\n";' % (className, debugObjectParamName, debugObjectParamName))
+for registerDefinition in registerJson['registers']:
+ propertyName = registerDefinition['id']
+ propertyTyp = getCppDataType(registerDefinition)
+ line = ('" - %s:" << %s->%s()' % (registerDefinition['description'], debugObjectParamName, propertyName))
+ if 'unit' in registerDefinition and registerDefinition['unit'] != '':
+ line += (' << " [%s]"' % registerDefinition['unit'])
+ writeLine(sourceFile, ' debug.nospace().noquote() << %s << "\\n";' % (line))
+
+writeLine(sourceFile, ' return debug.quote().space();')
+writeLine(sourceFile, '}')
+writeLine(sourceFile)
+
+sourceFile.close()
\ No newline at end of file
diff --git a/nymea-plugins-modbus.pro b/nymea-plugins-modbus.pro
index 2afbded..d27afcf 100644
--- a/nymea-plugins-modbus.pro
+++ b/nymea-plugins-modbus.pro
@@ -5,6 +5,7 @@ CONFIG += ordered
SUBDIRS += libnymea-sunspec
PLUGIN_DIRS = \
+ alphainnotec \
drexelundweiss \
energymeters \
modbuscommander \