Restructure sma plugin and add modbus inverter support
This commit is contained in:
parent
e4210add11
commit
5a1b6b773e
@ -18,6 +18,7 @@ PLUGIN_DIRS = \
|
||||
mypv \
|
||||
phoenixconnect \
|
||||
schrack \
|
||||
sma \
|
||||
stiebeleltron \
|
||||
sunspec \
|
||||
unipi \
|
||||
|
||||
@ -7,13 +7,14 @@ 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.
|
||||
|
||||
## Requirements
|
||||
|
||||
* The package "nymea-plugin-sma" must be installed.
|
||||
* The speedwire port `9522` must not be clocked for UDP packages in the network.
|
||||
* The speedwire port `9522` must not be blocked for UDP packages in the network.
|
||||
|
||||
## More
|
||||
https://www.sma.de/en/
|
||||
|
||||
@ -30,8 +30,11 @@
|
||||
|
||||
#include "integrationpluginsma.h"
|
||||
#include "plugininfo.h"
|
||||
#include "speedwirediscovery.h"
|
||||
#include "sunnywebboxdiscovery.h"
|
||||
|
||||
#include "sma.h"
|
||||
#include "speedwire/speedwirediscovery.h"
|
||||
#include "sunnywebbox/sunnywebboxdiscovery.h"
|
||||
#include "modbus/smamodbusdiscovery.h"
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
@ -150,7 +153,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
||||
if (result.serialNumber == 0)
|
||||
continue;
|
||||
|
||||
ThingDescriptor descriptor(speedwireInverterThingClassId, "SMA Inverter", "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString());
|
||||
ThingDescriptor descriptor(speedwireInverterThingClassId, Sma::getModelName(result.modelId), "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString());
|
||||
// We found an energy meter, let's check if we already added this one
|
||||
foreach (Thing *existingThing, myThings()) {
|
||||
if (existingThing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) {
|
||||
@ -174,6 +177,42 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
||||
});
|
||||
|
||||
speedwireDiscovery->startDiscovery();
|
||||
} else if (info->thingClassId() == modbusInverterThingClassId) {
|
||||
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."));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
|
||||
ThingDescriptor descriptor(modbusInverterThingClassId, "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);
|
||||
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);
|
||||
descriptor.setParams(params);
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
// Start the discovery process
|
||||
discovery->startDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,27 +225,29 @@ void IntegrationPluginSma::confirmPairing(ThingPairingInfo *info, const QString
|
||||
{
|
||||
Q_UNUSED(username)
|
||||
|
||||
// On speedwire the password length has a maximum of 12 characters
|
||||
if (secret.count() > 12) {
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The password can not be longer than 12 characters."));
|
||||
return;
|
||||
if (info->thingClassId() == speedwireInverterThingClassId) {
|
||||
// On speedwire the password length has a maximum of 12 characters
|
||||
if (secret.count() > 12) {
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The password can not be longer than 12 characters."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Init with the default password
|
||||
QString password = "0000";
|
||||
if (!secret.isEmpty()) {
|
||||
qCDebug(dcSma()) << "Pairing: Using password" << secret;
|
||||
password = secret;
|
||||
} else {
|
||||
qCDebug(dcSma()) << "Pairing: The given password is empty. Using default password" << password;
|
||||
}
|
||||
|
||||
// Just store details, we'll test the login in setupDevice
|
||||
pluginStorage()->beginGroup(info->thingId().toString());
|
||||
pluginStorage()->setValue("password", password);
|
||||
pluginStorage()->endGroup();
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
// Init with the default password
|
||||
QString password = "0000";
|
||||
if (!secret.isEmpty()) {
|
||||
qCDebug(dcSma()) << "Pairing: Using password" << secret;
|
||||
password = secret;
|
||||
} else {
|
||||
qCDebug(dcSma()) << "Pairing: The given password is empty. Using default password" << password;
|
||||
}
|
||||
|
||||
// Just store details, we'll test the login in setupDevice
|
||||
pluginStorage()->beginGroup(info->thingId().toString());
|
||||
pluginStorage()->setValue("password", password);
|
||||
pluginStorage()->endGroup();
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
||||
@ -367,6 +408,59 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
||||
qCDebug(dcSma()) << "Inverter: Start connecting using password" << password;
|
||||
inverter->startConnecting(password);
|
||||
|
||||
} else if (thing->thingClassId() == modbusInverterThingClassId) {
|
||||
|
||||
// Handle reconfigure
|
||||
if (m_modbusInverters.contains(thing)) {
|
||||
qCDebug(dcSma()) << "Reconfiguring existing thing" << thing->name();
|
||||
m_modbusInverters.take(thing)->deleteLater();
|
||||
|
||||
if (m_monitors.contains(thing)) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
}
|
||||
|
||||
MacAddress macAddress = MacAddress(thing->paramValue(modbusInverterThingMacAddressParamTypeId).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 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
|
||||
setupModbusInverterConnection(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||
}
|
||||
@ -380,15 +474,26 @@ void IntegrationPluginSma::postSetupThing(Thing *thing)
|
||||
if (!sunnyWebBox)
|
||||
return;
|
||||
|
||||
setupRefreshTimer();
|
||||
sunnyWebBox->getPlantOverview();
|
||||
thing->setStateValue(sunnyWebBoxConnectedStateTypeId, true);
|
||||
thing->setStateValue("connected", true);
|
||||
setupRefreshTimer();
|
||||
|
||||
} else if (thing->thingClassId() == speedwireInverterThingClassId) {
|
||||
SpeedwireInverter *inverter = m_speedwireInverters.value(thing);
|
||||
if (inverter) {
|
||||
thing->setStateValue(speedwireInverterConnectedStateTypeId, inverter->reachable());
|
||||
thing->setStateValue("connected", inverter->reachable());
|
||||
} else {
|
||||
thing->setStateValue(speedwireInverterConnectedStateTypeId, false);
|
||||
thing->setStateValue("connected", false);
|
||||
}
|
||||
|
||||
setupRefreshTimer();
|
||||
|
||||
} else if (thing->thingClassId() == modbusInverterThingClassId) {
|
||||
SmaInverterModbusTcpConnection *connection = m_modbusInverters.value(thing);
|
||||
if (connection) {
|
||||
thing->setStateValue("connected", connection->reachable());
|
||||
} else {
|
||||
thing->setStateValue("connected", false);
|
||||
}
|
||||
|
||||
setupRefreshTimer();
|
||||
@ -409,6 +514,14 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
|
||||
m_speedwireInverters.take(thing)->deleteLater();
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == modbusInverterThingClassId && m_modbusInverters.contains(thing)) {
|
||||
m_modbusInverters.take(thing)->deleteLater();
|
||||
}
|
||||
|
||||
if (m_monitors.contains(thing)) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
|
||||
if (myThings().isEmpty()) {
|
||||
qCDebug(dcSma()) << "Stopping timer";
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||
@ -450,7 +563,7 @@ void IntegrationPluginSma::setupRefreshTimer()
|
||||
if (m_refreshTimer)
|
||||
return;
|
||||
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, [=](){
|
||||
foreach (Thing *thing, myThings().filterByThingClassId(sunnyWebBoxThingClassId)) {
|
||||
SunnyWebBox *sunnyWebBox = m_sunnyWebBoxes.value(thing);
|
||||
@ -461,7 +574,158 @@ void IntegrationPluginSma::setupRefreshTimer()
|
||||
// Note: refresh will not be triggered if there is already a refresh process running
|
||||
inverter->refresh();
|
||||
}
|
||||
|
||||
foreach (SmaInverterModbusTcpConnection *connection, m_modbusInverters) {
|
||||
connection->update();
|
||||
}
|
||||
});
|
||||
|
||||
m_refreshTimer->start();
|
||||
}
|
||||
|
||||
void IntegrationPluginSma::setupModbusInverterConnection(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();
|
||||
|
||||
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);
|
||||
|
||||
// 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->setHostAddress(monitor->networkDeviceInfo().address());
|
||||
connection->connectDevice();
|
||||
} else if (!reachable) {
|
||||
// Note: We disable autoreconnect explicitly and we will
|
||||
// connect the device once the monitor says it is reachable again
|
||||
connection->disconnectDevice();
|
||||
}
|
||||
});
|
||||
|
||||
connect(connection, &SmaInverterModbusTcpConnection::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);
|
||||
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||
childThing->setStateValue("connected", false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(connection, &SmaInverterModbusTcpConnection::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();
|
||||
}
|
||||
});
|
||||
|
||||
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, info, [=](bool success){
|
||||
if (!success) {
|
||||
qCWarning(dcSma()) << "Connection init finished with errors" << thing->name() << connection->hostAddress().toString();
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
|
||||
connection->deleteLater();
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Could not initialize the communication with the inverter."));
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSma()) << "Connection init finished successfully" << connection;
|
||||
m_modbusInverters.insert(thing, connection);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
// Set connected true
|
||||
thing->setStateValue("connected", true);
|
||||
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
|
||||
childThing->setStateValue("connected", true);
|
||||
}
|
||||
|
||||
connect(connection, &SmaInverterModbusTcpConnection::updateFinished, thing, [=](){
|
||||
qCDebug(dcSma()) << "Updated" << connection;
|
||||
|
||||
// Grid voltage
|
||||
if (isModbusValueValid(connection->gridVoltagePhaseA()))
|
||||
thing->setStateValue(modbusInverterVoltagePhaseAStateTypeId, connection->gridVoltagePhaseA() / 100.0);
|
||||
|
||||
if (isModbusValueValid(connection->gridVoltagePhaseB()))
|
||||
thing->setStateValue(modbusInverterVoltagePhaseBStateTypeId, connection->gridVoltagePhaseB() / 100.0);
|
||||
|
||||
if (isModbusValueValid(connection->gridVoltagePhaseC()))
|
||||
thing->setStateValue(modbusInverterVoltagePhaseCStateTypeId, connection->gridVoltagePhaseC() / 100.0);
|
||||
|
||||
// Grid current
|
||||
if (isModbusValueValid(connection->gridCurrentPhaseA()))
|
||||
thing->setStateValue(modbusInverterCurrentPhaseAStateTypeId, connection->gridCurrentPhaseA() / 1000.0);
|
||||
|
||||
if (isModbusValueValid(connection->gridCurrentPhaseB()))
|
||||
thing->setStateValue(modbusInverterCurrentPhaseBStateTypeId, connection->gridCurrentPhaseB() / 1000.0);
|
||||
|
||||
if (isModbusValueValid(connection->gridCurrentPhaseC()))
|
||||
thing->setStateValue(modbusInverterCurrentPhaseCStateTypeId, connection->gridCurrentPhaseC() / 1000.0);
|
||||
|
||||
// Phase power
|
||||
if (isModbusValueValid(connection->currentPowerPhaseA()))
|
||||
thing->setStateValue(modbusInverterCurrentPowerPhaseAStateTypeId, connection->currentPowerPhaseA());
|
||||
|
||||
if (isModbusValueValid(connection->currentPowerPhaseB()))
|
||||
thing->setStateValue(modbusInverterCurrentPowerPhaseBStateTypeId, connection->currentPowerPhaseB());
|
||||
|
||||
if (isModbusValueValid(connection->currentPowerPhaseC()))
|
||||
thing->setStateValue(modbusInverterCurrentPowerPhaseCStateTypeId, connection->currentPowerPhaseC());
|
||||
|
||||
// Others
|
||||
if (isModbusValueValid(connection->totalYield()))
|
||||
thing->setStateValue(modbusInverterTotalEnergyProducedStateTypeId, connection->totalYield() / 1000.0); // kWh
|
||||
|
||||
if (isModbusValueValid(connection->dailyYield()))
|
||||
thing->setStateValue(modbusInverterEnergyProducedTodayStateTypeId, connection->dailyYield() / 1000.0); // kWh
|
||||
|
||||
// Power
|
||||
if (isModbusValueValid(connection->currentPower()))
|
||||
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, -connection->currentPower());
|
||||
|
||||
// Version
|
||||
thing->setStateValue(modbusInverterFirmwareVersionStateTypeId, Sma::buildSoftwareVersionString(connection->softwarePackage()));
|
||||
});
|
||||
|
||||
// Update registers
|
||||
connection->update();
|
||||
});
|
||||
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
bool IntegrationPluginSma::isModbusValueValid(quint32 value)
|
||||
{
|
||||
return value != 0xffffffff;
|
||||
}
|
||||
|
||||
bool IntegrationPluginSma::isModbusValueValid(qint32 value)
|
||||
{
|
||||
return value != static_cast<qint32>(0x80000000);
|
||||
}
|
||||
|
||||
bool IntegrationPluginSma::isModbusValueValid(quint64 value)
|
||||
{
|
||||
return value != 0xffffffffffffffff;
|
||||
}
|
||||
|
||||
|
||||
@ -33,10 +33,15 @@
|
||||
|
||||
#include <integrations/integrationplugin.h>
|
||||
#include <plugintimer.h>
|
||||
#include <network/networkdevicemonitor.h>
|
||||
|
||||
#include "sunnywebbox.h"
|
||||
#include "speedwiremeter.h"
|
||||
#include "speedwireinverter.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "sunnywebbox/sunnywebbox.h"
|
||||
#include "speedwire/speedwiremeter.h"
|
||||
#include "speedwire/speedwireinverter.h"
|
||||
|
||||
#include "smainvertermodbustcpconnection.h"
|
||||
|
||||
class IntegrationPluginSma: public IntegrationPlugin {
|
||||
Q_OBJECT
|
||||
@ -62,12 +67,22 @@ private slots:
|
||||
|
||||
void setupRefreshTimer();
|
||||
|
||||
void setupModbusInverterConnection(ThingSetupInfo *info);
|
||||
|
||||
private:
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
|
||||
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
|
||||
QHash<Thing *, SpeedwireMeter *> m_speedwireMeters;
|
||||
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
||||
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
|
||||
|
||||
// Sma modbus data validation
|
||||
bool isModbusValueValid(quint32 value);
|
||||
bool isModbusValueValid(qint32 value);
|
||||
bool isModbusValueValid(quint64 value);
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINSMA_H
|
||||
|
||||
@ -38,15 +38,14 @@
|
||||
"id": "c05e6a1a-252c-4f2b-8b31-09cf113d01c1",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "ff4ff872-2f0f-4ca4-9fe2-220eeaf16cc2",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"displayNameEvent": "Current power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -56,7 +55,6 @@
|
||||
"id": "16f34c5c-8dbb-4dcc-9faa-4b782d57226c",
|
||||
"name": "dayEnergyProduced",
|
||||
"displayName": "Day energy produced",
|
||||
"displayNameEvent": "Day energy produced changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
@ -65,7 +63,6 @@
|
||||
"id": "0bb4e227-7e38-49ca-9b32-ce4621c9305b",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"displayNameEvent": "Total energy produced changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
@ -74,7 +71,6 @@
|
||||
"id": "1974550b-6059-4b0e-83f4-70177e20dac3",
|
||||
"name": "mode",
|
||||
"displayName": "Mode",
|
||||
"displayNameEvent": "Mode changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "MPP"
|
||||
},
|
||||
@ -82,7 +78,6 @@
|
||||
"id": "4e64f9ca-7e5a-4897-8035-6f2ae88fde89",
|
||||
"name": "error",
|
||||
"displayName": "Error",
|
||||
"displayNameEvent": "Error changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "None"
|
||||
}
|
||||
@ -136,7 +131,6 @@
|
||||
"id": "35733d27-4fe0-439a-be71-7c1597481659",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
@ -144,7 +138,6 @@
|
||||
"id": "44ee2491-8376-41cd-a21d-185c736152ec",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"displayNameEvent": "Voltage phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
@ -154,7 +147,6 @@
|
||||
"id": "56ae3555-f874-4c2d-8833-17573dce477a",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"displayNameEvent": "Voltage phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
@ -164,7 +156,6 @@
|
||||
"id": "51cbb29b-29f0-480a-9d7d-b8f4e6a205ae",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"displayNameEvent": "Voltage phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
@ -174,7 +165,6 @@
|
||||
"id": "45bbdbef-1832-4870-bff5-299e580fb4da",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"displayNameEvent": "Current phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
@ -184,7 +174,6 @@
|
||||
"id": "b3a4fdd2-b6b8-4c58-9da3-2084ad414022",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"displayNameEvent": "Current phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
@ -194,7 +183,6 @@
|
||||
"id": "b3655188-3854-4336-ae3c-61d3bda6fc4d",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"displayNameEvent": "Current phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
@ -204,7 +192,6 @@
|
||||
"id": "d4ac7f37-e30a-44e4-93cb-ad16df18b8f1",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"displayNameEvent": "Current power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -214,7 +201,6 @@
|
||||
"id": "c5d09c63-7461-4fb8-a6fe-bc7aa919be30",
|
||||
"name": "currentPowerPhaseA",
|
||||
"displayName": "Current power phase A",
|
||||
"displayNameEvent": "Current power phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -224,7 +210,6 @@
|
||||
"id": "c52d4422-b521-4804-a7a7-c4398e91e760",
|
||||
"name": "currentPowerPhaseB",
|
||||
"displayName": "Current power phase B",
|
||||
"displayNameEvent": "Current power phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -234,7 +219,6 @@
|
||||
"id": "555e892c-3ca7-4100-9832-6ac13b87eb04",
|
||||
"name": "currentPowerPhaseC",
|
||||
"displayName": "Current power phase C",
|
||||
"displayNameEvent": "Current power phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -244,7 +228,6 @@
|
||||
"id": "4fb0a4c1-18ed-4d02-b6d0-c07e9b96a56d",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Total energy consumed",
|
||||
"displayNameEvent": "Total energy consumed changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -253,7 +236,6 @@
|
||||
"id": "76ca68d8-6781-4d2a-8663-440aec40b4de",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"displayNameEvent": "Total energy produced changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -262,7 +244,6 @@
|
||||
"id": "b4ff2c71-f81d-4904-bbac-0c0c6e8a5a33",
|
||||
"name": "energyConsumedPhaseA",
|
||||
"displayName": "Energy consumed phase A",
|
||||
"displayNameEvent": "Energy consumed phase A changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -271,7 +252,6 @@
|
||||
"id": "c4e5f569-ac5d-4761-a898-888880bfd59f",
|
||||
"name": "energyConsumedPhaseB",
|
||||
"displayName": "Energy consumed phase B",
|
||||
"displayNameEvent": "Energy consumed phase B changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -280,7 +260,6 @@
|
||||
"id": "aabc02d7-8dc3-4637-8bf2-dc2e0e737ad3",
|
||||
"name": "energyConsumedPhaseC",
|
||||
"displayName": "Energy consumed phase C",
|
||||
"displayNameEvent": "Energy consumed phase C changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -289,7 +268,6 @@
|
||||
"id": "754c3b67-768a-47f7-99d8-f66c198f0835",
|
||||
"name": "energyProducedPhaseA",
|
||||
"displayName": "Energy produced phase A",
|
||||
"displayNameEvent": "Energy produced phase A changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -298,7 +276,6 @@
|
||||
"id": "7eb08c45-24cf-40ce-be28-f3564f087672",
|
||||
"name": "energyProducedPhaseB",
|
||||
"displayName": "Energy produced phase B",
|
||||
"displayNameEvent": "Energy produced phase B changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -307,7 +284,6 @@
|
||||
"id": "1eb2bf01-5ec6-42e5-b348-ac1e95199d14",
|
||||
"name": "energyProducedPhaseC",
|
||||
"displayName": "Energy produced phase C",
|
||||
"displayNameEvent": "Energy produced phase C changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -316,7 +292,6 @@
|
||||
"id": "a685393c-8b7e-42c5-bb41-f9907c074626",
|
||||
"name": "firmwareVersion",
|
||||
"displayName": "Firmware version",
|
||||
"displayNameEvent": "Firmware version changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
@ -371,7 +346,6 @@
|
||||
"id": "aaff72c3-c70a-4a2f-bed1-89f38cebe442",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
@ -379,7 +353,6 @@
|
||||
"id": "6ef4eb16-a3d6-4bc9-972d-5e7cb81173a5",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"displayNameEvent": "Voltage phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
@ -389,7 +362,6 @@
|
||||
"id": "d9a5768b-1bf5-4933-810d-84dd7a688f71",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"displayNameEvent": "Voltage phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
@ -399,7 +371,6 @@
|
||||
"id": "fc168dc6-eecf-40b4-b214-3e28da0dbb12",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"displayNameEvent": "Voltage phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
@ -409,7 +380,6 @@
|
||||
"id": "2a6c59ca-853a-47d6-96fb-0c85edf32f52",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"displayNameEvent": "Current phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
@ -419,7 +389,6 @@
|
||||
"id": "4db96fec-737c-4c4b-bf07-5ef2fd62508a",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"displayNameEvent": "Current phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
@ -429,7 +398,6 @@
|
||||
"id": "0f23fb0e-a440-4ac2-9aff-896bc65feb2c",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"displayNameEvent": "Current phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
@ -439,7 +407,6 @@
|
||||
"id": "d7ceb482-5df8-4c0c-82bd-62ce7ba22c43",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"displayNameEvent": "Current power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -449,7 +416,6 @@
|
||||
"id": "b366f680-6134-488b-8362-b1b824a8daca",
|
||||
"name": "currentPowerMpp1",
|
||||
"displayName": "DC power MPP1",
|
||||
"displayNameEvent": "DC power MPP1 changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -459,7 +425,6 @@
|
||||
"id": "87d9b654-5558-47a3-9db9-ffd7c23b4774",
|
||||
"name": "currentPowerMpp2",
|
||||
"displayName": "DC power MPP2",
|
||||
"displayNameEvent": "DC power MPP2 changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
@ -469,7 +434,6 @@
|
||||
"id": "51cadd66-2cf1-485a-a2a9-191d11abfbd1",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"displayNameEvent": "Total energy produced changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -478,7 +442,6 @@
|
||||
"id": "e8bc8f81-e5c5-4900-b429-93fcaa262fcb",
|
||||
"name": "energyProducedToday",
|
||||
"displayName": "Energy produced today",
|
||||
"displayNameEvent": "Energy produced today changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
@ -487,7 +450,6 @@
|
||||
"id": "fdccf5de-7413-4480-9ca0-1151665dede8",
|
||||
"name": "frequency",
|
||||
"displayName": "Frequency",
|
||||
"displayNameEvent": "Frequency changed",
|
||||
"type": "double",
|
||||
"unit": "Hertz",
|
||||
"defaultValue": 0.00,
|
||||
@ -497,7 +459,168 @@
|
||||
"id": "6d76cc7b-9e00-4561-be7b-4e2a6b8f7b66",
|
||||
"name": "firmwareVersion",
|
||||
"displayName": "Firmware version",
|
||||
"displayNameEvent": "Firmware version changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "12e0429e-e8ce-48bd-a11c-faaf0bd71856",
|
||||
"name": "modbusInverter",
|
||||
"displayName": "SMA Inverter (Modbus)",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": [ "solarinverter" ],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "3cea46a0-9535-4612-9971-19167109e63c",
|
||||
"name":"macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "18ded0c1-308e-4a13-a12c-cf9a8ed5a26c",
|
||||
"name":"port",
|
||||
"displayName": "Port",
|
||||
"type": "int",
|
||||
"defaultValue": 502
|
||||
},
|
||||
{
|
||||
"id": "6322db2a-0554-4f83-9509-39870ad89027",
|
||||
"name":"slaveId",
|
||||
"displayName": "Slave ID",
|
||||
"type": "int",
|
||||
"defaultValue": 3
|
||||
},
|
||||
{
|
||||
"id": "563f2b12-b784-4a2c-856f-57a2b5ce2e9d",
|
||||
"name":"serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "3c60e2a7-31f3-4b0b-a3f9-ede042e82f22",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "5a717aff-6bdb-4679-94d6-ec1bce7fa2af",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "34eb5b54-7683-42ff-8320-9b2527d6381c",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "2bd0a069-9d16-4d58-9f78-df682d92005d",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "48d4a7b7-b09a-4255-83dd-9eab8ea3a51c",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "479b27c4-01fc-45ef-a462-b8d8499b3422",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f82bbba1-c68a-4c43-a3e5-10b00ed924d7",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "225beb67-95ca-495c-aca8-cd3fd4efedd5",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "9283d5a9-b185-4678-beb1-1c6ce6f76930",
|
||||
"name": "currentPowerPhaseA",
|
||||
"displayName": "Current power phase A",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "8a87319c-f6ab-4eb1-bb17-a65f80289a56",
|
||||
"name": "currentPowerPhaseB",
|
||||
"displayName": "Current power phase B",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1f930456-5947-476c-b74b-480f1e81a799",
|
||||
"name": "currentPowerPhaseC",
|
||||
"displayName": "Current power phase C",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "5e0ed108-7e93-4724-a831-319109d9daf8",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "b8fb66fa-46b5-4ed7-82a7-29fe5257caa9",
|
||||
"name": "energyProducedToday",
|
||||
"displayName": "Energy produced today",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "3f290cbc-0578-479a-ab98-d89b5549184d",
|
||||
"name": "firmwareVersion",
|
||||
"displayName": "Firmware version",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
|
||||
176
sma/modbus/smamodbusdiscovery.cpp
Normal file
176
sma/modbus/smamodbusdiscovery.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, 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 "smamodbusdiscovery.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "sma.h"
|
||||
|
||||
|
||||
SmaModbusDiscovery::SmaModbusDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress,QObject *parent)
|
||||
: QObject{parent},
|
||||
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
||||
m_port{port},
|
||||
m_modbusAddress{modbusAddress}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SmaModbusDiscovery::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);
|
||||
|
||||
// Check what might be left on finished
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||
qCDebug(dcSma()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
|
||||
|
||||
// Send a report request to nework device info not sent already...
|
||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
|
||||
if (!m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) {
|
||||
checkNetworkDevice(networkDeviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Give the last connections added right before the network discovery finished a chance to check the device...
|
||||
QTimer::singleShot(3000, this, [this](){
|
||||
qCDebug(dcSma()) << "Discovery: Grace period timer triggered.";
|
||||
finishDiscovery();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
QList<SmaModbusDiscovery::SmaModbusDiscoveryResult> SmaModbusDiscovery::discoveryResults() const
|
||||
{
|
||||
return m_discoveryResults;
|
||||
}
|
||||
|
||||
void SmaModbusDiscovery::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
|
||||
// the device we can assume this is what we are locking for (ip, port, modbus address, correct registers).
|
||||
// We cloud tough also filter the result only for certain software versions, manufactueres or whatever...
|
||||
|
||||
if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo))
|
||||
return;
|
||||
|
||||
SmaInverterModbusTcpConnection *connection = new SmaInverterModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
|
||||
m_connections.append(connection);
|
||||
m_verifiedNetworkDeviceInfos.append(networkDeviceInfo);
|
||||
|
||||
connect(connection, &SmaInverterModbusTcpConnection::reachableChanged, this, [=](bool reachable){
|
||||
if (!reachable) {
|
||||
// Disconnected ... done with this connection
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
// Modbus TCP connected...ok, let's try to initialize it!
|
||||
connect(connection, &SmaInverterModbusTcpConnection::initializationFinished, this, [=](bool success){
|
||||
if (!success) {
|
||||
qCDebug(dcSma()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
if (connection->deviceClass() != Sma::DeviceClassSolarInverter) {
|
||||
qCDebug(dcSma()) << "Discovery: Initialization successfull for" << networkDeviceInfo.address().toString() << "but the device class is not an inverter. Continue...";;
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
SmaModbusDiscoveryResult result;
|
||||
result.productName = Sma::getModelName(connection->modelIdentifier());
|
||||
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);
|
||||
|
||||
qCDebug(dcSma()) << "Discovery: --> Found" << result.productName;
|
||||
qCDebug(dcSma()) << " Device name:" << result.deviceName;
|
||||
qCDebug(dcSma()) << " Serial number:" << result.serialNumber;
|
||||
qCDebug(dcSma()) << " Software version:" << result.softwareVersion;
|
||||
qCDebug(dcSma()) << " " << result.networkDeviceInfo;
|
||||
|
||||
// Done with this connection
|
||||
cleanupConnection(connection);
|
||||
});
|
||||
|
||||
// Initializing...
|
||||
if (!connection->initialize()) {
|
||||
qCDebug(dcSma()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// If we get any error...skip this host...
|
||||
connect(connection, &SmaInverterModbusTcpConnection::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
|
||||
if (error != QModbusDevice::NoError) {
|
||||
qCDebug(dcSma()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// If check reachability failed...skip this host...
|
||||
connect(connection, &SmaInverterModbusTcpConnection::checkReachabilityFailed, this, [=](){
|
||||
qCDebug(dcSma()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
});
|
||||
|
||||
// Try to connect, maybe it works, maybe not...
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
void SmaModbusDiscovery::cleanupConnection(SmaInverterModbusTcpConnection *connection)
|
||||
{
|
||||
m_connections.removeAll(connection);
|
||||
connection->disconnectDevice();
|
||||
connection->deleteLater();
|
||||
}
|
||||
|
||||
void SmaModbusDiscovery::finishDiscovery()
|
||||
{
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||
|
||||
// Cleanup any leftovers...we don't care any more
|
||||
foreach (SmaInverterModbusTcpConnection *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");
|
||||
emit discoveryFinished();
|
||||
}
|
||||
82
sma/modbus/smamodbusdiscovery.h
Normal file
82
sma/modbus/smamodbusdiscovery.h
Normal file
@ -0,0 +1,82 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, 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 SMAMODBUSDISCOVERY_H
|
||||
#define SMAMODBUSDISCOVERY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "smainvertermodbustcpconnection.h"
|
||||
|
||||
class SmaModbusDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SmaModbusDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 3, QObject *parent = nullptr);
|
||||
typedef struct SmaModbusDiscoveryResult {
|
||||
QString productName;
|
||||
QString deviceName;
|
||||
QString serialNumber;
|
||||
quint16 port;
|
||||
quint16 modbusAddress;
|
||||
QString softwareVersion;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
} SmaModbusDiscoveryResult;
|
||||
|
||||
void startDiscovery();
|
||||
|
||||
QList<SmaModbusDiscoveryResult> discoveryResults() const;
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private:
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
quint16 m_port;
|
||||
quint16 m_modbusAddress;
|
||||
|
||||
QDateTime m_startDateTime;
|
||||
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
|
||||
|
||||
QList<SmaInverterModbusTcpConnection *> m_connections;
|
||||
|
||||
QList<SmaModbusDiscoveryResult> m_discoveryResults;
|
||||
|
||||
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
|
||||
void cleanupConnection(SmaInverterModbusTcpConnection *connection);
|
||||
|
||||
void finishDiscovery();
|
||||
|
||||
};
|
||||
|
||||
#endif // SMAMODBUSDISCOVERY_H
|
||||
269
sma/sma-inverter-registers.json
Normal file
269
sma/sma-inverter-registers.json
Normal file
@ -0,0 +1,269 @@
|
||||
{
|
||||
"className": "SmaInverter",
|
||||
"protocol": "TCP",
|
||||
"endianness": "BigEndian",
|
||||
"errorLimitUntilNotReachable": 20,
|
||||
"checkReachableRegister": "totalYield",
|
||||
"enums": [
|
||||
{
|
||||
"name": "Condition",
|
||||
"values": [
|
||||
{
|
||||
"key": "Fault",
|
||||
"value": 35
|
||||
},
|
||||
{
|
||||
"key": "Off",
|
||||
"value": 303
|
||||
},
|
||||
{
|
||||
"key": "Ok",
|
||||
"value": 307
|
||||
},
|
||||
{
|
||||
"key": "Warning",
|
||||
"value": 455
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "RecommendedAction",
|
||||
"values": [
|
||||
{
|
||||
"key": "ContactManufacturer",
|
||||
"value": 336
|
||||
},
|
||||
{
|
||||
"key": "ContactInstaller",
|
||||
"value": 337
|
||||
},
|
||||
{
|
||||
"key": "Invalid",
|
||||
"value": 338
|
||||
},
|
||||
{
|
||||
"key": "None",
|
||||
"value": 887
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "yield",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "totalYield",
|
||||
"address": 30513,
|
||||
"size": 4,
|
||||
"type": "uint64",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total yield",
|
||||
"unit": "Wh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "dailyYield",
|
||||
"address": 30517,
|
||||
"size": 4,
|
||||
"type": "uint64",
|
||||
"readSccalchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Today yield",
|
||||
"unit": "Wh",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "currentPower",
|
||||
"address": 30775,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Current power",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "currentPowerPhaseA",
|
||||
"address": 30777,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Current power L1",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "currentPowerPhaseB",
|
||||
"address": 30779,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Current power L2",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "currentPowerPhaseC",
|
||||
"address": 30781,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Current power L3",
|
||||
"unit": "W",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridVoltagePhaseA",
|
||||
"address": 30783,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid voltage L1",
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridVoltagePhaseB",
|
||||
"address": 30785,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid voltage L2",
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridVoltagePhaseC",
|
||||
"address": 30787,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid voltage L3",
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gridCurrent",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "gridCurrentPhaseA",
|
||||
"address": 30977,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid current L1",
|
||||
"unit": "A",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridCurrentPhaseB",
|
||||
"address": 30979,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid current L2",
|
||||
"unit": "A",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "gridCurrentPhaseC",
|
||||
"address": 30981,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid current L3",
|
||||
"unit": "A",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"registers": [
|
||||
{
|
||||
"id": "deviceName",
|
||||
"address": 40631,
|
||||
"size": 32,
|
||||
"type": "string",
|
||||
"readSchedule": "init",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Device name",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
}
|
||||
239
sma/sma.h
Normal file
239
sma/sma.h
Normal file
@ -0,0 +1,239 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, 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 SMA_H
|
||||
#define SMA_H
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QMetaObject>
|
||||
#include <QDataStream>
|
||||
|
||||
class Sma
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
enum DeviceClass {
|
||||
DeviceClassUnknown = 0x0000,
|
||||
DeviceClassAllDevices = 0x1f40,
|
||||
DeviceClassSolarInverter = 0x1f41,
|
||||
DeviceClassWindTurbine = 0x1f42,
|
||||
DeviceClassBatteryInverter = 0x1f47,
|
||||
DeviceClassConsumer = 0x1f61,
|
||||
DeviceClassSensorSystem = 0x1f80,
|
||||
DeviceClassElectricityMeter = 0x1f81,
|
||||
DeviceClassCommunicationProduct = 0x1fc0
|
||||
};
|
||||
Q_ENUM(DeviceClass)
|
||||
|
||||
inline static QString buildSoftwareVersionString(quint32 versionData) {
|
||||
|
||||
// Software version
|
||||
QByteArray rawData;
|
||||
QDataStream stream(&rawData, QIODevice::ReadWrite);
|
||||
stream << versionData;
|
||||
|
||||
quint8 major = static_cast<quint8>(rawData.at(0));
|
||||
quint8 minor = static_cast<quint8>(rawData.at(1));
|
||||
quint8 build = static_cast<quint8>(rawData.at(2));
|
||||
quint8 revision = static_cast<quint8>(rawData.at(3));;
|
||||
|
||||
// Revision types:
|
||||
// 0 -> N: No revision
|
||||
// 1 -> E: Experimental version
|
||||
// 2 -> A: Alpha version
|
||||
// 3 -> B: Beta version
|
||||
// 4 -> R: Release version
|
||||
// 5 -> S: Special version
|
||||
QChar revisionCharacter;
|
||||
switch (revision) {
|
||||
case 0:
|
||||
revisionCharacter = 'N';
|
||||
break;
|
||||
case 1:
|
||||
revisionCharacter = 'E';
|
||||
break;
|
||||
case 2:
|
||||
revisionCharacter = 'A';
|
||||
break;
|
||||
case 3:
|
||||
revisionCharacter = 'B';
|
||||
break;
|
||||
case 4:
|
||||
revisionCharacter = 'R';
|
||||
break;
|
||||
case 5:
|
||||
revisionCharacter = 'S';
|
||||
break;
|
||||
}
|
||||
|
||||
return QString("%1.%2.%3-%4").arg(major).arg(minor).arg(build).arg(revisionCharacter);
|
||||
}
|
||||
|
||||
inline static QString getModelName(quint16 modelIdentifier) {
|
||||
switch (modelIdentifier) {
|
||||
// Modbus
|
||||
case 9225: return "SB 5000SE-10";
|
||||
case 9226: return "SB 3600SE-10";
|
||||
case 9165: return "SB 3600TL-21";
|
||||
case 9075: return "SB 4000TL-21";
|
||||
case 9076: return "SB 5000TL-21";
|
||||
case 9162: return "SB 3500TL-JP-22";
|
||||
case 9164: return "SB 4500TL-JP-22";
|
||||
case 9198: return "SB 3000TL-US-22";
|
||||
case 9199: return "SB 3800TL-US-22";
|
||||
case 9200: return "SB 4000TL-US-22";
|
||||
case 9201: return "SB 5000TL-US-22";
|
||||
case 9274: return "SB 6000TL-US-22";
|
||||
case 9275: return "SB 7000TL-US-22";
|
||||
case 9293: return "SB 7700TL-US-22";
|
||||
case 9222: return "STP 10000TLEE-JP-10";
|
||||
case 9194: return "STP 12000TL-US-10";
|
||||
case 9195: return "STP 15000TL-US-10";
|
||||
case 9196: return "STP 20000TL-US-10";
|
||||
case 9197: return "STP 24000TL-US-10";
|
||||
case 9310: return "STP 30000TL-US-10";
|
||||
case 9271: return "STP 20000TLEE-JP-11";
|
||||
case 9272: return "STP 10000TLEE-JP-11";
|
||||
case 9354: return "STP 24500TL-JP-30";
|
||||
case 9311: return "STP 25000TL-JP-30";
|
||||
case 9223: return "SI6.0H-11";
|
||||
case 9224: return "SI8.0H-11";
|
||||
|
||||
// Speedwire / Modbus
|
||||
case 9015: return "SB 700";
|
||||
case 9016: return "SB 700U";
|
||||
case 9017: return "SB 1100";
|
||||
case 9018: return "SB 1100U";
|
||||
case 9019: return "SB 1100LV";
|
||||
case 9020: return "SB 1700";
|
||||
case 9021: return "SB 1900TLJ";
|
||||
case 9022: return "SB 2100TL";
|
||||
case 9023: return "SB 2500";
|
||||
case 9024: return "SB 2800";
|
||||
case 9025: return "SB 2800i";
|
||||
case 9026: return "SB 3000";
|
||||
case 9027: return "SB 3000US";
|
||||
case 9028: return "SB 3300";
|
||||
case 9029: return "SB 3300U";
|
||||
case 9030: return "SB 3300TL";
|
||||
case 9031: return "SB 3300TL HC";
|
||||
case 9032: return "SB 3800";
|
||||
case 9033: return "SB 3800U";
|
||||
case 9034: return "SB 4000US";
|
||||
case 9035: return "SB 4200TL";
|
||||
case 9036: return "SB 4200TL HC";
|
||||
case 9037: return "SB 5000TL";
|
||||
case 9038: return "SB 5000TLW";
|
||||
case 9039: return "SB 5000TL HC";
|
||||
case 9066: return "SB 1200";
|
||||
case 9067: return "STP 10000TL-10";
|
||||
case 9068: return "STP 12000TL-10";
|
||||
case 9069: return "STP 15000TL-10";
|
||||
case 9070: return "STP 17000TL-10";
|
||||
case 9074: return "SB 3000TL-21";
|
||||
case 9084: return "WB 3600TL-20";
|
||||
case 9085: return "WB 5000TL-20";
|
||||
case 9086: return "SB 3800US-10";
|
||||
case 9098: return "STP 5000TL-20";
|
||||
case 9099: return "STP 6000TL-20";
|
||||
case 9100: return "STP 7000TL-20";
|
||||
case 9101: return "STP 8000TL-10";
|
||||
case 9102: return "STPcase 9000TL-20";
|
||||
case 9103: return "STP 8000TL-20";
|
||||
case 9104: return "SB 3000TL-JP-21";
|
||||
case 9105: return "SB 3500TL-JP-21";
|
||||
case 9106: return "SB 4000TL-JP-21";
|
||||
case 9107: return "SB 4500TL-JP-21";
|
||||
case 9108: return "SCSMC";
|
||||
case 9109: return "SB 1600TL-10";
|
||||
case 9131: return "STP 20000TL-10";
|
||||
case 9139: return "STP 20000TLHE-10";
|
||||
case 9140: return "STP 15000TLHE-10";
|
||||
case 9157: return "Sunny Island 2012";
|
||||
case 9158: return "Sunny Island 2224";
|
||||
case 9159: return "Sunny Island 5048";
|
||||
case 9160: return "SB 3600TL-20";
|
||||
case 9168: return "SC630HE-11";
|
||||
case 9169: return "SC500HE-11";
|
||||
case 9170: return "SC400HE-11";
|
||||
case 9171: return "WB 3000TL-21";
|
||||
case 9172: return "WB 3600TL-21";
|
||||
case 9173: return "WB 4000TL-21";
|
||||
case 9174: return "WB 5000TL-21";
|
||||
case 9175: return "SC 250";
|
||||
case 9176: return "SMA Meteo Station";
|
||||
case 9177: return "SB 240-10";
|
||||
case 9179: return "Multigate-10";
|
||||
case 9180: return "Multigate-US-10";
|
||||
case 9181: return "STP 20000TLEE-10";
|
||||
case 9182: return "STP 15000TLEE-10";
|
||||
case 9183: return "SB 2000TLST-21";
|
||||
case 9184: return "SB 2500TLST-21";
|
||||
case 9185: return "SB 3000TLST-21";
|
||||
case 9186: return "WB 2000TLST-21";
|
||||
case 9187: return "WB 2500TLST-21";
|
||||
case 9188: return "WB 3000TLST-21";
|
||||
case 9189: return "WTP 5000TL-20";
|
||||
case 9190: return "WTP 6000TL-20";
|
||||
case 9191: return "WTP 7000TL-20";
|
||||
case 9192: return "WTP 8000TL-20";
|
||||
case 9193: return "WTPcase 9000TL-20";
|
||||
case 9254: return "Sunny Island 3324";
|
||||
case 9255: return "Sunny Island 4.0M";
|
||||
case 9256: return "Sunny Island 4248";
|
||||
case 9257: return "Sunny Island 4248U";
|
||||
case 9258: return "Sunny Island 4500";
|
||||
case 9259: return "Sunny Island 4548U";
|
||||
case 9260: return "Sunny Island 5.4M";
|
||||
case 9261: return "Sunny Island 5048U";
|
||||
case 9262: return "Sunny Island 6048U";
|
||||
case 9278: return "Sunny Island 3.0M";
|
||||
case 9279: return "Sunny Island 4.4M";
|
||||
case 9281: return "STP 10000TL-20";
|
||||
case 9282: return "STP 11000TL-20";
|
||||
case 9283: return "STP 12000TL-20";
|
||||
case 9284: return "STP 20000TL-30";
|
||||
case 9285: return "STP 25000TL-30";
|
||||
case 9301: return "SB1.5-1VL-40";
|
||||
case 9302: return "SB2.5-1VL-40";
|
||||
case 9303: return "SB2.0-1VL-40";
|
||||
case 9304: return "SB5.0-1SP-US-40";
|
||||
case 9305: return "SB6.0-1SP-US-40";
|
||||
case 9306: return "SB8.0-1SP-US-40";
|
||||
case 9307: return "Energy Meter";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif // SMA_H
|
||||
42
sma/sma.pro
42
sma/sma.pro
@ -2,25 +2,33 @@ include(../plugins.pri)
|
||||
|
||||
QT += network
|
||||
|
||||
# Generate modbus connection
|
||||
MODBUS_CONNECTIONS += sma-inverter-registers.json
|
||||
MODBUS_TOOLS_CONFIG += VERBOSE
|
||||
include(../modbus.pri)
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsma.cpp \
|
||||
speedwirediscovery.cpp \
|
||||
speedwireinterface.cpp \
|
||||
speedwireinverter.cpp \
|
||||
speedwireinverterreply.cpp \
|
||||
speedwireinverterrequest.cpp \
|
||||
speedwiremeter.cpp \
|
||||
sunnywebbox.cpp \
|
||||
sunnywebboxdiscovery.cpp
|
||||
modbus/smamodbusdiscovery.cpp \
|
||||
speedwire/speedwirediscovery.cpp \
|
||||
speedwire/speedwireinterface.cpp \
|
||||
speedwire/speedwireinverter.cpp \
|
||||
speedwire/speedwireinverterreply.cpp \
|
||||
speedwire/speedwireinverterrequest.cpp \
|
||||
speedwire/speedwiremeter.cpp \
|
||||
sunnywebbox/sunnywebbox.cpp \
|
||||
sunnywebbox/sunnywebboxdiscovery.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsma.h \
|
||||
speedwire.h \
|
||||
speedwirediscovery.h \
|
||||
speedwireinterface.h \
|
||||
speedwireinverter.h \
|
||||
speedwireinverterreply.h \
|
||||
speedwireinverterrequest.h \
|
||||
speedwiremeter.h \
|
||||
sunnywebbox.h \
|
||||
sunnywebboxdiscovery.h
|
||||
modbus/smamodbusdiscovery.h \
|
||||
sma.h \
|
||||
speedwire/speedwire.h \
|
||||
speedwire/speedwirediscovery.h \
|
||||
speedwire/speedwireinterface.h \
|
||||
speedwire/speedwireinverter.h \
|
||||
speedwire/speedwireinverterreply.h \
|
||||
speedwire/speedwireinverterrequest.h \
|
||||
speedwire/speedwiremeter.h \
|
||||
sunnywebbox/sunnywebbox.h \
|
||||
sunnywebbox/sunnywebboxdiscovery.h
|
||||
|
||||
@ -64,19 +64,6 @@ public:
|
||||
};
|
||||
Q_ENUM(ProtocolId)
|
||||
|
||||
enum DeviceClass {
|
||||
DeviceClassUnknown = 0x0000,
|
||||
DeviceClassAllDevices = 0x1f40,
|
||||
DeviceClassSolarInverter = 0x1f41,
|
||||
DeviceClassWindTurbine = 0x1f42,
|
||||
DeviceClassBatteryInverter = 0x1f47,
|
||||
DeviceClassConsumer = 0x1f61,
|
||||
DeviceClassSensorSystem = 0x1f80,
|
||||
DeviceClassElectricityMeter = 0x1f81,
|
||||
DeviceClassCommunicationProduct = 0x1fc0
|
||||
};
|
||||
Q_ENUM(DeviceClass)
|
||||
|
||||
class Header
|
||||
{
|
||||
public:
|
||||
@ -121,111 +108,6 @@ public:
|
||||
static quint16 tagVersion() { return 0; }
|
||||
static quint16 smaNet2Version() { return 0x0010; }
|
||||
|
||||
static QString getModelName(quint16 modelIdentifier) {
|
||||
switch (modelIdentifier) {
|
||||
case 9015: return "SB 700";
|
||||
case 9016: return "SB 700U";
|
||||
case 9017: return "SB 1100";
|
||||
case 9018: return "SB 1100U";
|
||||
case 9019: return "SB 1100LV";
|
||||
case 9020: return "SB 1700";
|
||||
case 9021: return "SB 1900TLJ";
|
||||
case 9022: return "SB 2100TL";
|
||||
case 9023: return "SB 2500";
|
||||
case 9024: return "SB 2800";
|
||||
case 9025: return "SB 2800i";
|
||||
case 9026: return "SB 3000";
|
||||
case 9027: return "SB 3000US";
|
||||
case 9028: return "SB 3300";
|
||||
case 9029: return "SB 3300U";
|
||||
case 9030: return "SB 3300TL";
|
||||
case 9031: return "SB 3300TL HC";
|
||||
case 9032: return "SB 3800";
|
||||
case 9033: return "SB 3800U";
|
||||
case 9034: return "SB 4000US";
|
||||
case 9035: return "SB 4200TL";
|
||||
case 9036: return "SB 4200TL HC";
|
||||
case 9037: return "SB 5000TL";
|
||||
case 9038: return "SB 5000TLW";
|
||||
case 9039: return "SB 5000TL HC";
|
||||
case 9066: return "SB 1200";
|
||||
case 9067: return "STP 10000TL-10";
|
||||
case 9068: return "STP 12000TL-10";
|
||||
case 9069: return "STP 15000TL-10";
|
||||
case 9070: return "STP 17000TL-10";
|
||||
case 9084: return "WB 3600TL-20";
|
||||
case 9085: return "WB 5000TL-20";
|
||||
case 9086: return "SB 3800US-10";
|
||||
case 9098: return "STP 5000TL-20";
|
||||
case 9099: return "STP 6000TL-20";
|
||||
case 9100: return "STP 7000TL-20";
|
||||
case 9101: return "STP 8000TL-10";
|
||||
case 9102: return "STPcase 9000TL-20";
|
||||
case 9103: return "STP 8000TL-20";
|
||||
case 9104: return "SB 3000TL-JP-21";
|
||||
case 9105: return "SB 3500TL-JP-21";
|
||||
case 9106: return "SB 4000TL-JP-21";
|
||||
case 9107: return "SB 4500TL-JP-21";
|
||||
case 9108: return "SCSMC";
|
||||
case 9109: return "SB 1600TL-10";
|
||||
case 9131: return "STP 20000TL-10";
|
||||
case 9139: return "STP 20000TLHE-10";
|
||||
case 9140: return "STP 15000TLHE-10";
|
||||
case 9157: return "Sunny Island 2012";
|
||||
case 9158: return "Sunny Island 2224";
|
||||
case 9159: return "Sunny Island 5048";
|
||||
case 9160: return "SB 3600TL-20";
|
||||
case 9168: return "SC630HE-11";
|
||||
case 9169: return "SC500HE-11";
|
||||
case 9170: return "SC400HE-11";
|
||||
case 9171: return "WB 3000TL-21";
|
||||
case 9172: return "WB 3600TL-21";
|
||||
case 9173: return "WB 4000TL-21";
|
||||
case 9174: return "WB 5000TL-21";
|
||||
case 9175: return "SC 250";
|
||||
case 9176: return "SMA Meteo Station";
|
||||
case 9177: return "SB 240-10";
|
||||
case 9179: return "Multigate-10";
|
||||
case 9180: return "Multigate-US-10";
|
||||
case 9181: return "STP 20000TLEE-10";
|
||||
case 9182: return "STP 15000TLEE-10";
|
||||
case 9183: return "SB 2000TLST-21";
|
||||
case 9184: return "SB 2500TLST-21";
|
||||
case 9185: return "SB 3000TLST-21";
|
||||
case 9186: return "WB 2000TLST-21";
|
||||
case 9187: return "WB 2500TLST-21";
|
||||
case 9188: return "WB 3000TLST-21";
|
||||
case 9189: return "WTP 5000TL-20";
|
||||
case 9190: return "WTP 6000TL-20";
|
||||
case 9191: return "WTP 7000TL-20";
|
||||
case 9192: return "WTP 8000TL-20";
|
||||
case 9193: return "WTPcase 9000TL-20";
|
||||
case 9254: return "Sunny Island 3324";
|
||||
case 9255: return "Sunny Island 4.0M";
|
||||
case 9256: return "Sunny Island 4248";
|
||||
case 9257: return "Sunny Island 4248U";
|
||||
case 9258: return "Sunny Island 4500";
|
||||
case 9259: return "Sunny Island 4548U";
|
||||
case 9260: return "Sunny Island 5.4M";
|
||||
case 9261: return "Sunny Island 5048U";
|
||||
case 9262: return "Sunny Island 6048U";
|
||||
case 9278: return "Sunny Island 3.0M";
|
||||
case 9279: return "Sunny Island 4.4M";
|
||||
case 9281: return "STP 10000TL-20";
|
||||
case 9282: return "STP 11000TL-20";
|
||||
case 9283: return "STP 12000TL-20";
|
||||
case 9284: return "STP 20000TL-30";
|
||||
case 9285: return "STP 25000TL-30";
|
||||
case 9301: return "SB1.5-1VL-40";
|
||||
case 9302: return "SB2.5-1VL-40";
|
||||
case 9303: return "SB2.0-1VL-40";
|
||||
case 9304: return "SB5.0-1SP-US-40";
|
||||
case 9305: return "SB6.0-1SP-US-40";
|
||||
case 9306: return "SB8.0-1SP-US-40";
|
||||
case 9307: return "Energy Meter";
|
||||
default: return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
// Multicast device discovery request packet, according to SMA documentation.
|
||||
// However, this does not seem to be supported anymore with version 3.x devices
|
||||
@ -32,7 +32,6 @@
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <speedwirediscovery.h>
|
||||
|
||||
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
|
||||
QObject(parent),
|
||||
@ -34,6 +34,7 @@
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
|
||||
#include "sma.h"
|
||||
#include "speedwire.h"
|
||||
#include "speedwireinterface.h"
|
||||
#include "speedwireinverterreply.h"
|
||||
@ -62,7 +63,7 @@ public:
|
||||
|
||||
bool reachable() const;
|
||||
|
||||
Speedwire::DeviceClass deviceClass() const;
|
||||
Sma::DeviceClass deviceClass() const;
|
||||
QString modelName() const;
|
||||
|
||||
double totalAcPower() const;
|
||||
@ -131,7 +132,7 @@ private:
|
||||
QQueue<SpeedwireInverterReply *> m_replyQueue;
|
||||
|
||||
// Properties
|
||||
Speedwire::DeviceClass m_deviceClass = Speedwire::DeviceClassUnknown;
|
||||
Sma::DeviceClass m_deviceClass = Sma::DeviceClassUnknown;
|
||||
QString m_modelName;
|
||||
QString m_softwareVersion;
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
#include "speedwiremeter.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "sma.h"
|
||||
|
||||
SpeedwireMeter::SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_address(address),
|
||||
@ -318,16 +320,9 @@ void SpeedwireMeter::processData(const QByteArray &data)
|
||||
} if (measurementChannel == 144 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) {
|
||||
// Software version
|
||||
// 90000000 01 02 08 52
|
||||
quint8 major, minor, build, revision;
|
||||
stream >> major >> minor >> build >> revision;
|
||||
// Revision types:
|
||||
// S: Special version
|
||||
// A: Alpha version
|
||||
// B: Beta version
|
||||
// R: Release version
|
||||
// E: Experimental version
|
||||
// N: No revision
|
||||
m_softwareVersion = QString("%1.%2.%3-%4").arg(major).arg(minor).arg(build).arg(QChar(revision));
|
||||
quint32 versionData;
|
||||
stream >> versionData;
|
||||
m_softwareVersion = Sma::buildSoftwareVersionString(versionData);
|
||||
|
||||
qCDebug(dcSma()) << "Meter: Software version" << m_softwareVersion;
|
||||
} else if (measurementChannel == 0 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) {
|
||||
@ -74,7 +74,6 @@ public:
|
||||
|
||||
QString softwareVersion() const;
|
||||
|
||||
|
||||
signals:
|
||||
void reachableChanged(bool reachable);
|
||||
void valuesUpdated();
|
||||
Reference in New Issue
Block a user