- Extend discovery to scan for multiple modbus servers
- Autoremove battery devices if they are offline
- Fix PV values and provide full battery functionality
This commit is contained in:
Simon Stürz 2023-06-06 14:13:42 +02:00
parent 8e32831047
commit 8eddf8a9cd
8 changed files with 289 additions and 165 deletions

View File

@ -1,10 +1,10 @@
# Huawei FusionSolar
Connects to a Huawei FusionSolar using Modbus RTU or TCP.
Connects to a Huawei FusionSolar using Modbus TCP.
## Huawei FusionSolar
The Huawei FusionSolar can be connected either via the Huawei SmartDongle or via a Modbus RTU (RS485) connection.
The Huawei FusionSolar can be connected using the Huawei SmartDongle.
The following devices are supported:
@ -12,32 +12,16 @@ The following devices are supported:
* Huawei Meter (connected internally to the Inverter)
* Luna2000 battery units 1 and 2 if connected
The batteries will be shown in the system as informative things, but will not be considered in the energy view and and energy balance. Since the batteries are connected internally behind the inverter, power coming from the battery will be shown as power coming from the inverter. The inverter power represents power from the PV or from the battery.
### Huawei SmartDongle
The [SmartDongle](https://solar.huawei.com/-/media/Solar/attachment/pdf/apac/datasheet/SmartDongle-WLAN-FE.pdf) can be used to communicate
with the Huawei Solar Inverter. In order to allow nymea to read from the device please make sure following requirements are met:
* SmartDongle software version must be at least `SP130`.
* SmartDongle software version must be at least `SP133`.
* Connect the Huawei SmartDongle to the network
* Use the official FusonSolar App to enable Modbus TCP access for the Dongle (full access). You can find more informations [here](https://forum.huawei.com/enterprise/en/modbus-tcp-guide/thread/789585-100027?page=1#comments-area).
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).
> The SmartDongle provides only access to the registers specified in the Huawei `openAPI`. Full modbus register access requires a modbus RTU connection.
### Direct modbus RTU connection (RS485).
If you want to communicate directly with the inverter using modbus RTU, you need to wire up the connection correctly and set up the Modbus RTU interface in the nymea settings using the right configuations. Then you can discover the Modbus RTU interface while adding the Huawei Modbus RTU inverter in nymea.
The Modbus RTU offers a full access to all modbus registers available on the inverter.
Please contact your installer how to enable a modbus RTU connection for your setup.
## More
https://solar.huawei.com/eu/

View File

@ -149,8 +149,54 @@
}
],
"blocks": [
{
"id": "identifyer",
"readSchedule": "init",
"registers": [
{
"id": "model",
"address": 30000,
"size": 15,
"type": "string",
"registerType": "holdingRegister",
"description": "Model",
"access": "RO"
},
{
"id": "serialNumber",
"address": 30015,
"size": 10,
"type": "string",
"registerType": "holdingRegister",
"description": "Serial number",
"access": "RO"
},
{
"id": "productNumber",
"address": 30025,
"size": 10,
"type": "string",
"registerType": "holdingRegister",
"description": "Product number",
"access": "RO"
}
]
}
],
"registers": [
{
"id": "inverterInputPower",
"address": 32064,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Inverter input power",
"unit": "kW",
"staticScaleFactor": -3,
"defaultValue": "0",
"access": "RO"
},
{
"id": "inverterActivePower",
"address": 32080,

View File

@ -35,7 +35,8 @@
NYMEA_LOGGING_CATEGORY(dcHuaweiFusionSolar, "HuaweiFusionSolar")
HuaweiFusionSolar::HuaweiFusionSolar(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :
HuaweiFusionModbusTcpConnection(hostAddress, port, slaveId, parent)
HuaweiFusionModbusTcpConnection(hostAddress, port, slaveId, parent),
m_slaveId(slaveId)
{
// Note: sometimes right after the discovery / setup the check fails the first time due to server busy error,
// this is a very slow or busy device since it returns quiet often that error. Don't faile with the first busy error...
@ -46,51 +47,12 @@ HuaweiFusionSolar::HuaweiFusionSolar(const QHostAddress &hostAddress, uint port,
m_registersQueue.clear();
}
});
}
bool HuaweiFusionSolar::initialize()
{
if (!reachable()) {
qCWarning(dcHuaweiFusionSolar()) << "Tried to initialize but the device is not to be reachable.";
return false;
}
if (m_initReply) {
qCWarning(dcHuaweiFusionSolar()) << "Tried to initialize but the init process is already running.";
return false;
}
qCDebug(dcHuaweiFusionSolar()) << "Initialize connection by reading \"Inverter active power\" register:" << 32080 << "size:" << 2;
m_initReply = readInverterActivePower();
if (!m_initReply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while initializing connection and read \"Inverter active power\" register from" << hostAddress().toString() << errorString();
return false;
}
if (m_initReply->isFinished()) {
m_initReply->deleteLater(); // Broadcast reply returns immediatly
m_initReply = nullptr;
return false;
}
connect(m_initReply, &QModbusReply::finished, this, [this](){
if (m_initReply->error() == QModbusDevice::NoError) {
qCDebug(dcHuaweiFusionSolar()) << "Initialization finished of HuaweiFusionSolar" << hostAddress().toString() << "finished successfully";
emit initializationFinished(true);
} else {
qCWarning(dcHuaweiFusionSolar()) << "Initialization finished of HuaweiFusionSolar" << hostAddress().toString() << "failed.";
emit initializationFinished(false);
connect(this, &HuaweiFusionModbusTcpConnection::initializationFinished, this, [=](bool success) {
if (success) {
qCDebug(dcHuawei()) << "Huawei init finished successfully:" << model() << serialNumber() << productNumber();
}
m_initReply->deleteLater();
m_initReply = nullptr;
});
connect(m_initReply, &QModbusReply::errorOccurred, this, [this] (QModbusDevice::Error error){
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while initializing connection and read \"Inverter active power\" registers from" << hostAddress().toString() << error << m_initReply->errorString();
});
return true;
}
bool HuaweiFusionSolar::update()
@ -101,6 +63,7 @@ bool HuaweiFusionSolar::update()
// Add the requests to queue, begin with power values, since they are most important
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterActivePower);
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterInputPower);
if (m_battery1Available)
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery1Power);
@ -128,6 +91,11 @@ bool HuaweiFusionSolar::update()
return true;
}
quint16 HuaweiFusionSolar::slaveId() const
{
return m_slaveId;
}
void HuaweiFusionSolar::readNextRegister()
{
// Check if currently a reply is pending
@ -186,6 +154,51 @@ void HuaweiFusionSolar::readNextRegister()
break;
}
case HuaweiFusionModbusTcpConnection::RegisterInverterInputPower: {
// Update registers from Inverter active power
qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Inverter input power\" register:" << 32064 << "size:" << 2;
QModbusReply *reply = readInverterInputPower();
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter input power\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter input power\" register" << 32064 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 2)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values();
} else {
processInverterInputPowerRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter input power\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter input power\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiFusionModbusTcpConnection::RegisterInverterDeviceStatus: {
// Update registers from Inverter device status
qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Inverter device status\" register:" << 32089 << "size:" << 1;

View File

@ -43,10 +43,12 @@ public:
explicit HuaweiFusionSolar(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);
~HuaweiFusionSolar() = default;
virtual bool initialize() override;
virtual bool update() override;
quint16 slaveId() const;
private:
quint16 m_slaveId;
QQueue<HuaweiFusionModbusTcpConnection::Registers> m_registersQueue;
QModbusReply *m_initReply = nullptr;

View File

@ -31,19 +31,19 @@
#include "huaweifusionsolardiscovery.h"
#include "extern-plugininfo.h"
HuaweiFusionSolarDiscovery::HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress, QObject *parent) :
QObject(parent),
m_networkDeviceDiscovery(networkDeviceDiscovery),
m_port(port),
m_modbusAddress(modbusAddress)
HuaweiFusionSolarDiscovery::HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, const QList<quint16> &slaveIds, QObject *parent)
: QObject{parent},
m_networkDeviceDiscovery{networkDeviceDiscovery},
m_port{port},
m_slaveIds{slaveIds}
{
}
void HuaweiFusionSolarDiscovery::startDiscovery()
{
qCInfo(dcHuawei()) << "Discovery: Start searching for Huawei FusionSolar SmartDongle in the network...";
m_startDateTime = QDateTime::currentDateTime();
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &HuaweiFusionSolarDiscovery::checkNetworkDevice);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
@ -56,50 +56,84 @@ void HuaweiFusionSolarDiscovery::startDiscovery()
});
}
NetworkDeviceInfos HuaweiFusionSolarDiscovery::discoveryResults() const
QList<HuaweiFusionSolarDiscovery::Result> HuaweiFusionSolarDiscovery::results() const
{
return m_discoveryResults;
return m_results;
}
void HuaweiFusionSolarDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
void HuaweiFusionSolarDiscovery::testNextConnection(const QHostAddress &address)
{
// The dongle must have a huawei registered mac address
if (!networkDeviceInfo.macAddressManufacturer().toLower().contains("huawei"))
if (!m_pendingConnectionAttempts.contains(address))
return;
HuaweiFusionSolar *connection = new HuaweiFusionSolar(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
m_connections.append(connection);
connect(connection, &HuaweiFusionSolar::reachableChanged, this, [=](bool reachable){
if (!reachable) {
// Disconnected ... done with this connection
cleanupConnection(connection);
return;
}
qCDebug(dcHuawei()) << "Discovery: --> Found" << networkDeviceInfo;
m_discoveryResults.append(networkDeviceInfo);
});
// If we get any error...skip this host...
connect(connection, &HuaweiFusionSolar::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
if (error != QModbusDevice::NoError) {
qCDebug(dcHuawei()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
}
});
// If check reachability failed...skip this host...
connect(connection, &HuaweiFusionSolar::checkReachabilityFailed, this, [=](){
qCDebug(dcHuawei()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
});
HuaweiFusionSolar *connection = m_pendingConnectionAttempts[address].dequeue();
if (m_pendingConnectionAttempts.value(address).isEmpty())
m_pendingConnectionAttempts.remove(address);
// Try to connect, maybe it works, maybe not,
// but retry only once to communicate with the device for reachability check...
connection->setCheckReachableRetries(1);
qCDebug(dcHuawei()) << "Discovery: Start searching on" << QString("%1:%2").arg(address.toString()).arg(connection->port()) << "slave ID:" << connection->slaveId();
// Try to connect, maybe it works, maybe not...
connection->connectDevice();
if (!connection->connectDevice()) {
qCDebug(dcHuawei()) << "Discovery: Failed to connect to" << QString("%1:%2").arg(address.toString()).arg(connection->port()) << "slave ID:" << connection->slaveId() << "Continue...";;
cleanupConnection(connection);
}
}
void HuaweiFusionSolarDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
QQueue<HuaweiFusionSolar *> connectionQueue;
foreach (quint16 slaveId, m_slaveIds) {
HuaweiFusionSolar *connection = new HuaweiFusionSolar(networkDeviceInfo.address(), m_port, slaveId, this);
m_connections.append(connection);
connectionQueue.enqueue(connection);
connect(connection, &HuaweiFusionSolar::reachableChanged, this, [=](bool reachable){
if (!reachable) {
// Disconnected ... done with this connection
cleanupConnection(connection);
return;
}
// Todo: initialize and check if available
connect(connection, &HuaweiFusionSolar::initializationFinished, this, [=](bool success){
Result result;
result.networkDeviceInfo = networkDeviceInfo;
result.slaveId = slaveId;
if (success) {
qCDebug(dcHuawei()) << "Huawei init finished successfully:" << connection->model() << connection->serialNumber() << connection->productNumber();
result.modelName = connection->model();
result.serialNumber = connection->serialNumber();
}
qCInfo(dcHuawei()) << "Discovery: --> Found" << networkDeviceInfo << "slave ID:" << slaveId;
m_results.append(result);
});
connection->initialize();
});
// If we get any error...skip this host...
connect(connection, &HuaweiFusionSolar::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
if (error != QModbusDevice::NoError) {
qCDebug(dcHuawei()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
}
});
// If check reachability failed...skip this host...
connect(connection, &HuaweiFusionSolar::checkReachabilityFailed, this, [=](){
qCDebug(dcHuawei()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
});
}
m_pendingConnectionAttempts[networkDeviceInfo.address()] = connectionQueue;
testNextConnection(networkDeviceInfo.address());
}
void HuaweiFusionSolarDiscovery::cleanupConnection(HuaweiFusionSolar *connection)
@ -109,6 +143,8 @@ void HuaweiFusionSolarDiscovery::cleanupConnection(HuaweiFusionSolar *connection
connection->disconnectDevice();
connection->deleteLater();
}
testNextConnection(connection->hostAddress());
}
void HuaweiFusionSolarDiscovery::finishDiscovery()
@ -119,7 +155,7 @@ void HuaweiFusionSolarDiscovery::finishDiscovery()
foreach (HuaweiFusionSolar *connection, m_connections)
cleanupConnection(connection);
qCInfo(dcHuawei()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count()
qCInfo(dcHuawei()) << "Discovery: Finished the discovery process. Found" << m_results.count()
<< "inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
emit discoveryFinished();

View File

@ -41,23 +41,33 @@ class HuaweiFusionSolarDiscovery : public QObject
{
Q_OBJECT
public:
explicit HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 1, QObject *parent = nullptr);
explicit HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, const QList<quint16> &slaveIds, QObject *parent = nullptr);
typedef struct Result {
QString modelName;
QString serialNumber;
quint16 slaveId;
NetworkDeviceInfo networkDeviceInfo;
} Result;
void startDiscovery();
NetworkDeviceInfos discoveryResults() const;
QList<Result> results() const;
signals:
void discoveryFinished();
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
quint16 m_port;
quint16 m_modbusAddress;
quint16 m_port = 502;
QList<quint16> m_slaveIds;
QDateTime m_startDateTime;
QHash<QHostAddress, QQueue<HuaweiFusionSolar *>> m_pendingConnectionAttempts;
QList<HuaweiFusionSolar *> m_connections;
NetworkDeviceInfos m_discoveryResults;
QList<Result> m_results;
void testNextConnection(const QHostAddress &address);
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupConnection(HuaweiFusionSolar *connection);

View File

@ -50,23 +50,33 @@ void IntegrationPluginHuawei::discoverThings(ThingDiscoveryInfo *info)
}
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
HuaweiFusionSolarDiscovery *discovery = new HuaweiFusionSolarDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 1, info);
QList<quint16> slaveIds = {1, 2, 3};
HuaweiFusionSolarDiscovery *discovery = new HuaweiFusionSolarDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, slaveIds, info);
connect(discovery, &HuaweiFusionSolarDiscovery::discoveryFinished, info, [=](){
foreach (const NetworkDeviceInfo &networkDeviceInfo, discovery->discoveryResults()) {
foreach (const HuaweiFusionSolarDiscovery::Result &result, discovery->results()) {
ThingDescriptor descriptor(huaweiFusionSolarInverterThingClassId, QT_TR_NOOP("Huawei Solar Inverter"), networkDeviceInfo.macAddress() + " - " + networkDeviceInfo.address().toString());
QString name = QT_TR_NOOP("Huawei Solar Inverter");
if (!result.modelName.isEmpty())
name = "Huawei " + result.modelName;
QString desctiption = result.networkDeviceInfo.macAddress() + " - " + result.networkDeviceInfo.address().toString();
if (!result.serialNumber.isEmpty()) {
desctiption = "SN: " + result.serialNumber + " " + result.networkDeviceInfo.macAddress() + " - " + result.networkDeviceInfo.address().toString();
}
ThingDescriptor descriptor(huaweiFusionSolarInverterThingClassId, name, desctiption) ;
qCDebug(dcHuawei()) << "Discovered:" << descriptor.title() << descriptor.description();
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(huaweiFusionSolarInverterThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
Things existingThings = myThings().filterByParam(huaweiFusionSolarInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
if (existingThings.count() == 1) {
qCDebug(dcHuawei()) << "This inverter already exists in the system:" << networkDeviceInfo;
qCDebug(dcHuawei()) << "This inverter already exists in the system:" << result.networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
}
ParamList params;
params << Param(huaweiFusionSolarInverterThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
// Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults...
params << Param(huaweiFusionSolarInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
params << Param(huaweiFusionSolarInverterThingSlaveIdParamTypeId, result.slaveId);
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
@ -116,7 +126,7 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
// Handle reconfigure
if (m_connections.contains(thing))
delete m_connections.take(thing);
m_connections.take(thing)->deleteLater();
if (m_monitors.contains(thing))
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
@ -136,18 +146,23 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
});
// Continue with setup only if we know that the network device is reachable
if (monitor->reachable()) {
setupFusionSolar(info);
if (info->isInitialSetup()) {
if (monitor->reachable()) {
setupFusionSolar(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...";
setupFusionSolar(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...";
setupFusionSolar(info);
}
});
setupFusionSolar(info);
}
return;
@ -356,11 +371,10 @@ void IntegrationPluginHuawei::postSetupThing(Thing *thing)
{
if (thing->thingClassId() == huaweiFusionSolarInverterThingClassId || thing->thingClassId() == huaweiRtuInverterThingClassId) {
if (!m_pluginTimer) {
qCDebug(dcHuawei()) << "Starting plugin timer...";
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
foreach(HuaweiFusionSolar *connection, m_connections) {
if (connection->connected()) {
if (connection->reachable()) {
connection->update();
}
}
@ -370,6 +384,7 @@ void IntegrationPluginHuawei::postSetupThing(Thing *thing)
}
});
qCDebug(dcHuawei()) << "Starting plugin timer...";
m_pluginTimer->start();
}
@ -481,7 +496,11 @@ void IntegrationPluginHuawei::setupFusionSolar(ThingSetupInfo *info)
});
connect(connection, &HuaweiFusionSolar::inverterActivePowerChanged, thing, [thing](float inverterActivePower){
thing->setStateValue(huaweiFusionSolarInverterCurrentPowerStateTypeId, inverterActivePower * -1000.0);
thing->setStateValue(huaweiFusionSolarInverterActivePowerStateTypeId, inverterActivePower * -1000.0);
});
connect(connection, &HuaweiFusionSolar::inverterInputPowerChanged, thing, [thing](float inverterInputPower){
thing->setStateValue(huaweiFusionSolarInverterCurrentPowerStateTypeId, inverterInputPower * -1000.0);
});
connect(connection, &HuaweiFusionSolar::inverterDeviceStatusReadFinished, thing, [thing](HuaweiFusionSolar::InverterDeviceStatus inverterDeviceStatus){
@ -510,25 +529,26 @@ void IntegrationPluginHuawei::setupFusionSolar(ThingSetupInfo *info)
// Battery 1
connect(connection, &HuaweiFusionSolar::lunaBattery1StatusReadFinished, thing, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery1Status){
qCDebug(dcHuawei()) << "Battery 1 status changed" << lunaBattery1Status;
if (lunaBattery1Status != HuaweiFusionSolar::BatteryDeviceStatusOffline) {
// Check if w have to create the energy storage
Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId);
bool alreadySetUp = false;
foreach (Thing *batteryThing, batteryThings) {
if (batteryThing->paramValue(huaweiBatteryThingUnitParamTypeId).toUInt() == 1) {
alreadySetUp = true;
}
qCDebug(dcHuawei()) << "Battery 1 status changed of" << thing << lunaBattery1Status;
Thing *batteryThing = nullptr;
foreach (Thing *bt, myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId)) {
if (bt->paramValue(huaweiBatteryThingUnitParamTypeId).toUInt() == 1) {
batteryThing = bt;
break;
}
}
if (!alreadySetUp) {
qCDebug(dcHuawei()) << "Set up huawei energy storage 1 for" << thing;
ThingDescriptor descriptor(huaweiBatteryThingClassId, "Luna 2000 Battery", QString(), thing->id());
ParamList params;
params.append(Param(huaweiBatteryThingUnitParamTypeId, 1));
descriptor.setParams(params);
emit autoThingsAppeared(ThingDescriptors() << descriptor);
}
// Check if w have to create the energy storage
if (lunaBattery1Status != HuaweiFusionSolar::BatteryDeviceStatusOffline && !batteryThing) {
qCDebug(dcHuawei()) << "Set up huawei energy storage 1 for" << thing;
ThingDescriptor descriptor(huaweiBatteryThingClassId, "Luna 2000 Battery 1", QString(), thing->id());
ParamList params;
params.append(Param(huaweiBatteryThingUnitParamTypeId, 1));
descriptor.setParams(params);
emit autoThingsAppeared(ThingDescriptors() << descriptor);
} else if (lunaBattery1Status == HuaweiFusionSolar::BatteryDeviceStatusOffline && batteryThing) {
qCDebug(dcHuawei()) << "Autoremove huawei energy storage 1 for" << thing << "because the battery is offline" << batteryThing;
emit autoThingDisappeared(batteryThing->id());
}
});
@ -558,26 +578,28 @@ void IntegrationPluginHuawei::setupFusionSolar(ThingSetupInfo *info)
// Battery 2
connect(connection, &HuaweiFusionSolar::lunaBattery2StatusReadFinished, thing, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery2Status){
qCDebug(dcHuawei()) << "Battery 2 status changed" << lunaBattery2Status;
if (lunaBattery2Status != HuaweiFusionSolar::BatteryDeviceStatusOffline) {
// Check if w have to create the energy storage
Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId);
bool alreadySetUp = false;
foreach (Thing *batteryThing, batteryThings) {
if (batteryThing->paramValue(huaweiBatteryThingUnitParamTypeId).toUInt() == 2) {
alreadySetUp = true;
}
}
if (!alreadySetUp) {
qCDebug(dcHuawei()) << "Set up huawei energy storage 2 for" << thing;
ThingDescriptor descriptor(huaweiBatteryThingClassId, "Luna 2000 Battery", QString(), thing->id());
ParamList params;
params.append(Param(huaweiBatteryThingUnitParamTypeId, 2));
descriptor.setParams(params);
emit autoThingsAppeared(ThingDescriptors() << descriptor);
qCDebug(dcHuawei()) << "Battery 2 status changed of" << thing << lunaBattery2Status;
Thing *batteryThing = nullptr;
foreach (Thing *bt, myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId)) {
if (bt->paramValue(huaweiBatteryThingUnitParamTypeId).toUInt() == 2) {
batteryThing = bt;
break;
}
}
// Check if w have to create the energy storage
if (lunaBattery2Status != HuaweiFusionSolar::BatteryDeviceStatusOffline && !batteryThing) {
qCDebug(dcHuawei()) << "Set up huawei energy storage 2 for" << thing;
ThingDescriptor descriptor(huaweiBatteryThingClassId, "Luna 2000 Battery 2", QString(), thing->id());
ParamList params;
params.append(Param(huaweiBatteryThingUnitParamTypeId, 2));
descriptor.setParams(params);
emit autoThingsAppeared(ThingDescriptors() << descriptor);
} else if (lunaBattery2Status == HuaweiFusionSolar::BatteryDeviceStatusOffline && batteryThing) {
qCDebug(dcHuawei()) << "Autoremove huawei energy storage 2 for" << thing << "because the battery is offline" << batteryThing;
emit autoThingDisappeared(batteryThing->id());
}
});
connect(connection, &HuaweiFusionSolar::lunaBattery2PowerReadFinished, thing, [this, thing](qint32 lunaBattery2Power){
@ -606,7 +628,8 @@ void IntegrationPluginHuawei::setupFusionSolar(ThingSetupInfo *info)
});
});
connection->connectDevice();
if (monitor->reachable())
connection->connectDevice();
}
void IntegrationPluginHuawei::evaluateEnergyProducedValue(Thing *inverterThing, float energyProduced)

View File

@ -52,6 +52,16 @@
{
"id": "f463f36e-69f9-4614-b690-664ce22d76e0",
"name": "currentPower",
"displayName": "Current power",
"displayNameEvent": "Current power changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": true
},
{
"id": "52a84e06-ff13-4c82-99e2-c8a2691a99d7",
"name": "activePower",
"displayName": "Active power",
"displayNameEvent": "Active power changed",
"type": "double",
@ -294,7 +304,7 @@
"displayName": "Huawei Battery",
"id": "40104aac-0456-475d-8bd6-18f946597d96",
"createMethods": ["auto"],
"interfaces": [ "battery", "connectable"],
"interfaces": [ "battery", "connectable", "energystorage"],
"paramTypes": [
{
"id": "019287a6-c593-45a8-9695-2e1ad8e81c32",