Merge PR #140: SMA: Add support for batteries

This commit is contained in:
jenkins 2023-08-17 14:11:20 +02:00
commit b755935606
11 changed files with 708 additions and 93 deletions

View File

@ -7,9 +7,8 @@ nymea plug-in for SMA solar equipment.
* Sunny WebBox
* SMA speedwire Meters
* SMA speedwire Inverters
* SMA 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.
* SMA solar inverters using modbus
* SMA battery inverters using modbus
## Requirements

View File

@ -34,7 +34,8 @@
#include "sma.h"
#include "speedwire/speedwirediscovery.h"
#include "sunnywebbox/sunnywebboxdiscovery.h"
#include "modbus/smamodbusdiscovery.h"
#include "modbus/smamodbussolarinverterdiscovery.h"
#include "modbus/smamodbusbatteryinverterdiscovery.h"
#include <network/networkdevicediscovery.h>
@ -201,7 +202,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
speedwireDiscovery->startDiscovery();
} else if (info->thingClassId() == modbusInverterThingClassId) {
} else if (info->thingClassId() == modbusSolarInverterThingClassId) {
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
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."));
@ -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
SmaModbusDiscovery *discovery = new SmaModbusDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 3, info);
connect(discovery, &SmaModbusDiscovery::discoveryFinished, info, [=](){
foreach (const SmaModbusDiscovery::SmaModbusDiscoveryResult &result, discovery->discoveryResults()) {
SmaModbusSolarInverterDiscovery *discovery = new SmaModbusSolarInverterDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 3, info);
connect(discovery, &SmaModbusSolarInverterDiscovery::discoveryFinished, info, [=](){
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();
// 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) {
qCDebug(dcSma()) << "This SMA inverter already exists in the system:" << result.serialNumber;
descriptor.setThingId(existingThings.first()->id());
}
ParamList params;
params << Param(modbusInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
params << Param(modbusInverterThingPortParamTypeId, result.port);
params << Param(modbusInverterThingSlaveIdParamTypeId, result.modbusAddress);
params << Param(modbusInverterThingSerialNumberParamTypeId, result.serialNumber);
params << Param(modbusSolarInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
params << Param(modbusSolarInverterThingPortParamTypeId, result.port);
params << Param(modbusSolarInverterThingSlaveIdParamTypeId, result.modbusAddress);
params << Param(modbusSolarInverterThingSerialNumberParamTypeId, result.serialNumber);
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
@ -237,6 +238,39 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
// Start the discovery process
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;
info->finish(Thing::ThingErrorNoError);
} else if (thing->thingClassId() == modbusInverterThingClassId) {
} else if (thing->thingClassId() == modbusSolarInverterThingClassId) {
// Handle reconfigure
if (m_modbusInverters.contains(thing)) {
if (m_modbusSolarInverters.contains(thing)) {
qCDebug(dcSma()) << "Reconfiguring existing thing" << thing->name();
m_modbusInverters.take(thing)->deleteLater();
m_modbusSolarInverters.take(thing)->deleteLater();
if (m_monitors.contains(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()) {
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."));
@ -515,13 +549,65 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
// Wait for the monitor to be ready
if (monitor->reachable()) {
// Thing already reachable...let's continue with the setup
setupModbusInverterConnection(info);
setupModbusSolarInverterConnection(info);
} else {
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){
if (reachable) {
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();
} else if (thing->thingClassId() == modbusInverterThingClassId) {
SmaInverterModbusTcpConnection *connection = m_modbusInverters.value(thing);
} else if (thing->thingClassId() == modbusSolarInverterThingClassId) {
SmaSolarInverterModbusTcpConnection *connection = m_modbusSolarInverters.value(thing);
if (connection) {
thing->setStateValue("connected", connection->reachable());
if (!connection->reachable()) {
@ -606,8 +692,8 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
m_speedwireInverters.take(thing)->deleteLater();
}
if (thing->thingClassId() == modbusInverterThingClassId && m_modbusInverters.contains(thing)) {
m_modbusInverters.take(thing)->deleteLater();
if (thing->thingClassId() == modbusSolarInverterThingClassId && m_modbusSolarInverters.contains(thing)) {
m_modbusSolarInverters.take(thing)->deleteLater();
}
if (m_monitors.contains(thing)) {
@ -677,7 +763,10 @@ void IntegrationPluginSma::setupRefreshTimer()
inverter->refresh();
}
foreach (SmaInverterModbusTcpConnection *connection, m_modbusInverters) {
foreach (SmaSolarInverterModbusTcpConnection *connection, m_modbusSolarInverters) {
connection->update();
}
foreach (SmaBatteryInverterModbusTcpConnection *connection, m_modbusBatteryInverters) {
connection->update();
}
});
@ -685,17 +774,17 @@ void IntegrationPluginSma::setupRefreshTimer()
m_refreshTimer->start();
}
void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
void IntegrationPluginSma::setupModbusSolarInverterConnection(ThingSetupInfo *info)
{
Thing *thing = info->thing();
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
uint port = thing->paramValue(modbusInverterThingPortParamTypeId).toUInt();
quint16 slaveId = thing->paramValue(modbusInverterThingSlaveIdParamTypeId).toUInt();
uint port = thing->paramValue(modbusSolarInverterThingPortParamTypeId).toUInt();
quint16 slaveId = thing->paramValue(modbusSolarInverterThingSlaveIdParamTypeId).toUInt();
qCDebug(dcSma()) << "Setting up SMA inverter on" << address.toString() << port << "unit ID:" << slaveId;
SmaInverterModbusTcpConnection *connection = new SmaInverterModbusTcpConnection(address, port, slaveId, this);
connect(info, &ThingSetupInfo::aborted, connection, &SmaInverterModbusTcpConnection::deleteLater);
SmaSolarInverterModbusTcpConnection *connection = new SmaSolarInverterModbusTcpConnection(address, port, slaveId, this);
connect(info, &ThingSetupInfo::aborted, connection, &SmaSolarInverterModbusTcpConnection::deleteLater);
// Reconnect on monitor reachable changed
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
@ -711,25 +800,25 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
// Note: We disable autoreconnect explicitly and we will
// connect the device once the monitor says it is reachable again
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;
if (reachable) {
// Connected true will be set after successfull init
connection->initialize();
} else {
thing->setStateValue("connected", false);
markModbusInverterAsDisconnected(thing);
markModbusSolarInverterAsDisconnected(thing);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", false);
}
}
});
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, thing, [=](bool success){
connect(connection, &SmaSolarInverterModbusTcpConnection::initializationFinished, thing, [=](bool success){
if (!thing->setupComplete())
return;
@ -741,11 +830,11 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
if (!success) {
// Try once to reconnect the device
connection->reconnectDevice();
markModbusInverterAsDisconnected(thing);
markModbusSolarInverterAsDisconnected(thing);
}
});
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, info, [=](bool success){
connect(connection, &SmaSolarInverterModbusTcpConnection::initializationFinished, info, [=](bool success){
if (!success) {
qCWarning(dcSma()) << "Connection init finished with errors" << thing->name() << connection->modbusTcpMaster()->hostAddress().toString();
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
@ -755,7 +844,7 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
}
qCDebug(dcSma()) << "Connection init finished successfully" << connection;
m_modbusInverters.insert(thing, connection);
m_modbusSolarInverters.insert(thing, connection);
info->finish(Thing::ThingErrorNoError);
// Set connected true
@ -764,52 +853,142 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
childThing->setStateValue("connected", true);
}
connect(connection, &SmaInverterModbusTcpConnection::updateFinished, thing, [=](){
connect(connection, &SmaSolarInverterModbusTcpConnection::updateFinished, thing, [=](){
qCDebug(dcSma()) << "Updated" << connection;
// Grid voltage
if (isModbusValueValid(connection->gridVoltagePhaseA()))
thing->setStateValue(modbusInverterVoltagePhaseAStateTypeId, connection->gridVoltagePhaseA() / 100.0);
thing->setStateValue(modbusSolarInverterVoltagePhaseAStateTypeId, connection->gridVoltagePhaseA() / 100.0);
if (isModbusValueValid(connection->gridVoltagePhaseB()))
thing->setStateValue(modbusInverterVoltagePhaseBStateTypeId, connection->gridVoltagePhaseB() / 100.0);
thing->setStateValue(modbusSolarInverterVoltagePhaseBStateTypeId, connection->gridVoltagePhaseB() / 100.0);
if (isModbusValueValid(connection->gridVoltagePhaseC()))
thing->setStateValue(modbusInverterVoltagePhaseCStateTypeId, connection->gridVoltagePhaseC() / 100.0);
thing->setStateValue(modbusSolarInverterVoltagePhaseCStateTypeId, connection->gridVoltagePhaseC() / 100.0);
// Grid current
if (isModbusValueValid(connection->gridCurrentPhaseA()))
thing->setStateValue(modbusInverterCurrentPhaseAStateTypeId, connection->gridCurrentPhaseA() / 1000.0);
thing->setStateValue(modbusSolarInverterCurrentPhaseAStateTypeId, connection->gridCurrentPhaseA() / 1000.0);
if (isModbusValueValid(connection->gridCurrentPhaseB()))
thing->setStateValue(modbusInverterCurrentPhaseBStateTypeId, connection->gridCurrentPhaseB() / 1000.0);
thing->setStateValue(modbusSolarInverterCurrentPhaseBStateTypeId, connection->gridCurrentPhaseB() / 1000.0);
if (isModbusValueValid(connection->gridCurrentPhaseC()))
thing->setStateValue(modbusInverterCurrentPhaseCStateTypeId, connection->gridCurrentPhaseC() / 1000.0);
thing->setStateValue(modbusSolarInverterCurrentPhaseCStateTypeId, connection->gridCurrentPhaseC() / 1000.0);
// Phase power
if (isModbusValueValid(connection->currentPowerPhaseA()))
thing->setStateValue(modbusInverterCurrentPowerPhaseAStateTypeId, connection->currentPowerPhaseA());
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseAStateTypeId, connection->currentPowerPhaseA());
if (isModbusValueValid(connection->currentPowerPhaseB()))
thing->setStateValue(modbusInverterCurrentPowerPhaseBStateTypeId, connection->currentPowerPhaseB());
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseBStateTypeId, connection->currentPowerPhaseB());
if (isModbusValueValid(connection->currentPowerPhaseC()))
thing->setStateValue(modbusInverterCurrentPowerPhaseCStateTypeId, connection->currentPowerPhaseC());
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseCStateTypeId, connection->currentPowerPhaseC());
// Others
if (isModbusValueValid(connection->totalYield()))
thing->setStateValue(modbusInverterTotalEnergyProducedStateTypeId, connection->totalYield() / 1000.0); // kWh
thing->setStateValue(modbusSolarInverterTotalEnergyProducedStateTypeId, connection->totalYield() / 1000.0); // kWh
if (isModbusValueValid(connection->dailyYield()))
thing->setStateValue(modbusInverterEnergyProducedTodayStateTypeId, connection->dailyYield() / 1000.0); // kWh
thing->setStateValue(modbusSolarInverterEnergyProducedTodayStateTypeId, connection->dailyYield() / 1000.0); // kWh
// Power
if (isModbusValueValid(connection->currentPower()))
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, -connection->currentPower());
thing->setStateValue(modbusSolarInverterCurrentPowerStateTypeId, -connection->currentPower());
// 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
@ -866,18 +1045,23 @@ void IntegrationPluginSma::markSpeedwireBatteryAsDisconnected(Thing *thing)
thing->setStateValue(speedwireBatteryChargingStateStateTypeId, "idle");
}
void IntegrationPluginSma::markModbusInverterAsDisconnected(Thing *thing)
void IntegrationPluginSma::markModbusSolarInverterAsDisconnected(Thing *thing)
{
thing->setStateValue(modbusInverterVoltagePhaseAStateTypeId, 0);
thing->setStateValue(modbusInverterVoltagePhaseBStateTypeId, 0);
thing->setStateValue(modbusInverterVoltagePhaseCStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPhaseAStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPhaseBStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPhaseCStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPowerPhaseAStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPowerPhaseBStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPowerPhaseCStateTypeId, 0);
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, 0);
thing->setStateValue(modbusSolarInverterVoltagePhaseAStateTypeId, 0);
thing->setStateValue(modbusSolarInverterVoltagePhaseBStateTypeId, 0);
thing->setStateValue(modbusSolarInverterVoltagePhaseCStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPhaseAStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPhaseBStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPhaseCStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseAStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseBStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPowerPhaseCStateTypeId, 0);
thing->setStateValue(modbusSolarInverterCurrentPowerStateTypeId, 0);
}
void IntegrationPluginSma::markModbusBatteryInverterAsDisconnected(Thing *thing)
{
thing->setStateValue(modbusBatteryInverterCurrentPowerStateTypeId, 0);
}
quint64 IntegrationPluginSma::getLocalSerialNumber()

View File

@ -42,7 +42,8 @@
#include "speedwire/speedwireinverter.h"
#include "speedwire/speedwireinterface.h"
#include "smainvertermodbustcpconnection.h"
#include "smasolarinvertermodbustcpconnection.h"
#include "smabatteryinvertermodbustcpconnection.h"
class IntegrationPluginSma: public IntegrationPlugin {
Q_OBJECT
@ -68,7 +69,8 @@ private slots:
void setupRefreshTimer();
void setupModbusInverterConnection(ThingSetupInfo *info);
void setupModbusSolarInverterConnection(ThingSetupInfo *info);
void setupModbusBatteryInverterConnection(ThingSetupInfo *info);
private:
PluginTimer *m_refreshTimer = nullptr;
@ -78,7 +80,8 @@ private:
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
QHash<Thing *, SpeedwireMeter *> m_speedwireMeters;
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
QHash<Thing *, SmaSolarInverterModbusTcpConnection *> m_modbusSolarInverters;
QHash<Thing *, SmaBatteryInverterModbusTcpConnection *> m_modbusBatteryInverters;
quint32 m_localSerialNumber = 0;
@ -89,7 +92,8 @@ private:
void markSpeedwireMeterAsDisconnected(Thing *thing);
void markSpeedwireInverterAsDisconnected(Thing *thing);
void markSpeedwireBatteryAsDisconnected(Thing *thing);
void markModbusInverterAsDisconnected(Thing *thing);
void markModbusSolarInverterAsDisconnected(Thing *thing);
void markModbusBatteryInverterAsDisconnected(Thing *thing);
quint64 getLocalSerialNumber();

View File

@ -534,8 +534,8 @@
},
{
"id": "12e0429e-e8ce-48bd-a11c-faaf0bd71856",
"name": "modbusInverter",
"displayName": "SMA Inverter (Modbus)",
"name": "modbusSolarInverter",
"displayName": "SMA Solar Inverter (Modbus)",
"createMethods": ["discovery", "user"],
"interfaces": [ "solarinverter" ],
"paramTypes": [
@ -693,6 +693,104 @@
"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": ""
}
]
}
]
}

View 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();
}

View 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

View File

@ -28,13 +28,13 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "smamodbusdiscovery.h"
#include "smamodbussolarinverterdiscovery.h"
#include "extern-plugininfo.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},
m_networkDeviceDiscovery{networkDeviceDiscovery},
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...";
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
// 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
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;
}
void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
void SmaModbusSolarInverterDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
// Create a kostal connection and try to initialize it.
// 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))
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_verifiedNetworkDeviceInfos.append(networkDeviceInfo);
connect(connection, &SmaInverterModbusTcpConnection::reachableChanged, this, [=](bool reachable){
connect(connection, &SmaSolarInverterModbusTcpConnection::reachableChanged, this, [=](bool reachable){
if (!reachable) {
// Disconnected ... done with this connection
cleanupConnection(connection);
@ -98,7 +98,7 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
}
// 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) {
qCDebug(dcSma()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
@ -147,7 +147,7 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
});
// 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...";;
cleanupConnection(connection);
});
@ -156,19 +156,19 @@ void SmaModbusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi
connection->connectDevice();
}
void SmaModbusDiscovery::cleanupConnection(SmaInverterModbusTcpConnection *connection)
void SmaModbusSolarInverterDiscovery::cleanupConnection(SmaSolarInverterModbusTcpConnection *connection)
{
m_connections.removeAll(connection);
connection->disconnectDevice();
connection->deleteLater();
}
void SmaModbusDiscovery::finishDiscovery()
void SmaModbusSolarInverterDiscovery::finishDiscovery()
{
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
// Cleanup any leftovers...we don't care any more
foreach (SmaInverterModbusTcpConnection *connection, m_connections)
foreach (SmaSolarInverterModbusTcpConnection *connection, m_connections)
cleanupConnection(connection);
qCInfo(dcSma()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() << "SMA inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");

View File

@ -28,21 +28,21 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SMAMODBUSDISCOVERY_H
#define SMAMODBUSDISCOVERY_H
#ifndef SMAMODBUSSOLARINVERTERDISCOVERY_H
#define SMAMODBUSSOLARINVERTERDISCOVERY_H
#include <QObject>
#include <QTimer>
#include <network/networkdevicediscovery.h>
#include "smainvertermodbustcpconnection.h"
#include "smasolarinvertermodbustcpconnection.h"
class SmaModbusDiscovery : public QObject
class SmaModbusSolarInverterDiscovery : public QObject
{
Q_OBJECT
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 {
QString productName;
QString deviceName;
@ -68,15 +68,15 @@ private:
QDateTime m_startDateTime;
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
QList<SmaInverterModbusTcpConnection *> m_connections;
QList<SmaSolarInverterModbusTcpConnection *> m_connections;
QList<SmaModbusDiscoveryResult> m_discoveryResults;
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupConnection(SmaInverterModbusTcpConnection *connection);
void cleanupConnection(SmaSolarInverterModbusTcpConnection *connection);
void finishDiscovery();
};
#endif // SMAMODBUSDISCOVERY_H
#endif // SMAMODBUSSOLARINVERTERDISCOVERY_H

View 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"
}
]
}

View File

@ -1,5 +1,5 @@
{
"className": "SmaInverter",
"className": "SmaSolarInverter",
"protocol": "TCP",
"endianness": "BigEndian",
"errorLimitUntilNotReachable": 20,

View File

@ -3,13 +3,14 @@ include(../plugins.pri)
QT += network
# 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
include(../modbus.pri)
SOURCES += \
integrationpluginsma.cpp \
modbus/smamodbusdiscovery.cpp \
modbus/smamodbusbatteryinverterdiscovery.cpp \
modbus/smamodbussolarinverterdiscovery.cpp \
speedwire/speedwirediscovery.cpp \
speedwire/speedwireinterface.cpp \
speedwire/speedwireinverter.cpp \
@ -21,7 +22,8 @@ SOURCES += \
HEADERS += \
integrationpluginsma.h \
modbus/smamodbusdiscovery.h \
modbus/smamodbusbatteryinverterdiscovery.h \
modbus/smamodbussolarinverterdiscovery.h \
sma.h \
speedwire/speedwire.h \
speedwire/speedwirediscovery.h \