diff --git a/alphainnotec/README.md b/alphainnotec/README.md new file mode 100644 index 0000000..ae540d4 --- /dev/null +++ b/alphainnotec/README.md @@ -0,0 +1,16 @@ +# alpha innotec + +Connect nymea to alpha innotec heat pumps. + +## Supported Things + +* alpha connect + +## Requirements + +* The package 'nymea-plugin-alphainnotec' must be installed +* Both devices must be in the same local area network. + +## 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 index ba1caea..d3fea27 100644 --- a/modbus/modbusdatautils.cpp +++ b/modbus/modbusdatautils.cpp @@ -49,13 +49,18 @@ qint16 ModbusDataUtils::convertToInt16(const QVector ®isters) return static_cast(registers.at(0)); } -quint32 ModbusDataUtils::convertToUInt32(const QVector ®isters) +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); - inputStream << registers.at(1); - inputStream << registers.at(0); + 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; @@ -63,13 +68,18 @@ quint32 ModbusDataUtils::convertToUInt32(const QVector ®isters) return result; } -qint32 ModbusDataUtils::convertToInt32(const QVector ®isters) +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); - inputStream << registers.at(1); - inputStream << registers.at(0); + 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; @@ -77,15 +87,22 @@ qint32 ModbusDataUtils::convertToInt32(const QVector ®isters) return result; } -quint64 ModbusDataUtils::convertToUInt64(const QVector ®isters) +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); - inputStream << registers.at(3); - inputStream << registers.at(2); - inputStream << registers.at(1); - inputStream << registers.at(0); + 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; @@ -93,15 +110,22 @@ quint64 ModbusDataUtils::convertToUInt64(const QVector ®isters) return result; } -qint64 ModbusDataUtils::convertToInt64(const QVector ®isters) +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); - inputStream << registers.at(3); - inputStream << registers.at(2); - inputStream << registers.at(1); - inputStream << registers.at(0); + 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; @@ -120,20 +144,20 @@ QString ModbusDataUtils::convertToString(const QVector ®isters) return QString::fromUtf8(bytes).trimmed(); } -float ModbusDataUtils::convertToFloat32(const QVector ®isters) +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); + quint32 rawValue = ModbusDataUtils::convertToUInt32(registers, byteOrder); float value = 0; memcpy(&value, &rawValue, sizeof(quint32)); return value; } -double ModbusDataUtils::convertToFloat64(const QVector ®isters) +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); + quint64 rawValue = ModbusDataUtils::convertToUInt64(registers, byteOrder); double value = 0; memcpy(&value, &rawValue, sizeof(quint64)); return value; @@ -149,28 +173,33 @@ QVector ModbusDataUtils::convertFromInt16(qint16 value) return ModbusDataUtils::convertFromUInt16(static_cast(value)); } -QVector ModbusDataUtils::convertFromUInt32(quint32 value) +QVector ModbusDataUtils::convertFromUInt32(quint32 value, ByteOrder byteOrder) { QByteArray data; - QDataStream inputStream(&data, QIODevice::WriteOnly); - inputStream << value; + 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; - values.prepend(registerValue); - } - return values; + 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) +QVector ModbusDataUtils::convertFromInt32(qint32 value, ByteOrder byteOrder) { - return ModbusDataUtils::convertFromUInt32(static_cast(value)); + return ModbusDataUtils::convertFromUInt32(static_cast(value), byteOrder); } -QVector ModbusDataUtils::convertFromUInt64(quint64 value) +QVector ModbusDataUtils::convertFromUInt64(quint64 value, ByteOrder byteOrder) { QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); @@ -181,12 +210,16 @@ QVector ModbusDataUtils::convertFromUInt64(quint64 value) for (int i = 0; i < 4; i++) { quint16 registerValue = 0; outputStream >> registerValue; - values.prepend(registerValue); + if (byteOrder == ByteOrderBigEndian) { + values.append(registerValue); + } else { + values.prepend(registerValue); + } } return values; } -QVector ModbusDataUtils::convertFromInt64(qint64 value) +QVector ModbusDataUtils::convertFromInt64(qint64 value, ByteOrder byteOrder) { QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); @@ -197,7 +230,11 @@ QVector ModbusDataUtils::convertFromInt64(qint64 value) for (int i = 0; i < 4; i++) { quint16 registerValue = 0; outputStream >> registerValue; - values.prepend(registerValue); + if (byteOrder == ByteOrderBigEndian) { + values.append(registerValue); + } else { + values.prepend(registerValue); + } } return values; } @@ -216,16 +253,16 @@ QVector ModbusDataUtils::convertFromString(const QString &value, quint1 return values; } -QVector ModbusDataUtils::convertFromFloat32(float value) +QVector ModbusDataUtils::convertFromFloat32(float value, ByteOrder byteOrder) { quint32 rawValue = 0; - memcpy(&rawValue, &value, sizeof(float)); - return ModbusDataUtils::convertFromUInt32(rawValue); + memcpy(&rawValue, &value, sizeof(float)); + return ModbusDataUtils::convertFromUInt32(rawValue, byteOrder); } -QVector ModbusDataUtils::convertFromFloat64(double value) +QVector ModbusDataUtils::convertFromFloat64(double value, ByteOrder byteOrder) { quint64 rawValue = 0; memcpy(&rawValue, &value, sizeof(double)); - return ModbusDataUtils::convertFromUInt64(rawValue); + return ModbusDataUtils::convertFromUInt64(rawValue, byteOrder); } diff --git a/modbus/modbusdatautils.h b/modbus/modbusdatautils.h index f442dab..f7a4bc6 100644 --- a/modbus/modbusdatautils.h +++ b/modbus/modbusdatautils.h @@ -45,6 +45,12 @@ public: }; Q_ENUM(Access) + enum ByteOrder { + ByteOrderLittleEndian, + ByteOrderBigEndian + }; + Q_ENUM(ByteOrder) + enum DataType { UInt8, UInt16, @@ -78,24 +84,24 @@ public: // Convert to static quint16 convertToUInt16(const QVector ®isters); static qint16 convertToInt16(const QVector ®isters); - static quint32 convertToUInt32(const QVector ®isters); - static qint32 convertToInt32(const QVector ®isters); - static quint64 convertToUInt64(const QVector ®isters); - static qint64 convertToInt64(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); - static double convertToFloat64(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); - static QVector convertFromInt32(qint32 value); - static QVector convertFromUInt64(quint64 value); - static QVector convertFromInt64(qint64 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); - static QVector convertFromFloat64(double value); + 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 index b8d4127..400b082 100644 --- a/modbus/tools/README.md +++ b/modbus/tools/README.md @@ -23,6 +23,7 @@ The basic structure of the modbus register JSON looks like following example: ``` { + "endianness": "BigEndian", "enums": [ { "name": "NameOfEnum", @@ -68,6 +69,15 @@ The basic structure of the modbus register JSON looks like following example: ``` +## 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 ` = `. diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index f6be585..83f3214 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -129,12 +129,13 @@ def writeEnumDefinition(fileDescriptor, enumDefinition): writeLine(fileDescriptor) -def getCppDataType(registerDefinition): - if 'enum' in registerDefinition: - return registerDefinition['enum'] +def getCppDataType(registerDefinition, rawType = False): + if not rawType: + if 'enum' in registerDefinition: + return registerDefinition['enum'] - if 'scaleFactor' in registerDefinition: - return 'float' + if 'scaleFactor' in registerDefinition or 'staticScaleFactor' in registerDefinition: + return 'float' if registerDefinition['type'] == 'uint16': return 'quint16' @@ -164,7 +165,68 @@ def getCppDataType(registerDefinition): 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': @@ -172,10 +234,11 @@ def getValueConversionMethod(registerDefinition): 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()))' % (enumName)) + 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()))' % (enumName)) + 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': @@ -183,32 +246,43 @@ def getValueConversionMethod(registerDefinition): elif registerDefinition['type'] == 'int16': return ('ModbusDataUtils::convertToInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty)) elif registerDefinition['type'] == 'uint32': - return ('ModbusDataUtils::convertToUInt32(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty)) + return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactorProperty)) elif registerDefinition['type'] == 'int32': - return ('ModbusDataUtils::convertToInt32(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty)) + 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())') + return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) elif registerDefinition['type'] == 'int32': - return ('ModbusDataUtils::convertToInt32(unit.values())') + return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) elif registerDefinition['type'] == 'uint64': - return ('ModbusDataUtils::convertToUInt64(unit.values())') + return ('ModbusDataUtils::convertToUInt64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) elif registerDefinition['type'] == 'int64': - return ('ModbusDataUtils::convertToInt64(unit.values())') + return ('ModbusDataUtils::convertToInt64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) elif registerDefinition['type'] == 'float': - return ('ModbusDataUtils::convertToFloat32(unit.values())') + return ('ModbusDataUtils::convertToFloat32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) elif registerDefinition['type'] == 'float64': - return ('ModbusDataUtils::convertToFloat64(unit.values())') + return ('ModbusDataUtils::convertToFloat64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) elif registerDefinition['type'] == 'string': return ('ModbusDataUtils::convertToString(unit.values())') -def writePropertyGetMethodDeclarations(fileDescriptor, registerDefinitions): +def writePropertyGetSetMethodDeclarations(fileDescriptor, registerDefinitions): for registerDefinition in registerDefinitions: propertyName = registerDefinition['id'] propertyTyp = getCppDataType(registerDefinition) @@ -218,13 +292,19 @@ def writePropertyGetMethodDeclarations(fileDescriptor, registerDefinitions): 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 writePropertyGetMethodImplementations(fileDescriptor, className, registerDefinitions): +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: @@ -235,6 +315,24 @@ def writePropertyGetMethodImplementations(fileDescriptor, className, registerDef 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: @@ -256,6 +354,7 @@ def writePropertyUpdateMethodImplementations(fileDescriptor, className, register 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()) {') @@ -263,6 +362,9 @@ def writePropertyUpdateMethodImplementations(fileDescriptor, className, register 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:])) @@ -296,7 +398,18 @@ def writeInternalPropertyReadMethodImplementations(fileDescriptor, className, re propertyName = registerDefinition['id'] writeLine(fileDescriptor, 'QModbusReply *%s::read%s()' % (className, propertyName[0].upper() + propertyName[1:])) writeLine(fileDescriptor, '{') - writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, %s);' % (registerDefinition['address'], registerDefinition['size'])) + + # 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) @@ -326,52 +439,63 @@ def writeInitializeMethod(fileDescriptor, className, registerDefinitions): writeLine(fileDescriptor, 'void %s::initialize()' % (className)) writeLine(fileDescriptor, '{') - 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, ' }') - writeLine(fileDescriptor) - + # First check if there are any init registers + initRequired = False for registerDefinition in registerDefinitions: - propertyName = registerDefinition['id'] - propertyTyp = getCppDataType(registerDefinition) + if registerDefinition['readSchedule'] == 'init': + initRequired = True + break - if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init': - 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, ' }') - writeLine(fileDescriptor) + 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, '}') writeLine(fileDescriptor) @@ -417,6 +541,11 @@ 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 ############################################################################# @@ -452,7 +581,7 @@ writeLine(headerFile, ' ~%s() = default;' % className) writeLine(headerFile) # Write registers get method declarations -writePropertyGetMethodDeclarations(headerFile, registerJson['registers']) +writePropertyGetSetMethodDeclarations(headerFile, registerJson['registers']) # Write init and update method declarations writeLine(headerFile, ' virtual void initialize();') @@ -516,7 +645,7 @@ writeLine(sourceFile, '}') writeLine(sourceFile) # Property get methods -writePropertyGetMethodImplementations(sourceFile, className, registerJson['registers']) +writePropertyGetSetMethodImplementations(sourceFile, className, registerJson['registers']) # Write init and update method implementation writeInitializeMethod(sourceFile, className, registerJson['registers']) 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 \