Huawei: Add Smartlogger 3000 support
parent
0e9ecd9d36
commit
f1474b226b
|
|
@ -1,6 +1,6 @@
|
|||
# Huawei FusionSolar
|
||||
|
||||
Connects to a Huawei FusionSolar using Modbus TCP.
|
||||
Connects to a Huawei Inverters and Meters using Modbus TCP/RTU.
|
||||
|
||||
## Huawei FusionSolar
|
||||
|
||||
|
|
@ -23,5 +23,9 @@ with the Huawei Solar Inverter. In order to allow nymea to read from the device
|
|||
|
||||
You can also contact the [official Huawei support](mailto:eu_inverter_support@huawei.com) in order to get the update files and instructions, or get it from [here](https://support.huawei.com/enterprise/en/digital-power/sdongle-pid-23826585/software).
|
||||
|
||||
## More
|
||||
https://solar.huawei.com/eu/
|
||||
## Huawei Smartlogger
|
||||
|
||||
The Huawei Smartlogger can be connected if the Modbus TCP protocol has been enabled on the device. By default, this option is disabled.
|
||||
In order to connect to the Smartlogger, the Modbus slave ID of the Meter has to be known. The ID is the same as for the Modbus RTU network of the Meter.
|
||||
|
||||
Once successfully connected, one meter and one inverter will show in the system summing up all individual inverters of the system.
|
||||
|
|
|
|||
|
|
@ -2,15 +2,23 @@ include(../plugins.pri)
|
|||
|
||||
# Generate modbus connection
|
||||
#MODBUS_TOOLS_CONFIG += VERBOSE
|
||||
MODBUS_CONNECTIONS += huawei-fusion-solar-registers.json huawei-registers.json
|
||||
MODBUS_CONNECTIONS += \
|
||||
huawei-fusion-solar-registers.json \
|
||||
huawei-registers.json \
|
||||
smartlogger-registers.json \
|
||||
|
||||
include(../modbus.pri)
|
||||
|
||||
HEADERS += \
|
||||
huaweifusionsolar.h \
|
||||
huaweifusionsolardiscovery.h \
|
||||
huaweismartlogger.h \
|
||||
huaweismartloggerdiscovery.h \
|
||||
integrationpluginhuawei.h
|
||||
|
||||
SOURCES += \
|
||||
huaweifusionsolar.cpp \
|
||||
huaweifusionsolardiscovery.cpp \
|
||||
huaweismartlogger.cpp \
|
||||
huaweismartloggerdiscovery.cpp \
|
||||
integrationpluginhuawei.cpp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 "huaweismartlogger.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcHuaweiSmartLoggerModbusTcpConnection)
|
||||
|
||||
HuaweiSmartLogger::HuaweiSmartLogger(const QHostAddress &hostAddress, uint port, quint16 meterSlaveId, QObject *parent)
|
||||
: HuaweiSmartLoggerModbusTcpConnection{hostAddress, port, 0, parent},
|
||||
m_meterSlaveId{meterSlaveId}
|
||||
{
|
||||
// Note: the smart logger only responds to requests on slave ID 0 (broadcast), the meter itself can be
|
||||
// accessed using the connected modbus RTU slave ID, therefore we need to handle 2 modbus slave IDs with one modbus TCP connection
|
||||
|
||||
}
|
||||
|
||||
bool HuaweiSmartLogger::update()
|
||||
{
|
||||
if (!m_modbusTcpMaster->connected())
|
||||
return false;
|
||||
|
||||
if (!m_pendingUpdateReplies.isEmpty()) {
|
||||
qCDebug(dcHuaweiSmartLoggerModbusTcpConnection()) << "Tried to update but there are still some update replies pending. Waiting for them to be finished...";
|
||||
return true;
|
||||
}
|
||||
|
||||
QModbusReply *reply = nullptr;
|
||||
|
||||
// Read Total active output power of all inverters
|
||||
qCDebug(dcHuaweiSmartLoggerModbusTcpConnection()) << "--> Read \"Total active output power of all inverters\" register:" << 40525 << "size:" << 2;
|
||||
reply = readInverterTotalActivePower();
|
||||
if (!reply) {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Error occurred while reading \"Total active output power of all inverters\" registers from" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply->isFinished()) {
|
||||
reply->deleteLater(); // Broadcast reply returns immediately
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pendingUpdateReplies.append(reply);
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply](){
|
||||
m_pendingUpdateReplies.removeAll(reply);
|
||||
handleModbusError(reply->error());
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
verifyUpdateFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
processInverterTotalActivePowerRegisterValues(unit.values());
|
||||
verifyUpdateFinished();
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
|
||||
QModbusResponse response = reply->rawResult();
|
||||
if (reply->error() == QModbusDevice::ProtocolError && response.isException()) {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Modbus reply error occurred while reading \"Total active output power of all inverters\" registers from" << m_modbusTcpMaster->hostAddress().toString() << error << reply->errorString() << ModbusDataUtils::exceptionCodeToString(response.exceptionCode());
|
||||
} else {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Modbus reply error occurred while reading \"Total active output power of all inverters\" registers from" << m_modbusTcpMaster->hostAddress().toString() << error << reply->errorString();
|
||||
}
|
||||
});
|
||||
|
||||
// Read Total energy yield produced by all inverters
|
||||
qCDebug(dcHuaweiSmartLoggerModbusTcpConnection()) << "--> Read \"Total energy yield produced by all inverters\" register:" << 40560 << "size:" << 2;
|
||||
reply = readInverterTotalEnergyProduced();
|
||||
if (!reply) {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Error occurred while reading \"Total energy yield produced by all inverters\" registers from" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply->isFinished()) {
|
||||
reply->deleteLater(); // Broadcast reply returns immediately
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pendingUpdateReplies.append(reply);
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply](){
|
||||
m_pendingUpdateReplies.removeAll(reply);
|
||||
handleModbusError(reply->error());
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
verifyUpdateFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
processInverterTotalEnergyProducedRegisterValues(unit.values());
|
||||
verifyUpdateFinished();
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
|
||||
QModbusResponse response = reply->rawResult();
|
||||
if (reply->error() == QModbusDevice::ProtocolError && response.isException()) {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Modbus reply error occurred while reading \"Total energy yield produced by all inverters\" registers from" << m_modbusTcpMaster->hostAddress().toString() << error << reply->errorString() << ModbusDataUtils::exceptionCodeToString(response.exceptionCode());
|
||||
} else {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Modbus reply error occurred while reading \"Total energy yield produced by all inverters\" registers from" << m_modbusTcpMaster->hostAddress().toString() << error << reply->errorString();
|
||||
}
|
||||
});
|
||||
|
||||
// Read meterData
|
||||
|
||||
// Temporary set the slave ID to the configured meter id, send the request and reset to broadcast...
|
||||
m_slaveId = m_meterSlaveId;
|
||||
reply = readBlockMeterData();
|
||||
m_slaveId = 0;
|
||||
qCDebug(dcHuaweiSmartLoggerModbusTcpConnection()) << "--> Read block \"meterData\" registers from:" << 32260 << "size:" << 105;
|
||||
if (!reply) {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Error occurred while reading block \"meterData\" registers";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply->isFinished()) {
|
||||
reply->deleteLater(); // Broadcast reply returns immediately
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pendingUpdateReplies.append(reply);
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply](){
|
||||
m_pendingUpdateReplies.removeAll(reply);
|
||||
handleModbusError(reply->error());
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
verifyUpdateFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
const QVector<quint16> blockValues = unit.values();
|
||||
processBlockMeterDataRegisterValues(blockValues);
|
||||
verifyUpdateFinished();
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){
|
||||
QModbusResponse response = reply->rawResult();
|
||||
if (reply->error() == QModbusDevice::ProtocolError && response.isException()) {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Modbus reply error occurred while updating block \"meterData\" registers" << error << reply->errorString() << ModbusDataUtils::exceptionCodeToString(response.exceptionCode());
|
||||
} else {
|
||||
qCWarning(dcHuaweiSmartLoggerModbusTcpConnection()) << "Modbus reply error occurred while updating block \"meterData\" registers" << error << reply->errorString();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 HUAWEISMARTLOGGER_H
|
||||
#define HUAWEISMARTLOGGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "huaweismartloggermodbustcpconnection.h"
|
||||
|
||||
class HuaweiSmartLogger : public HuaweiSmartLoggerModbusTcpConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HuaweiSmartLogger(const QHostAddress &hostAddress, uint port, quint16 meterSlaveId, QObject *parent = nullptr);
|
||||
|
||||
bool update() override;
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
quint16 m_meterSlaveId = 5;
|
||||
};
|
||||
|
||||
#endif // HUAWEISMARTLOGGER_H
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 "huaweismartloggerdiscovery.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
HuaweiSmartLoggerDiscovery::HuaweiSmartLoggerDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, QObject *parent)
|
||||
: QObject{parent},
|
||||
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
||||
m_port{port}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HuaweiSmartLoggerDiscovery::startDiscovery()
|
||||
{
|
||||
qCInfo(dcHuawei()) << "Discovery: Start searching for Huawei SmartLogger in the network...";
|
||||
m_startDateTime = QDateTime::currentDateTime();
|
||||
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &HuaweiSmartLoggerDiscovery::checkNetworkDevice);
|
||||
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [this, discoveryReply](){
|
||||
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
|
||||
|
||||
// Finish with some delay so the last added network device information objects still can be checked.
|
||||
QTimer::singleShot(3000, this, [this](){
|
||||
qCDebug(dcHuawei()) << "Discovery: Grace period timer triggered.";
|
||||
finishDiscovery();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
QList<HuaweiSmartLoggerDiscovery::Result> HuaweiSmartLoggerDiscovery::results() const
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
|
||||
void HuaweiSmartLoggerDiscovery::checkNetworkDevice(const QHostAddress &address)
|
||||
{
|
||||
HuaweiSmartLogger *connection = new HuaweiSmartLogger(address, m_port, 1, this);
|
||||
m_connections.append(connection);
|
||||
|
||||
connect(connection, &HuaweiSmartLogger::reachableChanged, this, [this, connection](bool reachable){
|
||||
if (!reachable) {
|
||||
// Disconnected ... done with this connection
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
Result result;
|
||||
result.address = connection->modbusTcpMaster()->hostAddress();
|
||||
qCInfo(dcHuawei()) << "Discovery: --> Found reachable device on" << result.address.toString();
|
||||
m_results.append(result);
|
||||
});
|
||||
|
||||
// If we get any error...skip this host...
|
||||
connect(connection->modbusTcpMaster(), &ModbusTcpMaster::connectionErrorOccurred, this, [this, connection](QModbusDevice::Error error){
|
||||
if (error != QModbusDevice::NoError) {
|
||||
qCDebug(dcHuawei()) << "Discovery: Connection error on" << connection->modbusTcpMaster()->hostAddress().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// If check reachability failed...skip this host...
|
||||
connect(connection, &HuaweiSmartLogger::checkReachabilityFailed, this, [this, connection](){
|
||||
qCDebug(dcHuawei()) << "Discovery: Check reachability failed on" << connection->modbusTcpMaster()->hostAddress().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
});
|
||||
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
void HuaweiSmartLoggerDiscovery::cleanupConnection(HuaweiSmartLogger *connection)
|
||||
{
|
||||
if (m_connections.contains(connection)) {
|
||||
m_connections.removeAll(connection);
|
||||
connection->disconnectDevice();
|
||||
connection->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void HuaweiSmartLoggerDiscovery::finishDiscovery()
|
||||
{
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||
|
||||
// Fill in finished network device information
|
||||
for (int i = 0; i < m_results.count(); i++)
|
||||
m_results[i].networkDeviceInfo = m_networkDeviceInfos.get(m_results.value(i).address);
|
||||
|
||||
// Cleanup any leftovers...we don't care any more
|
||||
foreach (HuaweiSmartLogger *connection, m_connections)
|
||||
cleanupConnection(connection);
|
||||
|
||||
qCInfo(dcHuawei()) << "Discovery: Finished the discovery process. Found" << m_results.count()
|
||||
<< "inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||
|
||||
emit discoveryFinished();
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 HUAWEISMARTLOGGERDISCOVERY_H
|
||||
#define HUAWEISMARTLOGGERDISCOVERY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "huaweismartlogger.h"
|
||||
|
||||
class HuaweiSmartLoggerDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HuaweiSmartLoggerDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, QObject *parent = nullptr);
|
||||
|
||||
typedef struct Result {
|
||||
QHostAddress address;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
} Result;
|
||||
|
||||
QList<Result> results() const;
|
||||
|
||||
void startDiscovery();
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private:
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
quint16 m_port = 502;
|
||||
QDateTime m_startDateTime;
|
||||
|
||||
QList<HuaweiSmartLogger *> m_connections;
|
||||
QList<Result> m_results;
|
||||
|
||||
NetworkDeviceInfos m_networkDeviceInfos;
|
||||
|
||||
private slots:
|
||||
void checkNetworkDevice(const QHostAddress &address);
|
||||
void cleanupConnection(HuaweiSmartLogger *connection);
|
||||
|
||||
void finishDiscovery();
|
||||
};
|
||||
|
||||
#endif // HUAWEISMARTLOGGERDISCOVERY_H
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "huaweifusionsolardiscovery.h"
|
||||
#include "huaweismartloggerdiscovery.h"
|
||||
#include "integrationpluginhuawei.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ IntegrationPluginHuawei::IntegrationPluginHuawei()
|
|||
void IntegrationPluginHuawei::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
if (info->thingClassId() == huaweiFusionSolarInverterThingClassId) {
|
||||
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcHuawei()) << "The network discovery is not available on this platform.";
|
||||
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
|
||||
|
|
@ -104,6 +106,61 @@ void IntegrationPluginHuawei::discoverThings(ThingDiscoveryInfo *info)
|
|||
// Start the discovery process
|
||||
discovery->startDiscovery();
|
||||
|
||||
} else if (info->thingClassId() == huaweiSmartLoggerThingClassId) {
|
||||
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcHuawei()) << "The network discovery is not available on this platform.";
|
||||
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
|
||||
HuaweiSmartLoggerDiscovery *discovery = new HuaweiSmartLoggerDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, info);
|
||||
connect(discovery, &HuaweiSmartLoggerDiscovery::discoveryFinished, info, [this, info, discovery](){
|
||||
foreach (const HuaweiSmartLoggerDiscovery::Result &result, discovery->results()) {
|
||||
|
||||
QString name = QT_TR_NOOP("Huawei SmartLogger");
|
||||
|
||||
QString description;
|
||||
switch (result.networkDeviceInfo.monitorMode()) {
|
||||
case NetworkDeviceInfo::MonitorModeMac:
|
||||
description += " MAC: " + result.networkDeviceInfo.macAddressInfos().constFirst().macAddress().toString() +
|
||||
" - " + result.networkDeviceInfo.address().toString();
|
||||
break;
|
||||
case NetworkDeviceInfo::MonitorModeHostName:
|
||||
description += " Host name: " + result.networkDeviceInfo.hostName() +
|
||||
" - " + result.networkDeviceInfo.address().toString();
|
||||
break;
|
||||
case NetworkDeviceInfo::MonitorModeIp:
|
||||
description += " IP: " + result.networkDeviceInfo.address().toString();
|
||||
break;
|
||||
}
|
||||
|
||||
ThingDescriptor descriptor(huaweiSmartLoggerThingClassId, name, description) ;
|
||||
qCDebug(dcHuawei()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||
|
||||
ParamList params;
|
||||
params << Param(huaweiSmartLoggerThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress());
|
||||
params << Param(huaweiSmartLoggerThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName());
|
||||
params << Param(huaweiSmartLoggerThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress());
|
||||
descriptor.setParams(params);
|
||||
|
||||
// Check if we already have set up this device
|
||||
Thing *existingThing = myThings().findByParams(params);
|
||||
if (existingThing) {
|
||||
qCDebug(dcHuawei()) << "This smartlogger already exists in the system:" << result.networkDeviceInfo;
|
||||
descriptor.setThingId(existingThing->id());
|
||||
}
|
||||
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
// Start the discovery process
|
||||
discovery->startDiscovery();
|
||||
|
||||
} else if (info->thingClassId() == huaweiRtuInverterThingClassId) {
|
||||
qCDebug(dcHuawei()) << "Discovering modbus RTU resources...";
|
||||
if (hardwareManager()->modbusRtuResource()->modbusRtuMasters().isEmpty()) {
|
||||
|
|
@ -182,6 +239,49 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
|
|||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == huaweiSmartLoggerThingClassId) {
|
||||
|
||||
// Handle reconfigure
|
||||
if (m_smartLoggerConnections.contains(thing))
|
||||
m_smartLoggerConnections.take(thing)->deleteLater();
|
||||
|
||||
if (m_monitors.contains(thing))
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
|
||||
// Create a monitor so we always get the correct IP in the network and see if the device is reachable without polling on our own
|
||||
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing);
|
||||
if (!monitor) {
|
||||
qCWarning(dcHuawei()) << "Failed to set up SmartLogger because the params are incomplete for creating a monitor:" << thing->params();
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The parameters are incomplete. Please reconfigure the device to fix this."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_monitors.insert(thing, monitor);
|
||||
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
});
|
||||
|
||||
// Continue with setup only if we know that the network device is reachable
|
||||
if (info->isInitialSetup()) {
|
||||
if (monitor->reachable()) {
|
||||
setupSmartLogger(info);
|
||||
} else {
|
||||
// otherwise wait until we reach the networkdevice before setting up the device
|
||||
qCDebug(dcHuawei()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable.";
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
|
||||
if (reachable) {
|
||||
qCDebug(dcHuawei()) << "Network device" << thing->name() << "is now reachable. Continue with the setup...";
|
||||
setupSmartLogger(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setupSmartLogger(info);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == huaweiRtuInverterThingClassId) {
|
||||
|
||||
uint address = thing->paramValue(huaweiRtuInverterThingSlaveAddressParamTypeId).toUInt();
|
||||
|
|
@ -399,7 +499,10 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
|
|||
|
||||
void IntegrationPluginHuawei::postSetupThing(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == huaweiFusionSolarInverterThingClassId || thing->thingClassId() == huaweiRtuInverterThingClassId) {
|
||||
if (thing->thingClassId() == huaweiFusionSolarInverterThingClassId ||
|
||||
thing->thingClassId() == huaweiRtuInverterThingClassId ||
|
||||
thing->thingClassId() == huaweiSmartLoggerThingClassId) {
|
||||
|
||||
if (!m_pluginTimer) {
|
||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
|
||||
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
|
||||
|
|
@ -409,6 +512,12 @@ void IntegrationPluginHuawei::postSetupThing(Thing *thing)
|
|||
}
|
||||
}
|
||||
|
||||
foreach(HuaweiSmartLoggerModbusTcpConnection *connection, m_smartLoggerConnections) {
|
||||
if (connection->reachable()) {
|
||||
connection->update();
|
||||
}
|
||||
}
|
||||
|
||||
foreach(HuaweiModbusRtuConnection *connection, m_rtuConnections) {
|
||||
connection->update();
|
||||
}
|
||||
|
|
@ -438,6 +547,12 @@ void IntegrationPluginHuawei::thingRemoved(Thing *thing)
|
|||
delete connection;
|
||||
}
|
||||
|
||||
if (m_smartLoggerConnections.contains(thing)) {
|
||||
HuaweiSmartLogger *connection = m_smartLoggerConnections.take(thing);
|
||||
connection->disconnectDevice();
|
||||
delete connection;
|
||||
}
|
||||
|
||||
if (m_rtuConnections.contains(thing)) {
|
||||
m_rtuConnections.take(thing)->deleteLater();
|
||||
}
|
||||
|
|
@ -662,6 +777,106 @@ void IntegrationPluginHuawei::setupFusionSolar(ThingSetupInfo *info)
|
|||
connection->connectDevice();
|
||||
}
|
||||
|
||||
void IntegrationPluginHuawei::setupSmartLogger(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
|
||||
uint port = thing->paramValue(huaweiSmartLoggerThingPortParamTypeId).toUInt();
|
||||
quint16 meterSlaveId = thing->paramValue(huaweiSmartLoggerThingMeterSlaveIdParamTypeId).toUInt();
|
||||
|
||||
qCDebug(dcHuawei()) << "Setup connection to smarlogger on" << monitor->networkDeviceInfo().address().toString() << port << "Meter slave ID" << meterSlaveId;
|
||||
|
||||
HuaweiSmartLogger *connection = new HuaweiSmartLogger(monitor->networkDeviceInfo().address(), port, meterSlaveId, this);
|
||||
connect(info, &ThingSetupInfo::aborted, connection, [this, connection, thing](){
|
||||
connection->deleteLater();
|
||||
m_smartLoggerConnections.remove(thing);
|
||||
});
|
||||
|
||||
m_smartLoggerConnections.insert(thing, connection);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
qCDebug(dcHuawei()) << "Setup huawei smart logger finished successfully";
|
||||
|
||||
// Reset history, just incase
|
||||
m_inverterEnergyProducedHistory[thing].clear();
|
||||
|
||||
// Add the current value to the history
|
||||
evaluateEnergyProducedValue(thing, thing->stateValue(huaweiSmartLoggerTotalEnergyProducedStateTypeId).toFloat());
|
||||
|
||||
connect(connection, &HuaweiSmartLogger::reachableChanged, thing, [=](bool reachable){
|
||||
qCDebug(dcHuawei()) << "Reachable changed to" << reachable << "for" << thing;
|
||||
thing->setStateValue("connected", reachable);
|
||||
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||
childThing->setStateValue("connected", reachable);
|
||||
|
||||
if (!reachable) {
|
||||
// Set power values to 0 since we don't know what the current value is
|
||||
if (childThing->thingClassId() == huaweiSmartLoggerThingClassId) {
|
||||
thing->setStateValue(huaweiSmartLoggerCurrentPowerStateTypeId, 0);
|
||||
}
|
||||
|
||||
if (childThing->thingClassId() == huaweiMeterThingClassId) {
|
||||
thing->setStateValue(huaweiMeterCurrentPowerStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterCurrentPhaseAStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterCurrentPhaseBStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterCurrentPhaseCStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterCurrentPowerPhaseAStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterCurrentPowerPhaseBStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterCurrentPowerPhaseCStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterVoltagePhaseAStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterVoltagePhaseBStateTypeId, 0);
|
||||
thing->setStateValue(huaweiMeterVoltagePhaseCStateTypeId, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
|
||||
if (!thing->setupComplete())
|
||||
return;
|
||||
|
||||
qCDebug(dcHuawei()) << "Network device monitor for" << thing->name() << (reachable ? "is now reachable" : "is not reachable any more" );
|
||||
|
||||
if (reachable && !thing->stateValue("connected").toBool()) {
|
||||
connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address());
|
||||
connection->connectDevice();
|
||||
} else if (!reachable) {
|
||||
// Note: We disable autoreconnect explicitly and we will
|
||||
// connect the device once the monitor says it is reachable again
|
||||
connection->disconnectDevice();
|
||||
}
|
||||
});
|
||||
|
||||
connect(connection, &HuaweiSmartLoggerModbusTcpConnection::updateFinished, thing, [this, thing, connection](){
|
||||
qCDebug(dcHuawei()) << "Smartlogger update finished" << thing << qobject_cast<HuaweiSmartLoggerModbusTcpConnection *>(connection);
|
||||
|
||||
// Set inverter data
|
||||
thing->setStateValue(huaweiSmartLoggerCurrentPowerStateTypeId, connection->inverterTotalActivePower() * -1);
|
||||
thing->setStateValue(huaweiSmartLoggerTotalEnergyProducedStateTypeId, connection->inverterTotalEnergyProduced());
|
||||
|
||||
// Update the meter data
|
||||
Thing *meterThing = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiMeterThingClassId).first();
|
||||
if (!meterThing)
|
||||
return;
|
||||
|
||||
meterThing->setStateValue(huaweiMeterCurrentPowerStateTypeId, connection->meterActivePower());
|
||||
meterThing->setStateValue(huaweiMeterCurrentPhaseAStateTypeId, connection->meterCurrentPhaseA() * -1);
|
||||
meterThing->setStateValue(huaweiMeterCurrentPhaseBStateTypeId, connection->meterCurrentPhaseB()* -1);
|
||||
meterThing->setStateValue(huaweiMeterCurrentPhaseCStateTypeId, connection->meterCurrentPhaseC()* -1);
|
||||
meterThing->setStateValue(huaweiMeterCurrentPowerPhaseAStateTypeId, connection->meterPowerPhaseA());
|
||||
meterThing->setStateValue(huaweiMeterCurrentPowerPhaseBStateTypeId, connection->meterPowerPhaseB());
|
||||
meterThing->setStateValue(huaweiMeterCurrentPowerPhaseCStateTypeId, connection->meterPowerPhaseC());
|
||||
meterThing->setStateValue(huaweiMeterVoltagePhaseAStateTypeId, connection->meterVoltagePhaseA());
|
||||
meterThing->setStateValue(huaweiMeterVoltagePhaseBStateTypeId, connection->meterVoltagePhaseB());
|
||||
meterThing->setStateValue(huaweiMeterVoltagePhaseCStateTypeId, connection->meterVoltagePhaseC());
|
||||
|
||||
meterThing->setStateValue(huaweiMeterTotalEnergyProducedStateTypeId, connection->meterNegativeActiveElectricity());
|
||||
meterThing->setStateValue(huaweiMeterTotalEnergyConsumedStateTypeId, connection->meterPositiveActiveElectricity());
|
||||
});
|
||||
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
void IntegrationPluginHuawei::evaluateEnergyProducedValue(Thing *inverterThing, float energyProduced)
|
||||
{
|
||||
// Add the value to our small history
|
||||
|
|
|
|||
|
|
@ -37,8 +37,10 @@
|
|||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "huaweifusionsolar.h"
|
||||
#include "huaweimodbusrtuconnection.h"
|
||||
#include "huaweismartlogger.h"
|
||||
|
||||
class IntegrationPluginHuawei: public IntegrationPlugin
|
||||
{
|
||||
|
|
@ -60,9 +62,11 @@ private:
|
|||
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
QHash<Thing *, HuaweiFusionSolar *> m_connections;
|
||||
QHash<Thing *, HuaweiSmartLogger *> m_smartLoggerConnections;
|
||||
QHash<Thing *, HuaweiModbusRtuConnection *> m_rtuConnections;
|
||||
|
||||
void setupFusionSolar(ThingSetupInfo *info);
|
||||
void setupSmartLogger(ThingSetupInfo *info);
|
||||
|
||||
QHash<Thing *, QList<float>> m_inverterEnergyProducedHistory;
|
||||
void evaluateEnergyProducedValue(Thing *inverterThing, float energyProduced);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@
|
|||
"id": "a51f0ceb-bd2c-444f-8b39-77cf8a4e1bc6",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
|
|
@ -68,7 +67,6 @@
|
|||
"id": "f463f36e-69f9-4614-b690-664ce22d76e0",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"displayNameEvent": "Current power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
|
|
@ -78,7 +76,6 @@
|
|||
"id": "52a84e06-ff13-4c82-99e2-c8a2691a99d7",
|
||||
"name": "activePower",
|
||||
"displayName": "Active power",
|
||||
"displayNameEvent": "Active power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
|
|
@ -87,8 +84,84 @@
|
|||
{
|
||||
"id": "e97fe328-6ca4-4fe4-86f7-fee6e9e406a5",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "AC energy",
|
||||
"displayNameEvent": "AC energy changed",
|
||||
"displayName": "Total energy produced",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
"cached": true
|
||||
}
|
||||
],
|
||||
"actionTypes": [ ]
|
||||
},
|
||||
{
|
||||
"name": "huaweiSmartLogger",
|
||||
"displayName": "SmartLogger",
|
||||
"id": "32d3627b-c12c-4600-98e8-4808dc96a053",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": ["solarinverter", "connectable", "networkdevice"],
|
||||
"discoveryType": "weak",
|
||||
"providedInterfaces": [ "solarinverter", "energymeter" ],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "245eb970-2bc2-413f-97f6-cda550ab020f",
|
||||
"name":"macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "6198f36f-d09d-4d22-a9fc-b56b0f59d70f",
|
||||
"name": "address",
|
||||
"displayName": "Host address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "b65c9f90-9b8d-4f2c-a3e8-95b03cfe0744",
|
||||
"name": "hostName",
|
||||
"displayName": "Host name",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "d6dfcb22-ff39-496e-8761-d5ead3976034",
|
||||
"name":"port",
|
||||
"displayName": "Port",
|
||||
"type": "int",
|
||||
"defaultValue": 502
|
||||
},
|
||||
{
|
||||
"id": "1dcb2126-464b-4843-ae05-e53c555b98b5",
|
||||
"name":"meterSlaveId",
|
||||
"displayName": "Meter slave ID",
|
||||
"type": "int",
|
||||
"defaultValue": 5
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "f9debf3b-7dff-4f5c-8d51-1edfe7ca54bb",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "31a1e083-f5c9-41fd-90b0-da0370d93037",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "8d5a4798-5933-4044-93a8-5f2c0789f842",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -135,7 +208,6 @@
|
|||
"id": "191ffa22-de6f-4325-8698-56b817f78df5",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
|
|
@ -144,7 +216,6 @@
|
|||
"id": "6064d90e-1b6b-40fd-9da0-6ebc713efb7d",
|
||||
"name": "currentPower",
|
||||
"displayName": "Active power",
|
||||
"displayNameEvent": "Active power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
|
|
@ -153,8 +224,7 @@
|
|||
{
|
||||
"id": "49b92919-301c-4ff7-ae63-0c1a2184e3f4",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "AC energy",
|
||||
"displayNameEvent": "AC energy changed",
|
||||
"displayName": "Total energy produced",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -176,7 +246,6 @@
|
|||
"id": "720ece7a-b0b3-4fa3-9f52-6f23042624a5",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
|
|
@ -185,7 +254,6 @@
|
|||
"id": "f480dc82-68e2-44e2-839c-df38b9c10310",
|
||||
"name": "currentPower",
|
||||
"displayName": "Total real power",
|
||||
"displayNameEvent": "Total real power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -194,8 +262,7 @@
|
|||
{
|
||||
"id": "759554dd-74c5-4836-9792-96e02eb816f0",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "AC energy",
|
||||
"displayNameEvent": "AC energy changed",
|
||||
"displayName": "Total energy produced",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -204,8 +271,7 @@
|
|||
{
|
||||
"id": "2cf8d885-37f7-478f-819e-c4e20f2dbe01",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Total real energy imported",
|
||||
"displayNameEvent": "Total real energy imported changed",
|
||||
"displayName": "Total energy imported",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -215,7 +281,6 @@
|
|||
"id": "af48ff45-11ba-401e-a812-bb1db0896449",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Phase A current",
|
||||
"displayNameEvent": "Phase A current changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -225,7 +290,6 @@
|
|||
"id": "fb5082e4-a2d8-4958-a47d-e80928795ece",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Phase B current",
|
||||
"displayNameEvent": "Phase B current changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -235,7 +299,6 @@
|
|||
"id": "bdd9aa8b-93fe-4b6b-8a31-08e99d85a06c",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Phase C current",
|
||||
"displayNameEvent": "Phase C current changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -245,7 +308,6 @@
|
|||
"id": "ecc03e9b-88b1-424f-a179-66bbdebaaea9",
|
||||
"name": "currentPowerPhaseA",
|
||||
"displayName": "Current power phase A",
|
||||
"displayNameEvent": "Current power phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -255,7 +317,6 @@
|
|||
"id": "7971cbde-b2ea-4474-b68a-71e040ed3b1d",
|
||||
"name": "currentPowerPhaseB",
|
||||
"displayName": "Current power phase B",
|
||||
"displayNameEvent": "Current power phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -265,7 +326,6 @@
|
|||
"id": "7ca21c4d-6763-49e4-a056-4c9c76923971",
|
||||
"name": "currentPowerPhaseC",
|
||||
"displayName": "Current power phase C",
|
||||
"displayNameEvent": "Current power phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -275,7 +335,6 @@
|
|||
"id": "ea5d7924-19a8-415c-aeeb-e04ce08bed33",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"displayNameEvent": "Voltage phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -285,7 +344,6 @@
|
|||
"id": "f15856d1-645f-4d34-89a7-c1585ca329cc",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"displayNameEvent": "Voltage phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -295,21 +353,10 @@
|
|||
"id": "aafb5de4-caa1-4a90-8149-cdf85ae5dc2b",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"displayNameEvent": "Voltage phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1e2252be-80b3-4e9a-97f7-105d6d1c50f9",
|
||||
"name": "frequency",
|
||||
"displayName": "Frequency",
|
||||
"displayNameEvent": "Frequency changed",
|
||||
"type": "double",
|
||||
"unit": "Hertz",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
}
|
||||
],
|
||||
"actionTypes": [ ]
|
||||
|
|
@ -334,7 +381,6 @@
|
|||
"id": "917bc284-9d43-430c-a8c3-642d302448e6",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
|
|
@ -343,7 +389,6 @@
|
|||
"id": "223ddf60-ff73-4acf-b8ab-6337aeb972e8",
|
||||
"name": "batteryCritical",
|
||||
"displayName": "Battery critical",
|
||||
"displayNameEvent": "Battery critical changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
|
|
@ -351,7 +396,6 @@
|
|||
"id": "94d609bf-1f67-47c4-a23d-2fd14e7c0b21",
|
||||
"name": "batteryLevel",
|
||||
"displayName": "Battery level",
|
||||
"displayNameEvent": "Battery level changed",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"minValue": 0,
|
||||
|
|
@ -362,7 +406,6 @@
|
|||
"id": "53ca1f8a-0267-40aa-b563-762a943c8f55",
|
||||
"name": "currentPower",
|
||||
"displayName": "Total real power",
|
||||
"displayNameEvent": "Total real power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
|
|
@ -372,7 +415,6 @@
|
|||
"id": "3eed974a-0acb-4e38-bcb8-0e3f6fbfd51a",
|
||||
"name": "capacity",
|
||||
"displayName": "Capacity",
|
||||
"displayNameEvent": "Capacity changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
|
|
@ -381,7 +423,6 @@
|
|||
"id": "d9604513-d5a9-463a-ad18-d2f259a7a99d",
|
||||
"name": "chargingState",
|
||||
"displayName": "Charging state",
|
||||
"displayNameEvent": "Charging state changed",
|
||||
"type": "QString",
|
||||
"possibleValues": ["idle", "charging", "discharging"],
|
||||
"defaultValue": "idle"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,265 @@
|
|||
{
|
||||
"className": "HuaweiSmartLogger",
|
||||
"protocol": "TCP",
|
||||
"endianness": "BigEndian",
|
||||
"errorLimitUntilNotReachable": 15,
|
||||
"checkReachableRegister": "inverterTotalActivePower",
|
||||
"enums": [ ],
|
||||
"blocks": [
|
||||
{
|
||||
"id": "meterData",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "meterVoltagePhaseA",
|
||||
"address": 32260,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Voltage phase A",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterVoltagePhaseB",
|
||||
"address": 32262,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Voltage phase B",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterVoltagePhaseC",
|
||||
"address": 32264,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Voltage phase C",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterData1Dummy0",
|
||||
"address": 32266,
|
||||
"size": 6,
|
||||
"type": "raw",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterCurrentPhaseA",
|
||||
"address": 32272,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Current phase A",
|
||||
"unit": "A",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterCurrentPhaseB",
|
||||
"address": 32274,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Current phase B",
|
||||
"unit": "A",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterCurrentPhaseC",
|
||||
"address": 32276,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Current phase C",
|
||||
"unit": "A",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterActivePower",
|
||||
"address": 32278,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total active output power of all inverters",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterData1Dummy2",
|
||||
"address": 32280,
|
||||
"size": 55,
|
||||
"type": "raw",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterPowerPhaseA",
|
||||
"address": 32335,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Active power phase A",
|
||||
"staticScaleFactor": -3,
|
||||
"unit": "kW",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterPowerPhaseB",
|
||||
"address": 32337,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Active power phase B",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterPowerPhaseC",
|
||||
"address": 32339,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Active power phase C",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterTotalActiveElectricity",
|
||||
"address": 32341,
|
||||
"size": 4,
|
||||
"type": "int64",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Total active electricity",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterTotalReactiveElectricity",
|
||||
"address": 32345,
|
||||
"size": 4,
|
||||
"type": "int64",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Total reactive electricity",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kvarh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterNegativeActiveElectricity",
|
||||
"address": 32349,
|
||||
"size": 4,
|
||||
"type": "int64",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Negatoive active electricity",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterNegativeReactiveElectricity",
|
||||
"address": 32353,
|
||||
"size": 4,
|
||||
"type": "int64",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Negative reactive electricity",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kvarh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterPositiveActiveElectricity",
|
||||
"address": 32357,
|
||||
"size": 4,
|
||||
"type": "int64",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Positive active electricity",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "meterPositiveReactiveElectricity",
|
||||
"address": 32361,
|
||||
"size": 4,
|
||||
"type": "int64",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Positive reactive electricity",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kvarh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"registers": [
|
||||
{
|
||||
"id": "inverterTotalActivePower",
|
||||
"address": 40525,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Total active output power of all inverters",
|
||||
"unit": "W",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "inverterTotalEnergyProduced",
|
||||
"address": 40560,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Total energy yield produced by all inverters",
|
||||
"unit": "kWh",
|
||||
"staticScaleFactor": -2,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue