Merge PR #171: New plugin: Sungrow
This commit is contained in:
commit
12e233b22a
8
debian/control
vendored
8
debian/control
vendored
@ -227,6 +227,14 @@ Description: nymea.io plugin for Stiebel Eltron heat pumps
|
||||
This package will install the nymea.io plugin for Stiebel Eltron heat pumps.
|
||||
|
||||
|
||||
Package: nymea-plugin-sungrow
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
Description: nymea integration plugin for Sungrow devices
|
||||
This package contains the nymea integration plugin for Sungrow solar inverters, meters and batteries.
|
||||
|
||||
|
||||
Package: nymea-plugin-sunspec
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
|
||||
2
debian/nymea-plugin-sungrow.install.in
vendored
Normal file
2
debian/nymea-plugin-sungrow.install.in
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsungrow.so
|
||||
sungrow/translations/*qm usr/share/nymea/translations/
|
||||
@ -24,6 +24,7 @@ PLUGIN_DIRS = \
|
||||
sma \
|
||||
solax \
|
||||
stiebeleltron \
|
||||
sungrow \
|
||||
sunspec \
|
||||
unipi \
|
||||
vestel \
|
||||
|
||||
26
sungrow/README.md
Normal file
26
sungrow/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Sungrow
|
||||
|
||||
Connects Sungrow inverters to nymea.
|
||||
|
||||
Currently supported models:
|
||||
|
||||
* SH3K6
|
||||
* SH4K6
|
||||
* SH5K-20
|
||||
* SH5K-V13
|
||||
* SH3K6-30
|
||||
* SH4K6-30
|
||||
* SH5K-30
|
||||
* SH3.0RS
|
||||
* SH3.6RS
|
||||
* SH4.0RS
|
||||
* SH5.0RS
|
||||
* SH6.0RS
|
||||
* SH5.0RT
|
||||
* SH6.0RT
|
||||
* SH8.0RT
|
||||
* SH10RT
|
||||
|
||||
# Requirements
|
||||
|
||||
nymea uses the modbus TCP connection in order to connect to the Sungrow inverter. Therefore the inverter must be reachable using the local network.
|
||||
390
sungrow/integrationpluginsungrow.cpp
Normal file
390
sungrow/integrationpluginsungrow.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, 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 "integrationpluginsungrow.h"
|
||||
#include "plugininfo.h"
|
||||
#include "sungrowdiscovery.h"
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
#include <hardwaremanager.h>
|
||||
|
||||
IntegrationPluginSungrow::IntegrationPluginSungrow()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginSungrow::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcSungrow()) << "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
|
||||
SungrowDiscovery *discovery = new SungrowDiscovery(hardwareManager()->networkDeviceDiscovery(), m_modbusTcpPort, m_modbusSlaveAddress, info);
|
||||
connect(discovery, &SungrowDiscovery::discoveryFinished, discovery, &SungrowDiscovery::deleteLater);
|
||||
connect(discovery, &SungrowDiscovery::discoveryFinished, info, [=](){
|
||||
foreach (const SungrowDiscovery::SungrowDiscoveryResult &result, discovery->discoveryResults()) {
|
||||
QString title = "Sungrow " + QString::number(result.nominalOutputPower) + "kW Inverter";
|
||||
|
||||
if (!result.serialNumber.isEmpty())
|
||||
title.append(" " + result.serialNumber);
|
||||
|
||||
ThingDescriptor descriptor(sungrowInverterTcpThingClassId, title, result.networkDeviceInfo.address().toString() + " " + result.networkDeviceInfo.macAddress());
|
||||
qCInfo(dcSungrow()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||
|
||||
// Check if we already have set up this device
|
||||
Things existingThings = myThings().filterByParam(sungrowInverterTcpThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||
if (existingThings.count() == 1) {
|
||||
qCDebug(dcSungrow()) << "This Sungrow inverter already exists in the system:" << result.networkDeviceInfo;
|
||||
descriptor.setThingId(existingThings.first()->id());
|
||||
}
|
||||
|
||||
ParamList params;
|
||||
params << Param(sungrowInverterTcpThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||
descriptor.setParams(params);
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
// Start the discovery process
|
||||
discovery->startDiscovery();
|
||||
}
|
||||
|
||||
void IntegrationPluginSungrow::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
qCInfo(dcSungrow()) << "Setup" << thing << thing->params();
|
||||
|
||||
if (thing->thingClassId() == sungrowInverterTcpThingClassId) {
|
||||
|
||||
// Handle reconfiguration
|
||||
if (m_tcpConnections.contains(thing)) {
|
||||
qCDebug(dcSungrow()) << "Reconfiguring existing thing" << thing->name();
|
||||
m_tcpConnections.take(thing)->deleteLater();
|
||||
if (m_monitors.contains(thing)) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
}
|
||||
|
||||
MacAddress macAddress = MacAddress(thing->paramValue(sungrowInverterTcpThingMacAddressParamTypeId).toString());
|
||||
if (!macAddress.isValid()) {
|
||||
qCWarning(dcSungrow()) << "The configured MAC address is not valid" << thing->params();
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure this inverter."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the monitor
|
||||
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
|
||||
m_monitors.insert(thing, monitor);
|
||||
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
|
||||
// Clean up in case the setup gets aborted
|
||||
if (m_monitors.contains(thing)) {
|
||||
qCDebug(dcSungrow()) << "Unregister monitor because the setup has been aborted.";
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
});
|
||||
|
||||
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
|
||||
|
||||
qCInfo(dcSungrow()) << "Setting up Sungrow on" << address.toString();
|
||||
auto sungrowConnection = new SungrowModbusTcpConnection(address, m_modbusTcpPort , m_modbusSlaveAddress, this);
|
||||
connect(info, &ThingSetupInfo::aborted, sungrowConnection, &SungrowModbusTcpConnection::deleteLater);
|
||||
|
||||
// Reconnect on monitor reachable changed
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
|
||||
qCDebug(dcSungrow()) << "Network device monitor reachable changed for" << thing->name() << reachable;
|
||||
if (!thing->setupComplete())
|
||||
return;
|
||||
|
||||
if (reachable && !thing->stateValue("connected").toBool()) {
|
||||
sungrowConnection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address());
|
||||
sungrowConnection->reconnectDevice();
|
||||
} else if (!reachable) {
|
||||
// Note: Auto reconnect is disabled explicitly and
|
||||
// the device will be connected once the monitor says it is reachable again
|
||||
sungrowConnection->disconnectDevice();
|
||||
}
|
||||
});
|
||||
|
||||
connect(sungrowConnection, &SungrowModbusTcpConnection::reachableChanged, thing, [this, thing, sungrowConnection](bool reachable){
|
||||
qCInfo(dcSungrow()) << "Reachable changed to" << reachable << "for" << thing;
|
||||
if (reachable) {
|
||||
// Connected true will be set after successfull init
|
||||
sungrowConnection->initialize();
|
||||
} else {
|
||||
thing->setStateValue("connected", false);
|
||||
thing->setStateValue(sungrowInverterTcpCurrentPowerStateTypeId, 0);
|
||||
|
||||
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||
childThing->setStateValue("connected", false);
|
||||
}
|
||||
|
||||
Thing *child = getMeterThing(thing);
|
||||
if (child) {
|
||||
child->setStateValue(sungrowMeterCurrentPowerStateTypeId, 0);
|
||||
child->setStateValue(sungrowMeterCurrentPhaseAStateTypeId, 0);
|
||||
child->setStateValue(sungrowMeterCurrentPhaseBStateTypeId, 0);
|
||||
child->setStateValue(sungrowMeterCurrentPhaseCStateTypeId, 0);
|
||||
child->setStateValue(sungrowMeterApparentPowerPhaseAStateTypeId, 0);
|
||||
child->setStateValue(sungrowMeterApparentPowerPhaseBStateTypeId, 0);
|
||||
child->setStateValue(sungrowMeterApparentPowerPhaseCStateTypeId, 0);
|
||||
}
|
||||
|
||||
child = getBatteryThing(thing);
|
||||
if (child) {
|
||||
child->setStateValue(sungrowBatteryCurrentPowerStateTypeId, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(sungrowConnection, &SungrowModbusTcpConnection::initializationFinished, thing, [=](bool success){
|
||||
thing->setStateValue("connected", success);
|
||||
|
||||
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||
childThing->setStateValue("connected", success);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
// Try once to reconnect the device
|
||||
sungrowConnection->reconnectDevice();
|
||||
} else {
|
||||
qCInfo(dcSungrow()) << "Connection initialized successfully for" << thing;
|
||||
sungrowConnection->update();
|
||||
}
|
||||
});
|
||||
|
||||
connect(sungrowConnection, &SungrowModbusTcpConnection::updateFinished, thing, [=](){
|
||||
qCDebug(dcSungrow()) << "Updated" << sungrowConnection;
|
||||
|
||||
if (myThings().filterByParentId(thing->id()).filterByThingClassId(sungrowMeterThingClassId).isEmpty()) {
|
||||
qCDebug(dcSungrow()) << "There is no meter set up for this inverter. Creating a meter for" << thing << sungrowConnection->modbusTcpMaster();
|
||||
ThingClass meterThingClass = thingClass(sungrowMeterThingClassId);
|
||||
ThingDescriptor descriptor(sungrowMeterThingClassId, meterThingClass.displayName() + " " + sungrowConnection->serialNumber(), QString(), thing->id());
|
||||
emit autoThingsAppeared(ThingDescriptors() << descriptor);
|
||||
}
|
||||
|
||||
// Check if a battery is connected to this Sungrow inverter
|
||||
if (sungrowConnection->batteryType() != SungrowModbusTcpConnection::BatteryTypeNoBattery &&
|
||||
myThings().filterByParentId(thing->id()).filterByThingClassId(sungrowBatteryThingClassId).isEmpty()) {
|
||||
qCDebug(dcSungrow()) << "There is a battery connected but not set up yet. Creating a battery.";
|
||||
ThingClass batteryThingClass = thingClass(sungrowBatteryThingClassId);
|
||||
ThingDescriptor descriptor(sungrowBatteryThingClassId, batteryThingClass.displayName() + " " + sungrowConnection->serialNumber(), QString(), thing->id());
|
||||
emit autoThingsAppeared(ThingDescriptors() << descriptor);
|
||||
}
|
||||
|
||||
// Update inverter states
|
||||
thing->setStateValue(sungrowInverterTcpCurrentPowerStateTypeId, static_cast<double>(sungrowConnection->totalPVPower()) * -1);
|
||||
thing->setStateValue(sungrowInverterTcpTemperatureStateTypeId, sungrowConnection->inverterTemperature());
|
||||
thing->setStateValue(sungrowInverterTcpFrequencyStateTypeId, sungrowConnection->gridFrequency());
|
||||
thing->setStateValue(sungrowInverterTcpTotalEnergyProducedStateTypeId, sungrowConnection->totalPVGeneration());
|
||||
|
||||
// Update the meter if available
|
||||
Thing *meterThing = getMeterThing(thing);
|
||||
if (meterThing) {
|
||||
auto runningState = sungrowConnection->runningState();
|
||||
qCDebug(dcSungrow()) << "Power generated from PV:" << (runningState & (0x1 << 0) ? "true" : "false");
|
||||
qCDebug(dcSungrow()) << "Battery charging:" << (runningState & (0x1 << 1) ? "true" : "false");
|
||||
qCDebug(dcSungrow()) << "Battery discharging:" << (runningState & (0x1 << 2) ? "true" : "false");
|
||||
qCDebug(dcSungrow()) << "Positive load power:" << (runningState & (0x1 << 3) ? "true" : "false");
|
||||
qCDebug(dcSungrow()) << "Feed-in power:" << (runningState & (0x1 << 4) ? "true" : "false");
|
||||
qCDebug(dcSungrow()) << "Import power from grid:" << (runningState & (0x1 << 5) ? "true" : "false");
|
||||
qCDebug(dcSungrow()) << "Negative load power:" << (runningState & (0x1 << 7) ? "true" : "false");
|
||||
meterThing->setStateValue(sungrowMeterCurrentPowerStateTypeId, sungrowConnection->totalActivePower() * -1);
|
||||
meterThing->setStateValue(sungrowMeterTotalEnergyConsumedStateTypeId, sungrowConnection->totalImportEnergy());
|
||||
meterThing->setStateValue(sungrowMeterTotalEnergyProducedStateTypeId, sungrowConnection->totalExportEnergy());
|
||||
meterThing->setStateValue(sungrowMeterCurrentPhaseAStateTypeId, sungrowConnection->phaseACurrent() * -1);
|
||||
meterThing->setStateValue(sungrowMeterCurrentPhaseBStateTypeId, sungrowConnection->phaseBCurrent() * -1);
|
||||
meterThing->setStateValue(sungrowMeterCurrentPhaseCStateTypeId, sungrowConnection->phaseCCurrent() * -1);
|
||||
meterThing->setStateValue(sungrowMeterVoltagePhaseAStateTypeId, sungrowConnection->phaseAVoltage());
|
||||
meterThing->setStateValue(sungrowMeterVoltagePhaseBStateTypeId, sungrowConnection->phaseBVoltage());
|
||||
meterThing->setStateValue(sungrowMeterVoltagePhaseCStateTypeId, sungrowConnection->phaseCVoltage());
|
||||
meterThing->setStateValue(sungrowMeterApparentPowerPhaseAStateTypeId, sungrowConnection->phaseAVoltage() * sungrowConnection->phaseACurrent() * -1);
|
||||
meterThing->setStateValue(sungrowMeterApparentPowerPhaseBStateTypeId, sungrowConnection->phaseBVoltage() * sungrowConnection->phaseBCurrent() * -1);
|
||||
meterThing->setStateValue(sungrowMeterApparentPowerPhaseCStateTypeId, sungrowConnection->phaseCVoltage() * sungrowConnection->phaseCCurrent() * -1);
|
||||
meterThing->setStateValue(sungrowMeterFrequencyStateTypeId, sungrowConnection->gridFrequency());
|
||||
}
|
||||
|
||||
// Update the battery if available
|
||||
Thing *batteryThing = getBatteryThing(thing);
|
||||
if (batteryThing) {
|
||||
batteryThing->setStateValue(sungrowBatteryVoltageStateTypeId, sungrowConnection->batteryVoltage());
|
||||
batteryThing->setStateValue(sungrowBatteryTemperatureStateTypeId, sungrowConnection->batteryTemperature());
|
||||
batteryThing->setStateValue(sungrowBatteryBatteryLevelStateTypeId, sungrowConnection->batteryLevel());
|
||||
batteryThing->setStateValue(sungrowBatteryBatteryCriticalStateTypeId, sungrowConnection->batteryLevel() < 5);
|
||||
|
||||
batteryThing->setStateValue(sungrowBatteryCurrentPowerStateTypeId, sungrowConnection->batteryPower());
|
||||
quint16 runningState = sungrowConnection->runningState();
|
||||
if (runningState & (0x1 << 1)) { //Bit 1: Battery charging bit
|
||||
batteryThing->setStateValue(sungrowBatteryChargingStateStateTypeId, "charging");
|
||||
} else if (runningState & (0x1 << 2)) { //Bit 2: Battery discharging bit
|
||||
batteryThing->setStateValue(sungrowBatteryChargingStateStateTypeId, "discharging");
|
||||
} else {
|
||||
batteryThing->setStateValue(sungrowBatteryChargingStateStateTypeId, "idle");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_tcpConnections.insert(thing, sungrowConnection);
|
||||
|
||||
if (monitor->reachable())
|
||||
sungrowConnection->connectDevice();
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == sungrowMeterThingClassId) {
|
||||
|
||||
// Get the parent thing and the associated connection
|
||||
Thing *connectionThing = myThings().findById(thing->parentId());
|
||||
if (!connectionThing) {
|
||||
qCWarning(dcSungrow()) << "Failed to set up Sungrow energy meter because the parent thing with ID" << thing->parentId().toString() << "could not be found.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
auto sungrowConnection = m_tcpConnections.value(connectionThing);
|
||||
if (!sungrowConnection) {
|
||||
qCWarning(dcSungrow()) << "Failed to set up Sungrow energy meter because the connection for" << connectionThing << "does not exist.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: The states will be handled in the parent inverter thing on updated
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == sungrowBatteryThingClassId) {
|
||||
// Get the parent thing and the associated connection
|
||||
Thing *connectionThing = myThings().findById(thing->parentId());
|
||||
if (!connectionThing) {
|
||||
qCWarning(dcSungrow()) << "Failed to set up Sungrow battery because the parent thing with ID" << thing->parentId().toString() << "could not be found.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
auto sungrowConnection = m_tcpConnections.value(connectionThing);
|
||||
if (!sungrowConnection) {
|
||||
qCWarning(dcSungrow()) << "Failed to set up Sungrow battery because the connection for" << connectionThing << "does not exist.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: The states will be handled in the parent inverter thing on updated
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSungrow::postSetupThing(Thing *thing)
|
||||
{
|
||||
|
||||
if (thing->thingClassId() == sungrowInverterTcpThingClassId) {
|
||||
|
||||
// Create the update timer if not already set up
|
||||
if (!m_refreshTimer) {
|
||||
qCDebug(dcSungrow()) << "Starting plugin timer...";
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, [this] {
|
||||
foreach(auto thing, myThings().filterByThingClassId(sungrowInverterTcpThingClassId)) {
|
||||
auto monitor = m_monitors.value(thing);
|
||||
if (!monitor->reachable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto connection = m_tcpConnections.value(thing);
|
||||
if (connection->initializing()) {
|
||||
qCDebug(dcSungrow()) << "Skip updating" << connection->modbusTcpMaster() << "since the connection is still initializing.";
|
||||
continue;
|
||||
}
|
||||
if (connection->reachable()) {
|
||||
qCDebug(dcSungrow()) << "Updating connection" << connection->modbusTcpMaster()->hostAddress().toString();
|
||||
connection->update();
|
||||
} else {
|
||||
qCDebug(dcSungrow()) << "Device not reachable. Probably a TCP connection error. Reconnecting TCP socket";
|
||||
connection->reconnectDevice();
|
||||
}
|
||||
}
|
||||
});
|
||||
m_refreshTimer->start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == sungrowMeterThingClassId || thing->thingClassId() == sungrowBatteryThingClassId) {
|
||||
Thing *connectionThing = myThings().findById(thing->parentId());
|
||||
if (connectionThing) {
|
||||
thing->setStateValue("connected", connectionThing->stateValue("connected"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSungrow::thingRemoved(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == sungrowInverterTcpThingClassId && m_tcpConnections.contains(thing)) {
|
||||
auto connection = m_tcpConnections.take(thing);
|
||||
connection->modbusTcpMaster()->disconnectDevice();
|
||||
delete connection;
|
||||
}
|
||||
|
||||
// Unregister related hardware resources
|
||||
if (m_monitors.contains(thing))
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
|
||||
if (myThings().isEmpty() && m_refreshTimer) {
|
||||
qCDebug(dcSungrow()) << "Stopping refresh timer";
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||
m_refreshTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Thing *IntegrationPluginSungrow::getMeterThing(Thing *parentThing)
|
||||
{
|
||||
Things meterThings = myThings().filterByParentId(parentThing->id()).filterByThingClassId(sungrowMeterThingClassId);
|
||||
if (meterThings.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
return meterThings.first();
|
||||
}
|
||||
|
||||
Thing *IntegrationPluginSungrow::getBatteryThing(Thing *parentThing)
|
||||
{
|
||||
Things batteryThings = myThings().filterByParentId(parentThing->id()).filterByThingClassId(sungrowBatteryThingClassId);
|
||||
if (batteryThings.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
return batteryThings.first();
|
||||
}
|
||||
73
sungrow/integrationpluginsungrow.h
Normal file
73
sungrow/integrationpluginsungrow.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, 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 INTEGRATIONPLUGINSUNGROW_H
|
||||
#define INTEGRATIONPLUGINSUNGROW_H
|
||||
|
||||
#include <plugintimer.h>
|
||||
#include <integrations/integrationplugin.h>
|
||||
#include <network/networkdevicemonitor.h>
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "sungrowmodbustcpconnection.h"
|
||||
|
||||
class IntegrationPluginSungrow: public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsungrow.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginSungrow();
|
||||
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void postSetupThing(Thing *thing) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
|
||||
private:
|
||||
const int m_modbusTcpPort = 502;
|
||||
const quint16 m_modbusSlaveAddress = 1;
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
QHash<Thing *, SungrowModbusTcpConnection *> m_tcpConnections;
|
||||
|
||||
void setupSungrowTcpConnection(ThingSetupInfo *info);
|
||||
|
||||
Thing *getMeterThing(Thing *parentThing);
|
||||
Thing *getBatteryThing(Thing *parentThing);
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINSUNGROW_H
|
||||
|
||||
|
||||
289
sungrow/integrationpluginsungrow.json
Normal file
289
sungrow/integrationpluginsungrow.json
Normal file
@ -0,0 +1,289 @@
|
||||
{
|
||||
"name": "Sungrow",
|
||||
"displayName": "Sungrow",
|
||||
"id": "250c9b83-1127-4013-bbd0-11e7ea482057",
|
||||
"vendors": [
|
||||
{
|
||||
"name": "sungrow",
|
||||
"displayName": "Sungrow",
|
||||
"id": "cdc58e0d-bfdb-45d9-b961-9c0b036c35aa",
|
||||
"thingClasses": [
|
||||
{
|
||||
"name": "sungrowInverterTcp",
|
||||
"displayName": "Sungrow Inverter",
|
||||
"id": "59cb2da4-da07-11ee-adea-7397f8a9afe9",
|
||||
"createMethods": ["discovery"],
|
||||
"discoveryType": "weak",
|
||||
"interfaces": ["solarinverter", "connectable"],
|
||||
"providedInterfaces": [ "energymeter", "energystorage"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "62137142-da07-11ee-9522-2f74f3b1fc5d",
|
||||
"name":"macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress",
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "6c68b170-da07-11ee-9891-3335ced0ad72",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "6fb11fe8-da07-11ee-b77f-8393cc11be21",
|
||||
"name": "currentPower",
|
||||
"displayName": "Active power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "7bf092a2-da07-11ee-abe9-138e66e6f2a5",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.0,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "80b35fc2-da07-11ee-a73d-5774b082c92b",
|
||||
"name": "temperature",
|
||||
"displayName": "Temperature",
|
||||
"type": "double",
|
||||
"unit": "DegreeCelsius",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "84bcbc58-da07-11ee-b94d-a39792ee6f59",
|
||||
"name": "frequency",
|
||||
"displayName": "Frequency",
|
||||
"type": "double",
|
||||
"unit": "Hertz",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sungrowMeter",
|
||||
"displayName": "Sungrow Meter",
|
||||
"id": "a935e49c-da07-11ee-bd11-3fcf27dc6373",
|
||||
"createMethods": ["auto"],
|
||||
"interfaces": [ "energymeter", "connectable"],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "d26b59dc-da07-11ee-b494-3781cc3081a7",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "d777530e-da07-11ee-a526-c7d23dd34f57",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e80bb836-da07-11ee-afd1-dbff8484ba11",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "ebefd252-da07-11ee-a8a4-c7ea9df0b6f9",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "efd82b30-da07-11ee-9668-7b523696940d",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f3d850c0-da07-11ee-883f-4ff8b7e55bdc",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f82603de-da07-11ee-93bc-6b9f9f333c30",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "fcf77988-da07-11ee-99da-3b2415014506",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "167aa300-faef-11ee-859a-bb6f4e8be7c9",
|
||||
"name": "apparentPowerPhaseA",
|
||||
"displayName": "Apparent power phase A",
|
||||
"type": "double",
|
||||
"unit": "VoltAmpere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "2c3ac134-faef-11ee-9c28-9f6bb77683d3",
|
||||
"name": "apparentPowerPhaseB",
|
||||
"displayName": "Apparent power phase B",
|
||||
"type": "double",
|
||||
"unit": "VoltAmpere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "3f7ca9e2-faef-11ee-81e7-6f53d07e9197",
|
||||
"name": "apparentPowerPhaseC",
|
||||
"displayName": "Apparent power phase C",
|
||||
"type": "double",
|
||||
"unit": "VoltAmpere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "00eb83c2-da08-11ee-b67d-1f74a41e6218",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total returned energy",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "03ef972a-da08-11ee-9a1f-d741d1e276be",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Total imported energy",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "07b526ea-da08-11ee-ab24-ab0d1ca8555d",
|
||||
"name": "frequency",
|
||||
"displayName": "Frequency",
|
||||
"type": "double",
|
||||
"unit": "Hertz",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sungrowBattery",
|
||||
"displayName": "Sungrow Battery",
|
||||
"id": "0aea1b90-da08-11ee-9195-afc9f857a324",
|
||||
"createMethods": ["auto"],
|
||||
"interfaces": ["energystorage", "connectable"],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "0ff3c834-da08-11ee-bac7-0f1044f86ea3",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "13f48cde-da08-11ee-81c8-3362a92b58c8",
|
||||
"name": "batteryCritical",
|
||||
"displayName": "Battery critical",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "1738bcf8-da08-11ee-b1d0-a3e1183dabba",
|
||||
"name": "batteryLevel",
|
||||
"displayName": "Battery level",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1aa8cf86-da08-11ee-9697-3b0a8023db88",
|
||||
"name": "currentPower",
|
||||
"displayName": "Total real power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1e6bf760-da08-11ee-ba39-c31ecdbb8fc9",
|
||||
"name": "voltage",
|
||||
"displayName": "Voltage",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "221ccae2-da08-11ee-9e4d-4b6abbf9e564",
|
||||
"name": "temperature",
|
||||
"displayName": "Temperature",
|
||||
"type": "double",
|
||||
"unit": "DegreeCelsius",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "264ac092-da08-11ee-ad8d-9f0751d6c499",
|
||||
"name": "capacity",
|
||||
"displayName": "Capacity",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "29e8a6d8-da08-11ee-87ba-539307b7d2ee",
|
||||
"name": "chargingState",
|
||||
"displayName": "Charging state",
|
||||
"type": "QString",
|
||||
"possibleValues": ["idle", "charging", "discharging"],
|
||||
"defaultValue": "idle",
|
||||
"cached": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
13
sungrow/meta.json
Normal file
13
sungrow/meta.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Sungrow Inverter",
|
||||
"tagline": "Connect to Sungrow inverters.",
|
||||
"icon": "sungrow.png",
|
||||
"stability": "consumer",
|
||||
"offline": true,
|
||||
"technologies": [
|
||||
"network"
|
||||
],
|
||||
"categories": [
|
||||
"energy"
|
||||
]
|
||||
}
|
||||
667
sungrow/sungrow-registers.json
Normal file
667
sungrow/sungrow-registers.json
Normal file
@ -0,0 +1,667 @@
|
||||
{
|
||||
"className": "Sungrow",
|
||||
"protocol": "TCP",
|
||||
"endianness": "LittleEndian",
|
||||
"errorLimitUntilNotReachable": 5,
|
||||
"queuedRequests": true,
|
||||
"queuedRequestsDelay": 400,
|
||||
"checkReachableRegister": "totalPVPower",
|
||||
"enums": [
|
||||
{
|
||||
"name": "SystemState",
|
||||
"values": [
|
||||
{
|
||||
"key": "Stop",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"key": "Standby",
|
||||
"value": 8
|
||||
},
|
||||
{
|
||||
"key": "InitialStandby",
|
||||
"value": 16
|
||||
},
|
||||
{
|
||||
"key": "Startup",
|
||||
"value": 32
|
||||
},
|
||||
{
|
||||
"key": "Running",
|
||||
"value": 64
|
||||
},
|
||||
{
|
||||
"key": "Fault",
|
||||
"value": 256
|
||||
},
|
||||
{
|
||||
"key": "RunningMainMode",
|
||||
"value": 1024
|
||||
},
|
||||
{
|
||||
"key": "RunningForcedMode",
|
||||
"value": 2048
|
||||
},
|
||||
{
|
||||
"key": "RunningOffGridMode",
|
||||
"value": 4096
|
||||
},
|
||||
{
|
||||
"key": "Restarting",
|
||||
"value": 9473
|
||||
},
|
||||
{
|
||||
"key": "RunningExternalEMSMode",
|
||||
"value": 16384
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BatteryType",
|
||||
"values": [
|
||||
{
|
||||
"key": "LeadAcidNarada",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"key": "LiIonSamsung",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"key": "NoBattery",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"key": "LeadAcidOther",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"key": "LiIonUS2000A",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"key": "LiIonLG",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"key": "LiIonUS2000B",
|
||||
"value": 6
|
||||
},
|
||||
{
|
||||
"key": "LiIonGCL",
|
||||
"value": 7
|
||||
},
|
||||
{
|
||||
"key": "LiIonBSG",
|
||||
"value": 8
|
||||
},
|
||||
{
|
||||
"key": "LiIonSungrow",
|
||||
"value": 9
|
||||
},
|
||||
{
|
||||
"key": "LiIonBYD",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"key": "LiIonTawaki",
|
||||
"value": 11
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"id": "version",
|
||||
"readSchedule": "init",
|
||||
"registers": [
|
||||
{
|
||||
"id": "protocolNumber",
|
||||
"address": 4949,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Protocol number",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "protocolVersion",
|
||||
"address": 4951,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Device type code",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "armSoftwareVersion",
|
||||
"address": 4953,
|
||||
"size": 15,
|
||||
"type": "string",
|
||||
"registerType": "inputRegister",
|
||||
"description": "ARM software version",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dspSoftwareVersion",
|
||||
"address": 4968,
|
||||
"size": 15,
|
||||
"type": "string",
|
||||
"registerType": "inputRegister",
|
||||
"description": "ARM software version",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "identification",
|
||||
"readSchedule": "init",
|
||||
"registers": [
|
||||
{
|
||||
"id": "serialNumber",
|
||||
"address": 4989,
|
||||
"size": 10,
|
||||
"type": "string",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Serial number",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "deviceTypeCode",
|
||||
"address": 4999,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Device type code",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "nominalOutputPower",
|
||||
"address": 5000,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Nominal output power",
|
||||
"unit": "kW",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "energyValues1",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "inverterTemperature",
|
||||
"address": 5007,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Inverter temperature",
|
||||
"unit": "°C",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dummy0",
|
||||
"address": 5008,
|
||||
"size": 8,
|
||||
"type": "raw",
|
||||
"registerType": "inputRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalPVPower",
|
||||
"address": 5016,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total PV power",
|
||||
"defaultValue": "0",
|
||||
"unit": "W",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "phaseAVoltage",
|
||||
"address": 5018,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Phase A voltage",
|
||||
"unit": "V",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "phaseBVoltage",
|
||||
"address": 5019,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Phase B voltage",
|
||||
"unit": "V",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "phaseCVoltage",
|
||||
"address": 5020,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Phase C voltage",
|
||||
"unit": "V",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dummy1",
|
||||
"address": 5021,
|
||||
"size": 11,
|
||||
"type": "raw",
|
||||
"registerType": "inputRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "reactivePower",
|
||||
"address": 5032,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Reactive power",
|
||||
"defaultValue": "0",
|
||||
"unit": "var",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "powerFactor",
|
||||
"address": 5034,
|
||||
"size": 1,
|
||||
"type": "int32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Power factor",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -3,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridFrequency",
|
||||
"address": 5035,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Grid frequency",
|
||||
"defaultValue": "0",
|
||||
"unit": "Hz",
|
||||
"staticScaleFactor": -2,
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "energyValues2",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "systemState",
|
||||
"address": 12999,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"enum": "SystemState",
|
||||
"registerType": "inputRegister",
|
||||
"description": "System state",
|
||||
"defaultValue": "SystemStateStop",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "runningState",
|
||||
"address": 13000,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Running state",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dailyPVGeneration",
|
||||
"address": 13001,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Daily PV generation",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalPVGeneration",
|
||||
"address": 13002,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total PV generation",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dailyPVExport",
|
||||
"address": 13004,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Daily PV export",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalPVExport",
|
||||
"address": 13005,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total PV export´",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "loadPower",
|
||||
"address": 13007,
|
||||
"size": 2,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Load power",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "exportPower",
|
||||
"address": 13009,
|
||||
"size": 2,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Export power",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dailyBatteryChargePV",
|
||||
"address": 13011,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Daily battery charge from PV",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalBatteryChargePV",
|
||||
"address": 13012,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total battery charge from PV",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dummy2",
|
||||
"address": 13014,
|
||||
"size": 5,
|
||||
"type": "raw",
|
||||
"registerType": "inputRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryVoltage",
|
||||
"address": 13019,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Battery voltage",
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryCurrent",
|
||||
"address": 13020,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Battery current",
|
||||
"unit": "A",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryPower",
|
||||
"address": 13021,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Battery power",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryLevel",
|
||||
"address": 13022,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Battery level",
|
||||
"unit": "%",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryHealthState",
|
||||
"address": 13023,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Battery health state",
|
||||
"unit": "%",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryTemperature",
|
||||
"address": 13024,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Battery temperature",
|
||||
"unit": "°C",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dummy3",
|
||||
"address": 13025,
|
||||
"size": 4,
|
||||
"type": "raw",
|
||||
"registerType": "inputRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridState",
|
||||
"address": 13029,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Grid state",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "phaseACurrent",
|
||||
"address": 13030,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Phase A current",
|
||||
"unit": "A",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "phaseBCurrent",
|
||||
"address": 13031,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Phase B current",
|
||||
"unit": "A",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "phaseCCurrent",
|
||||
"address": 13032,
|
||||
"size": 1,
|
||||
"type": "int16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Phase C current",
|
||||
"unit": "A",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalActivePower",
|
||||
"address": 13033,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total active power",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dailyImportEnergy",
|
||||
"address": 13035,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Daily import energy",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalImportEnergy",
|
||||
"address": 13036,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total import energy",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dummy4",
|
||||
"address": 13038,
|
||||
"size": 7,
|
||||
"type": "raw",
|
||||
"registerType": "inputRegister",
|
||||
"description": "none",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalExportEnergy",
|
||||
"address": 13045,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "inputRegister",
|
||||
"description": "Total export energy",
|
||||
"unit": "kWh",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "batteryInformation",
|
||||
"readSchedule": "init",
|
||||
"registers": [
|
||||
{
|
||||
"id": "batteryType",
|
||||
"address": 13054,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery type",
|
||||
"enum": "BatteryType",
|
||||
"defaultValue": "BatteryTypeNoBattery",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryNominalVoltage",
|
||||
"address": 13055,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery nominal voltage",
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"staticScaleFactor": -1,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryCapacity",
|
||||
"address": 13056,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery capacity",
|
||||
"unit": "Ah",
|
||||
"defaultValue": "10",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"registers": [
|
||||
]
|
||||
}
|
||||
BIN
sungrow/sungrow.png
Normal file
BIN
sungrow/sungrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
14
sungrow/sungrow.pro
Normal file
14
sungrow/sungrow.pro
Normal file
@ -0,0 +1,14 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
# Generate modbus connection
|
||||
MODBUS_CONNECTIONS += sungrow-registers.json
|
||||
#MODBUS_TOOLS_CONFIG += VERBOSE
|
||||
include(../modbus.pri)
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsungrow.h \
|
||||
sungrowdiscovery.h
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsungrow.cpp \
|
||||
sungrowdiscovery.cpp
|
||||
161
sungrow/sungrowdiscovery.cpp
Normal file
161
sungrow/sungrowdiscovery.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, 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 "sungrowdiscovery.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
SungrowDiscovery::SungrowDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress, QObject *parent) :
|
||||
QObject{parent},
|
||||
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
||||
m_port{port},
|
||||
m_modbusAddress{modbusAddress}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SungrowDiscovery::startDiscovery()
|
||||
{
|
||||
qCDebug(dcSungrow()) << "Discovery: Start searching for Sungrow inverters in the network";
|
||||
m_startDateTime = QDateTime::currentDateTime();
|
||||
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &SungrowDiscovery::checkNetworkDevice);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=] () {
|
||||
qCDebug(dcSungrow()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
|
||||
|
||||
// Give the last connections added right before the network discovery finished a chance to check the device...
|
||||
QTimer::singleShot(3000, this, [this] () {
|
||||
qCDebug(dcSungrow()) << "Discovery: Grace period timer triggered.";
|
||||
finishDiscovery();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
QList<SungrowDiscovery::SungrowDiscoveryResult> SungrowDiscovery::discoveryResults() const
|
||||
{
|
||||
return m_discoveryResults;
|
||||
}
|
||||
|
||||
void SungrowDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
|
||||
{
|
||||
/* Create a Sungrow connection and try to initialize it.
|
||||
* Only if initialized successfully and all information have been fetched correctly from
|
||||
* the device we can assume this is what we are locking for (ip, port, modbus address, correct registers).
|
||||
*/
|
||||
|
||||
qCDebug(dcSungrow()) << "Creating Sungrow Modbus TCP connection for" << networkDeviceInfo.address() << "Port:" << m_port << "Slave Address" << m_modbusAddress;
|
||||
SungrowModbusTcpConnection *connection = new SungrowModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
|
||||
connection->modbusTcpMaster()->setTimeout(5000);
|
||||
connection->modbusTcpMaster()->setNumberOfRetries(0);
|
||||
m_connections.append(connection);
|
||||
|
||||
connect(connection, &SungrowModbusTcpConnection::reachableChanged, this, [=](bool reachable){
|
||||
qCDebug(dcSungrow()) << "Sungrow Modbus TCP Connection reachable changed:" << reachable;
|
||||
if (!reachable) {
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
qCDebug(dcSungrow()) << "Connected, proceeding with initialization";
|
||||
|
||||
connect(connection, &SungrowModbusTcpConnection::initializationFinished, this, [=](bool success){
|
||||
if (!success) {
|
||||
qCDebug(dcSungrow()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSungrow()) << "Discovery: Initialized successfully" << networkDeviceInfo << connection->serialNumber();
|
||||
qCDebug(dcSungrow()) << " - Protocol number:" << connection->protocolNumber();
|
||||
qCDebug(dcSungrow()) << " - Protocol version:" << connection->protocolVersion();
|
||||
qCDebug(dcSungrow()) << " - ARM software version:" << connection->armSoftwareVersion();
|
||||
qCDebug(dcSungrow()) << " - DSP software version:" << connection->dspSoftwareVersion();
|
||||
|
||||
if (connection->deviceTypeCode() >= 0xd00 && connection->deviceTypeCode() <= 0xeff) {
|
||||
SungrowDiscoveryResult result;
|
||||
result.networkDeviceInfo = networkDeviceInfo;
|
||||
result.serialNumber = connection->serialNumber();
|
||||
result.nominalOutputPower = connection->nominalOutputPower();
|
||||
result.deviceType = connection->deviceTypeCode();
|
||||
m_discoveryResults.append(result);
|
||||
}
|
||||
|
||||
connection->disconnectDevice();
|
||||
});
|
||||
|
||||
qCDebug(dcSungrow()) << "Discovery: The host" << networkDeviceInfo << "is reachable. Starting with initialization.";
|
||||
if (!connection->initialize()) {
|
||||
qCDebug(dcSungrow()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString() << "Continue...";
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// In case of an error skip the host
|
||||
connect(connection->modbusTcpMaster(), &ModbusTcpMaster::connectionStateChanged, this, [=](bool connected){
|
||||
if (connected) {
|
||||
qCDebug(dcSungrow()) << "Discovery: Connected with" << networkDeviceInfo.address().toString() << m_port;
|
||||
}
|
||||
});
|
||||
|
||||
// In case of an error skip the host
|
||||
connect(connection->modbusTcpMaster(), &ModbusTcpMaster::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
|
||||
if (error != QModbusDevice::NoError) {
|
||||
qCDebug(dcSungrow()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// If the reachability check failed skip the host
|
||||
connect(connection, &SungrowModbusTcpConnection::checkReachabilityFailed, this, [=](){
|
||||
qCDebug(dcSungrow()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";
|
||||
cleanupConnection(connection);
|
||||
});
|
||||
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
void SungrowDiscovery::cleanupConnection(SungrowModbusTcpConnection *connection)
|
||||
{
|
||||
qCDebug(dcSungrow()) << "Discovery: Cleanup connection" << connection->modbusTcpMaster();
|
||||
m_connections.removeAll(connection);
|
||||
connection->disconnectDevice();
|
||||
connection->deleteLater();
|
||||
}
|
||||
|
||||
void SungrowDiscovery::finishDiscovery()
|
||||
{
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||
|
||||
foreach (SungrowModbusTcpConnection *connection, m_connections)
|
||||
cleanupConnection(connection);
|
||||
|
||||
qCDebug(dcSungrow()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() << "Sungrow inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||
emit discoveryFinished();
|
||||
}
|
||||
76
sungrow/sungrowdiscovery.h
Normal file
76
sungrow/sungrowdiscovery.h
Normal file
@ -0,0 +1,76 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, 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 SUNGROWDISCOVERY_H
|
||||
#define SUNGROWDISCOVERY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "sungrowmodbustcpconnection.h"
|
||||
|
||||
class SungrowDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SungrowDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 1, QObject *parent = nullptr);
|
||||
typedef struct SungrowDiscoveryResult {
|
||||
QString serialNumber;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
float nominalOutputPower;
|
||||
int deviceType;
|
||||
} SungrowDiscoveryResult;
|
||||
|
||||
void startDiscovery();
|
||||
|
||||
QList<SungrowDiscoveryResult> discoveryResults() const;
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private:
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
quint16 m_port;
|
||||
quint16 m_modbusAddress;
|
||||
|
||||
QDateTime m_startDateTime;
|
||||
|
||||
QList<SungrowModbusTcpConnection *> m_connections;
|
||||
QList<SungrowDiscoveryResult> m_discoveryResults;
|
||||
|
||||
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
|
||||
void cleanupConnection(SungrowModbusTcpConnection *connection);
|
||||
|
||||
void finishDiscovery();
|
||||
};
|
||||
|
||||
#endif // SUNGROWDISCOVERY_H
|
||||
@ -0,0 +1,203 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>IntegrationPluginSungrow</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginsungrow.cpp" line="47"/>
|
||||
<source>The network device discovery is not available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginsungrow.cpp" line="103"/>
|
||||
<source>The MAC address is not known. Please reconfigure this inverter.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Sungrow</name>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="55"/>
|
||||
<source>Active power</source>
|
||||
<extracomment>The name of the StateType ({6fb11fe8-da07-11ee-b77f-8393cc11be21}) of ThingClass sungrowInverterTcp</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="58"/>
|
||||
<source>Battery critical</source>
|
||||
<extracomment>The name of the StateType ({13f48cde-da08-11ee-81c8-3362a92b58c8}) of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="61"/>
|
||||
<source>Battery level</source>
|
||||
<extracomment>The name of the StateType ({1738bcf8-da08-11ee-b1d0-a3e1183dabba}) of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="64"/>
|
||||
<source>Capacity</source>
|
||||
<extracomment>The name of the StateType ({264ac092-da08-11ee-ad8d-9f0751d6c499}) of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="67"/>
|
||||
<source>Charging state</source>
|
||||
<extracomment>The name of the StateType ({29e8a6d8-da08-11ee-87ba-539307b7d2ee}) of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="70"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="73"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="76"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({0ff3c834-da08-11ee-bac7-0f1044f86ea3}) of ThingClass sungrowBattery
|
||||
----------
|
||||
The name of the StateType ({d26b59dc-da07-11ee-b494-3781cc3081a7}) of ThingClass sungrowMeter
|
||||
----------
|
||||
The name of the StateType ({6c68b170-da07-11ee-9891-3335ced0ad72}) of ThingClass sungrowInverterTcp</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="79"/>
|
||||
<source>Current phase A</source>
|
||||
<extracomment>The name of the StateType ({f3d850c0-da07-11ee-883f-4ff8b7e55bdc}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="82"/>
|
||||
<source>Current phase B</source>
|
||||
<extracomment>The name of the StateType ({f82603de-da07-11ee-93bc-6b9f9f333c30}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="85"/>
|
||||
<source>Current phase C</source>
|
||||
<extracomment>The name of the StateType ({fcf77988-da07-11ee-99da-3b2415014506}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="88"/>
|
||||
<source>Current power</source>
|
||||
<extracomment>The name of the StateType ({d777530e-da07-11ee-a526-c7d23dd34f57}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="91"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="94"/>
|
||||
<source>Frequency</source>
|
||||
<extracomment>The name of the StateType ({07b526ea-da08-11ee-ab24-ab0d1ca8555d}) of ThingClass sungrowMeter
|
||||
----------
|
||||
The name of the StateType ({84bcbc58-da07-11ee-b94d-a39792ee6f59}) of ThingClass sungrowInverterTcp</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="97"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: sungrowInverterTcp, Type: thing, ID: {62137142-da07-11ee-9522-2f74f3b1fc5d})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="100"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="103"/>
|
||||
<source>Sungrow</source>
|
||||
<extracomment>The name of the vendor ({cdc58e0d-bfdb-45d9-b961-9c0b036c35aa})
|
||||
----------
|
||||
The name of the plugin Sungrow ({250c9b83-1127-4013-bbd0-11e7ea482057})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="106"/>
|
||||
<source>Sungrow Battery</source>
|
||||
<extracomment>The name of the ThingClass ({0aea1b90-da08-11ee-9195-afc9f857a324})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="109"/>
|
||||
<source>Sungrow Inverter</source>
|
||||
<extracomment>The name of the ThingClass ({59cb2da4-da07-11ee-adea-7397f8a9afe9})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="112"/>
|
||||
<source>Sungrow Meter</source>
|
||||
<extracomment>The name of the ThingClass ({a935e49c-da07-11ee-bd11-3fcf27dc6373})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="115"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="118"/>
|
||||
<source>Temperature</source>
|
||||
<extracomment>The name of the StateType ({221ccae2-da08-11ee-9e4d-4b6abbf9e564}) of ThingClass sungrowBattery
|
||||
----------
|
||||
The name of the StateType ({80b35fc2-da07-11ee-a73d-5774b082c92b}) of ThingClass sungrowInverterTcp</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="121"/>
|
||||
<source>Total energy produced</source>
|
||||
<extracomment>The name of the StateType ({7bf092a2-da07-11ee-abe9-138e66e6f2a5}) of ThingClass sungrowInverterTcp</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="124"/>
|
||||
<source>Total imported energy</source>
|
||||
<extracomment>The name of the StateType ({03ef972a-da08-11ee-9a1f-d741d1e276be}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="127"/>
|
||||
<source>Total real power</source>
|
||||
<extracomment>The name of the StateType ({1aa8cf86-da08-11ee-9697-3b0a8023db88}) of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="130"/>
|
||||
<source>Total returned energy</source>
|
||||
<extracomment>The name of the StateType ({00eb83c2-da08-11ee-b67d-1f74a41e6218}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="133"/>
|
||||
<source>Voltage</source>
|
||||
<extracomment>The name of the StateType ({1e6bf760-da08-11ee-ba39-c31ecdbb8fc9}) of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="136"/>
|
||||
<source>Voltage phase A</source>
|
||||
<extracomment>The name of the StateType ({e80bb836-da07-11ee-afd1-dbff8484ba11}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="139"/>
|
||||
<source>Voltage phase B</source>
|
||||
<extracomment>The name of the StateType ({ebefd252-da07-11ee-a8a4-c7ea9df0b6f9}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="142"/>
|
||||
<source>Voltage phase C</source>
|
||||
<extracomment>The name of the StateType ({efd82b30-da07-11ee-9668-7b523696940d}) of ThingClass sungrowMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="145"/>
|
||||
<source>charging</source>
|
||||
<extracomment>The name of a possible value of StateType {29e8a6d8-da08-11ee-87ba-539307b7d2ee} of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="148"/>
|
||||
<source>discharging</source>
|
||||
<extracomment>The name of a possible value of StateType {29e8a6d8-da08-11ee-87ba-539307b7d2ee} of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/sungrow/plugininfo.h" line="151"/>
|
||||
<source>idle</source>
|
||||
<extracomment>The name of a possible value of StateType {29e8a6d8-da08-11ee-87ba-539307b7d2ee} of ThingClass sungrowBattery</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
Reference in New Issue
Block a user