From 5a1b6b773ef3005f314b8fe2aaf93ccba8f58eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 18 Nov 2022 01:34:41 +0100 Subject: [PATCH] Restructure sma plugin and add modbus inverter support --- nymea-plugins-modbus.pro | 1 + sma/README.md | 3 +- sma/integrationpluginsma.cpp | 320 ++++++++++++++++-- sma/integrationpluginsma.h | 21 +- sma/integrationpluginsma.json | 205 ++++++++--- sma/modbus/smamodbusdiscovery.cpp | 176 ++++++++++ sma/modbus/smamodbusdiscovery.h | 82 +++++ sma/sma-inverter-registers.json | 269 +++++++++++++++ sma/sma.h | 239 +++++++++++++ sma/sma.pro | 42 ++- sma/{ => speedwire}/speedwire.h | 118 ------- sma/{ => speedwire}/speedwirediscovery.cpp | 1 - sma/{ => speedwire}/speedwirediscovery.h | 0 sma/{ => speedwire}/speedwireinterface.cpp | 0 sma/{ => speedwire}/speedwireinterface.h | 0 sma/{ => speedwire}/speedwireinverter.cpp | 0 sma/{ => speedwire}/speedwireinverter.h | 5 +- .../speedwireinverterreply.cpp | 0 sma/{ => speedwire}/speedwireinverterreply.h | 0 .../speedwireinverterrequest.cpp | 0 .../speedwireinverterrequest.h | 0 sma/{ => speedwire}/speedwiremeter.cpp | 15 +- sma/{ => speedwire}/speedwiremeter.h | 1 - sma/{ => sunnywebbox}/sunnywebbox.cpp | 0 sma/{ => sunnywebbox}/sunnywebbox.h | 0 .../sunnywebboxdiscovery.cpp | 0 sma/{ => sunnywebbox}/sunnywebboxdiscovery.h | 0 27 files changed, 1276 insertions(+), 222 deletions(-) create mode 100644 sma/modbus/smamodbusdiscovery.cpp create mode 100644 sma/modbus/smamodbusdiscovery.h create mode 100644 sma/sma-inverter-registers.json create mode 100644 sma/sma.h rename sma/{ => speedwire}/speedwire.h (61%) rename sma/{ => speedwire}/speedwirediscovery.cpp (99%) rename sma/{ => speedwire}/speedwirediscovery.h (100%) rename sma/{ => speedwire}/speedwireinterface.cpp (100%) rename sma/{ => speedwire}/speedwireinterface.h (100%) rename sma/{ => speedwire}/speedwireinverter.cpp (100%) rename sma/{ => speedwire}/speedwireinverter.h (98%) rename sma/{ => speedwire}/speedwireinverterreply.cpp (100%) rename sma/{ => speedwire}/speedwireinverterreply.h (100%) rename sma/{ => speedwire}/speedwireinverterrequest.cpp (100%) rename sma/{ => speedwire}/speedwireinverterrequest.h (100%) rename sma/{ => speedwire}/speedwiremeter.cpp (96%) rename sma/{ => speedwire}/speedwiremeter.h (99%) rename sma/{ => sunnywebbox}/sunnywebbox.cpp (100%) rename sma/{ => sunnywebbox}/sunnywebbox.h (100%) rename sma/{ => sunnywebbox}/sunnywebboxdiscovery.cpp (100%) rename sma/{ => sunnywebbox}/sunnywebboxdiscovery.h (100%) diff --git a/nymea-plugins-modbus.pro b/nymea-plugins-modbus.pro index 1176e20..5387f04 100644 --- a/nymea-plugins-modbus.pro +++ b/nymea-plugins-modbus.pro @@ -18,6 +18,7 @@ PLUGIN_DIRS = \ mypv \ phoenixconnect \ schrack \ + sma \ stiebeleltron \ sunspec \ unipi \ diff --git a/sma/README.md b/sma/README.md index bbb61b6..ad56627 100644 --- a/sma/README.md +++ b/sma/README.md @@ -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/ diff --git a/sma/integrationpluginsma.cpp b/sma/integrationpluginsma.cpp index 843008a..44bc212 100644 --- a/sma/integrationpluginsma.cpp +++ b/sma/integrationpluginsma.cpp @@ -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 @@ -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(0x80000000); +} + +bool IntegrationPluginSma::isModbusValueValid(quint64 value) +{ + return value != 0xffffffffffffffff; +} + diff --git a/sma/integrationpluginsma.h b/sma/integrationpluginsma.h index fc509d0..50609bf 100644 --- a/sma/integrationpluginsma.h +++ b/sma/integrationpluginsma.h @@ -33,10 +33,15 @@ #include #include +#include -#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 m_monitors; + QHash m_sunnyWebBoxes; QHash m_speedwireMeters; QHash m_speedwireInverters; + QHash m_modbusInverters; + + // Sma modbus data validation + bool isModbusValueValid(quint32 value); + bool isModbusValueValid(qint32 value); + bool isModbusValueValid(quint64 value); }; #endif // INTEGRATIONPLUGINSMA_H diff --git a/sma/integrationpluginsma.json b/sma/integrationpluginsma.json index 80cc5aa..76a31b5 100644 --- a/sma/integrationpluginsma.json +++ b/sma/integrationpluginsma.json @@ -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": "" } diff --git a/sma/modbus/smamodbusdiscovery.cpp b/sma/modbus/smamodbusdiscovery.cpp new file mode 100644 index 0000000..c2621d8 --- /dev/null +++ b/sma/modbus/smamodbusdiscovery.cpp @@ -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 . +* +* 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::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(); +} diff --git a/sma/modbus/smamodbusdiscovery.h b/sma/modbus/smamodbusdiscovery.h new file mode 100644 index 0000000..0ed6290 --- /dev/null +++ b/sma/modbus/smamodbusdiscovery.h @@ -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 . +* +* 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 +#include + +#include + +#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 discoveryResults() const; + +signals: + void discoveryFinished(); + +private: + NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; + quint16 m_port; + quint16 m_modbusAddress; + + QDateTime m_startDateTime; + NetworkDeviceInfos m_verifiedNetworkDeviceInfos; + + QList m_connections; + + QList m_discoveryResults; + + void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo); + void cleanupConnection(SmaInverterModbusTcpConnection *connection); + + void finishDiscovery(); + +}; + +#endif // SMAMODBUSDISCOVERY_H diff --git a/sma/sma-inverter-registers.json b/sma/sma-inverter-registers.json new file mode 100644 index 0000000..6132087 --- /dev/null +++ b/sma/sma-inverter-registers.json @@ -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" + } + ] +} diff --git a/sma/sma.h b/sma/sma.h new file mode 100644 index 0000000..fef1fcf --- /dev/null +++ b/sma/sma.h @@ -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 . +* +* 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 +#include +#include +#include + +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(rawData.at(0)); + quint8 minor = static_cast(rawData.at(1)); + quint8 build = static_cast(rawData.at(2)); + quint8 revision = static_cast(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 diff --git a/sma/sma.pro b/sma/sma.pro index 7e031fc..ce064d7 100644 --- a/sma/sma.pro +++ b/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 diff --git a/sma/speedwire.h b/sma/speedwire/speedwire.h similarity index 61% rename from sma/speedwire.h rename to sma/speedwire/speedwire.h index df92f5c..b1f1734 100644 --- a/sma/speedwire.h +++ b/sma/speedwire/speedwire.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 diff --git a/sma/speedwirediscovery.cpp b/sma/speedwire/speedwirediscovery.cpp similarity index 99% rename from sma/speedwirediscovery.cpp rename to sma/speedwire/speedwirediscovery.cpp index 42a1dd0..83547f1 100644 --- a/sma/speedwirediscovery.cpp +++ b/sma/speedwire/speedwirediscovery.cpp @@ -32,7 +32,6 @@ #include "extern-plugininfo.h" #include -#include SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) : QObject(parent), diff --git a/sma/speedwirediscovery.h b/sma/speedwire/speedwirediscovery.h similarity index 100% rename from sma/speedwirediscovery.h rename to sma/speedwire/speedwirediscovery.h diff --git a/sma/speedwireinterface.cpp b/sma/speedwire/speedwireinterface.cpp similarity index 100% rename from sma/speedwireinterface.cpp rename to sma/speedwire/speedwireinterface.cpp diff --git a/sma/speedwireinterface.h b/sma/speedwire/speedwireinterface.h similarity index 100% rename from sma/speedwireinterface.h rename to sma/speedwire/speedwireinterface.h diff --git a/sma/speedwireinverter.cpp b/sma/speedwire/speedwireinverter.cpp similarity index 100% rename from sma/speedwireinverter.cpp rename to sma/speedwire/speedwireinverter.cpp diff --git a/sma/speedwireinverter.h b/sma/speedwire/speedwireinverter.h similarity index 98% rename from sma/speedwireinverter.h rename to sma/speedwire/speedwireinverter.h index ce2d34e..dd814d6 100644 --- a/sma/speedwireinverter.h +++ b/sma/speedwire/speedwireinverter.h @@ -34,6 +34,7 @@ #include #include +#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 m_replyQueue; // Properties - Speedwire::DeviceClass m_deviceClass = Speedwire::DeviceClassUnknown; + Sma::DeviceClass m_deviceClass = Sma::DeviceClassUnknown; QString m_modelName; QString m_softwareVersion; diff --git a/sma/speedwireinverterreply.cpp b/sma/speedwire/speedwireinverterreply.cpp similarity index 100% rename from sma/speedwireinverterreply.cpp rename to sma/speedwire/speedwireinverterreply.cpp diff --git a/sma/speedwireinverterreply.h b/sma/speedwire/speedwireinverterreply.h similarity index 100% rename from sma/speedwireinverterreply.h rename to sma/speedwire/speedwireinverterreply.h diff --git a/sma/speedwireinverterrequest.cpp b/sma/speedwire/speedwireinverterrequest.cpp similarity index 100% rename from sma/speedwireinverterrequest.cpp rename to sma/speedwire/speedwireinverterrequest.cpp diff --git a/sma/speedwireinverterrequest.h b/sma/speedwire/speedwireinverterrequest.h similarity index 100% rename from sma/speedwireinverterrequest.h rename to sma/speedwire/speedwireinverterrequest.h diff --git a/sma/speedwiremeter.cpp b/sma/speedwire/speedwiremeter.cpp similarity index 96% rename from sma/speedwiremeter.cpp rename to sma/speedwire/speedwiremeter.cpp index 152ae87..5178b56 100644 --- a/sma/speedwiremeter.cpp +++ b/sma/speedwire/speedwiremeter.cpp @@ -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) { diff --git a/sma/speedwiremeter.h b/sma/speedwire/speedwiremeter.h similarity index 99% rename from sma/speedwiremeter.h rename to sma/speedwire/speedwiremeter.h index 0599cf3..63989c3 100644 --- a/sma/speedwiremeter.h +++ b/sma/speedwire/speedwiremeter.h @@ -74,7 +74,6 @@ public: QString softwareVersion() const; - signals: void reachableChanged(bool reachable); void valuesUpdated(); diff --git a/sma/sunnywebbox.cpp b/sma/sunnywebbox/sunnywebbox.cpp similarity index 100% rename from sma/sunnywebbox.cpp rename to sma/sunnywebbox/sunnywebbox.cpp diff --git a/sma/sunnywebbox.h b/sma/sunnywebbox/sunnywebbox.h similarity index 100% rename from sma/sunnywebbox.h rename to sma/sunnywebbox/sunnywebbox.h diff --git a/sma/sunnywebboxdiscovery.cpp b/sma/sunnywebbox/sunnywebboxdiscovery.cpp similarity index 100% rename from sma/sunnywebboxdiscovery.cpp rename to sma/sunnywebbox/sunnywebboxdiscovery.cpp diff --git a/sma/sunnywebboxdiscovery.h b/sma/sunnywebbox/sunnywebboxdiscovery.h similarity index 100% rename from sma/sunnywebboxdiscovery.h rename to sma/sunnywebbox/sunnywebboxdiscovery.h