Merge PR #140: SMA: Add support for batteries
This commit is contained in:
commit
b755935606
@ -7,9 +7,8 @@ nymea plug-in for SMA solar equipment.
|
|||||||
* Sunny WebBox
|
* Sunny WebBox
|
||||||
* SMA speedwire Meters
|
* SMA speedwire Meters
|
||||||
* SMA speedwire Inverters
|
* SMA speedwire Inverters
|
||||||
* SMA inverters using modbus
|
* SMA solar inverters using modbus
|
||||||
|
* SMA battery inverters using modbus
|
||||||
> Note: the SMA battery equipment is still missing due to testing possibilities. Will be added as soon someone can with the appropriate setup.
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,8 @@
|
|||||||
#include "sma.h"
|
#include "sma.h"
|
||||||
#include "speedwire/speedwirediscovery.h"
|
#include "speedwire/speedwirediscovery.h"
|
||||||
#include "sunnywebbox/sunnywebboxdiscovery.h"
|
#include "sunnywebbox/sunnywebboxdiscovery.h"
|
||||||
#include "modbus/smamodbusdiscovery.h"
|
#include "modbus/smamodbussolarinverterdiscovery.h"
|
||||||
|
#include "modbus/smamodbusbatteryinverterdiscovery.h"
|
||||||
|
|
||||||
#include <network/networkdevicediscovery.h>
|
#include <network/networkdevicediscovery.h>
|
||||||
|
|
||||||
@ -201,7 +202,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
|
|
||||||
speedwireDiscovery->startDiscovery();
|
speedwireDiscovery->startDiscovery();
|
||||||
|
|
||||||
} else if (info->thingClassId() == modbusInverterThingClassId) {
|
} else if (info->thingClassId() == modbusSolarInverterThingClassId) {
|
||||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||||
qCWarning(dcSma()) << "The network discovery is not available on this platform.";
|
qCWarning(dcSma()) << "The network discovery is not available on this platform.";
|
||||||
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
|
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
|
||||||
@ -209,25 +210,25 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
|
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
|
||||||
SmaModbusDiscovery *discovery = new SmaModbusDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 3, info);
|
SmaModbusSolarInverterDiscovery *discovery = new SmaModbusSolarInverterDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 3, info);
|
||||||
connect(discovery, &SmaModbusDiscovery::discoveryFinished, info, [=](){
|
connect(discovery, &SmaModbusSolarInverterDiscovery::discoveryFinished, info, [=](){
|
||||||
foreach (const SmaModbusDiscovery::SmaModbusDiscoveryResult &result, discovery->discoveryResults()) {
|
foreach (const SmaModbusSolarInverterDiscovery::SmaModbusDiscoveryResult &result, discovery->discoveryResults()) {
|
||||||
|
|
||||||
ThingDescriptor descriptor(modbusInverterThingClassId, "SMA inverter " + result.productName, QT_TR_NOOP("Serial: ") + result.serialNumber + " (" + result.networkDeviceInfo.address().toString() + ")");
|
ThingDescriptor descriptor(modbusSolarInverterThingClassId, "SMA inverter " + result.productName, QT_TR_NOOP("Serial: ") + result.serialNumber + " (" + result.networkDeviceInfo.address().toString() + ")");
|
||||||
qCDebug(dcSma()) << "Discovered:" << descriptor.title() << descriptor.description();
|
qCDebug(dcSma()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||||
|
|
||||||
// Note: use the serial and not the mac address as identifier because more than one inverter might be behind a network device
|
// Note: use the serial and not the mac address as identifier because more than one inverter might be behind a network device
|
||||||
Things existingThings = myThings().filterByParam(modbusInverterThingSerialNumberParamTypeId, result.serialNumber);
|
Things existingThings = myThings().filterByParam(modbusSolarInverterThingSerialNumberParamTypeId, result.serialNumber);
|
||||||
if (existingThings.count() == 1) {
|
if (existingThings.count() == 1) {
|
||||||
qCDebug(dcSma()) << "This SMA inverter already exists in the system:" << result.serialNumber;
|
qCDebug(dcSma()) << "This SMA inverter already exists in the system:" << result.serialNumber;
|
||||||
descriptor.setThingId(existingThings.first()->id());
|
descriptor.setThingId(existingThings.first()->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
ParamList params;
|
ParamList params;
|
||||||
params << Param(modbusInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
params << Param(modbusSolarInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||||
params << Param(modbusInverterThingPortParamTypeId, result.port);
|
params << Param(modbusSolarInverterThingPortParamTypeId, result.port);
|
||||||
params << Param(modbusInverterThingSlaveIdParamTypeId, result.modbusAddress);
|
params << Param(modbusSolarInverterThingSlaveIdParamTypeId, result.modbusAddress);
|
||||||
params << Param(modbusInverterThingSerialNumberParamTypeId, result.serialNumber);
|
params << Param(modbusSolarInverterThingSerialNumberParamTypeId, result.serialNumber);
|
||||||
descriptor.setParams(params);
|
descriptor.setParams(params);
|
||||||
info->addThingDescriptor(descriptor);
|
info->addThingDescriptor(descriptor);
|
||||||
}
|
}
|
||||||
@ -237,6 +238,39 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
|
|
||||||
// Start the discovery process
|
// Start the discovery process
|
||||||
discovery->startDiscovery();
|
discovery->startDiscovery();
|
||||||
|
} else if (info->thingClassId() == modbusBatteryInverterThingClassId) {
|
||||||
|
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||||
|
qCWarning(dcSma()) << "The network discovery is not available on this platform.";
|
||||||
|
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("Unable to scan the network. Please ensure that the system is installed correctly."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmaModbusBatteryInverterDiscovery *discovery = new SmaModbusBatteryInverterDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 3, info);
|
||||||
|
connect(discovery, &SmaModbusBatteryInverterDiscovery::discoveryFinished, info, [=](){
|
||||||
|
foreach (const SmaModbusBatteryInverterDiscovery::Result &result, discovery->discoveryResults()) {
|
||||||
|
|
||||||
|
qCInfo(dcSma()) << "Discovered:" << result.deviceName << result.serialNumber << result.networkDeviceInfo.address().toString();
|
||||||
|
ThingDescriptor descriptor(modbusBatteryInverterThingClassId, "SMA battery inverter", QT_TR_NOOP("Serial: ") + result.serialNumber + " (" + result.networkDeviceInfo.address().toString() + ")");
|
||||||
|
|
||||||
|
Things existingThings = myThings().filterByParam(modbusBatteryInverterThingSerialNumberParamTypeId, result.serialNumber);
|
||||||
|
if (existingThings.count() == 1) {
|
||||||
|
qCInfo(dcSma()) << "This SMA inverter already exists in the system:" << result.serialNumber;
|
||||||
|
descriptor.setThingId(existingThings.first()->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
ParamList params;
|
||||||
|
params << Param(modbusBatteryInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||||
|
params << Param(modbusBatteryInverterThingPortParamTypeId, result.port);
|
||||||
|
params << Param(modbusBatteryInverterThingSlaveIdParamTypeId, result.modbusAddress);
|
||||||
|
params << Param(modbusBatteryInverterThingSerialNumberParamTypeId, result.serialNumber);
|
||||||
|
descriptor.setParams(params);
|
||||||
|
info->addThingDescriptor(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
});
|
||||||
|
discovery->startDiscovery();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,19 +507,19 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
|||||||
qCDebug(dcSma()) << "Battery: Setup SMA battery" << thing;
|
qCDebug(dcSma()) << "Battery: Setup SMA battery" << thing;
|
||||||
info->finish(Thing::ThingErrorNoError);
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
|
||||||
} else if (thing->thingClassId() == modbusInverterThingClassId) {
|
} else if (thing->thingClassId() == modbusSolarInverterThingClassId) {
|
||||||
|
|
||||||
// Handle reconfigure
|
// Handle reconfigure
|
||||||
if (m_modbusInverters.contains(thing)) {
|
if (m_modbusSolarInverters.contains(thing)) {
|
||||||
qCDebug(dcSma()) << "Reconfiguring existing thing" << thing->name();
|
qCDebug(dcSma()) << "Reconfiguring existing thing" << thing->name();
|
||||||
m_modbusInverters.take(thing)->deleteLater();
|
m_modbusSolarInverters.take(thing)->deleteLater();
|
||||||
|
|
||||||
if (m_monitors.contains(thing)) {
|
if (m_monitors.contains(thing)) {
|
||||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MacAddress macAddress = MacAddress(thing->paramValue(modbusInverterThingMacAddressParamTypeId).toString());
|
MacAddress macAddress = MacAddress(thing->paramValue(modbusSolarInverterThingMacAddressParamTypeId).toString());
|
||||||
if (!macAddress.isValid()) {
|
if (!macAddress.isValid()) {
|
||||||
qCWarning(dcSma()) << "The configured mac address is not valid" << thing->params();
|
qCWarning(dcSma()) << "The configured mac address is not valid" << thing->params();
|
||||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
|
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
|
||||||
@ -515,13 +549,65 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
|||||||
// Wait for the monitor to be ready
|
// Wait for the monitor to be ready
|
||||||
if (monitor->reachable()) {
|
if (monitor->reachable()) {
|
||||||
// Thing already reachable...let's continue with the setup
|
// Thing already reachable...let's continue with the setup
|
||||||
setupModbusInverterConnection(info);
|
setupModbusSolarInverterConnection(info);
|
||||||
} else {
|
} else {
|
||||||
qCDebug(dcSma()) << "Waiting for the network monitor to get reachable before continue to set up the connection" << thing->name() << address.toString() << "...";
|
qCDebug(dcSma()) << "Waiting for the network monitor to get reachable before continue to set up the connection" << thing->name() << address.toString() << "...";
|
||||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
|
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
|
||||||
if (reachable) {
|
if (reachable) {
|
||||||
qCDebug(dcSma()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continue setup...";
|
qCDebug(dcSma()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continue setup...";
|
||||||
setupModbusInverterConnection(info);
|
setupModbusSolarInverterConnection(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == modbusBatteryInverterThingClassId) {
|
||||||
|
|
||||||
|
if (m_modbusBatteryInverters.contains(thing)) {
|
||||||
|
qCDebug(dcSma()) << "Reconfiguring existing thing" << thing->name();
|
||||||
|
m_modbusBatteryInverters.take(thing)->deleteLater();
|
||||||
|
|
||||||
|
if (m_monitors.contains(thing)) {
|
||||||
|
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddress macAddress = MacAddress(thing->paramValue(modbusBatteryInverterThingMacAddressParamTypeId).toString());
|
||||||
|
if (!macAddress.isValid()) {
|
||||||
|
qCWarning(dcSma()) << "The configured mac address is not valid" << thing->params();
|
||||||
|
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the monitor
|
||||||
|
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
|
||||||
|
m_monitors.insert(thing, monitor);
|
||||||
|
|
||||||
|
QHostAddress address = monitor->networkDeviceInfo().address();
|
||||||
|
if (address.isNull()) {
|
||||||
|
qCWarning(dcSma()) << "Cannot set up sma modbus battery inverter. The host address is not known yet. Maybe it will be available in the next run...";
|
||||||
|
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The host address is not known yet. Trying again later."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up in case the setup gets aborted
|
||||||
|
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
|
||||||
|
if (m_monitors.contains(thing)) {
|
||||||
|
qCDebug(dcSma()) << "Unregister monitor because setup has been aborted.";
|
||||||
|
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for the monitor to be ready
|
||||||
|
if (monitor->reachable()) {
|
||||||
|
// Thing already reachable...let's continue with the setup
|
||||||
|
setupModbusBatteryInverterConnection(info);
|
||||||
|
} else {
|
||||||
|
qCDebug(dcSma()) << "Waiting for the network monitor to become reachable before continue to set up the connection" << thing->name() << address.toString() << "...";
|
||||||
|
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
|
||||||
|
if (reachable) {
|
||||||
|
qCDebug(dcSma()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continuing with setup...";
|
||||||
|
setupModbusBatteryInverterConnection(info);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -571,8 +657,8 @@ void IntegrationPluginSma::postSetupThing(Thing *thing)
|
|||||||
|
|
||||||
setupRefreshTimer();
|
setupRefreshTimer();
|
||||||
|
|
||||||
} else if (thing->thingClassId() == modbusInverterThingClassId) {
|
} else if (thing->thingClassId() == modbusSolarInverterThingClassId) {
|
||||||
SmaInverterModbusTcpConnection *connection = m_modbusInverters.value(thing);
|
SmaSolarInverterModbusTcpConnection *connection = m_modbusSolarInverters.value(thing);
|
||||||
if (connection) {
|
if (connection) {
|
||||||
thing->setStateValue("connected", connection->reachable());
|
thing->setStateValue("connected", connection->reachable());
|
||||||
if (!connection->reachable()) {
|
if (!connection->reachable()) {
|
||||||
@ -606,8 +692,8 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
|
|||||||
m_speedwireInverters.take(thing)->deleteLater();
|
m_speedwireInverters.take(thing)->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thing->thingClassId() == modbusInverterThingClassId && m_modbusInverters.contains(thing)) {
|
if (thing->thingClassId() == modbusSolarInverterThingClassId && m_modbusSolarInverters.contains(thing)) {
|
||||||
m_modbusInverters.take(thing)->deleteLater();
|
m_modbusSolarInverters.take(thing)->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_monitors.contains(thing)) {
|
if (m_monitors.contains(thing)) {
|
||||||
@ -677,7 +763,10 @@ void IntegrationPluginSma::setupRefreshTimer()
|
|||||||
inverter->refresh();
|
inverter->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (SmaInverterModbusTcpConnection *connection, m_modbusInverters) {
|
foreach (SmaSolarInverterModbusTcpConnection *connection, m_modbusSolarInverters) {
|
||||||
|
connection->update();
|
||||||
|
}
|
||||||
|
foreach (SmaBatteryInverterModbusTcpConnection *connection, m_modbusBatteryInverters) {
|
||||||
connection->update();
|
connection->update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -685,17 +774,17 @@ void IntegrationPluginSma::setupRefreshTimer()
|
|||||||
m_refreshTimer->start();
|
m_refreshTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
|
void IntegrationPluginSma::setupModbusSolarInverterConnection(ThingSetupInfo *info)
|
||||||
{
|
{
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
|
|
||||||
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
|
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
|
||||||
uint port = thing->paramValue(modbusInverterThingPortParamTypeId).toUInt();
|
uint port = thing->paramValue(modbusSolarInverterThingPortParamTypeId).toUInt();
|
||||||
quint16 slaveId = thing->paramValue(modbusInverterThingSlaveIdParamTypeId).toUInt();
|
quint16 slaveId = thing->paramValue(modbusSolarInverterThingSlaveIdParamTypeId).toUInt();
|
||||||
|
|
||||||
qCDebug(dcSma()) << "Setting up SMA inverter on" << address.toString() << port << "unit ID:" << slaveId;
|
qCDebug(dcSma()) << "Setting up SMA inverter on" << address.toString() << port << "unit ID:" << slaveId;
|
||||||
SmaInverterModbusTcpConnection *connection = new SmaInverterModbusTcpConnection(address, port, slaveId, this);
|
SmaSolarInverterModbusTcpConnection *connection = new SmaSolarInverterModbusTcpConnection(address, port, slaveId, this);
|
||||||
connect(info, &ThingSetupInfo::aborted, connection, &SmaInverterModbusTcpConnection::deleteLater);
|
connect(info, &ThingSetupInfo::aborted, connection, &SmaSolarInverterModbusTcpConnection::deleteLater);
|
||||||
|
|
||||||
// Reconnect on monitor reachable changed
|
// Reconnect on monitor reachable changed
|
||||||
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
|
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
|
||||||
@ -711,25 +800,25 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
|
|||||||
// Note: We disable autoreconnect explicitly and we will
|
// Note: We disable autoreconnect explicitly and we will
|
||||||
// connect the device once the monitor says it is reachable again
|
// connect the device once the monitor says it is reachable again
|
||||||
connection->disconnectDevice();
|
connection->disconnectDevice();
|
||||||
markModbusInverterAsDisconnected(thing);
|
markModbusSolarInverterAsDisconnected(thing);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::reachableChanged, thing, [this, thing, connection](bool reachable){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::reachableChanged, thing, [this, thing, connection](bool reachable){
|
||||||
qCDebug(dcSma()) << "Reachable changed to" << reachable << "for" << thing;
|
qCDebug(dcSma()) << "Reachable changed to" << reachable << "for" << thing;
|
||||||
if (reachable) {
|
if (reachable) {
|
||||||
// Connected true will be set after successfull init
|
// Connected true will be set after successfull init
|
||||||
connection->initialize();
|
connection->initialize();
|
||||||
} else {
|
} else {
|
||||||
thing->setStateValue("connected", false);
|
thing->setStateValue("connected", false);
|
||||||
markModbusInverterAsDisconnected(thing);
|
markModbusSolarInverterAsDisconnected(thing);
|
||||||
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||||
childThing->setStateValue("connected", false);
|
childThing->setStateValue("connected", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, thing, [=](bool success){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::initializationFinished, thing, [=](bool success){
|
||||||
if (!thing->setupComplete())
|
if (!thing->setupComplete())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -741,11 +830,11 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
// Try once to reconnect the device
|
// Try once to reconnect the device
|
||||||
connection->reconnectDevice();
|
connection->reconnectDevice();
|
||||||
markModbusInverterAsDisconnected(thing);
|
markModbusSolarInverterAsDisconnected(thing);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, info, [=](bool success){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::initializationFinished, info, [=](bool success){
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCWarning(dcSma()) << "Connection init finished with errors" << thing->name() << connection->modbusTcpMaster()->hostAddress().toString();
|
qCWarning(dcSma()) << "Connection init finished with errors" << thing->name() << connection->modbusTcpMaster()->hostAddress().toString();
|
||||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
|
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
|
||||||
@ -755,7 +844,7 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(dcSma()) << "Connection init finished successfully" << connection;
|
qCDebug(dcSma()) << "Connection init finished successfully" << connection;
|
||||||
m_modbusInverters.insert(thing, connection);
|
m_modbusSolarInverters.insert(thing, connection);
|
||||||
info->finish(Thing::ThingErrorNoError);
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
|
||||||
// Set connected true
|
// Set connected true
|
||||||
@ -764,52 +853,142 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
|
|||||||
childThing->setStateValue("connected", true);
|
childThing->setStateValue("connected", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::updateFinished, thing, [=](){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::updateFinished, thing, [=](){
|
||||||
qCDebug(dcSma()) << "Updated" << connection;
|
qCDebug(dcSma()) << "Updated" << connection;
|
||||||
|
|
||||||
// Grid voltage
|
// Grid voltage
|
||||||
if (isModbusValueValid(connection->gridVoltagePhaseA()))
|
if (isModbusValueValid(connection->gridVoltagePhaseA()))
|
||||||
thing->setStateValue(modbusInverterVoltagePhaseAStateTypeId, connection->gridVoltagePhaseA() / 100.0);
|
thing->setStateValue(modbusSolarInverterVoltagePhaseAStateTypeId, connection->gridVoltagePhaseA() / 100.0);
|
||||||
|
|
||||||
if (isModbusValueValid(connection->gridVoltagePhaseB()))
|
if (isModbusValueValid(connection->gridVoltagePhaseB()))
|
||||||
thing->setStateValue(modbusInverterVoltagePhaseBStateTypeId, connection->gridVoltagePhaseB() / 100.0);
|
thing->setStateValue(modbusSolarInverterVoltagePhaseBStateTypeId, connection->gridVoltagePhaseB() / 100.0);
|
||||||
|
|
||||||
if (isModbusValueValid(connection->gridVoltagePhaseC()))
|
if (isModbusValueValid(connection->gridVoltagePhaseC()))
|
||||||
thing->setStateValue(modbusInverterVoltagePhaseCStateTypeId, connection->gridVoltagePhaseC() / 100.0);
|
thing->setStateValue(modbusSolarInverterVoltagePhaseCStateTypeId, connection->gridVoltagePhaseC() / 100.0);
|
||||||
|
|
||||||
// Grid current
|
// Grid current
|
||||||
if (isModbusValueValid(connection->gridCurrentPhaseA()))
|
if (isModbusValueValid(connection->gridCurrentPhaseA()))
|
||||||
thing->setStateValue(modbusInverterCurrentPhaseAStateTypeId, connection->gridCurrentPhaseA() / 1000.0);
|
thing->setStateValue(modbusSolarInverterCurrentPhaseAStateTypeId, connection->gridCurrentPhaseA() / 1000.0);
|
||||||
|
|
||||||
if (isModbusValueValid(connection->gridCurrentPhaseB()))
|
if (isModbusValueValid(connection->gridCurrentPhaseB()))
|
||||||
thing->setStateValue(modbusInverterCurrentPhaseBStateTypeId, connection->gridCurrentPhaseB() / 1000.0);
|
thing->setStateValue(modbusSolarInverterCurrentPhaseBStateTypeId, connection->gridCurrentPhaseB() / 1000.0);
|
||||||
|
|
||||||
if (isModbusValueValid(connection->gridCurrentPhaseC()))
|
if (isModbusValueValid(connection->gridCurrentPhaseC()))
|
||||||
thing->setStateValue(modbusInverterCurrentPhaseCStateTypeId, connection->gridCurrentPhaseC() / 1000.0);
|
thing->setStateValue(modbusSolarInverterCurrentPhaseCStateTypeId, connection->gridCurrentPhaseC() / 1000.0);
|
||||||
|
|
||||||
// Phase power
|
// Phase power
|
||||||
if (isModbusValueValid(connection->currentPowerPhaseA()))
|
if (isModbusValueValid(connection->currentPowerPhaseA()))
|
||||||
thing->setStateValue(modbusInverterCurrentPowerPhaseAStateTypeId, connection->currentPowerPhaseA());
|
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseAStateTypeId, connection->currentPowerPhaseA());
|
||||||
|
|
||||||
if (isModbusValueValid(connection->currentPowerPhaseB()))
|
if (isModbusValueValid(connection->currentPowerPhaseB()))
|
||||||
thing->setStateValue(modbusInverterCurrentPowerPhaseBStateTypeId, connection->currentPowerPhaseB());
|
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseBStateTypeId, connection->currentPowerPhaseB());
|
||||||
|
|
||||||
if (isModbusValueValid(connection->currentPowerPhaseC()))
|
if (isModbusValueValid(connection->currentPowerPhaseC()))
|
||||||
thing->setStateValue(modbusInverterCurrentPowerPhaseCStateTypeId, connection->currentPowerPhaseC());
|
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseCStateTypeId, connection->currentPowerPhaseC());
|
||||||
|
|
||||||
// Others
|
// Others
|
||||||
if (isModbusValueValid(connection->totalYield()))
|
if (isModbusValueValid(connection->totalYield()))
|
||||||
thing->setStateValue(modbusInverterTotalEnergyProducedStateTypeId, connection->totalYield() / 1000.0); // kWh
|
thing->setStateValue(modbusSolarInverterTotalEnergyProducedStateTypeId, connection->totalYield() / 1000.0); // kWh
|
||||||
|
|
||||||
if (isModbusValueValid(connection->dailyYield()))
|
if (isModbusValueValid(connection->dailyYield()))
|
||||||
thing->setStateValue(modbusInverterEnergyProducedTodayStateTypeId, connection->dailyYield() / 1000.0); // kWh
|
thing->setStateValue(modbusSolarInverterEnergyProducedTodayStateTypeId, connection->dailyYield() / 1000.0); // kWh
|
||||||
|
|
||||||
// Power
|
// Power
|
||||||
if (isModbusValueValid(connection->currentPower()))
|
if (isModbusValueValid(connection->currentPower()))
|
||||||
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, -connection->currentPower());
|
thing->setStateValue(modbusSolarInverterCurrentPowerStateTypeId, -connection->currentPower());
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
thing->setStateValue(modbusInverterFirmwareVersionStateTypeId, Sma::buildSoftwareVersionString(connection->softwarePackage()));
|
thing->setStateValue(modbusSolarInverterFirmwareVersionStateTypeId, Sma::buildSoftwareVersionString(connection->softwarePackage()));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update registers
|
||||||
|
connection->update();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection->connectDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSma::setupModbusBatteryInverterConnection(ThingSetupInfo *info)
|
||||||
|
{
|
||||||
|
Thing *thing = info->thing();
|
||||||
|
|
||||||
|
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
|
||||||
|
uint port = thing->paramValue(modbusBatteryInverterThingPortParamTypeId).toUInt();
|
||||||
|
quint16 slaveId = thing->paramValue(modbusBatteryInverterThingSlaveIdParamTypeId).toUInt();
|
||||||
|
|
||||||
|
qCDebug(dcSma()) << "Setting up SMA inverter on" << address.toString() << port << "unit ID:" << slaveId;
|
||||||
|
SmaBatteryInverterModbusTcpConnection *connection = new SmaBatteryInverterModbusTcpConnection(address, port, slaveId, this);
|
||||||
|
connect(info, &ThingSetupInfo::aborted, connection, &SmaBatteryInverterModbusTcpConnection::deleteLater);
|
||||||
|
|
||||||
|
// Reconnect on monitor reachable changed
|
||||||
|
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
|
||||||
|
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
|
||||||
|
qCDebug(dcSma()) << "Network device monitor reachable changed for" << thing->name() << reachable;
|
||||||
|
if (!thing->setupComplete())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (reachable && !thing->stateValue("connected").toBool()) {
|
||||||
|
connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address());
|
||||||
|
connection->connectDevice();
|
||||||
|
} else if (!reachable) {
|
||||||
|
// Note: We disable autoreconnect explicitly and we will
|
||||||
|
// connect the device once the monitor says it is reachable again
|
||||||
|
connection->disconnectDevice();
|
||||||
|
markModbusBatteryInverterAsDisconnected(thing);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::reachableChanged, thing, [this, thing, connection](bool reachable){
|
||||||
|
qCDebug(dcSma()) << "Reachable changed to" << reachable << "for" << thing;
|
||||||
|
if (reachable) {
|
||||||
|
// Connected true will be set after successfull init
|
||||||
|
connection->initialize();
|
||||||
|
} else {
|
||||||
|
thing->setStateValue("connected", false);
|
||||||
|
markModbusBatteryInverterAsDisconnected(thing);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::initializationFinished, thing, [=](bool success){
|
||||||
|
if (!thing->setupComplete())
|
||||||
|
return;
|
||||||
|
|
||||||
|
thing->setStateValue("connected", success);
|
||||||
|
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||||
|
childThing->setStateValue("connected", success);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
// Try once to reconnect the device
|
||||||
|
connection->reconnectDevice();
|
||||||
|
markModbusBatteryInverterAsDisconnected(thing);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::initializationFinished, info, [=](bool success){
|
||||||
|
if (!success) {
|
||||||
|
qCWarning(dcSma()) << "Connection init finished with errors" << thing->name() << connection->modbusTcpMaster()->hostAddress().toString();
|
||||||
|
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
|
||||||
|
connection->deleteLater();
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Could not initialize the communication with the battery inverter."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcSma()) << "Connection init finished successfully" << connection;
|
||||||
|
m_modbusBatteryInverters.insert(thing, connection);
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
|
||||||
|
thing->setStateValue("connected", true);
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::updateFinished, thing, [=](){
|
||||||
|
qCDebug(dcSma()) << "Updated" << connection;
|
||||||
|
thing->setStateValue(modbusBatteryInverterFirmwareVersionStateTypeId, Sma::buildSoftwareVersionString(connection->softwarePackage()));
|
||||||
|
|
||||||
|
thing->setStateValue(modbusBatteryInverterBatteryLevelStateTypeId, connection->batterySOC());
|
||||||
|
thing->setStateValue(modbusBatteryInverterBatteryCriticalStateTypeId, connection->batterySOC() <= 5);
|
||||||
|
thing->setStateValue(modbusBatteryInverterCurrentPowerStateTypeId, -connection->currentPower());
|
||||||
|
thing->setStateValue(modbusBatteryInverterChargingStateStateTypeId, connection->currentPower() == 0 ? "idle" : (connection->currentPower() > 0 ? "charging" : "discharging"));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update registers
|
// Update registers
|
||||||
@ -866,18 +1045,23 @@ void IntegrationPluginSma::markSpeedwireBatteryAsDisconnected(Thing *thing)
|
|||||||
thing->setStateValue(speedwireBatteryChargingStateStateTypeId, "idle");
|
thing->setStateValue(speedwireBatteryChargingStateStateTypeId, "idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginSma::markModbusInverterAsDisconnected(Thing *thing)
|
void IntegrationPluginSma::markModbusSolarInverterAsDisconnected(Thing *thing)
|
||||||
{
|
{
|
||||||
thing->setStateValue(modbusInverterVoltagePhaseAStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterVoltagePhaseAStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterVoltagePhaseBStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterVoltagePhaseBStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterVoltagePhaseCStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterVoltagePhaseCStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPhaseAStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPhaseAStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPhaseBStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPhaseBStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPhaseCStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPhaseCStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPowerPhaseAStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseAStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPowerPhaseBStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseBStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPowerPhaseCStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseCStateTypeId, 0);
|
||||||
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, 0);
|
thing->setStateValue(modbusSolarInverterCurrentPowerStateTypeId, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSma::markModbusBatteryInverterAsDisconnected(Thing *thing)
|
||||||
|
{
|
||||||
|
thing->setStateValue(modbusBatteryInverterCurrentPowerStateTypeId, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 IntegrationPluginSma::getLocalSerialNumber()
|
quint64 IntegrationPluginSma::getLocalSerialNumber()
|
||||||
|
|||||||
@ -42,7 +42,8 @@
|
|||||||
#include "speedwire/speedwireinverter.h"
|
#include "speedwire/speedwireinverter.h"
|
||||||
#include "speedwire/speedwireinterface.h"
|
#include "speedwire/speedwireinterface.h"
|
||||||
|
|
||||||
#include "smainvertermodbustcpconnection.h"
|
#include "smasolarinvertermodbustcpconnection.h"
|
||||||
|
#include "smabatteryinvertermodbustcpconnection.h"
|
||||||
|
|
||||||
class IntegrationPluginSma: public IntegrationPlugin {
|
class IntegrationPluginSma: public IntegrationPlugin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -68,7 +69,8 @@ private slots:
|
|||||||
|
|
||||||
void setupRefreshTimer();
|
void setupRefreshTimer();
|
||||||
|
|
||||||
void setupModbusInverterConnection(ThingSetupInfo *info);
|
void setupModbusSolarInverterConnection(ThingSetupInfo *info);
|
||||||
|
void setupModbusBatteryInverterConnection(ThingSetupInfo *info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginTimer *m_refreshTimer = nullptr;
|
PluginTimer *m_refreshTimer = nullptr;
|
||||||
@ -78,7 +80,8 @@ private:
|
|||||||
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
|
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
|
||||||
QHash<Thing *, SpeedwireMeter *> m_speedwireMeters;
|
QHash<Thing *, SpeedwireMeter *> m_speedwireMeters;
|
||||||
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
||||||
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
|
QHash<Thing *, SmaSolarInverterModbusTcpConnection *> m_modbusSolarInverters;
|
||||||
|
QHash<Thing *, SmaBatteryInverterModbusTcpConnection *> m_modbusBatteryInverters;
|
||||||
|
|
||||||
quint32 m_localSerialNumber = 0;
|
quint32 m_localSerialNumber = 0;
|
||||||
|
|
||||||
@ -89,7 +92,8 @@ private:
|
|||||||
void markSpeedwireMeterAsDisconnected(Thing *thing);
|
void markSpeedwireMeterAsDisconnected(Thing *thing);
|
||||||
void markSpeedwireInverterAsDisconnected(Thing *thing);
|
void markSpeedwireInverterAsDisconnected(Thing *thing);
|
||||||
void markSpeedwireBatteryAsDisconnected(Thing *thing);
|
void markSpeedwireBatteryAsDisconnected(Thing *thing);
|
||||||
void markModbusInverterAsDisconnected(Thing *thing);
|
void markModbusSolarInverterAsDisconnected(Thing *thing);
|
||||||
|
void markModbusBatteryInverterAsDisconnected(Thing *thing);
|
||||||
|
|
||||||
quint64 getLocalSerialNumber();
|
quint64 getLocalSerialNumber();
|
||||||
|
|
||||||
|
|||||||
@ -534,8 +534,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "12e0429e-e8ce-48bd-a11c-faaf0bd71856",
|
"id": "12e0429e-e8ce-48bd-a11c-faaf0bd71856",
|
||||||
"name": "modbusInverter",
|
"name": "modbusSolarInverter",
|
||||||
"displayName": "SMA Inverter (Modbus)",
|
"displayName": "SMA Solar Inverter (Modbus)",
|
||||||
"createMethods": ["discovery", "user"],
|
"createMethods": ["discovery", "user"],
|
||||||
"interfaces": [ "solarinverter" ],
|
"interfaces": [ "solarinverter" ],
|
||||||
"paramTypes": [
|
"paramTypes": [
|
||||||
@ -693,6 +693,104 @@
|
|||||||
"defaultValue": ""
|
"defaultValue": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "06bed8fd-cadb-4cef-8440-7806fb0165e6",
|
||||||
|
"name": "modbusBatteryInverter",
|
||||||
|
"displayName": "SMA Battery Inverter (Modbus)",
|
||||||
|
"createMethods": ["discovery", "user"],
|
||||||
|
"interfaces": [ "energystorage", "connectable" ],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "03a5a009-0edc-4370-924a-785e7fcee30a",
|
||||||
|
"name":"macAddress",
|
||||||
|
"displayName": "MAC address",
|
||||||
|
"type": "QString",
|
||||||
|
"inputType": "MacAddress",
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "089d29e3-8ce0-42ca-93cf-463ad5a486af",
|
||||||
|
"name":"port",
|
||||||
|
"displayName": "Port",
|
||||||
|
"type": "int",
|
||||||
|
"defaultValue": 502
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "081814d7-26bb-445e-bccd-7f33c0d933ea",
|
||||||
|
"name":"slaveId",
|
||||||
|
"displayName": "Slave ID",
|
||||||
|
"type": "int",
|
||||||
|
"defaultValue": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9e2a69a0-c62c-4c53-b9f4-a2f9cb54f02c",
|
||||||
|
"name":"serialNumber",
|
||||||
|
"displayName": "Serial number",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": "",
|
||||||
|
"readOnly": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [
|
||||||
|
{
|
||||||
|
"id": "9c4999a1-304d-4724-99cd-eb0cd27590ef",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fe5ca68e-ddc2-45e7-aac2-b0e67ac40f87",
|
||||||
|
"name": "batteryLevel",
|
||||||
|
"displayName": "Battery level",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Percentage",
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "56f18b28-ed88-4c1a-a297-a5cad109b055",
|
||||||
|
"name": "batteryCritical",
|
||||||
|
"displayName": "Battery critical",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e1a91af1-8d1a-4564-9ade-b5488d63b90d",
|
||||||
|
"name": "currentPower",
|
||||||
|
"displayName": "Current power",
|
||||||
|
"type": "double",
|
||||||
|
"unit": "Watt",
|
||||||
|
"defaultValue": 0,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13cdb994-dd9e-49ac-a347-d2ab9aef5b45",
|
||||||
|
"name": "capacity",
|
||||||
|
"displayName": "Capacity",
|
||||||
|
"type": "double",
|
||||||
|
"unit": "KiloWattHour",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a313b416-5ded-43c9-b1a1-a9af50492d0b",
|
||||||
|
"name": "chargingState",
|
||||||
|
"displayName": "Charging state",
|
||||||
|
"type": "QString",
|
||||||
|
"possibleValues": ["idle", "charging", "discharging"],
|
||||||
|
"defaultValue": "idle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "952b3d30-1c09-4b0e-b303-56c89d3fa108",
|
||||||
|
"name": "firmwareVersion",
|
||||||
|
"displayName": "Firmware version",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
150
sma/modbus/smamodbusbatteryinverterdiscovery.cpp
Normal file
150
sma/modbus/smamodbusbatteryinverterdiscovery.cpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2023, 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 "smamodbusbatteryinverterdiscovery.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
#include "sma.h"
|
||||||
|
|
||||||
|
SmaModbusBatteryInverterDiscovery::SmaModbusBatteryInverterDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress, QObject *parent):
|
||||||
|
QObject(parent),
|
||||||
|
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
||||||
|
m_port(port),
|
||||||
|
m_modbusAddress(modbusAddress)
|
||||||
|
{
|
||||||
|
m_gracePeriodTimer.setSingleShot(true);
|
||||||
|
m_gracePeriodTimer.setInterval(3000);
|
||||||
|
connect(&m_gracePeriodTimer, &QTimer::timeout, this, [this](){
|
||||||
|
qCDebug(dcSma()) << "Discovery: Grace period timer triggered.";
|
||||||
|
finishDiscovery();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmaModbusBatteryInverterDiscovery::startDiscovery()
|
||||||
|
{
|
||||||
|
qCInfo(dcSma()) << "Discovery: Searching for SMA battery inverters in the network...";
|
||||||
|
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||||
|
|
||||||
|
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &SmaModbusBatteryInverterDiscovery::checkNetworkDevice);
|
||||||
|
|
||||||
|
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||||
|
qCDebug(dcSma()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
|
||||||
|
m_gracePeriodTimer.start();
|
||||||
|
discoveryReply->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<SmaModbusBatteryInverterDiscovery::Result> SmaModbusBatteryInverterDiscovery::discoveryResults() const
|
||||||
|
{
|
||||||
|
return m_discoveryResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmaModbusBatteryInverterDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
|
||||||
|
{
|
||||||
|
qCInfo(dcSma()) << "Checking network device:" << networkDeviceInfo << "Port:" << m_port << "Slave ID:" << m_modbusAddress;
|
||||||
|
|
||||||
|
SmaBatteryInverterModbusTcpConnection *connection = new SmaBatteryInverterModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
|
||||||
|
m_connections.append(connection);
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::reachableChanged, this, [=](bool reachable){
|
||||||
|
if (!reachable) {
|
||||||
|
cleanupConnection(connection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::initializationFinished, this, [=](bool success){
|
||||||
|
if (!success) {
|
||||||
|
qCInfo(dcSma()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Skipping result...";;
|
||||||
|
cleanupConnection(connection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection->deviceClass() != Sma::DeviceClassBatteryInverter) {
|
||||||
|
qCInfo(dcSma()) << "Discovery: Initialization successful for" << networkDeviceInfo.address().toString() << "but the device class is not a battery inverter. Skipping result...";;
|
||||||
|
cleanupConnection(connection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result result;
|
||||||
|
result.deviceName = connection->deviceName();
|
||||||
|
result.serialNumber = QString::number(connection->serialNumber());
|
||||||
|
result.port = m_port;
|
||||||
|
result.modbusAddress = m_modbusAddress;
|
||||||
|
result.softwareVersion = Sma::buildSoftwareVersionString(connection->softwarePackage());
|
||||||
|
result.networkDeviceInfo = networkDeviceInfo;
|
||||||
|
m_discoveryResults.append(result);
|
||||||
|
|
||||||
|
qCInfo(dcSma()) << "Discovery: --> Found";
|
||||||
|
qCInfo(dcSma()) << " Device name:" << result.deviceName;
|
||||||
|
qCInfo(dcSma()) << " Serial number:" << result.serialNumber;
|
||||||
|
qCInfo(dcSma()) << " Software version:" << result.softwareVersion;
|
||||||
|
qCInfo(dcSma()) << " " << result.networkDeviceInfo;
|
||||||
|
|
||||||
|
cleanupConnection(connection);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!connection->initialize()) {
|
||||||
|
qCDebug(dcSma()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString();
|
||||||
|
cleanupConnection(connection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(connection, &SmaBatteryInverterModbusTcpConnection::checkReachabilityFailed, this, [=](){
|
||||||
|
qCDebug(dcSma()) << "Discovery: Checking reachability failed on" << networkDeviceInfo.address().toString();
|
||||||
|
cleanupConnection(connection);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection->connectDevice();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmaModbusBatteryInverterDiscovery::cleanupConnection(SmaBatteryInverterModbusTcpConnection *connection)
|
||||||
|
{
|
||||||
|
m_connections.removeAll(connection);
|
||||||
|
connection->disconnectDevice();
|
||||||
|
connection->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmaModbusBatteryInverterDiscovery::finishDiscovery()
|
||||||
|
{
|
||||||
|
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||||
|
|
||||||
|
// Cleanup any leftovers...we don't care any more
|
||||||
|
foreach (SmaBatteryInverterModbusTcpConnection *connection, m_connections)
|
||||||
|
cleanupConnection(connection);
|
||||||
|
|
||||||
|
qCInfo(dcSma()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count()
|
||||||
|
<< "SMA battery inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||||
|
m_gracePeriodTimer.stop();
|
||||||
|
|
||||||
|
emit discoveryFinished();
|
||||||
|
|
||||||
|
}
|
||||||
81
sma/modbus/smamodbusbatteryinverterdiscovery.h
Normal file
81
sma/modbus/smamodbusbatteryinverterdiscovery.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2023, 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 SMAMODBUSBATTERYINVERTERDISCOVERY_H
|
||||||
|
#define SMAMODBUSBATTERYINVERTERDISCOVERY_H
|
||||||
|
|
||||||
|
#include <network/networkdevicediscovery.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "smabatteryinvertermodbustcpconnection.h"
|
||||||
|
|
||||||
|
class SmaModbusBatteryInverterDiscovery : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SmaModbusBatteryInverterDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 3, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
struct Result {
|
||||||
|
QString deviceName;
|
||||||
|
QString serialNumber;
|
||||||
|
int port;
|
||||||
|
int modbusAddress;
|
||||||
|
QString softwareVersion;
|
||||||
|
NetworkDeviceInfo networkDeviceInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
void startDiscovery();
|
||||||
|
|
||||||
|
QList<Result> discoveryResults() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void discoveryFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||||
|
uint m_port;
|
||||||
|
uint m_modbusAddress;
|
||||||
|
|
||||||
|
QTimer m_gracePeriodTimer;
|
||||||
|
QDateTime m_startDateTime;
|
||||||
|
|
||||||
|
QList<SmaBatteryInverterModbusTcpConnection *> m_connections;
|
||||||
|
|
||||||
|
QList<Result> m_discoveryResults;
|
||||||
|
|
||||||
|
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
|
||||||
|
void cleanupConnection(SmaBatteryInverterModbusTcpConnection *connection);
|
||||||
|
|
||||||
|
void finishDiscovery();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SMAMODBUSBATTERYINVERTERDISCOVERY_H
|
||||||
@ -28,13 +28,13 @@
|
|||||||
*
|
*
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
#include "smamodbusdiscovery.h"
|
#include "smamodbussolarinverterdiscovery.h"
|
||||||
#include "extern-plugininfo.h"
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
#include "sma.h"
|
#include "sma.h"
|
||||||
|
|
||||||
|
|
||||||
SmaModbusDiscovery::SmaModbusDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress,QObject *parent)
|
SmaModbusSolarInverterDiscovery::SmaModbusSolarInverterDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress,QObject *parent)
|
||||||
: QObject{parent},
|
: QObject{parent},
|
||||||
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
||||||
m_port{port},
|
m_port{port},
|
||||||
@ -43,13 +43,13 @@ SmaModbusDiscovery::SmaModbusDiscovery(NetworkDeviceDiscovery *networkDeviceDisc
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmaModbusDiscovery::startDiscovery()
|
void SmaModbusSolarInverterDiscovery::startDiscovery()
|
||||||
{
|
{
|
||||||
qCInfo(dcSma()) << "Discovery: Start searching for SMA modbus inverters in the network...";
|
qCInfo(dcSma()) << "Discovery: Start searching for SMA modbus inverters in the network...";
|
||||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||||
|
|
||||||
// Imedialty check any new device gets discovered
|
// Imedialty check any new device gets discovered
|
||||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &SmaModbusDiscovery::checkNetworkDevice);
|
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &SmaModbusSolarInverterDiscovery::checkNetworkDevice);
|
||||||
|
|
||||||
// Check what might be left on finished
|
// Check what might be left on finished
|
||||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||||
@ -71,12 +71,12 @@ void SmaModbusDiscovery::startDiscovery()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<SmaModbusDiscovery::SmaModbusDiscoveryResult> SmaModbusDiscovery::discoveryResults() const
|
QList<SmaModbusSolarInverterDiscovery::SmaModbusDiscoveryResult> SmaModbusSolarInverterDiscovery::discoveryResults() const
|
||||||
{
|
{
|
||||||
return m_discoveryResults;
|
return m_discoveryResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
|
void SmaModbusSolarInverterDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
|
||||||
{
|
{
|
||||||
// Create a kostal connection and try to initialize it.
|
// Create a kostal connection and try to initialize it.
|
||||||
// Only if initialized successfully and all information have been fetched correctly from
|
// Only if initialized successfully and all information have been fetched correctly from
|
||||||
@ -86,11 +86,11 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
|
|||||||
if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo))
|
if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SmaInverterModbusTcpConnection *connection = new SmaInverterModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
|
SmaSolarInverterModbusTcpConnection *connection = new SmaSolarInverterModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
|
||||||
m_connections.append(connection);
|
m_connections.append(connection);
|
||||||
m_verifiedNetworkDeviceInfos.append(networkDeviceInfo);
|
m_verifiedNetworkDeviceInfos.append(networkDeviceInfo);
|
||||||
|
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::reachableChanged, this, [=](bool reachable){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::reachableChanged, this, [=](bool reachable){
|
||||||
if (!reachable) {
|
if (!reachable) {
|
||||||
// Disconnected ... done with this connection
|
// Disconnected ... done with this connection
|
||||||
cleanupConnection(connection);
|
cleanupConnection(connection);
|
||||||
@ -98,7 +98,7 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Modbus TCP connected...ok, let's try to initialize it!
|
// Modbus TCP connected...ok, let's try to initialize it!
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, this, [=](bool success){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::initializationFinished, this, [=](bool success){
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCDebug(dcSma()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
qCDebug(dcSma()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||||
cleanupConnection(connection);
|
cleanupConnection(connection);
|
||||||
@ -147,7 +147,7 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
|
|||||||
});
|
});
|
||||||
|
|
||||||
// If check reachability failed...skip this host...
|
// If check reachability failed...skip this host...
|
||||||
connect(connection, &SmaInverterModbusTcpConnection::checkReachabilityFailed, this, [=](){
|
connect(connection, &SmaSolarInverterModbusTcpConnection::checkReachabilityFailed, this, [=](){
|
||||||
qCDebug(dcSma()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
qCDebug(dcSma()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||||
cleanupConnection(connection);
|
cleanupConnection(connection);
|
||||||
});
|
});
|
||||||
@ -156,19 +156,19 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
|
|||||||
connection->connectDevice();
|
connection->connectDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmaModbusDiscovery::cleanupConnection(SmaInverterModbusTcpConnection *connection)
|
void SmaModbusSolarInverterDiscovery::cleanupConnection(SmaSolarInverterModbusTcpConnection *connection)
|
||||||
{
|
{
|
||||||
m_connections.removeAll(connection);
|
m_connections.removeAll(connection);
|
||||||
connection->disconnectDevice();
|
connection->disconnectDevice();
|
||||||
connection->deleteLater();
|
connection->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmaModbusDiscovery::finishDiscovery()
|
void SmaModbusSolarInverterDiscovery::finishDiscovery()
|
||||||
{
|
{
|
||||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||||
|
|
||||||
// Cleanup any leftovers...we don't care any more
|
// Cleanup any leftovers...we don't care any more
|
||||||
foreach (SmaInverterModbusTcpConnection *connection, m_connections)
|
foreach (SmaSolarInverterModbusTcpConnection *connection, m_connections)
|
||||||
cleanupConnection(connection);
|
cleanupConnection(connection);
|
||||||
|
|
||||||
qCInfo(dcSma()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() << "SMA inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
qCInfo(dcSma()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() << "SMA inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||||
@ -28,21 +28,21 @@
|
|||||||
*
|
*
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
#ifndef SMAMODBUSDISCOVERY_H
|
#ifndef SMAMODBUSSOLARINVERTERDISCOVERY_H
|
||||||
#define SMAMODBUSDISCOVERY_H
|
#define SMAMODBUSSOLARINVERTERDISCOVERY_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <network/networkdevicediscovery.h>
|
#include <network/networkdevicediscovery.h>
|
||||||
|
|
||||||
#include "smainvertermodbustcpconnection.h"
|
#include "smasolarinvertermodbustcpconnection.h"
|
||||||
|
|
||||||
class SmaModbusDiscovery : public QObject
|
class SmaModbusSolarInverterDiscovery : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SmaModbusDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 3, QObject *parent = nullptr);
|
explicit SmaModbusSolarInverterDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 3, QObject *parent = nullptr);
|
||||||
typedef struct SmaModbusDiscoveryResult {
|
typedef struct SmaModbusDiscoveryResult {
|
||||||
QString productName;
|
QString productName;
|
||||||
QString deviceName;
|
QString deviceName;
|
||||||
@ -68,15 +68,15 @@ private:
|
|||||||
QDateTime m_startDateTime;
|
QDateTime m_startDateTime;
|
||||||
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
|
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
|
||||||
|
|
||||||
QList<SmaInverterModbusTcpConnection *> m_connections;
|
QList<SmaSolarInverterModbusTcpConnection *> m_connections;
|
||||||
|
|
||||||
QList<SmaModbusDiscoveryResult> m_discoveryResults;
|
QList<SmaModbusDiscoveryResult> m_discoveryResults;
|
||||||
|
|
||||||
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
|
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
|
||||||
void cleanupConnection(SmaInverterModbusTcpConnection *connection);
|
void cleanupConnection(SmaSolarInverterModbusTcpConnection *connection);
|
||||||
|
|
||||||
void finishDiscovery();
|
void finishDiscovery();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SMAMODBUSDISCOVERY_H
|
#endif // SMAMODBUSSOLARINVERTERDISCOVERY_H
|
||||||
97
sma/sma-battery-inverter-registers.json
Normal file
97
sma/sma-battery-inverter-registers.json
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"className": "SmaBatteryInverter",
|
||||||
|
"protocol": "TCP",
|
||||||
|
"endianness": "BigEndian",
|
||||||
|
"errorLimitUntilNotReachable": 20,
|
||||||
|
"checkReachableRegister": "currentPower",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": "identification",
|
||||||
|
"readSchedule": "init",
|
||||||
|
"registers": [
|
||||||
|
{
|
||||||
|
"id": "deviceClass",
|
||||||
|
"address": 30051,
|
||||||
|
"size": 2,
|
||||||
|
"type": "uint32",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Device class",
|
||||||
|
"defaultValue": "0",
|
||||||
|
"access": "RO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "modelIdentifier",
|
||||||
|
"address": 30053,
|
||||||
|
"size": 2,
|
||||||
|
"type": "uint32",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Device type (model identifier)",
|
||||||
|
"defaultValue": "0",
|
||||||
|
"access": "RO"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "information",
|
||||||
|
"readSchedule": "init",
|
||||||
|
"registers": [
|
||||||
|
{
|
||||||
|
"id": "serialNumber",
|
||||||
|
"address": 30057,
|
||||||
|
"size": 2,
|
||||||
|
"type": "uint32",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Serial number",
|
||||||
|
"defaultValue": "0",
|
||||||
|
"access": "RO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "softwarePackage",
|
||||||
|
"address": 30059,
|
||||||
|
"size": 2,
|
||||||
|
"type": "uint32",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Firmware version",
|
||||||
|
"defaultValue": "0",
|
||||||
|
"access": "RO"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"registers": [
|
||||||
|
{
|
||||||
|
"id": "deviceName",
|
||||||
|
"address": 40631,
|
||||||
|
"size": 32,
|
||||||
|
"type": "string",
|
||||||
|
"readSchedule": "init",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Device name",
|
||||||
|
"access": "RO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "currentPower",
|
||||||
|
"address": 30775,
|
||||||
|
"size": 2,
|
||||||
|
"type": "int32",
|
||||||
|
"readSchedule": "update",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Current power",
|
||||||
|
"unit": "W",
|
||||||
|
"defaultValue": "0",
|
||||||
|
"access": "RO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "batterySOC",
|
||||||
|
"address": 30845,
|
||||||
|
"size": 2,
|
||||||
|
"type": "uint32",
|
||||||
|
"readSchedule": "update",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Battery State Of Charge",
|
||||||
|
"unit": "%",
|
||||||
|
"defaultValue": 0,
|
||||||
|
"access": "RO"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"className": "SmaInverter",
|
"className": "SmaSolarInverter",
|
||||||
"protocol": "TCP",
|
"protocol": "TCP",
|
||||||
"endianness": "BigEndian",
|
"endianness": "BigEndian",
|
||||||
"errorLimitUntilNotReachable": 20,
|
"errorLimitUntilNotReachable": 20,
|
||||||
@ -3,13 +3,14 @@ include(../plugins.pri)
|
|||||||
QT += network
|
QT += network
|
||||||
|
|
||||||
# Generate modbus connection
|
# Generate modbus connection
|
||||||
MODBUS_CONNECTIONS += sma-inverter-registers.json
|
MODBUS_CONNECTIONS += sma-solar-inverter-registers.json sma-battery-inverter-registers.json
|
||||||
MODBUS_TOOLS_CONFIG += VERBOSE
|
MODBUS_TOOLS_CONFIG += VERBOSE
|
||||||
include(../modbus.pri)
|
include(../modbus.pri)
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
integrationpluginsma.cpp \
|
integrationpluginsma.cpp \
|
||||||
modbus/smamodbusdiscovery.cpp \
|
modbus/smamodbusbatteryinverterdiscovery.cpp \
|
||||||
|
modbus/smamodbussolarinverterdiscovery.cpp \
|
||||||
speedwire/speedwirediscovery.cpp \
|
speedwire/speedwirediscovery.cpp \
|
||||||
speedwire/speedwireinterface.cpp \
|
speedwire/speedwireinterface.cpp \
|
||||||
speedwire/speedwireinverter.cpp \
|
speedwire/speedwireinverter.cpp \
|
||||||
@ -21,7 +22,8 @@ SOURCES += \
|
|||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
integrationpluginsma.h \
|
integrationpluginsma.h \
|
||||||
modbus/smamodbusdiscovery.h \
|
modbus/smamodbusbatteryinverterdiscovery.h \
|
||||||
|
modbus/smamodbussolarinverterdiscovery.h \
|
||||||
sma.h \
|
sma.h \
|
||||||
speedwire/speedwire.h \
|
speedwire/speedwire.h \
|
||||||
speedwire/speedwirediscovery.h \
|
speedwire/speedwirediscovery.h \
|
||||||
|
|||||||
Reference in New Issue
Block a user