diff --git a/debian/control b/debian/control index de77f4b2..fb28f9ea 100644 --- a/debian/control +++ b/debian/control @@ -756,15 +756,6 @@ Description: nymea integration plugin to send and receive strings over a serial arbitrary data via serial ports. -Package: nymea-plugin-sma -Architecture: any -Depends: ${shlibs:Depends}, - ${misc:Depends}, -Conflicts: nymea-plugins-translations (< 1.0.1) -Description: nymea integration plugin for SMA solar inverters - This package contains the nymea integration plugin for SMA solar inverters. - - Package: nymea-plugin-systemmonitor Architecture: any Depends: ${shlibs:Depends}, diff --git a/debian/nymea-plugin-sma.install.in b/debian/nymea-plugin-sma.install.in deleted file mode 100644 index 4090f199..00000000 --- a/debian/nymea-plugin-sma.install.in +++ /dev/null @@ -1,2 +0,0 @@ -usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsma.so -sma/translations/*qm usr/share/nymea/translations/ diff --git a/nymea-plugins.pro b/nymea-plugins.pro index d0802476..094e8882 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -61,7 +61,6 @@ PLUGIN_DIRS = \ serialportcommander \ sgready \ simpleheatpump \ - sma \ somfytahoma \ sonos \ sunposition \ diff --git a/sma/README.md b/sma/README.md deleted file mode 100644 index bbb61b63..00000000 --- a/sma/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# SMA - -nymea plug-in for SMA solar equipment. - -## Supported Things - -* Sunny WebBox -* SMA speedwire Meters -* SMA speedwire Inverters - -> 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. - -## More -https://www.sma.de/en/ diff --git a/sma/integrationpluginsma.cpp b/sma/integrationpluginsma.cpp deleted file mode 100644 index 843008a9..00000000 --- a/sma/integrationpluginsma.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "integrationpluginsma.h" -#include "plugininfo.h" -#include "speedwirediscovery.h" -#include "sunnywebboxdiscovery.h" - -#include - -IntegrationPluginSma::IntegrationPluginSma() -{ - -} - -void IntegrationPluginSma::init() -{ - -} - -void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info) -{ - if (info->thingClassId() == sunnyWebBoxThingClassId) { - if (!hardwareManager()->networkDeviceDiscovery()->available()) { - qCWarning(dcSma()) << "Failed to discover network devices. The network device discovery is not available."; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to discover devices in your network.")); - return; - } - - qCDebug(dcSma()) << "Starting Sunny WebBox discovery..."; - SunnyWebBoxDiscovery *webBoxDiscovery = new SunnyWebBoxDiscovery(hardwareManager()->networkManager(), hardwareManager()->networkDeviceDiscovery(), info); - connect(webBoxDiscovery, &SunnyWebBoxDiscovery::discoveryFinished, this, [=](){ - webBoxDiscovery->deleteLater(); - ThingDescriptors descriptors; - foreach (const NetworkDeviceInfo &networkDeviceInfo, webBoxDiscovery->discoveryResults()) { - QString title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")"; - QString description; - if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { - description = networkDeviceInfo.macAddress(); - } else { - description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; - } - - ThingDescriptor descriptor(sunnyWebBoxThingClassId, title, description); - - // Check for reconfiguration - foreach (Thing *existingThing, myThings()) { - if (existingThing->paramValue(sunnyWebBoxThingMacAddressParamTypeId).toString() == networkDeviceInfo.macAddress()) { - descriptor.setThingId(existingThing->id()); - break; - } - } - - ParamList params; - params << Param(sunnyWebBoxThingHostParamTypeId, networkDeviceInfo.address().toString()); - params << Param(sunnyWebBoxThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); - descriptor.setParams(params); - descriptors.append(descriptor); - } - info->addThingDescriptors(descriptors); - info->finish(Thing::ThingErrorNoError); - }); - - webBoxDiscovery->startDiscovery(); - - } else if (info->thingClassId() == speedwireMeterThingClassId) { - SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info); - if (!speedwireDiscovery->initialize()) { - qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed."; - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network.")); - return; - } - - connect(speedwireDiscovery, &SpeedwireDiscovery::discoveryFinished, this, [=](){ - qCDebug(dcSma()) << "Speed wire discovery finished."; - speedwireDiscovery->deleteLater(); - - ThingDescriptors descriptors; - foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) { - if (result.deviceType == SpeedwireInterface::DeviceTypeMeter) { - if (result.serialNumber == 0) - continue; - - ThingDescriptor descriptor(speedwireMeterThingClassId, "SMA Energy Meter", "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(speedwireMeterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) { - descriptor.setThingId(existingThing->id()); - break; - } - } - - ParamList params; - params << Param(speedwireMeterThingHostParamTypeId, result.address.toString()); - params << Param(speedwireMeterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); - params << Param(speedwireMeterThingSerialNumberParamTypeId, result.serialNumber); - params << Param(speedwireMeterThingModelIdParamTypeId, result.modelId); - descriptor.setParams(params); - descriptors.append(descriptor); - } - } - - info->addThingDescriptors(descriptors); - info->finish(Thing::ThingErrorNoError); - }); - - speedwireDiscovery->startDiscovery(); - } else if (info->thingClassId() == speedwireInverterThingClassId) { - SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info); - if (!speedwireDiscovery->initialize()) { - qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed."; - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network.")); - return; - } - - connect(speedwireDiscovery, &SpeedwireDiscovery::discoveryFinished, this, [=](){ - qCDebug(dcSma()) << "Speed wire discovery finished."; - speedwireDiscovery->deleteLater(); - - ThingDescriptors descriptors; - foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) { - if (result.deviceType == SpeedwireInterface::DeviceTypeInverter) { - if (result.serialNumber == 0) - continue; - - ThingDescriptor descriptor(speedwireInverterThingClassId, "SMA Inverter", "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) { - descriptor.setThingId(existingThing->id()); - break; - } - } - - ParamList params; - params << Param(speedwireInverterThingHostParamTypeId, result.address.toString()); - params << Param(speedwireInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); - params << Param(speedwireInverterThingSerialNumberParamTypeId, result.serialNumber); - params << Param(speedwireInverterThingModelIdParamTypeId, result.modelId); - descriptor.setParams(params); - descriptors.append(descriptor); - } - } - - info->addThingDescriptors(descriptors); - info->finish(Thing::ThingErrorNoError); - }); - - speedwireDiscovery->startDiscovery(); - } -} - -void IntegrationPluginSma::startPairing(ThingPairingInfo *info) -{ - info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter the password of your inverter. If no password has been explicitly set, leave it empty to use the default password for SMA inverters.")); -} - -void IntegrationPluginSma::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) -{ - 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; - } - - // 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) -{ - Thing *thing = info->thing(); - qCDebug(dcSma()) << "Setup thing" << thing << thing->params(); - - if (thing->thingClassId() == sunnyWebBoxThingClassId) { - // Check if a Sunny WebBox is already added with this mac address - foreach (SunnyWebBox *sunnyWebBox, m_sunnyWebBoxes.values()) { - if (sunnyWebBox->macAddress() == thing->paramValue(sunnyWebBoxThingMacAddressParamTypeId).toString()){ - qCWarning(dcSma()) << "Thing with mac address" << thing->paramValue(sunnyWebBoxThingMacAddressParamTypeId).toString() << " already added!"; - info->finish(Thing::ThingErrorThingInUse); - return; - } - } - - if (m_sunnyWebBoxes.contains(thing)) { - qCDebug(dcSma()) << "Setup after reconfiguration, cleaning up..."; - m_sunnyWebBoxes.take(thing)->deleteLater(); - } - - SunnyWebBox *sunnyWebBox = new SunnyWebBox(hardwareManager()->networkManager(), QHostAddress(thing->paramValue(sunnyWebBoxThingHostParamTypeId).toString()), this); - sunnyWebBox->setMacAddress(thing->paramValue(sunnyWebBoxThingMacAddressParamTypeId).toString()); - - connect(info, &ThingSetupInfo::aborted, sunnyWebBox, &SunnyWebBox::deleteLater); - connect(sunnyWebBox, &SunnyWebBox::destroyed, this, [thing, this] { m_sunnyWebBoxes.remove(thing);}); - - QString requestId = sunnyWebBox->getPlantOverview(); - connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, info, [=] (const QString &messageId, SunnyWebBox::Overview overview) { - qCDebug(dcSma()) << "Received plant overview" << messageId << "Finish setup"; - Q_UNUSED(overview) - - info->finish(Thing::ThingErrorNoError); - connect(sunnyWebBox, &SunnyWebBox::connectedChanged, this, &IntegrationPluginSma::onConnectedChanged); - connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived); - m_sunnyWebBoxes.insert(info->thing(), sunnyWebBox); - }); - - } else if (thing->thingClassId() == speedwireMeterThingClassId) { - - QHostAddress address = QHostAddress(thing->paramValue(speedwireMeterThingHostParamTypeId).toString()); - quint32 serialNumber = static_cast(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt()); - quint16 modelId = static_cast(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt()); - - if (m_speedwireMeters.contains(thing)) { - m_speedwireMeters.take(thing)->deleteLater(); - } - - SpeedwireMeter *meter = new SpeedwireMeter(address, modelId, serialNumber, this); - if (!meter->initialize()) { - qCWarning(dcSma()) << "Setup failed. Could not initialize meter interface."; - info->finish(Thing::ThingErrorHardwareFailure); - return; - } - - connect(meter, &SpeedwireMeter::reachableChanged, thing, [=](bool reachable){ - thing->setStateValue(speedwireMeterConnectedStateTypeId, reachable); - }); - - connect(meter, &SpeedwireMeter::valuesUpdated, thing, [=](){ - thing->setStateValue(speedwireMeterConnectedStateTypeId, true); - thing->setStateValue(speedwireMeterCurrentPowerStateTypeId, meter->currentPower()); - thing->setStateValue(speedwireMeterCurrentPowerPhaseAStateTypeId, meter->currentPowerPhaseA()); - thing->setStateValue(speedwireMeterCurrentPowerPhaseBStateTypeId, meter->currentPowerPhaseB()); - thing->setStateValue(speedwireMeterCurrentPowerPhaseCStateTypeId, meter->currentPowerPhaseC()); - thing->setStateValue(speedwireMeterVoltagePhaseAStateTypeId, meter->voltagePhaseA()); - thing->setStateValue(speedwireMeterVoltagePhaseBStateTypeId, meter->voltagePhaseB()); - thing->setStateValue(speedwireMeterVoltagePhaseCStateTypeId, meter->voltagePhaseC()); - thing->setStateValue(speedwireMeterTotalEnergyConsumedStateTypeId, meter->totalEnergyConsumed()); - thing->setStateValue(speedwireMeterTotalEnergyProducedStateTypeId, meter->totalEnergyProduced()); - thing->setStateValue(speedwireMeterEnergyConsumedPhaseAStateTypeId, meter->energyConsumedPhaseA()); - thing->setStateValue(speedwireMeterEnergyConsumedPhaseBStateTypeId, meter->energyConsumedPhaseB()); - thing->setStateValue(speedwireMeterEnergyConsumedPhaseCStateTypeId, meter->energyConsumedPhaseC()); - thing->setStateValue(speedwireMeterEnergyProducedPhaseAStateTypeId, meter->energyProducedPhaseA()); - thing->setStateValue(speedwireMeterEnergyProducedPhaseBStateTypeId, meter->energyProducedPhaseB()); - thing->setStateValue(speedwireMeterEnergyProducedPhaseCStateTypeId, meter->energyProducedPhaseC()); - thing->setStateValue(speedwireMeterCurrentPhaseAStateTypeId, meter->amperePhaseA()); - thing->setStateValue(speedwireMeterCurrentPhaseBStateTypeId, meter->amperePhaseB()); - thing->setStateValue(speedwireMeterCurrentPhaseCStateTypeId, meter->amperePhaseC()); - thing->setStateValue(speedwireMeterFirmwareVersionStateTypeId, meter->softwareVersion()); - }); - - m_speedwireMeters.insert(thing, meter); - info->finish(Thing::ThingErrorNoError); - - } else if (thing->thingClassId() == speedwireInverterThingClassId) { - - QHostAddress address = QHostAddress(thing->paramValue(speedwireInverterThingHostParamTypeId).toString()); - quint32 serialNumber = static_cast(thing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt()); - quint16 modelId = static_cast(thing->paramValue(speedwireInverterThingModelIdParamTypeId).toUInt()); - - if (m_speedwireInverters.contains(thing)) { - m_speedwireInverters.take(thing)->deleteLater(); - } - - SpeedwireInverter *inverter = new SpeedwireInverter(address, modelId, serialNumber, this); - if (!inverter->initialize()) { - qCWarning(dcSma()) << "Setup failed. Could not initialize inverter interface."; - info->finish(Thing::ThingErrorHardwareFailure); - return; - } - - qCDebug(dcSma()) << "Inverter: Interface initialized successfully."; - - QString password; - pluginStorage()->beginGroup(info->thing()->id().toString()); - password = pluginStorage()->value("password", "0000").toString(); - pluginStorage()->endGroup(); - - // Connection exists only as long info exists - connect(inverter, &SpeedwireInverter::loginFinished, info, [=](bool success){ - if (!success) { - qCWarning(dcSma()) << "Failed to set up inverter. Wrong password."; - - // Remove invalid password from settings - pluginStorage()->beginGroup(info->thing()->id().toString()); - pluginStorage()->remove(""); - pluginStorage()->endGroup(); - - delete inverter; - info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Failed to log in with the given password. Please try again.")); - return; - } - - qCDebug(dcSma()) << "Inverter set up successfully."; - m_speedwireInverters.insert(thing, inverter); - info->finish(Thing::ThingErrorNoError); - // Note: the data is already refreshing here - }); - - // Make sure an aborted setup will clean up the object - connect(info, &ThingSetupInfo::aborted, inverter, &SpeedwireInverter::deleteLater); - - // Runtime connections - connect(inverter, &SpeedwireInverter::reachableChanged, thing, [=](bool reachable){ - thing->setStateValue(speedwireInverterConnectedStateTypeId, reachable); - }); - - connect(inverter, &SpeedwireInverter::valuesUpdated, thing, [=](){ - thing->setStateValue(speedwireInverterTotalEnergyProducedStateTypeId, inverter->totalEnergyProduced()); - thing->setStateValue(speedwireInverterEnergyProducedTodayStateTypeId, inverter->todayEnergyProduced()); - thing->setStateValue(speedwireInverterCurrentPowerStateTypeId, -inverter->totalAcPower()); - thing->setStateValue(speedwireInverterFrequencyStateTypeId, inverter->gridFrequency()); - - thing->setStateValue(speedwireInverterVoltagePhaseAStateTypeId, inverter->voltageAcPhase1()); - thing->setStateValue(speedwireInverterVoltagePhaseBStateTypeId, inverter->voltageAcPhase2()); - thing->setStateValue(speedwireInverterVoltagePhaseCStateTypeId, inverter->voltageAcPhase3()); - - thing->setStateValue(speedwireInverterCurrentPhaseAStateTypeId, inverter->currentAcPhase1()); - thing->setStateValue(speedwireInverterCurrentPhaseBStateTypeId, inverter->currentAcPhase2()); - thing->setStateValue(speedwireInverterCurrentPhaseCStateTypeId, inverter->currentAcPhase3()); - - thing->setStateValue(speedwireInverterCurrentPowerMpp1StateTypeId, inverter->powerDcMpp1()); - thing->setStateValue(speedwireInverterCurrentPowerMpp2StateTypeId, inverter->powerDcMpp2()); - }); - - qCDebug(dcSma()) << "Inverter: Start connecting using password" << password; - inverter->startConnecting(password); - - } else { - Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); - } -} - -void IntegrationPluginSma::postSetupThing(Thing *thing) -{ - qCDebug(dcSma()) << "Post setup thing" << thing->name(); - if (thing->thingClassId() == sunnyWebBoxThingClassId) { - SunnyWebBox *sunnyWebBox = m_sunnyWebBoxes.value(thing); - if (!sunnyWebBox) - return; - - setupRefreshTimer(); - sunnyWebBox->getPlantOverview(); - thing->setStateValue(sunnyWebBoxConnectedStateTypeId, true); - } else if (thing->thingClassId() == speedwireInverterThingClassId) { - SpeedwireInverter *inverter = m_speedwireInverters.value(thing); - if (inverter) { - thing->setStateValue(speedwireInverterConnectedStateTypeId, inverter->reachable()); - } else { - thing->setStateValue(speedwireInverterConnectedStateTypeId, false); - } - - setupRefreshTimer(); - } -} - -void IntegrationPluginSma::thingRemoved(Thing *thing) -{ - if (thing->thingClassId() == sunnyWebBoxThingClassId) { - m_sunnyWebBoxes.take(thing)->deleteLater(); - } - - if (thing->thingClassId() == speedwireMeterThingClassId && m_speedwireMeters.contains(thing)) { - m_speedwireMeters.take(thing)->deleteLater(); - } - - if (thing->thingClassId() == speedwireInverterThingClassId && m_speedwireInverters.contains(thing)) { - m_speedwireInverters.take(thing)->deleteLater(); - } - - if (myThings().isEmpty()) { - qCDebug(dcSma()) << "Stopping timer"; - hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer); - m_refreshTimer = nullptr; - } -} - -void IntegrationPluginSma::onConnectedChanged(bool connected) -{ - Thing *thing = m_sunnyWebBoxes.key(static_cast(sender())); - if (!thing) - return; - - thing->setStateValue(sunnyWebBoxConnectedStateTypeId, connected); -} - -void IntegrationPluginSma::onPlantOverviewReceived(const QString &messageId, SunnyWebBox::Overview overview) -{ - Q_UNUSED(messageId) - - qCDebug(dcSma()) << "Plant overview received" << overview.status; - Thing *thing = m_sunnyWebBoxes.key(static_cast(sender())); - if (!thing) - return; - - thing->setStateValue(sunnyWebBoxCurrentPowerStateTypeId, overview.power); - thing->setStateValue(sunnyWebBoxDayEnergyProducedStateTypeId, overview.dailyYield); - thing->setStateValue(sunnyWebBoxTotalEnergyProducedStateTypeId, overview.totalYield); - thing->setStateValue(sunnyWebBoxModeStateTypeId, overview.status); - if (!overview.error.isEmpty()){ - qCDebug(dcSma()) << "Received error" << overview.error; - thing->setStateValue(sunnyWebBoxErrorStateTypeId, overview.error); - } -} - -void IntegrationPluginSma::setupRefreshTimer() -{ - // If already set up... - if (m_refreshTimer) - return; - - m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(2); - connect(m_refreshTimer, &PluginTimer::timeout, this, [=](){ - foreach (Thing *thing, myThings().filterByThingClassId(sunnyWebBoxThingClassId)) { - SunnyWebBox *sunnyWebBox = m_sunnyWebBoxes.value(thing); - sunnyWebBox->getPlantOverview(); - } - - foreach (SpeedwireInverter *inverter, m_speedwireInverters) { - // Note: refresh will not be triggered if there is already a refresh process running - inverter->refresh(); - } - }); - - m_refreshTimer->start(); -} diff --git a/sma/integrationpluginsma.h b/sma/integrationpluginsma.h deleted file mode 100644 index fc509d0b..00000000 --- a/sma/integrationpluginsma.h +++ /dev/null @@ -1,73 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 INTEGRATIONPLUGINSMA_H -#define INTEGRATIONPLUGINSMA_H - -#include -#include - -#include "sunnywebbox.h" -#include "speedwiremeter.h" -#include "speedwireinverter.h" - -class IntegrationPluginSma: public IntegrationPlugin { - Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsma.json") - Q_INTERFACES(IntegrationPlugin) - -public: - explicit IntegrationPluginSma(); - - void init() override; - void discoverThings(ThingDiscoveryInfo *info) override; - - void startPairing(ThingPairingInfo *info) override; - void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override; - - void setupThing(ThingSetupInfo *info) override; - void postSetupThing(Thing *thing) override; - void thingRemoved(Thing *thing) override; - -private slots: - void onConnectedChanged(bool connected); - void onPlantOverviewReceived(const QString &messageId, SunnyWebBox::Overview overview); - - void setupRefreshTimer(); - -private: - PluginTimer *m_refreshTimer = nullptr; - - QHash m_sunnyWebBoxes; - QHash m_speedwireMeters; - QHash m_speedwireInverters; -}; - -#endif // INTEGRATIONPLUGINSMA_H diff --git a/sma/integrationpluginsma.json b/sma/integrationpluginsma.json deleted file mode 100644 index 80cc5aad..00000000 --- a/sma/integrationpluginsma.json +++ /dev/null @@ -1,510 +0,0 @@ -{ - "id": "b8442bbf-9d3f-4aa2-9443-b3a31ae09bac", - "name": "sma", - "displayName": "SMA", - "vendors": [ - { - "id": "16d5a4a3-36d5-46c0-b7dd-df166ddf5981", - "name": "Sma", - "displayName": "SMA Solar Technology AG", - "thingClasses": [ - { - "id": "49304127-ce9b-45dd-8511-05030a4ac003", - "name": "sunnyWebBox", - "displayName": "Sunny WebBox", - "createMethods": ["discovery", "user"], - "interfaces": ["solarinverter"], - "paramTypes": [ - { - "id": "864d4162-e3ce-48b8-b8ac-c1b971b52d42", - "name": "host", - "displayName": "Host address", - "type": "QString", - "inputType": "IPv4Address", - "defaultValue": "192.168.0.168" - }, - { - "id": "03f32361-4e13-4597-a346-af8d16a986b3", - "name": "macAddress", - "displayName": "MAC address", - "type": "QString", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": "00:00:00:00:00:00" - } - ], - "stateTypes": [ - { - "id": "c05e6a1a-252c-4f2b-8b31-09cf113d01c1", - "name": "connected", - "displayName": "Connected", - "displayNameEvent": "Connected changed", - "type": "bool", - "defaultValue": false - }, - { - "id": "ff4ff872-2f0f-4ca4-9fe2-220eeaf16cc2", - "name": "currentPower", - "displayName": "Current power", - "displayNameEvent": "Current power changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0, - "cached": false - }, - { - "id": "16f34c5c-8dbb-4dcc-9faa-4b782d57226c", - "name": "dayEnergyProduced", - "displayName": "Day energy produced", - "displayNameEvent": "Day energy produced changed", - "type": "double", - "unit": "KiloWattHour", - "defaultValue": 0 - }, - { - "id": "0bb4e227-7e38-49ca-9b32-ce4621c9305b", - "name": "totalEnergyProduced", - "displayName": "Total energy produced", - "displayNameEvent": "Total energy produced changed", - "type": "double", - "unit": "KiloWattHour", - "defaultValue": 0 - }, - { - "id": "1974550b-6059-4b0e-83f4-70177e20dac3", - "name": "mode", - "displayName": "Mode", - "displayNameEvent": "Mode changed", - "type": "QString", - "defaultValue": "MPP" - }, - { - "id": "4e64f9ca-7e5a-4897-8035-6f2ae88fde89", - "name": "error", - "displayName": "Error", - "displayNameEvent": "Error changed", - "type": "QString", - "defaultValue": "None" - } - ] - }, - { - "id": "0c5097af-e136-4430-9fb4-0ccbb30c3e1c", - "name": "speedwireMeter", - "displayName": "SMA Energy Meter", - "createMethods": ["discovery", "user"], - "interfaces": [ "energymeter" ], - "paramTypes": [ - { - "id": "d90193e6-a996-4e49-bf6d-564d596d7e74", - "name": "host", - "displayName": "Host address", - "type": "QString", - "inputType": "IPv4Address", - "defaultValue": "192.168.0.168" - }, - { - "id": "2780eab7-1f1c-4cc7-a789-a8790329ca9e", - "name": "macAddress", - "displayName": "MAC address", - "type": "QString", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": "" - }, - { - "id": "7c81a0c5-9bc6-43bb-a01a-4de5fe656bba", - "name": "serialNumber", - "displayName": "Serial number", - "type": "QString", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": "" - }, - { - "id": "abdc114d-1fac-4454-8b82-871ed5cdf28c", - "name": "modelId", - "displayName": "Model ID", - "type": "uint", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": "" - } - ], - "stateTypes": [ - { - "id": "35733d27-4fe0-439a-be71-7c1597481659", - "name": "connected", - "displayName": "Connected", - "displayNameEvent": "Connected changed", - "type": "bool", - "defaultValue": false - }, - { - "id": "44ee2491-8376-41cd-a21d-185c736152ec", - "name": "voltagePhaseA", - "displayName": "Voltage phase A", - "displayNameEvent": "Voltage phase A changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0, - "cached": false - }, - { - "id": "56ae3555-f874-4c2d-8833-17573dce477a", - "name": "voltagePhaseB", - "displayName": "Voltage phase B", - "displayNameEvent": "Voltage phase B changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0, - "cached": false - }, - { - "id": "51cbb29b-29f0-480a-9d7d-b8f4e6a205ae", - "name": "voltagePhaseC", - "displayName": "Voltage phase C", - "displayNameEvent": "Voltage phase C changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0, - "cached": false - }, - { - "id": "45bbdbef-1832-4870-bff5-299e580fb4da", - "name": "currentPhaseA", - "displayName": "Current phase A", - "displayNameEvent": "Current phase A changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0, - "cached": false - }, - { - "id": "b3a4fdd2-b6b8-4c58-9da3-2084ad414022", - "name": "currentPhaseB", - "displayName": "Current phase B", - "displayNameEvent": "Current phase B changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0, - "cached": false - }, - { - "id": "b3655188-3854-4336-ae3c-61d3bda6fc4d", - "name": "currentPhaseC", - "displayName": "Current phase C", - "displayNameEvent": "Current phase C changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0, - "cached": false - }, - { - "id": "d4ac7f37-e30a-44e4-93cb-ad16df18b8f1", - "name": "currentPower", - "displayName": "Current power", - "displayNameEvent": "Current power changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0, - "cached": false - }, - { - "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, - "cached": false - }, - { - "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, - "cached": false - }, - { - "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, - "cached": false - }, - { - "id": "4fb0a4c1-18ed-4d02-b6d0-c07e9b96a56d", - "name": "totalEnergyConsumed", - "displayName": "Total energy consumed", - "displayNameEvent": "Total energy consumed changed", - "type": "double", - "unit": "KiloWattHour", - "defaultValue": 0.00 - }, - { - "id": "76ca68d8-6781-4d2a-8663-440aec40b4de", - "name": "totalEnergyProduced", - "displayName": "Total energy produced", - "displayNameEvent": "Total energy produced changed", - "type": "double", - "unit": "KiloWattHour", - "defaultValue": 0.00 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "id": "a685393c-8b7e-42c5-bb41-f9907c074626", - "name": "firmwareVersion", - "displayName": "Firmware version", - "displayNameEvent": "Firmware version changed", - "type": "QString", - "defaultValue": "" - } - ] - }, - { - "id": "b63a0669-f2ac-4769-abea-e14cafb2309a", - "name": "speedwireInverter", - "displayName": "SMA Inverter", - "createMethods": ["discovery", "user"], - "setupMethod": "EnterPin", - "interfaces": [ "solarinverter" ], - "paramTypes": [ - { - "id": "c8098d53-69eb-4d0b-9f07-e43c4a0ea9a9", - "name": "host", - "displayName": "Host address", - "type": "QString", - "inputType": "IPv4Address", - "defaultValue": "192.168.0.168" - }, - { - "id": "7df0ab60-0f11-4495-8e0d-508ba2b6d858", - "name": "macAddress", - "displayName": "MAC address", - "type": "QString", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": "" - }, - { - "id": "e42242b4-2811-47f9-b42b-b150ed233217", - "name": "serialNumber", - "displayName": "Serial number", - "type": "QString", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": "" - }, - { - "id": "d9892f74-5b93-4c98-8da2-72aca033273a", - "name": "modelId", - "displayName": "Model ID", - "type": "uint", - "inputType": "TextLine", - "readOnly": true, - "defaultValue": 0 - } - ], - "stateTypes": [ - { - "id": "aaff72c3-c70a-4a2f-bed1-89f38cebe442", - "name": "connected", - "displayName": "Connected", - "displayNameEvent": "Connected changed", - "type": "bool", - "defaultValue": false - }, - { - "id": "6ef4eb16-a3d6-4bc9-972d-5e7cb81173a5", - "name": "voltagePhaseA", - "displayName": "Voltage phase A", - "displayNameEvent": "Voltage phase A changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0, - "cached": false - }, - { - "id": "d9a5768b-1bf5-4933-810d-84dd7a688f71", - "name": "voltagePhaseB", - "displayName": "Voltage phase B", - "displayNameEvent": "Voltage phase B changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0, - "cached": false - }, - { - "id": "fc168dc6-eecf-40b4-b214-3e28da0dbb12", - "name": "voltagePhaseC", - "displayName": "Voltage phase C", - "displayNameEvent": "Voltage phase C changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0, - "cached": false - }, - { - "id": "2a6c59ca-853a-47d6-96fb-0c85edf32f52", - "name": "currentPhaseA", - "displayName": "Current phase A", - "displayNameEvent": "Current phase A changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0, - "cached": false - }, - { - "id": "4db96fec-737c-4c4b-bf07-5ef2fd62508a", - "name": "currentPhaseB", - "displayName": "Current phase B", - "displayNameEvent": "Current phase B changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0, - "cached": false - }, - { - "id": "0f23fb0e-a440-4ac2-9aff-896bc65feb2c", - "name": "currentPhaseC", - "displayName": "Current phase C", - "displayNameEvent": "Current phase C changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0, - "cached": false - }, - { - "id": "d7ceb482-5df8-4c0c-82bd-62ce7ba22c43", - "name": "currentPower", - "displayName": "Current power", - "displayNameEvent": "Current power changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0, - "cached": false - }, - { - "id": "b366f680-6134-488b-8362-b1b824a8daca", - "name": "currentPowerMpp1", - "displayName": "DC power MPP1", - "displayNameEvent": "DC power MPP1 changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0, - "cached": false - }, - { - "id": "87d9b654-5558-47a3-9db9-ffd7c23b4774", - "name": "currentPowerMpp2", - "displayName": "DC power MPP2", - "displayNameEvent": "DC power MPP2 changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0, - "cached": false - }, - { - "id": "51cadd66-2cf1-485a-a2a9-191d11abfbd1", - "name": "totalEnergyProduced", - "displayName": "Total energy produced", - "displayNameEvent": "Total energy produced changed", - "type": "double", - "unit": "KiloWattHour", - "defaultValue": 0.00 - }, - { - "id": "e8bc8f81-e5c5-4900-b429-93fcaa262fcb", - "name": "energyProducedToday", - "displayName": "Energy produced today", - "displayNameEvent": "Energy produced today changed", - "type": "double", - "unit": "KiloWattHour", - "defaultValue": 0.00 - }, - { - "id": "fdccf5de-7413-4480-9ca0-1151665dede8", - "name": "frequency", - "displayName": "Frequency", - "displayNameEvent": "Frequency changed", - "type": "double", - "unit": "Hertz", - "defaultValue": 0.00, - "cached": false - }, - { - "id": "6d76cc7b-9e00-4561-be7b-4e2a6b8f7b66", - "name": "firmwareVersion", - "displayName": "Firmware version", - "displayNameEvent": "Firmware version changed", - "type": "QString", - "defaultValue": "" - } - ] - } - ] - } - ] -} - diff --git a/sma/meta.json b/sma/meta.json deleted file mode 100644 index be875944..00000000 --- a/sma/meta.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "title": "SMA", - "tagline": "Connect to SMA solar equipment.", - "icon": "sma.png", - "stability": "consumer", - "offline": true, - "technologies": [ - "network" - ], - "categories": [ - "energy" - ] -} diff --git a/sma/sma.png b/sma/sma.png deleted file mode 100644 index 3273ac85..00000000 Binary files a/sma/sma.png and /dev/null differ diff --git a/sma/sma.pro b/sma/sma.pro deleted file mode 100644 index 7e031fce..00000000 --- a/sma/sma.pro +++ /dev/null @@ -1,26 +0,0 @@ -include(../plugins.pri) - -QT += network - -SOURCES += \ - integrationpluginsma.cpp \ - speedwirediscovery.cpp \ - speedwireinterface.cpp \ - speedwireinverter.cpp \ - speedwireinverterreply.cpp \ - speedwireinverterrequest.cpp \ - speedwiremeter.cpp \ - sunnywebbox.cpp \ - sunnywebboxdiscovery.cpp - -HEADERS += \ - integrationpluginsma.h \ - speedwire.h \ - speedwirediscovery.h \ - speedwireinterface.h \ - speedwireinverter.h \ - speedwireinverterreply.h \ - speedwireinverterrequest.h \ - speedwiremeter.h \ - sunnywebbox.h \ - sunnywebboxdiscovery.h diff --git a/sma/speedwire.h b/sma/speedwire.h deleted file mode 100644 index df92f5c5..00000000 --- a/sma/speedwire.h +++ /dev/null @@ -1,301 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SPEEDWIRE_H -#define SPEEDWIRE_H - -#include -#include -#include -#include -#include - -class Speedwire -{ - Q_GADGET -public: - enum Command { - CommandIdentify = 0x00000201, - CommandQueryStatus = 0x51800200, - CommandQueryAc = 0x51000200, - CommandQueryDc = 0x53800200, - CommandQueryEnergy = 0x54000200, - CommandQueryDevice = 0x58000200, - CommandQueryDeviceResponse = 58000201, - CommandLogin = 0xfffd040c, - CommandLogout = 0xfffd010e, - CommandLoginResponse = 0x0ffdf40d - }; - Q_ENUM(Command) - - enum ProtocolId { - ProtocolIdUnknown = 0x0000, - ProtocolIdMeter = 0x6069, - ProtocolIdInverter = 0x6065, - ProtocolIdDiscoveryResponse = 0x0001, - ProtocolIdDiscovery = 0xffff - }; - 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: - Header() = default; - quint32 smaSignature = 0; - quint16 headerLength = 0; - quint16 tagType = 0; - quint16 tagVersion = 0; - quint16 group = 0; - quint16 payloadLength = 0; - quint16 smaNet2Version = 0; - ProtocolId protocolId = ProtocolIdUnknown; - - inline bool isValid() const { - return smaSignature == Speedwire::smaSignature() && protocolId != ProtocolIdUnknown; - } - }; - - typedef struct InverterPacket { - quint8 wordCount = 0; - quint8 control = 0; - quint16 destinationModelId = 0; - quint32 destinationSerialNumber = 0; - quint16 destinationControl = 0; - quint16 sourceModelId = 0; - quint32 sourceSerialNumber = 0; - quint16 sourceControl = 0; - quint16 errorCode = 0; - quint16 fragmentId = 0; - quint16 packetId = 0; - quint32 command = 0; - } InverterPacket; - - Speedwire() = default; - - //static QHash deviceTypes = { {0x0000, "Unknwon"} }; - - static quint16 port() { return 9522; } - static QHostAddress multicastAddress() { return QHostAddress("239.12.255.254"); } - static quint32 smaSignature() { return 0x534d4100; } - static quint16 tag0() { return 0x02a0; } - 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 - // 0x53, 0x4d, 0x41, 0x00, 0x00, 0x04, 0x02, 0xa0, // sma signature, tag0 - // 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x20, // 0xffffffff group, 0x0000 length, 0x0020 "SMA Net ?", Version ? - // 0x00, 0x00, 0x00, 0x00 // 0x0000 protocol, 0x00 #long words, 0x00 ctrl - - // Unicast device discovery request packet, according to SMA documentation - // 0x53, 0x4d, 0x41, 0x00, 0x00, 0x04, 0x02, 0xa0, // sma signature, tag0 - // 0x00, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, 0x10, // 0x26 length, 0x0010 "SMA Net 2", Version 0 - // 0x60, 0x65, 0x09, 0xa0, 0xff, 0xff, 0xff, 0xff, // 0x6065 protocol, 0x09 #long words, 0xa0 ctrl, 0xffff dst susyID any, 0xffffffff dst serial any - // 0xff, 0xff, 0x00, 0x00, 0x7d, 0x00, 0x52, 0xbe, // 0x0000 dst cntrl, 0x007d src susy id, 0x3a28be52 src serial - // 0x28, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000 src cntrl, 0x0000 error code, 0x0000 fragment ID - // 0x01, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, // 0x8001 packet ID - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00 - - static QByteArray discoveryDatagramMulticast() { return QByteArray::fromHex("534d4100000402a0ffffffff0000002000000000"); } - static QByteArray discoveryResponseDatagram() { return QByteArray::fromHex("534d4100000402A000000001000200000001"); } - static QByteArray discoveryDatagramUnicast() { return QByteArray::fromHex("534d4100000402a00000000100260010606509a0ffffffffffff00007d0052be283a000000000000018000020000000000000000000000000000"); } - - static Speedwire::Header parseHeader(QDataStream &stream) { - stream.setByteOrder(QDataStream::BigEndian); - Header header; - quint16 protocolId; - stream >> header.smaSignature >> header.headerLength; - stream >> header.tagType >> header.tagVersion >> header.group; - stream >> header.payloadLength >> header.smaNet2Version; - stream >> protocolId; - header.protocolId = static_cast(protocolId); - return header; - }; - - static Speedwire::InverterPacket parseInverterPacket(QDataStream &stream) { - // Make sure the data stream is little endian - stream.setByteOrder(QDataStream::LittleEndian); - InverterPacket packet; - stream >> packet.wordCount; - stream >> packet.control; - stream >> packet.destinationModelId; - stream >> packet.destinationSerialNumber; - stream >> packet.destinationControl; - stream >> packet.sourceModelId; - stream >> packet.sourceSerialNumber; - stream >> packet.sourceControl; - stream >> packet.errorCode; - stream >> packet.fragmentId; - stream >> packet.packetId; - stream >> packet.command; - return packet; - }; -}; - -inline QDebug operator<<(QDebug debug, const Speedwire::Header &header) -{ - debug.nospace() << "SpeedwireHeader(" << header.protocolId << ", payload size: " << header.payloadLength << ", group: " << header.payloadLength << ")"; - return debug.maybeSpace(); -} - -inline QDebug operator<<(QDebug debug, const Speedwire::InverterPacket &packet) -{ - debug.nospace() << "InverterPacket(" << packet.sourceSerialNumber; - debug.nospace() << ", Model ID: " << packet.sourceModelId; - debug.nospace() << ", command: " << packet.command; - debug.nospace() << ", error: " << packet.errorCode; - debug.nospace() << ", fragment: " << packet.fragmentId; - debug.nospace() << ", packet ID: " << packet.fragmentId; - debug.nospace() << ")"; - return debug.maybeSpace(); -} - - -#endif // SPEEDWIRE_H diff --git a/sma/speedwirediscovery.cpp b/sma/speedwirediscovery.cpp deleted file mode 100644 index 42a1dd01..00000000 --- a/sma/speedwirediscovery.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "speedwirediscovery.h" -#include "extern-plugininfo.h" - -#include -#include - -SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) : - QObject(parent), - m_networkDeviceDiscovery(networkDeviceDiscovery) -{ - // More details: https://github.com/RalfOGit/libspeedwire/ - - - - // // Request: 534d4100000402a00000000100260010 606509a0 ffffffffffff0000 7d0052be283a0000 000000000180 00020000 00000000 00000000 00000000 => command = 0x00000200, first = 0x00000000; last = 0x00000000; trailer = 0x00000000 - // // Response 534d4100000402a000000001004e0010 606513a0 7d0052be283a00c0 7a01842a71b30000 000000000180 01020000 00000000 00000000 00030000 00ff0000 00000000 01007a01 842a71b3 00000a00 0c000000 00000000 00000000 01010000 00000000 - - // qCDebug(dcSma()) << "SpeedwireDiscovery: Create speed wire interface for multicast" << m_multicastAddress.toString() << "on port" << m_port; - // QByteArray exampleData = QByteArray::fromHex("534d4100000402a000000001024400106069010e714369aee618a41600010400000000000001080000000021391229100002040000004415000208000000001575a137d800030400000000000003080000000003debed0e800040400000017c6000408000000001008c2070000090400000000000009080000000027c77bed20000a04000000481d000a08000000001722823410000d0400000003b00015040000000000001508000000000d1e1e0e3000160400000015120016080000000006c5a2d8b800170400000000000017080000000001bd6f680000180400000007990018080000000004def712b8001d040000000000001d08000000000eeefaafd0001e040000001666001e0800000000074b38bf88001f040000000a300020040000037bcb00210400000003ad0029040000000000002908000000000a9b1afec8002a040000001a81002a08000000000803e62b88002b040000000000002b080000000001511459b8002c0400000006d5002c0800000000052c8455b80031040000000000003108000000000cf83b37100032040000001b5f0032080000000008a6e257f80033040000000c3f003404000003747900350400000003c8003d040000000000003d08000000000a53d0ba08003e040000001482003e080000000007800fd188003f040000000000003f080000000001185820c8004004000000095800400800000000064563b1900045040000000000004508000000000d26d3eae0004604000000168900460800000000082b4fc5a80047040000000a440048040000037ed1004904000000038e900000000102085200000000"); - // processDatagram(QHostAddress("127.0.0.1"), m_port, exampleData); - - m_multicastSocket = new QUdpSocket(this); - connect(m_multicastSocket, &QUdpSocket::readyRead, this, &SpeedwireDiscovery::readPendingDatagramsMulticast); - connect(m_multicastSocket, &QUdpSocket::stateChanged, this, &SpeedwireDiscovery::onSocketStateChanged); - connect(m_multicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError))); - - m_unicastSocket = new QUdpSocket(this); - connect(m_unicastSocket, &QUdpSocket::readyRead, this, &SpeedwireDiscovery::readPendingDatagramsUnicast); - connect(m_unicastSocket, &QUdpSocket::stateChanged, this, &SpeedwireDiscovery::onSocketStateChanged); - connect(m_unicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError))); - - m_discoveryTimer.setInterval(1000); - m_discoveryTimer.setSingleShot(false); - connect(&m_discoveryTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest); -} - -SpeedwireDiscovery::~SpeedwireDiscovery() -{ - if (m_initialized) { - if (!m_multicastSocket->leaveMulticastGroup(m_multicastAddress)) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString(); - } - - m_multicastSocket->close(); - } -} - -bool SpeedwireDiscovery::initialize() -{ - m_multicastSocket->close(); - m_initialized = false; - - // Setup multicast socket - if (!m_multicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind multicast socket to port" << m_port << m_multicastSocket->errorString(); - return false; - } - - if (!m_multicastSocket->joinMulticastGroup(m_multicastAddress)) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_multicastSocket->errorString(); - return false; - } - - // Setup unicast socket - if (!m_unicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind to port" << m_port << m_multicastSocket->errorString(); - return false; - } - - qCDebug(dcSma()) << "SpeedwireDiscovery: Interface initialized successfully."; - m_initialized = true; - return m_initialized; -} - -bool SpeedwireDiscovery::initialized() const -{ - return m_initialized; -} - -bool SpeedwireDiscovery::startDiscovery() -{ - // 1. Discover all network devices - // 2. Send upd multicast and unicast messages to verify if it is a SMA speedwire device - - if (m_discoveryRunning) - return true; - - if (!m_initialized) { - qCDebug(dcSma()) << "SpeedwireDiscovery: Failed to start discovery because the socket has not been initialized successfully."; - return false; - } - - // CLean up - m_results.clear(); - m_networkDeviceInfos.clear(); - - qCDebug(dcSma()) << "SpeedwireDiscovery: Start discovering network..."; - NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover(); - connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater); - connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ - qCDebug(dcSma()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; - m_networkDeviceInfos = discoveryReply->networkDeviceInfos(); - - foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { - // 2. Send unicast to all results and start requesting on multicast address - sendUnicastDiscoveryRequest(networkDeviceInfo.address()); - } - - startMulticastDiscovery(); - }); - - return true; -} - -bool SpeedwireDiscovery::discoveryRunning() const -{ - return m_discoveryRunning; -} - -QList SpeedwireDiscovery::discoveryResult() const -{ - return m_results.values(); -} - -void SpeedwireDiscovery::startMulticastDiscovery() -{ - // Start sending multicast messages - sendDiscoveryRequest(); - - m_discoveryRunning = true; - QTimer::singleShot(5000, this, &SpeedwireDiscovery::onDiscoveryProcessFinished); - - m_discoveryTimer.start(); -} - -void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress) -{ - if (m_unicastSocket->writeDatagram(Speedwire::discoveryDatagramUnicast(), targetHostAddress, m_port) < 0) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send unicast discovery datagram to address" << targetHostAddress.toString(); - return; - } - - qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to unicast address" << targetHostAddress.toString(); -} - -void SpeedwireDiscovery::readPendingDatagramsMulticast() -{ - QUdpSocket *socket = qobject_cast(sender()); - - QByteArray datagram; - QHostAddress senderAddress; - quint16 senderPort; - - while (socket->hasPendingDatagrams()) { - datagram.resize(socket->pendingDatagramSize()); - socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); - qCDebug(dcSma()) << "SpeedwireDiscovery: Received multicast data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort); - //qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex(); - processDatagram(senderAddress, senderPort, datagram); - } -} - -void SpeedwireDiscovery::readPendingDatagramsUnicast() -{ - QUdpSocket *socket = qobject_cast(sender()); - - QByteArray datagram; - QHostAddress senderAddress; - quint16 senderPort; - - while (socket->hasPendingDatagrams()) { - datagram.resize(socket->pendingDatagramSize()); - socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); - qCDebug(dcSma()) << "SpeedwireDiscovery: Received unicast data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort); - //qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex(); - processDatagram(senderAddress, senderPort, datagram); - } -} - - -void SpeedwireDiscovery::onSocketError(QAbstractSocket::SocketError error) -{ - qCDebug(dcSma()) << "SpeedwireDiscovery: Socket error" << error; -} - -void SpeedwireDiscovery::onSocketStateChanged(QAbstractSocket::SocketState socketState) -{ - qCDebug(dcSma()) << "SpeedwireDiscovery: Socket state changed" << socketState; -} - -void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram) -{ - // Check min size of SMA datagrams - if (datagram.size() < 18) { - qCDebug(dcSma()) << "SpeedwireDiscovery: Received datagram is to short to be a SMA speedwire message. Ignoring data..."; - return; - } - - // Ignore discovery requests - if (datagram == Speedwire::discoveryDatagramMulticast() || datagram == Speedwire::discoveryDatagramUnicast()) - return; - - QDataStream stream(datagram); - Speedwire::Header header = Speedwire::parseHeader(stream); - if (!header.isValid()) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Datagram header is not valid. Ignoring data..."; - return; - } - - qCDebug(dcSma()) << "SpeedwireDiscovery:" << header; - - if (header.protocolId == Speedwire::ProtocolIdDiscoveryResponse) { - qCDebug(dcSma()) << "SpeedwireDiscovery: Received discovery response from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort); - - // "534d4100 0004 02a0 0000 0001 0002 0000 0001 0004 0010 0001 0003 0004 0020 0000 0001 0004 0030 c0a8 b219 0004 0040 0000 0000 0002 0070 ef0c 00000000" - // "534d4100 0004 02a0 0000 0001 0002 0000 0001 0004 0010 0001 0001 0004 0020 0000 0001 0004 0030 c0a8 b216 0004 0040 0000 0001 00000000" - - if (!datagram.startsWith(Speedwire::discoveryResponseDatagram())) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Received discovery reply but the message start does not match the required schema. Ignoring data..."; - return; - } - - if (!m_results.contains(senderAddress)) { - qCDebug(dcSma()) << "SpeedwireDiscovery: --> Found SMA device on" << senderAddress.toString(); - if (!m_networkDeviceInfos.hasHostAddress(senderAddress)) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Found SMA using UDP discovery but the host is not in the network discovery result list. Not adding to results" << senderAddress.toString(); - return; - } - - SpeedwireDiscoveryResult result; - result.address = senderAddress; - if (m_networkDeviceInfos.hasHostAddress(senderAddress)) { - result.networkDeviceInfo = m_networkDeviceInfos.get(senderAddress); - } - result.deviceType = SpeedwireInterface::DeviceTypeUnknown; - m_results.insert(senderAddress, result); - } else { - if (m_networkDeviceInfos.hasHostAddress(senderAddress)) { - m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress); - } - } - return; - } - - // We received SMA data, let's parse depending on the protocol id - - if (header.protocolId == Speedwire::ProtocolIdMeter) { - // Example: 010e 714369ae - quint16 modelId; - quint32 serialNumber; - stream >> modelId >> serialNumber; - qCDebug(dcSma()) << "SpeedwireDiscovery: Meter identifier: Model ID:" << modelId << "Serial number:" << serialNumber; - - if (!m_results.contains(senderAddress)) { - SpeedwireDiscoveryResult result; - result.address = senderAddress; - result.deviceType = SpeedwireInterface::DeviceTypeMeter; - m_results.insert(senderAddress, result); - } - - if (m_networkDeviceInfos.hasHostAddress(senderAddress)) { - m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress); - } - - m_results[senderAddress].modelId = modelId; - m_results[senderAddress].serialNumber = serialNumber; - } else if (header.protocolId == Speedwire::ProtocolIdInverter) { - Speedwire::InverterPacket inverterPacket = Speedwire::parseInverterPacket(stream); - // Response from inverter 534d4100 0004 02a0 0000 0001 004e 0010 6065 1390 7d00 52be283a 0000 b500 c2c12e12 0000 0000 00000 1800102000000000000000000000003000000ff0000ecd5ff1f0100b500c2c12e1200000a000c00000000000000030000000101000000000000 - qCDebug(dcSma()) << "SpeedwireDiscovery:" << inverterPacket; - - if (!m_results.contains(senderAddress)) { - SpeedwireDiscoveryResult result; - result.address = senderAddress; - result.deviceType = SpeedwireInterface::DeviceTypeInverter; - m_results.insert(senderAddress, result); - } - - if (m_networkDeviceInfos.hasHostAddress(senderAddress)) { - m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress); - } - - m_results[senderAddress].modelId = inverterPacket.sourceModelId; - m_results[senderAddress].serialNumber = inverterPacket.sourceSerialNumber; - } else { - qCWarning(dcSma()) << "SpeedwireDiscovery: Unhandled data received" << datagram.toHex(); - return; - } -} - -void SpeedwireDiscovery::sendDiscoveryRequest() -{ - if (m_multicastSocket->writeDatagram(Speedwire::discoveryDatagramMulticast(), m_multicastAddress, m_port) < 0) { - qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send discovery datagram to multicast address" << m_multicastAddress.toString(); - return; - } - - qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << m_multicastAddress.toString(); -} - -void SpeedwireDiscovery::onDiscoveryProcessFinished() -{ - qCDebug(dcSma()) << "SpeedwireDiscovery: Discovey finished. Found" << m_results.count() << "SMA devices in the network"; - m_discoveryTimer.stop(); - m_discoveryRunning = false; - - foreach (const SpeedwireDiscoveryResult &result, m_results) { - qCDebug(dcSma()) << "SpeedwireDiscovery: ============================================"; - qCDebug(dcSma()) << "SpeedwireDiscovery: Device type:" << result.deviceType; - qCDebug(dcSma()) << "SpeedwireDiscovery: Address:" << result.address.toString(); - qCDebug(dcSma()) << "SpeedwireDiscovery: Hostname:" << result.networkDeviceInfo.hostName(); - qCDebug(dcSma()) << "SpeedwireDiscovery: MAC:" << result.networkDeviceInfo.macAddress(); - qCDebug(dcSma()) << "SpeedwireDiscovery: MAC manufacturer:" << result.networkDeviceInfo.macAddressManufacturer(); - qCDebug(dcSma()) << "SpeedwireDiscovery: Model ID:" << result.modelId; - qCDebug(dcSma()) << "SpeedwireDiscovery: Serial number:" << result.serialNumber; - } - - emit discoveryFinished(); -} diff --git a/sma/speedwirediscovery.h b/sma/speedwirediscovery.h deleted file mode 100644 index 0c181ddf..00000000 --- a/sma/speedwirediscovery.h +++ /dev/null @@ -1,100 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SPEEDWIREDISCOVERY_H -#define SPEEDWIREDISCOVERY_H - -#include -#include -#include - -#include - -#include "speedwire.h" -#include "speedwireinterface.h" - -class SpeedwireDiscovery : public QObject -{ - Q_OBJECT -public: - typedef struct SpeedwireDiscoveryResult { - QHostAddress address; - NetworkDeviceInfo networkDeviceInfo; - SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown; - quint16 modelId = 0; - quint32 serialNumber = 0; - } SpeedwireDiscoveryResult; - - explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); - ~SpeedwireDiscovery(); - - bool initialize(); - bool initialized() const; - - bool startDiscovery(); - bool discoveryRunning() const; - - QList discoveryResult() const; - -signals: - void discoveryFinished(); - -private: - NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; - QUdpSocket *m_multicastSocket = nullptr; - QUdpSocket *m_unicastSocket = nullptr; - QHostAddress m_multicastAddress = Speedwire::multicastAddress(); - quint16 m_port = Speedwire::port(); - bool m_initialized = false; - - // Discovery - QTimer m_discoveryTimer; - bool m_discoveryRunning = false; - NetworkDeviceInfos m_networkDeviceInfos; - QHash m_results; - - void startMulticastDiscovery(); - void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress); - -private slots: - void readPendingDatagramsMulticast(); - void readPendingDatagramsUnicast(); - void onSocketError(QAbstractSocket::SocketError error); - void onSocketStateChanged(QAbstractSocket::SocketState socketState); - - void processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram); - - void sendDiscoveryRequest(); - - void onDiscoveryProcessFinished(); - -}; - -#endif // SPEEDWIREDISCOVERY_H diff --git a/sma/speedwireinterface.cpp b/sma/speedwireinterface.cpp deleted file mode 100644 index a5fa1d77..00000000 --- a/sma/speedwireinterface.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "speedwireinterface.h" -#include "extern-plugininfo.h" - -SpeedwireInterface::SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent) : - QObject(parent), - m_address(address), - m_multicast(multicast) -{ - - qCDebug(dcSma()) << "SpeedwireInterface: Create interface for" << address.toString() << (multicast ? "multicast" : "unicast"); - m_socket = new QUdpSocket(this); - connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams); - connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged); - connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError))); -} - -SpeedwireInterface::~SpeedwireInterface() -{ - deinitialize(); -} - -bool SpeedwireInterface::initialize() -{ - if (!m_socket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) { - qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << m_port; - return false; - } - - if (m_multicast && !m_socket->joinMulticastGroup(m_multicastAddress)) { - qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_socket->errorString(); - return false; - } - - qCDebug(dcSma()) << "SpeedwireInterface: Interface initialized successfully."; - m_initialized = true; - return m_initialized; -} - -void SpeedwireInterface::deinitialize() -{ - if (m_initialized) { - if (m_multicast) { - if (!m_socket->leaveMulticastGroup(m_multicastAddress)) { - qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << m_multicastAddress.toString(); - } - } - - m_socket->close(); - m_initialized = false; - } -} - -bool SpeedwireInterface::initialized() const -{ - return m_initialized; -} - -quint16 SpeedwireInterface::sourceModelId() const -{ - return m_sourceModelId; -} - -quint32 SpeedwireInterface::sourceSerialNumber() const -{ - return m_sourceSerialNumber; -} - -void SpeedwireInterface::sendData(const QByteArray &data) -{ - qCDebug(dcSma()) << "SpeedwireInterface: -->" << m_address.toString() << m_port << data.toHex(); - if (m_socket->writeDatagram(data, m_address, m_port) < 0) { - qCWarning(dcSma()) << "SpeedwireInterface: failed to send data" << m_socket->errorString(); - } -} - -void SpeedwireInterface::readPendingDatagrams() -{ - QByteArray datagram; - QHostAddress senderAddress; - quint16 senderPort; - - while (m_socket->hasPendingDatagrams()) { - datagram.resize(m_socket->pendingDatagramSize()); - m_socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); - - // Process only data coming from our target address - if (senderAddress != m_address) - continue; - - qCDebug(dcSma()) << "SpeedwireInterface: Received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort); - //qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex(); - emit dataReceived(datagram); - } -} - -void SpeedwireInterface::onSocketError(QAbstractSocket::SocketError error) -{ - qCDebug(dcSma()) << "SpeedwireInterface: Socket error" << error; -} - -void SpeedwireInterface::onSocketStateChanged(QAbstractSocket::SocketState socketState) -{ - qCDebug(dcSma()) << "SpeedwireInterface: Socket state changed" << socketState; -} diff --git a/sma/speedwireinterface.h b/sma/speedwireinterface.h deleted file mode 100644 index fa242873..00000000 --- a/sma/speedwireinterface.h +++ /dev/null @@ -1,88 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SPEEDWIREINTERFACE_H -#define SPEEDWIREINTERFACE_H - -#include -#include -#include - -#include "speedwire.h" - -class SpeedwireInterface : public QObject -{ - Q_OBJECT -public: - enum DeviceType { - DeviceTypeUnknown, - DeviceTypeMeter, - DeviceTypeInverter - }; - Q_ENUM(DeviceType) - - explicit SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent = nullptr); - ~SpeedwireInterface(); - - bool initialize(); - void deinitialize(); - - bool initialized() const; - - quint16 sourceModelId() const; - quint32 sourceSerialNumber() const; - -public slots: - void sendData(const QByteArray &data); - -signals: - void dataReceived(const QByteArray &data); - -private: - QUdpSocket *m_socket = nullptr; - QHostAddress m_address; - quint16 m_port = Speedwire::port(); - QHostAddress m_multicastAddress = Speedwire::multicastAddress(); - bool m_multicast = false; - bool m_initialized = false; - - // Requester - quint16 m_sourceModelId = 0x007d; - quint32 m_sourceSerialNumber = 0x3a28be52; - -private slots: - void readPendingDatagrams(); - void onSocketError(QAbstractSocket::SocketError error); - void onSocketStateChanged(QAbstractSocket::SocketState socketState); - -}; - - -#endif // SPEEDWIREINTERFACE_H diff --git a/sma/speedwireinverter.cpp b/sma/speedwireinverter.cpp deleted file mode 100644 index 39329c97..00000000 --- a/sma/speedwireinverter.cpp +++ /dev/null @@ -1,1273 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "speedwireinverter.h" -#include "extern-plugininfo.h" - -#include - -SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) : - QObject(parent), - m_address(address), - m_modelId(modelId), - m_serialNumber(serialNumber) -{ - qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString(); - m_interface = new SpeedwireInterface(m_address, false, this); - connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData); -} - -bool SpeedwireInverter::initialize() -{ - return m_interface->initialize(); -} - -bool SpeedwireInverter::initialized() const -{ - return m_interface->initialized(); -} - -SpeedwireInverter::State SpeedwireInverter::state() const -{ - return m_state; -} - -bool SpeedwireInverter::reachable() const -{ - return m_reachable; -} - -QString SpeedwireInverter::modelName() const -{ - return m_modelName; -} - -double SpeedwireInverter::totalAcPower() const -{ - return m_totalAcPower; -} - -double SpeedwireInverter::gridFrequency() const -{ - return m_gridFrequency; -} - -double SpeedwireInverter::totalEnergyProduced() const -{ - return m_totalEnergyProduced; -} - -double SpeedwireInverter::todayEnergyProduced() const -{ - return m_todayEnergyProduced; -} - -double SpeedwireInverter::voltageAcPhase1() const -{ - return m_voltageAcPhase1; -} - -double SpeedwireInverter::voltageAcPhase2() const -{ - return m_voltageAcPhase2; -} - -double SpeedwireInverter::voltageAcPhase3() const -{ - return m_voltageAcPhase3; -} - -double SpeedwireInverter::currentAcPhase1() const -{ - return m_currentAcPhase1; -} - -double SpeedwireInverter::currentAcPhase2() const -{ - return m_currentAcPhase2; -} - -double SpeedwireInverter::currentAcPhase3() const -{ - return m_currentAcPhase3; -} - -double SpeedwireInverter::powerAcPhase1() const -{ - return m_powerAcPhase1; -} - -double SpeedwireInverter::powerAcPhase2() const -{ - return m_powerAcPhase2; -} - -double SpeedwireInverter::powerAcPhase3() const -{ - return m_powerAcPhase3; -} - -double SpeedwireInverter::powerDcMpp1() const -{ - return m_powerDcMpp1; -} - -double SpeedwireInverter::powerDcMpp2() const -{ - return m_powerDcMpp2; -} - -SpeedwireInverterReply *SpeedwireInverter::sendIdentifyRequest() -{ - // Request 534d4100000402a000000001002600106065 09 a0 ffff ffffffff 0000 7d00 52be283a 0000 0000 0000 0180 00020000 000000000000000000000000 - // Response 534d4100000402a000000001004e00106065 13 90 7d00 52be283a 0000 b500 c2c12e12 0000 0000 0000 0180 01020000 00000000000000000003000000ff0000ecd5ff1f0100b500c2c12e1200000a000c00000000000000030000000101000000000000 - - qCDebug(dcSma()) << "Inverter: Sending identify request to" << m_address.toString(); - SpeedwireInverterRequest request; - request.setPacketId(0x8001); - request.setCommand(Speedwire::CommandIdentify); - request.setRequestData(Speedwire::discoveryDatagramUnicast()); - return createReply(request); -} - -SpeedwireInverterReply *SpeedwireInverter::sendLoginRequest(const QString &password, bool loginAsUser) -{ - qCDebug(dcSma()) << "Inverter: Sending login request as" << (loginAsUser ? "user" : "installer") << "using password" << password; - - // Request: 534d4100000402a000000001003a001060650ea0 b500 c2c12e12 0001 7d00 52be283a 0001 0000 0000 0180 0c04fdff0 7000000 84030000 3408b261 00000000 b8b8b8b8888888888888888800000000 // Login request - - // Response: 534d4100000402a000000001003a001060650ed0 7d00 52be283a 0001 b500 c2c12e12 0001 0000000001800 d04fdff0 7000000 84030000 3408b261 00000000 b8b8b8b8888888888888888800000000 // Login OK - // Response: 534d4100000402a000000001003a001060650ed0 7d00 52be283a 0001 b500 c2c12e12 0001 0001000001800 d04fdff0 7000000 84030000 b709b261 00000000 b8b8b8b9888888888888888800000000 // Login FAILED, error 1 - - // Build the header - QByteArray datagram; - QDataStream stream(&datagram, QIODevice::WriteOnly); - buildDefaultHeader(stream, 58, 0xa0); - - // Reset the packet id counter, otherwise there will be no response - //m_packetId = 0; - quint16 packetId = m_packetId++ | 0x8000; - Speedwire::Command command = Speedwire::CommandLogin; - - // The payload is little endian encoded - buildPacket(stream, command, packetId); - - // User type: 7 = user, a = installer - stream << (loginAsUser ? static_cast(0x00000007) : static_cast(0x0000000a)); - // Timeout - stream << static_cast(900); // 900 ms - // Current time - stream << static_cast(QDateTime::currentMSecsSinceEpoch() / 1000.0); - // Zeros - stream << static_cast(0); - - // Encode password - QByteArray passwordData = password.toUtf8(); - QByteArray encodedPassword(12, loginAsUser ? 0x88 : 0xBB); - for (int i = 0; i < password.count(); i++) { - encodedPassword[i] = (passwordData.at(i) + (loginAsUser ? 0x88 : 0xBB) % 0xff); - } - - // Add encoded password - for (int i = 0; i < encodedPassword.count(); i++) { - stream << static_cast(encodedPassword.at(i)); - } - - // End of data - stream << static_cast(0); - - // Final datagram - SpeedwireInverterRequest request; - request.setPacketId(packetId); - request.setCommand(command); - request.setRequestData(datagram); - return createReply(request); -} - -SpeedwireInverterReply *SpeedwireInverter::sendLogoutRequest() -{ - // Request 534d4100000402a000000001 0022 0010 6065 08a0 ffffffffffff0003 7d0052be283a0003 00000000 0280 0e01fdff ffffffff 00000000 => logoff command = 0xfffd01e0 - - // 534d4100000402a000000001 0022 0010 6065 08a0 ffffffffffff0003 7d0052be283a0003 00000000 0480 0e01fdff ffffffff 00000000 - - // Request 534d4100000402a000000001 0022 0010 6065 08a0 ffffffffffff0003 7d0040be283a0003 000000000380 e001fdff ffffffff 00000000 - // Request 534d4100000402a000000001 0022 0010 6065 08a0 ffffffffffff0003 7d0042be283a0003 000000000180 e001fdff ffffffff 00000000 - // 534d4100000402a000000001 0022 0010 6065 08a0 ffffffffffff0003 7d0052be283a0003 000000000080 0e01fdff ffffffff 00000000 - - // Build the header - QByteArray datagram; - QDataStream stream(&datagram, QIODevice::WriteOnly); - buildDefaultHeader(stream, 34); - - // Reset the packet id counter, otherwise there will be no response - quint16 packetId = m_packetId++ | 0x8000; - Speedwire::Command command = Speedwire::CommandLogout; - - // The payload is little endian encoded - stream.setByteOrder(QDataStream::LittleEndian); - - // Target - // stream << static_cast(m_modelId); - // stream << static_cast(m_serialNumber); - stream << static_cast(0xffff); - stream << static_cast(0xffffffff); - stream << static_cast(0x0300); - - // Source - stream << m_interface->sourceModelId(); - stream << m_interface->sourceSerialNumber(); - stream << static_cast(0x0300); - - stream << static_cast(0); - stream << static_cast(0); - stream << packetId; - - stream << command; - // Only first word - stream << static_cast(0xffffffff); - stream << static_cast(0); - - SpeedwireInverterRequest request; - request.setPacketId(packetId); - request.setCommand(command); - request.setRequestData(datagram); - request.setRetries(0); - return createReply(request); -} - -SpeedwireInverterReply *SpeedwireInverter::sendSoftwareVersionRequest() -{ - qCDebug(dcSma()) << "Inverter: Sending software version request to" << m_address.toString(); - - // Request 534d4100000402a00000000100260010 6065 09a0 7a01 842a71b3 0001 7d00 42be283a 0001 000000000380 00020058 00348200 ff348200 00000000 => query software version - // Response 534d4100000402a000000001004e0010 6065 13a0 7d00 42be283a 00a1 7a01 842a71b3 0001 000000000380 01020058 0a000000 0a000000 01348200 2ae5e65f 00000000 00000000 feffffff feffffff 040a1003 040a1003 00000000 00000000 00000000 code = 0x00823401 3 (BCD).10 (BCD).10 (BIN) Typ R (Enum) - - // Build the header - QByteArray datagram; - QDataStream stream(&datagram, QIODevice::WriteOnly); - buildDefaultHeader(stream, 38, 0xa0); - - // Reset the packet id counter, otherwise there will be no response - quint16 packetId = m_packetId++ | 0x8000; - Speedwire::Command command = Speedwire::CommandQueryDevice; - - // The payload is little endian encoded - buildPacket(stream, command, packetId); - - // First and last word - stream << static_cast(0x00823400); - stream << static_cast(0x008234ff); - - // End of data - stream << static_cast(0); - - // Final datagram - SpeedwireInverterRequest request; - request.setPacketId(packetId); - request.setCommand(command); - request.setRequestData(datagram); - return createReply(request); -} - -SpeedwireInverterReply *SpeedwireInverter::sendDeviceTypeRequest() -{ - qCDebug(dcSma()) << "Inverter: Sending software version request to" << m_address.toString(); - // Build the header - QByteArray datagram; - QDataStream stream(&datagram, QIODevice::WriteOnly); - buildDefaultHeader(stream, 38, 0xa0); - - // Reset the packet id counter, otherwise there will be no response - quint16 packetId = m_packetId++ | 0x8000; - Speedwire::Command command = Speedwire::CommandQueryDevice; - - // The payload is little endian encoded - buildPacket(stream, command, packetId); - - // 2 words - stream << static_cast(0x00821e00); - stream << static_cast(0x008220ff); - - // End of data - stream << static_cast(0); - - // Final datagram - SpeedwireInverterRequest request; - request.setPacketId(packetId); - request.setCommand(command); - request.setRequestData(datagram); - return createReply(request); -} - -void SpeedwireInverter::startConnecting(const QString &password) -{ - m_password = password; - refresh(); -} - -void SpeedwireInverter::refresh() -{ - // Only refresh if not already busy... - if (m_state != StateIdle && m_state != StateDisconnected) - return; - - // Run the state machine - setState(StateInitializing); -} - -void SpeedwireInverter::sendNextReply() -{ - // Pending reply - if (m_currentReply) - return; - - // No reply left - if (m_replyQueue.isEmpty()) - return; - - // Pick the next reply and send request - m_currentReply = m_replyQueue.dequeue(); - qCDebug(dcSma()) << "Inverter: --> Sending" << m_currentReply->request().command() << "packet ID:" << m_currentReply->request().packetId(); - m_interface->sendData(m_currentReply->request().requestData()); - m_currentReply->startWaiting(); -} - -SpeedwireInverterReply *SpeedwireInverter::createReply(const SpeedwireInverterRequest &request) -{ - SpeedwireInverterReply *reply = new SpeedwireInverterReply(request, this); - connect(reply, &SpeedwireInverterReply::timeout, this, &SpeedwireInverter::onReplyTimeout); - connect(reply, &SpeedwireInverterReply::finished, this, &SpeedwireInverter::onReplyFinished); - // Make sure the reply gets deleted once finished - connect(reply, &SpeedwireInverterReply::finished, reply, &SpeedwireInverterReply::deleteLater); - - // Schedule request - m_replyQueue.enqueue(reply); - sendNextReply(); - - return reply; -} - -void SpeedwireInverter::buildDefaultHeader(QDataStream &stream, quint16 payloadSize, quint8 control) -{ - // Header (big endian) - // 534d4100000402a00000000100260010606509a0 - - // 534d4100 : SMA\0 signature - // 0004 : header length - // 02a0 : Tag0 type - // 0000 : Tag version - // 0001 : Group - // 0026 : payload length - // 0010 : SMA Net 2 version - // 6065 : inverter protocol id - // 09 : length of long words = (payload length - 2) / 4 - // a0 : control ? - - stream.setByteOrder(QDataStream::BigEndian); - stream << Speedwire::smaSignature(); - stream << static_cast(4); // Header length - stream << Speedwire::tag0(); - stream << Speedwire::tagVersion(); - stream << static_cast(1); // Group 1 = default - stream << payloadSize; - stream << Speedwire::smaNet2Version(); - stream << static_cast(Speedwire::ProtocolIdInverter); - stream << static_cast((payloadSize - 2) / 4); // wordCount - stream << control; -} - -void SpeedwireInverter::buildPacket(QDataStream &stream, quint32 command, quint16 packetId) -{ - // ========= packet header (little endian) - // == 7a01842a71b30001 - - // 7a01 : destination model id - // 842a71b3 : destination serial number - // 0001 : destination control field - - // == 7d0042be283a0001 - - // 7d00 : source model id - // 42be283a: source serial number - // 0001 : source control field - - // == 0000 0000 0380 00020058 - - // 0000 : error code - // 0000 : fragment id - // 0380 : packet id - // 00020058 : command id = CommandQueryDevice - - stream.setByteOrder(QDataStream::LittleEndian); - // Destination - stream << m_modelId << m_serialNumber; - // Destination Ctrl - stream << static_cast(0x0100); - // Source - stream << m_interface->sourceModelId() << m_interface->sourceSerialNumber(); - // Destination Ctrl - stream << static_cast(0x0100); - - // Packet information - quint16 errorCode = 0; - quint16 fragmentId = 0; - stream << errorCode << fragmentId << packetId; - - // Command - stream << static_cast(command); -} - -SpeedwireInverterReply *SpeedwireInverter::sendQueryRequest(Speedwire::Command command, quint32 firstWord, quint32 secondWord) -{ - qCDebug(dcSma()) << "Inverter: Sending query request to" << m_address.toString(); - - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000380 00020058 00348200 ff348200 00000000 => query software version - // Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000380 01020058 0a000000 0a000000 01348200 2ae5e65f 00000000 00000000 feffffff feffffff 040a1003 040a1003 00000000 00000000 00000000 code = 0x00823401 3 (BCD).10 (BCD).10 (BIN) Typ R (Enum) - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00020058 001e8200 ff208200 00000000 => query device type - // Response 534d4100000402a000000001009e0010 606527a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01020058 01000000 03000000 011e8210 6f89e95f 534e3a20 33303130 35333831 31360000 00000000 00000000 00000000 00000000 - // 011f8208 6f89e95f 411f0001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 => 1f41 solar inverter - // 01208208 6f89e95f 96240000 80240000 81240001 82240000 feffff00 00000000 00000000 00000000 00000000 - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00028053 001e2500 ff1e2500 00000000 => query spot dc power - // Response 534d4100000402a000000001005e0010 606517a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01028053 00000000 01000000 011e2540 61a7e95f 57000000 57000000 57000000 57000000 01000000 - // 021e2540 61a7e95f 5e000000 5e000000 5e000000 5e000000 01000000 00000000 - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000580 00028053 001f4500 ff214500 00000000 => query spot dc voltage/current - // Response 534d4100000402a00000000100960010 606525a0 7d0042be283a00a1 7a01842a71b30001 000000000580 01028053 02000000 05000000 011f4540 61a7e95f 05610000 05610000 05610000 05610000 01000000 - // 021f4540 61a7e95f 505b0000 505b0000 505b0000 505b0000 01000000 - // 01214540 61a7e95f 60010000 60010000 60010000 60010000 01000000 - // 02214540 61a7e95f 95010000 95010000 95010000 95010000 01000000 00000000 - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000680 00020051 00404600 ff424600 00000000 => query spot ac power - // Response 534d4100000402a000000001007a0010 60651ea0 7d0042be283a00a1 7a01842a71b30001 000000000680 01020051 09000000 0b000000 01404640 61a7e95f 38000000 38000000 38000000 38000000 01000000 - // 01414640 61a7e95f 37000000 37000000 37000000 37000000 01000000 - // 01424640 61a7e95f 39000000 39000000 39000000 39000000 01000000 00000000 - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000780 00020051 00484600 ff554600 00000000 => query spot ac voltage/current - // Response 534d4100000402a000000001013e0010 60654fa0 7d0042be283a00a1 7a01842a71b30001 000000000780 01020051 0c000000 15000000 01484600 61a7e95f 5a590000 5a590000 5a590000 5a590000 01000000 - // 01494600 61a7e95f cf590000 cf590000 cf590000 cf590000 01000000 - // 014a4600 61a7e95f 7a590000 7a590000 7a590000 7a590000 01000000 - // 014b4600 61a7e95f f19a0000 f19a0000 f19a0000 f19a0000 01000000 - // 014c4600 61a7e95f 3c9b0000 3c9b0000 3c9b0000 3c9b0000 01000000 - // 014d4600 61a7e95f 189b0000 189b0000 189b0000 189b0000 01000000 - // 014e4600 51a7e95f 1d000000 1d000000 1d000000 1d000000 01000000 - // 01534640 61a7e95f 24010000 24010000 24010000 24010000 01000000 - // 01544640 61a7e95f 1e010000 1e010000 1e010000 1e010000 01000000 - // 01554640 61a7e95f 23010000 23010000 23010000 23010000 01000000 00000000 - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000980 00028051 00482100 ff482100 00000000 => query device status - // Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000980 01028051 00000000 00000000 01482108 59c5e95f 33010001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000 - // Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000a80 00028051 00644100 ff644100 00000000 => query grid relay status - // Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000a80 01028051 07000000 07000000 01644108 59c5e95f 33000001 37010000 fdffff00 feffff00 00000000 00000000 00000000 00000000 00000000 - - - // Build the header - QByteArray datagram; - QDataStream stream(&datagram, QIODevice::WriteOnly); - buildDefaultHeader(stream); - - // Reset the packet id counter, otherwise there will be no response - quint16 packetId = m_packetId++ | 0x8000; - - // The payload is little endian encoded - buildPacket(stream, command, packetId); - - // First and second word - stream << firstWord; - stream << secondWord; - - // End of data - stream << static_cast(0); - - // Final datagram - SpeedwireInverterRequest request; - request.setPacketId(packetId); - request.setCommand(command); - request.setRequestData(datagram); - return createReply(request); -} - -void SpeedwireInverter::processSoftwareVersionResponse(const QByteArray &response) -{ - // 07000000 07000000 01348200 2ff5b261 00000000 00000000 feffffff feffffff 00055302 00055302 00000000 00000000 00000000 - qCDebug(dcSma()) << "Inverter: Process software version request response" << response.toHex(); - // TODO: -// QDataStream stream(response); -// stream.setByteOrder(QDataStream::LittleEndian); - -// // First -// quint32 firstWord; -// quint32 secondWord; -// stream >> firstWord >> secondWord; -// quint8 byte1, byte2, byte3, byte4; -// stream >> byte1 >> byte2 >> byte3 >> byte4; - - // BCD - // 00 82 34 01 ?? -// QString softwareVersion = QString("%1.%2.%3.%4").arg(byte1).arg(byte2).arg(byte3).arg(byte4); -// qCDebug(dcSma()) << "Inverter: Software version" << softwareVersion; - -} - -void SpeedwireInverter::processDeviceTypeResponse(const QByteArray &response) -{ - // Request 534d4100000402a00000000100260010 606509a0 b500 c2c12e12 0001 7d00 43be283a 0001 0000 0000 0280 0002 0058 001e8200 ff208200 00000000 - // Response 534d4100000402a00000000100c60010 60653190 7d00 43be283a 00a1 b500 c2c12e12 0001 0000 0000 0280 0102 0058 00000000 03000000 - // 011e8210 85f2b661 534e3a20333035303534313436000000 feffff00 00000000 00000000 00000000 // SN: 305054146 - // 011f8208 85f2b661 411f 0001 421f 0000 feff ff00 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 // 411f -> 1f41 -> device type soar inverter - // 01208208 85f2b661 8a23 0000 8b23 0000 8c23 0001 8e23 0000 8f23 0000 e523 0000 e623 0000 e723 0000 // 238c -> 0001 -> STP 7000TL-20 - // 01208208 85f2b661 e923 0000 e823 0000 4124 0000 4224 0000 4324 0000 feff ff00 0000 0000 0000 0000 - // 00000000 - - // Response data: 00000000 03000000 -> first, last, then data - // 011e8210 3799b961 534e3a20 33303530 35343134 36000000 feffff00 00000000 00000000 00000000 // serialnumber as text - // 011f8208 3799b961 411f 0001 421f 0000 feff ff00 0000000000000000000000000000000000000000 // device class - // 01208208 3799b961 8a23 0000 8b23 0000 8c23 0001 8e23 0000 8f23 0000 e523 0000 e623 0000 e723 0000 // device model - // 01208208 3799b961 e923 0000 e823 0000 4124 0000 4224 0000 4324 0000 feff ff00 0000000000000000 // device model - // 00000000 // End of data - - - qCDebug(dcSma()) << "Inverter: Process device type response" << response.toHex(); - // TODO: - -} - -void SpeedwireInverter::processAcPowerResponse(const QByteArray &response) -{ - // Request 534d4100000402a00000000100260010606509a0 b500 c2c12e12 0001 7d00 52be283a 0001 0000 0000 0180 00020051 00404600 ff424600 00000000 - - // Response 534d4100000402a0000000010026001060650990 7d00 52be283a 00e1 b500 c2c12e12 0001 ffff 0000 0180 01020051 00404600 ff424600 00000000 // Error: login required - - // Response 534d4100000402a000000001007a001060651e90 7d00 52be283a 00a1 b500 c2c12e12 0001 0000 0000 0580 01020051 - // No sun - // 07000000 09000000 - // 01 4046 40 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 01 4146 40 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 01 4246 40 7503ba61 00000080 00000080 00000080 00000080 01000000 00000000 - - // Sun - // 07000000 09000000 - // 01 4046 40 77fbba61 23000000 23000000 23000000 23000000 01000000 - // 01 4146 40 77fbba61 23000000 23000000 23000000 23000000 01000000 - // 01 4246 40 77fbba61 23000000 23000000 23000000 23000000 01000000 - // 00000000 - - // 40464001 - qCDebug(dcSma()) << "Inverter: Process AC power query response"; // << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - - // Each line has 7 words - quint32 measurementId; - quint32 measurementType; // ? - - while (!stream.atEnd()) { - // First row - stream >> measurementId; - - // End of data, we are done - if (measurementId == 0) - return; - - // Unknown - stream >> measurementType; - - quint8 measurmentNumber = static_cast(measurementId & 0xff); - measurementId = measurementId & 0x00ffff00; - - // Read measurent lines - if (measurementId == 0x464000 && measurmentNumber == 0x01) { - quint32 powerAcPhase1; - stream >> powerAcPhase1; - m_powerAcPhase1 = readValue(powerAcPhase1, 1000.0); - qCDebug(dcSma()) << "Inverter: Power AC phase 1" << m_powerAcPhase1 << "W"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x464100 && measurmentNumber == 0x01) { - quint32 powerAcPhase2; - stream >> powerAcPhase2; - m_powerAcPhase2 = readValue(powerAcPhase2, 1000.0); - qCDebug(dcSma()) << "Inverter: Power AC phase 2" << m_powerAcPhase2 << "W"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x464200 && measurmentNumber == 0x01) { - quint32 powerAcPhase3; - stream >> powerAcPhase3; - m_powerAcPhase3 = readValue(powerAcPhase3, 1000.0); - qCDebug(dcSma()) << "Inverter: Power AC phase 3" << m_powerAcPhase3 << "W"; - readUntilEndOfMeasurement(stream); - } - } -} - - -void SpeedwireInverter::processAcVoltageCurrentResponse(const QByteArray &response) -{ - // No sun - // 0a000000 0f000000 - // 01 4846 00 7503ba61 ffffffff ffffffff ffffffff ffffffff 01000000 - // 01 4946 00 7503ba61 ffffffff ffffffff ffffffff ffffffff 01000000 - // 01 4a46 00 7503ba61 ffffffff ffffffff ffffffff ffffffff 01000000 - // 01 5046 00 7503ba61 ffffffff ffffffff ffffffff ffffffff 01000000 - // 01 5146 00 7503ba61 ffffffff ffffffff ffffffff ffffffff 01000000 - // 01 5246 00 7503ba61 ffffffff ffffffff ffffffff ffffffff 01000000 - // 00000000 - - // Sun - // 0a000000 0f000000 - // 01484600 c1f0ba61 f9580000 f9580000 f9580000 f9580000 01000000 - // 01494600 c1f0ba61 ff580000 ff580000 ff580000 ff580000 01000000 - // 014a4600 c1f0ba61 02590000 02590000 02590000 02590000 01000000 - // 01504600 c1f0ba61 00000000 00000000 00000000 00000000 01000000 - // 01514600 c1f0ba61 00000000 00000000 00000000 00000000 01000000 - // 01524600 c1f0ba61 00000000 00000000 00000000 00000000 01000000 - // 00000000 - - qCDebug(dcSma()) << "Inverter: Process AC voltage/current query response"; // << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - - // Each line has 7 words - quint32 measurementId; - quint32 measurementType; // ? - - while (!stream.atEnd()) { - // First row - stream >> measurementId; - - // End of data, we are done - if (measurementId == 0) - return; - - // Unknown - stream >> measurementType; - - quint8 measurmentNumber = static_cast(measurementId & 0xff); - measurementId = measurementId & 0x00ffff00; - - // Read measurent lines - if (measurementId == 0x464800 && measurmentNumber == 0x01) { - quint32 voltageAcPhase1; - stream >> voltageAcPhase1; - m_voltageAcPhase1 = readValue(voltageAcPhase1, 100.0); - qCDebug(dcSma()) << "Inverter: Voltage AC phase 1" << m_voltageAcPhase1 << "V"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x464900 && measurmentNumber == 0x01) { - quint32 voltageAcPhase2; - stream >> voltageAcPhase2; - m_voltageAcPhase2 = readValue(voltageAcPhase2, 100.0); - qCDebug(dcSma()) << "Inverter: Voltage AC phase 2" << m_voltageAcPhase2 << "V"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x464a00 && measurmentNumber == 0x01) { - quint32 voltageAcPhase3; - stream >> voltageAcPhase3; - m_voltageAcPhase3 = readValue(voltageAcPhase3, 100.0); - qCDebug(dcSma()) << "Inverter: Voltage AC phase 3" << m_voltageAcPhase3 << "V"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x465000 && measurmentNumber == 0x01) { - quint32 currentAcPhase1; - stream >> currentAcPhase1; - m_currentAcPhase1 = readValue(currentAcPhase1, 1000.0); - qCDebug(dcSma()) << "Inverter: Current AC phase 1" << m_currentAcPhase1 << "A"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x465100 && measurmentNumber == 0x01) { - quint32 currentAcPhase2; - stream >> currentAcPhase2; - m_currentAcPhase2 = readValue(currentAcPhase2, 1000.0); - qCDebug(dcSma()) << "Inverter: Current AC phase 2" << m_currentAcPhase2 << "A"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x465200 && measurmentNumber == 0x01) { - quint32 currentAcPhase3; - stream >> currentAcPhase3; - m_currentAcPhase3 = readValue(currentAcPhase3, 1000.0); - qCDebug(dcSma()) << "Inverter: Current AC phase 3" << m_currentAcPhase3 << "A"; - readUntilEndOfMeasurement(stream); - } - } -} - -void SpeedwireInverter::processAcTotalPowerResponse(const QByteArray &response) -{ - // 00000000 00000000 - // 013f2640 8606bb61 16010000 16010000 16010000 16010000 01000000 - // 00000000 - qCDebug(dcSma()) << "Inverter: Process AC total power query response"; // << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - - // Each line has 7 words - quint32 measurementId; - quint32 measurementType; // ? - - while (!stream.atEnd()) { - // First row - stream >> measurementId; - - // End of data, we are done - if (measurementId == 0) - return; - - // Unknown - stream >> measurementType; - - quint8 measurmentNumber = static_cast(measurementId & 0xff); - measurementId = measurementId & 0x00ffff00; - - // Read measurent lines - if (measurementId == 0x263f00 && measurmentNumber == 0x01) { - quint32 totalAcPower; - stream >> totalAcPower; - m_totalAcPower = readValue(totalAcPower); - qCDebug(dcSma()) << "Inverter: Total AC power" << m_totalAcPower << "W"; - readUntilEndOfMeasurement(stream); - } - } -} - - -void SpeedwireInverter::processDcPowerResponse(const QByteArray &response) -{ - // No sun - // 00000000 01000000 - // 011e2540 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 021e2540 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 00000000 - - // Sun - // 00000000 01000000 - // 011e2540 7b0dbb61 8a000000 8a000000 8a000000 8a000000 01000000 - // 021e2540 7b0dbb61 8f000000 8f000000 8f000000 8f000000 01000000 - // 00000000 - qCDebug(dcSma()) << "Inverter: Process DC power query response"; // << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - - // Each line has 7 words - quint32 measurementId; - quint32 measurementType; // ? - - while (!stream.atEnd()) { - // First row - stream >> measurementId; - - // End of data, we are done - if (measurementId == 0) - return; - - // Unknown - stream >> measurementType; - - quint8 measurmentNumber = static_cast(measurementId & 0xff); - measurementId = measurementId & 0x00ffff00; - - // Read measurent lines - if (measurementId == 0x251e00 && measurmentNumber == 0x01) { - quint32 powerMpp1; - stream >> powerMpp1; - m_powerDcMpp1 = readValue(powerMpp1); - qCDebug(dcSma()) << "Inverter: DC power MPP1" << m_powerDcMpp1 << "W"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x251e00 && measurmentNumber == 0x02) { - quint32 powerMpp2; - stream >> powerMpp2; - m_powerDcMpp2 = readValue(powerMpp2); - qCDebug(dcSma()) << "Inverter: DC power MPP2" << m_powerDcMpp2 << "W"; - readUntilEndOfMeasurement(stream); - } - } -} - -void SpeedwireInverter::processDcVoltageCurrentResponse(const QByteArray &response) -{ - // 02000000 05000000 - // 011f4540 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 021f4540 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 01214540 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 02214540 7503ba61 00000080 00000080 00000080 00000080 01000000 - // 00000000 - - // 02000000 05000000 - // 011f4540 160ebb61 009a0000 009a0000 009a0000 009a0000 01000000 - // 021f4540 160ebb61 02a00000 02a00000 02a00000 02a00000 01000000 - // 01214540 160ebb61 76010000 76010000 76010000 76010000 01000000 - // 02214540 160ebb61 77010000 77010000 77010000 77010000 01000000 - // 00000000 - - qCDebug(dcSma()) << "Inverter: Process DC voltage/current response"; // << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - - // Each line has 7 words - quint32 measurementId; - quint32 measurementType; // ? - - while (!stream.atEnd()) { - // First row - stream >> measurementId; - - // End of data, we are done - if (measurementId == 0) - return; - - // Unknown - stream >> measurementType; - - quint8 measurmentNumber = static_cast(measurementId & 0xff); - measurementId = measurementId & 0x00ffff00; - - // Read measurent lines - if (measurementId == 0x451f00 && measurmentNumber == 0x01) { - quint32 voltageMpp1; - stream >> voltageMpp1; - m_voltageDcMpp1 = readValue(voltageMpp1, 100.0); - qCDebug(dcSma()) << "Inverter: DC voltage MPP1" << m_voltageDcMpp1 << "V"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x451e00 && measurmentNumber == 0x02) { - quint32 voltageMpp2; - stream >> voltageMpp2; - m_voltageDcMpp2 = readValue(voltageMpp2, 100.0); - qCDebug(dcSma()) << "Inverter: DC voltage MPP2" << m_voltageDcMpp2 << "V"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x452100 && measurmentNumber == 0x01) { - quint32 currentMpp1; - stream >> currentMpp1; - m_currentDcMpp1 = readValue(currentMpp1, 1000.0); - qCDebug(dcSma()) << "Inverter: DC current MPP1" << m_currentDcMpp1 << "A"; - readUntilEndOfMeasurement(stream); - } else if (measurementId == 0x452100 && measurmentNumber == 0x02) { - quint32 currentMpp2; - stream >> currentMpp2; - m_currentDcMpp2 = readValue(currentMpp2, 1000.0); - qCDebug(dcSma()) << "Inverter: DC current MPP2" << m_currentDcMpp2 << "A"; - readUntilEndOfMeasurement(stream); - } - } -} - -void SpeedwireInverter::processEnergyProductionResponse(const QByteArray &response) -{ - // 00000000 01000000 - // 01012600 6f03ba61 4d147402 0000000 00122260 01523ba6 17c05000 00000000 - // 000000000 - - qCDebug(dcSma()) << "Inverter: Process energy production response";// << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - quint32 dataId, dataType; - stream >> dataId >> dataType; - - quint32 unknwonWord; - quint32 totalEnergy, todayEnergy; - stream >> totalEnergy >> unknwonWord >> unknwonWord >> unknwonWord >> todayEnergy; - - m_todayEnergyProduced = readValue(todayEnergy, 1000.0); - m_totalEnergyProduced = readValue(totalEnergy, 1000.0); - qCDebug(dcSma()) << "Inverter: Energy total:" << m_totalEnergyProduced << "kWh"; - qCDebug(dcSma()) << "Inverter: Energy today:" << m_todayEnergyProduced << "kWh"; -} - -void SpeedwireInverter::processGridFrequencyResponse(const QByteArray &response) -{ - // 10000000 10000000 - // 01574600 c20cbb61 89130000 89130000 89130000 89130000 010000000 - // 0000000 - qCDebug(dcSma()) << "Inverter: Process grid frequency response"; // << response.toHex(); - QDataStream stream(response); - stream.setByteOrder(QDataStream::LittleEndian); - quint32 firstWord, secondWord; - stream >> firstWord >> secondWord; - - // Each line has 7 words - quint32 measurementId; - quint32 measurementType; // ? - - while (!stream.atEnd()) { - // First row - stream >> measurementId; - - // End of data, we are done - if (measurementId == 0) - return; - - // Unknown - stream >> measurementType; - - quint8 measurmentNumber = static_cast(measurementId & 0xff); - measurementId = measurementId & 0x00ffff00; - - // Read measurent lines - if (measurementId == 0x465700 && measurmentNumber == 0x01) { - quint32 frequency; - stream >> frequency; - m_gridFrequency = readValue(frequency, 100.0); - qCDebug(dcSma()) << "Inverter: Grid frequency" << m_gridFrequency << "Hz"; - readUntilEndOfMeasurement(stream); - } - } -} - -void SpeedwireInverter::processInverterStatusResponse(const QByteArray &response) -{ - // 00000000 00000000 - // 01482108 b527bb61 23000000 2f010000 33010001 c7010000 feffff00 00000000 00000000 00000000 - // 00000000 - qCDebug(dcSma()) << "Inverter: Process inverter status response" << response.toHex(); - // TODO: -} - -void SpeedwireInverter::readUntilEndOfMeasurement(QDataStream &stream) -{ - // Read until end of line (0x01000000) - quint32 word; - while (!stream.atEnd()) { - stream >> word; - if (word == 1) { - return; - } - } -} - -double SpeedwireInverter::readValue(quint32 value, double divisor) -{ - if (value == 0x80000000 || value == 0xffffffff) - return 0; - - return value / divisor; -} - -void SpeedwireInverter::setReachable(bool reachable) -{ - if (m_reachable == reachable) - return; - - m_reachable = reachable; - emit reachableChanged(m_reachable); -} - -void SpeedwireInverter::processData(const QByteArray &data) -{ - if (data.size() < 18) { - qCDebug(dcSma()) << "Inverter: The received datagram is to short to be a SMA speedwire message. Ignoring data..."; - return; - } - - QDataStream stream(data); - Speedwire::Header header = Speedwire::parseHeader(stream); - if (!header.isValid()) { - qCWarning(dcSma()) << "Inverter: Datagram header is not valid. Ignoring data..."; - return; - } - - if (header.protocolId != Speedwire::ProtocolIdInverter) { - qCWarning(dcSma()) << "Inverter: Received datagram from different protocol" << header.protocolId << "Ignoring data..."; - return; - } - - Speedwire::InverterPacket packet = Speedwire::parseInverterPacket(stream); - if (packet.sourceModelId != m_modelId || packet.sourceSerialNumber != m_serialNumber) { - qCWarning(dcSma()) << "Inverter: Received datagram from different inverter" << packet.sourceSerialNumber << "Ignoring data..."; - return; - } - - qCDebug(dcSma()) << "Inverter: <-- Received" << static_cast(packet.command) << "Packet ID:" << packet.packetId; - //qCDebug(dcSma()) << "Inverter:" << data.toHex(); - if (m_currentReply && m_currentReply->request().packetId() == packet.packetId) { - qCDebug(dcSma()) << "Inverter: Received response for current reply" << static_cast(m_currentReply->request().command()) << "Packet ID:" << m_currentReply->request().packetId(); - m_currentReply->m_responseData = data; - m_currentReply->m_responseHeader = header; - m_currentReply->m_responsePacket = packet; - // Set the payload - while (!stream.atEnd()) { - quint8 byte; - stream >> byte; - m_currentReply->m_responsePayload.append(byte); - } - - if (packet.errorCode != 0) { - m_currentReply->finishReply(SpeedwireInverterReply::ErrorInverterError); - } else { - m_currentReply->finishReply(SpeedwireInverterReply::ErrorNoError); - } - } else { - if (m_currentReply) { - qCWarning(dcSma()) << "Inverter: Received unexpected data: waiting for" << static_cast(m_currentReply->request().command()) << "Packet ID:" << m_currentReply->request().packetId(); - } else { - qCWarning(dcSma()) << "Inverter: Received unexpected data: not waiting for any response."; - } - qCWarning(dcSma()) << "Inverter:" << header; - qCWarning(dcSma()) << "Inverter:" << packet; - qCWarning(dcSma()) << "Inverter:" << data.toHex(); - } -} - -void SpeedwireInverter::onReplyTimeout() -{ - SpeedwireInverterReply *reply = qobject_cast(sender()); - qCDebug(dcSma()) << "Inverter: Reply timeout" << reply->request().packetId() << reply->request().command(); - reply->m_retries += 1; - if (reply->m_retries <= reply->m_maxRetries) { - qCDebug(dcSma()) << "Inverter: Resend request" << reply->m_retries << "/" << reply->m_maxRetries; - m_replyQueue.prepend(reply); - m_currentReply = nullptr; - sendNextReply(); - } else { - if (reply->m_maxRetries == 0) { - qCWarning(dcSma()) << "Inverter: No response received for request. Finish reply with" << SpeedwireInverterReply::ErrorTimeout; - } else { - qCWarning(dcSma()) << "Inverter: No response received for request after" << reply->m_maxRetries << "attempts. Finish reply with" << SpeedwireInverterReply::ErrorTimeout; - } - // Finish with timeout error - reply->finishReply(SpeedwireInverterReply::ErrorTimeout); - } -} - -void SpeedwireInverter::onReplyFinished() -{ - SpeedwireInverterReply *reply = qobject_cast(sender()); - if (m_currentReply == reply) { - // Note: the reply is self deleting on finished - m_currentReply = nullptr; - sendNextReply(); - } -} - -void SpeedwireInverter::setState(State state) -{ - if (m_state == state) - return; - - qCDebug(dcSma()) << "Inverter: State changed" << state; - m_state = state; - emit stateChanged(m_state); - - switch (m_state) { - case StateIdle: - break; - case StateDisconnected: - setReachable(false); - break; - case StateInitializing: { - // Try to fetch ac power - qCDebug(dcSma()) << "Inverter: Request AC power..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00464000, 0x004642ff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - if (reply->error() == SpeedwireInverterReply::ErrorTimeout) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - - // TODO: try to send identify request and retry 3 times before giving up, - // still need to figure out why the inverter stops responding sometimes and how we can - // make it communicative again, a reconfugre always fixes this issue...somehow... - - setState(StateDisconnected); - return; - } - - // Reachable, but received an inverter error, probably not logged - if (reply->error() == SpeedwireInverterReply::ErrorInverterError) { - qCDebug(dcSma()) << "Inverter: Query data request finished with inverter error. Try to login..."; - setState(StateLogin); - return; - } - } - - // We where able to read data...emit the signal for the setup just incase - emit loginFinished(true); - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processAcPowerResponse(reply->responseData()); - - - if (m_deviceInformationFetched) { - setState(StateQueryData); - } else { - setState(StateGetInformation); - } - }); - break; - } - case StateLogin: { - SpeedwireInverterReply *loginReply = sendLoginRequest(m_password); - connect(loginReply, &SpeedwireInverterReply::finished, this, [=](){ - if (loginReply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to login to inverter:" << loginReply->error(); - emit loginFinished(false); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Login request finished successfully."; - emit loginFinished(true); - setReachable(true); - - // Logged in successfully, reinit the data fetch process - setState(StateInitializing); - }); - break; - } - case StateGetInformation: { - SpeedwireInverterReply *softwareVersionReply = sendSoftwareVersionRequest(); - connect(softwareVersionReply, &SpeedwireInverterReply::finished, this, [=](){ - if (softwareVersionReply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to get software version from inverter:" << softwareVersionReply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Software version request finished successfully."; - processSoftwareVersionResponse(softwareVersionReply->responsePayload()); - - SpeedwireInverterReply *deviceTypeReply = sendDeviceTypeRequest(); - connect(deviceTypeReply, &SpeedwireInverterReply::finished, this, [=](){ - if (deviceTypeReply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to get device information from inverter:" << deviceTypeReply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Get device information finished successfully."; - processDeviceTypeResponse(deviceTypeReply->responsePayload()); - m_deviceInformationFetched = true; - setState(StateQueryData); - }); - }); - break; - } - case StateQueryData: { - // Query inverter status - qCDebug(dcSma()) << "Inverter: Request inverter status..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryStatus, 0x00214800, 0x002148FF); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to get status from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Get inverter status request finished successfully" << reply->request().command(); - processInverterStatusResponse(reply->responsePayload()); - - // Query AC voltage / current - qCDebug(dcSma()) << "Inverter: Request AC voltage and current..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00464800, 0x004655ff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processAcVoltageCurrentResponse(reply->responsePayload()); - - // Query DC power - qCDebug(dcSma()) << "Inverter: Request DC power..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryDc, 0x00251e00, 0x00251eff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processDcPowerResponse(reply->responsePayload()); - - // Query DC voltage/current - qCDebug(dcSma()) << "Inverter: Request DC voltage and current..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryDc, 0x00451f00, 0x004521ff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processDcVoltageCurrentResponse(reply->responsePayload()); - - // Query energy production - qCDebug(dcSma()) << "Inverter: Request energy production..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryEnergy, 0x00260100, 0x002622ff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processEnergyProductionResponse(reply->responsePayload()); - - // Query total AC power - qCDebug(dcSma()) << "Inverter: Request total AC power..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00263f00, 0x00263fff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processAcTotalPowerResponse(reply->responsePayload()); - - // Query grid frequency - qCDebug(dcSma()) << "Inverter: Request grid frequency..."; - SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00465700, 0x004657ff); - connect(reply, &SpeedwireInverterReply::finished, this, [=](){ - if (reply->error() != SpeedwireInverterReply::ErrorNoError) { - qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error(); - setState(StateDisconnected); - return; - } - - qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command(); - processGridFrequencyResponse(reply->responsePayload()); - - setReachable(true); - emit valuesUpdated(); - setState(StateIdle); - }); - }); - }); - }); - }); - }); - }); - break; - } - } -} diff --git a/sma/speedwireinverter.h b/sma/speedwireinverter.h deleted file mode 100644 index ce2d34e5..00000000 --- a/sma/speedwireinverter.h +++ /dev/null @@ -1,202 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SPEEDWIREINVERTER_H -#define SPEEDWIREINVERTER_H - -#include -#include - -#include "speedwire.h" -#include "speedwireinterface.h" -#include "speedwireinverterreply.h" -#include "speedwireinverterrequest.h" - -class SpeedwireInverter : public QObject -{ - Q_OBJECT -public: - enum State { - StateIdle, - StateDisconnected, - StateInitializing, - StateLogin, - StateGetInformation, - StateQueryData - }; - Q_ENUM(State) - - explicit SpeedwireInverter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr); - - bool initialize(); - bool initialized() const; - - State state() const; - - bool reachable() const; - - Speedwire::DeviceClass deviceClass() const; - QString modelName() const; - - double totalAcPower() const; - - double gridFrequency() const; - - double totalEnergyProduced() const; - double todayEnergyProduced() const; - - double voltageAcPhase1() const; - double voltageAcPhase2() const; - double voltageAcPhase3() const; - - double currentAcPhase1() const; - double currentAcPhase2() const; - double currentAcPhase3() const; - - double powerAcPhase1() const; - double powerAcPhase2() const; - double powerAcPhase3() const; - - double powerDcMpp1() const; - double powerDcMpp2() const; - - double voltageDcMpp1() const; - double voltageDcMpp2() const; - - double currentDcMpp1() const; - double currentDcMpp2() const; - - // Query methods - SpeedwireInverterReply *sendIdentifyRequest(); - SpeedwireInverterReply *sendLoginRequest(const QString &password = "0000", bool loginAsUser = true); - SpeedwireInverterReply *sendLogoutRequest(); - SpeedwireInverterReply *sendSoftwareVersionRequest(); - SpeedwireInverterReply *sendDeviceTypeRequest(); - - // Start connecting - void startConnecting(const QString &password = "0000"); - -public slots: - void refresh(); - -signals: - void reachableChanged(bool reachable); - void loginFinished(bool success); - void stateChanged(State state); - void valuesUpdated(); - -private: - SpeedwireInterface *m_interface = nullptr; - QHostAddress m_address; - QString m_password; - - bool m_initialized = false; - quint16 m_modelId = 0; - quint32 m_serialNumber = 0; - - bool m_reachable = false; - State m_state = StateDisconnected; - quint16 m_packetId = 1; - - bool m_deviceInformationFetched = false; - - SpeedwireInverterReply *m_currentReply = nullptr; - QQueue m_replyQueue; - - // Properties - Speedwire::DeviceClass m_deviceClass = Speedwire::DeviceClassUnknown; - QString m_modelName; - QString m_softwareVersion; - - double m_totalAcPower = 0; - double m_totalEnergyProduced = 0; - double m_todayEnergyProduced = 0; - - double m_gridFrequency = 0; - - double m_voltageAcPhase1 = 0; - double m_voltageAcPhase2 = 0; - double m_voltageAcPhase3 = 0; - - double m_currentAcPhase1 = 0; - double m_currentAcPhase2 = 0; - double m_currentAcPhase3 = 0; - - double m_powerAcPhase1 = 0; - double m_powerAcPhase2 = 0; - double m_powerAcPhase3 = 0; - - double m_powerDcMpp1 = 0; - double m_powerDcMpp2 = 0; - - double m_voltageDcMpp1 = 0; - double m_voltageDcMpp2 = 0; - - double m_currentDcMpp1 = 0; - double m_currentDcMpp2 = 0; - - void setState(State state); - - void sendNextReply(); - SpeedwireInverterReply *createReply(const SpeedwireInverterRequest &request); - - // Request builder function - void buildDefaultHeader(QDataStream &stream, quint16 payloadSize = 38, quint8 control = 0xa0); - void buildPacket(QDataStream &stream, quint32 command, quint16 packetId); - - // Send generic request for internal use - SpeedwireInverterReply *sendQueryRequest(Speedwire::Command command, quint32 firstWord, quint32 secondWord); - - // Response process methods - void processSoftwareVersionResponse(const QByteArray &response); - void processDeviceTypeResponse(const QByteArray &response); - void processAcPowerResponse(const QByteArray &response); - void processAcVoltageCurrentResponse(const QByteArray &response); - void processAcTotalPowerResponse(const QByteArray &response); - void processDcPowerResponse(const QByteArray &response); - void processDcVoltageCurrentResponse(const QByteArray &response); - void processEnergyProductionResponse(const QByteArray &response); - void processGridFrequencyResponse(const QByteArray &response); - void processInverterStatusResponse(const QByteArray &response); - - void readUntilEndOfMeasurement(QDataStream &stream); - double readValue(quint32 value, double divisor = 1.0); - - void setReachable(bool reachable); - -private slots: - void processData(const QByteArray &data); - - void onReplyTimeout(); - void onReplyFinished(); - -}; - -#endif // SPEEDWIREINVERTER_H diff --git a/sma/speedwireinverterreply.cpp b/sma/speedwireinverterreply.cpp deleted file mode 100644 index b7b2941a..00000000 --- a/sma/speedwireinverterreply.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "speedwireinverterreply.h" -#include "extern-plugininfo.h" - -SpeedwireInverterReply::SpeedwireInverterReply(const SpeedwireInverterRequest &request, QObject *parent) : - QObject(parent), - m_request(request) -{ - m_maxRetries = m_request.retries(); - - m_timer.setInterval(m_timeout); - m_timer.setSingleShot(true); - connect(&m_timer, &QTimer::timeout, this, &SpeedwireInverterReply::timeout); -} - -SpeedwireInverterRequest SpeedwireInverterReply::request() const -{ - return m_request; -} - -SpeedwireInverterReply::Error SpeedwireInverterReply::error() const -{ - return m_error; -} - -QByteArray SpeedwireInverterReply::responseData() const -{ - return m_responseData; -} - -Speedwire::Header SpeedwireInverterReply::responseHeader() const -{ - return m_responseHeader; -} - -Speedwire::InverterPacket SpeedwireInverterReply::responsePacket() const -{ - return m_responsePacket; -} - -QByteArray SpeedwireInverterReply::responsePayload() const -{ - return m_responsePayload; -} - -void SpeedwireInverterReply::startWaiting() -{ - m_timer.start(); -} - -void SpeedwireInverterReply::finishReply(Error error) -{ - m_timer.stop(); - m_error = error; - emit finished(); -} diff --git a/sma/speedwireinverterreply.h b/sma/speedwireinverterreply.h deleted file mode 100644 index 3f6a3a16..00000000 --- a/sma/speedwireinverterreply.h +++ /dev/null @@ -1,89 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SPEEDWIREINVERTERREPLY_H -#define SPEEDWIREINVERTERREPLY_H - -#include -#include - -#include "speedwireinverterrequest.h" - -class SpeedwireInverterReply : public QObject -{ - Q_OBJECT - - friend class SpeedwireInverter; - -public: - enum Error { - ErrorNoError, // Response on, no error - ErrorInverterError, // Inverter returned error - ErrorTimeout // Request timeouted - }; - Q_ENUM(Error) - - // Request - SpeedwireInverterRequest request() const; - - Error error() const; - - // Response - QByteArray responseData() const; - Speedwire::Header responseHeader() const; - Speedwire::InverterPacket responsePacket() const; - QByteArray responsePayload() const; - -signals: - void finished(); - void timeout(); - -private: - explicit SpeedwireInverterReply(const SpeedwireInverterRequest &request, QObject *parent = nullptr); - - QTimer m_timer; - Error m_error = ErrorNoError; - SpeedwireInverterRequest m_request; - quint8 m_retries = 0; - quint8 m_maxRetries = 3; - int m_timeout = 3000; - - QByteArray m_responseData; - Speedwire::Header m_responseHeader; - Speedwire::InverterPacket m_responsePacket; - QByteArray m_responsePayload; - - - void finishReply(Error error); - void startWaiting(); - -}; - -#endif // SPEEDWIREINVERTERREPLY_H diff --git a/sma/speedwireinverterrequest.cpp b/sma/speedwireinverterrequest.cpp deleted file mode 100644 index 6797c719..00000000 --- a/sma/speedwireinverterrequest.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "speedwireinverterrequest.h" - -SpeedwireInverterRequest::SpeedwireInverterRequest() -{ - -} - -Speedwire::Command SpeedwireInverterRequest::command() const -{ - return m_command; -} - -void SpeedwireInverterRequest::setCommand(Speedwire::Command command) -{ - m_command = command; -} - -quint16 SpeedwireInverterRequest::packetId() const -{ - return m_packetId; -} - -void SpeedwireInverterRequest::setPacketId(quint16 packetId) -{ - m_packetId = packetId; -} - -QByteArray SpeedwireInverterRequest::requestData() const -{ - return m_requestData; -} - -void SpeedwireInverterRequest::setRequestData(const QByteArray &requestData) -{ - m_requestData = requestData; -} - -quint8 SpeedwireInverterRequest::retries() const -{ - return m_retries; -} - -void SpeedwireInverterRequest::setRetries(quint8 retries) -{ - m_retries = retries; -} diff --git a/sma/speedwireinverterrequest.h b/sma/speedwireinverterrequest.h deleted file mode 100644 index be0b7216..00000000 --- a/sma/speedwireinverterrequest.h +++ /dev/null @@ -1,62 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SPEEDWIREINVERTERREQUEST_H -#define SPEEDWIREINVERTERREQUEST_H - -#include - -#include "speedwire.h" - -class SpeedwireInverterRequest -{ -public: - explicit SpeedwireInverterRequest(); - - Speedwire::Command command() const; - void setCommand(Speedwire::Command command); - - quint16 packetId() const; - void setPacketId(quint16 packetId); - - QByteArray requestData() const; - void setRequestData(const QByteArray &requestData); - - quint8 retries() const; - void setRetries(quint8 retries); - -private: - Speedwire::Command m_command; - quint16 m_packetId = 0; - QByteArray m_requestData; - quint8 m_retries = 2; // Default try 2 times before timeout -}; - -#endif // SPEEDWIREINVERTERREQUEST_H diff --git a/sma/speedwiremeter.cpp b/sma/speedwiremeter.cpp deleted file mode 100644 index 152ae871..00000000 --- a/sma/speedwiremeter.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2021, 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 "speedwiremeter.h" -#include "extern-plugininfo.h" - -SpeedwireMeter::SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) : - QObject(parent), - m_address(address), - m_modelId(modelId), - m_serialNumber(serialNumber) -{ - m_interface = new SpeedwireInterface(m_address, true, this); - connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData); - - // Reachable timestamp - m_timer.setInterval(5000); - m_timer.setSingleShot(false); - connect(&m_timer, &QTimer::timeout, this, &SpeedwireMeter::evaluateReachable); -} - -bool SpeedwireMeter::initialize() -{ - bool initSuccess = m_interface->initialize(); - if (initSuccess) - m_timer.start(); - - return initSuccess; -} - -bool SpeedwireMeter::initialized() const -{ - return m_interface->initialized(); -} - -bool SpeedwireMeter::reachable() const -{ - return m_reachable; -} - -double SpeedwireMeter::currentPower() const -{ - return m_currentPower; -} - -double SpeedwireMeter::totalEnergyProduced() const -{ - return m_totalEnergyProduced; -} - -double SpeedwireMeter::totalEnergyConsumed() const -{ - return m_totalEnergyConsumed; -} - -double SpeedwireMeter::energyConsumedPhaseA() const -{ - return m_energyConsumedPhaseA; -} - -double SpeedwireMeter::energyConsumedPhaseB() const -{ - return m_energyConsumedPhaseB; -} - -double SpeedwireMeter::energyConsumedPhaseC() const -{ - return m_energyConsumedPhaseC; -} - -double SpeedwireMeter::energyProducedPhaseA() const -{ - return m_energyProducedPhaseA; -} - -double SpeedwireMeter::energyProducedPhaseB() const -{ - return m_energyProducedPhaseB; -} - -double SpeedwireMeter::energyProducedPhaseC() const -{ - return m_energyProducedPhaseC; -} - -double SpeedwireMeter::currentPowerPhaseA() const -{ - return m_currentPowerPhaseA; -} - -double SpeedwireMeter::currentPowerPhaseB() const -{ - return m_currentPowerPhaseB; -} - -double SpeedwireMeter::currentPowerPhaseC() const -{ - return m_currentPowerPhaseC; -} - -double SpeedwireMeter::voltagePhaseA() const -{ - return m_voltagePhaseA; -} - -double SpeedwireMeter::voltagePhaseB() const -{ - return m_voltagePhaseB; -} - -double SpeedwireMeter::voltagePhaseC() const -{ - return m_voltagePhaseC; -} - -double SpeedwireMeter::amperePhaseA() const -{ - return m_amperePhaseA; -} - -double SpeedwireMeter::amperePhaseB() const -{ - return m_amperePhaseB; -} - -double SpeedwireMeter::amperePhaseC() const -{ - return m_amperePhaseC; -} - -QString SpeedwireMeter::softwareVersion() const -{ - return m_softwareVersion; -} - -void SpeedwireMeter::evaluateReachable() -{ - // Note: the meter sends every second the data on the multicast - qint64 currentTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000; - // If the meter has not sent data within the last 5 seconds it seems not to be reachable - bool reachable = false; - if (currentTimestamp - m_lastSeenTimestamp < 10) { - reachable = true; - } - - if (m_reachable != reachable) { - qCDebug(dcSma()) << "Meter: reachable changed to" << reachable; - m_reachable = reachable; - emit reachableChanged(m_reachable); - } - - // Restart the timer - if (m_reachable) { - m_timer.start(); - } else { - // Reachable will be triggered automatically once data arrives - // No need to run the timer all the time - m_timer.stop(); - } -} - -void SpeedwireMeter::processData(const QByteArray &data) -{ - qCDebug(dcSma()) << "Meter: data received" << data.toHex(); - QDataStream stream(data); - stream.setByteOrder(QDataStream::BigEndian); - - Speedwire::Header header = Speedwire::parseHeader(stream); - if (!header.isValid()) { - qCDebug(dcSma()) << "Meter: Datagram header is not valid. Ignoring data..."; - return; - } - - if (header.protocolId != Speedwire::ProtocolIdMeter) { - qCDebug(dcSma()) << "Meter: received header protocol which is not from the meter protocol. Ignoring data..."; - return; - } - - quint16 modelId; - quint32 serialNumber; - stream >> modelId >> serialNumber; - if (m_modelId != modelId && serialNumber != m_serialNumber) { - qCDebug(dcSma()) << "Meter: received meter data from an other meter. Ignoring data..."; - } - - qCDebug(dcSma()) << "Meter: Model ID:" << modelId; - qCDebug(dcSma()) << "Meter: Serial number:" << serialNumber; - - // Parse the packet data - // Timestamp e618a416 - qCDebug(dcSma()) << "Meter: ======================= Meter measurements"; - quint32 timestamp; - stream >> timestamp; - qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp; - - // Obis data - //00 01 04 00 00000000 00 01 08 00 0000002139122910 00 02 04 00 00004415 00 02 08 00 0000001575a137d8 00 03 04 00 00000000 00 03 08 00 00000003debed0e8 00040400000017c6000408000000001008c2070000090400000000000009080000000027c77bed20000a04000000481d000a08000000001722823410000d0400000003b00015040000000000001508000000000d1e1e0e3000160400000015120016080000000006c5a2d8b800170400000000000017080000000001bd6f680000180400000007990018080000000004def712b8001d040000000000001d08000000000eeefaafd0001e040000001666001e0800000000074b38bf88001f040000000a300020040000037bcb00210400000003ad0029040000000000002908000000000a9b1afec8002a040000001a81002a08000000000803e62b88002b040000000000002b080000000001511459b8002c0400000006d5002c0800000000052c8455b80031040000000000003108000000000cf83b37100032040000001b5f0032080000000008a6e257f80033040000000c3f003404000003747900350400000003c8003d040000000000003d08000000000a53d0ba08003e040000001482003e080000000007800fd188003f040000000000003f080000000001185820c8004004000000095800400800000000064563b1900045040000000000004508000000000d26d3eae0004604000000168900460800000000082b4fc5a80047040000000a440048040000037ed1004904000000038e90000000 01020852 00000000 - while (!stream.atEnd()) { - quint8 measurementChannel; - quint8 measurementIndex; - quint8 measurmentType; - quint8 measurmentTariff; - - stream >> measurementChannel >> measurementIndex >> measurmentType >> measurmentTariff; - - if (measurmentType == 4) { - qint32 measurement; - stream >> measurement; - - if (measurementIndex == 1 && measurement != 0) { - m_currentPower = measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power" << m_currentPower << "W"; - } else if (measurementIndex == 2 && measurement != 0) { - m_currentPower = -measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power" << m_currentPower << "W"; - } else if (measurementIndex == 21 && measurement != 0) { - m_currentPowerPhaseA = measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power phase A" << m_currentPowerPhaseA << "W"; - } else if (measurementIndex == 22 && measurement != 0) { - m_currentPowerPhaseA = -measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power phase A" << m_currentPowerPhaseA << "W"; - } else if (measurementIndex == 41 && measurement != 0) { - m_currentPowerPhaseB = measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power phase B" << m_currentPowerPhaseB << "W"; - } else if (measurementIndex == 42 && measurement != 0) { - m_currentPowerPhaseB = -measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power phase B" << m_currentPowerPhaseB << "W"; - } else if (measurementIndex == 61 && measurement != 0) { - m_currentPowerPhaseC = measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power phase C" << m_currentPowerPhaseC << "W"; - } else if (measurementIndex == 62 && measurement != 0) { - m_currentPowerPhaseC = -measurement / 10.0; - qCDebug(dcSma()) << "Meter: Current power phase C" << m_currentPowerPhaseC << "W"; - } else if (measurementIndex == 31) { - m_amperePhaseA = measurement / 1000.0; - qCDebug(dcSma()) << "Meter: Ampere phase A" << m_amperePhaseA << "A"; - } else if (measurementIndex == 51) { - m_amperePhaseB = measurement / 1000.0; - qCDebug(dcSma()) << "Meter: Ampere phase B" << m_amperePhaseB << "A"; - } else if (measurementIndex == 71) { - m_amperePhaseC = measurement / 1000.0; - qCDebug(dcSma()) << "Meter: Ampere phase C" << m_amperePhaseC << "A"; - } else if (measurementIndex == 32) { - m_voltagePhaseA = measurement / 1000.0; - qCDebug(dcSma()) << "Meter: Voltage phase A" << m_voltagePhaseA << "V"; - } else if (measurementIndex == 52) { - m_voltagePhaseB = measurement / 1000.0; - qCDebug(dcSma()) << "Meter: Voltage phase B" << m_voltagePhaseB << "V"; - } else if (measurementIndex == 72) { - m_voltagePhaseC = measurement / 1000.0; - qCDebug(dcSma()) << "Meter: Voltage phase C" << m_voltagePhaseC << "V"; - } else { -// qCDebug(dcSma()) << "Meter: --> Channel:" << measurementChannel << "Index:" << measurementIndex << "Type:" << measurmentType << "Rate:" << measurmentTariff; -// qCDebug(dcSma()) << "Meter: Value:" << measurement; - } - - - } else if (measurmentType == 8) { - qint64 measurement; - stream >> measurement; - - if (measurementIndex == 1 && measurement != 0) { - m_totalEnergyConsumed = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Total energy consumed" << m_totalEnergyConsumed << "kWh"; - } else if (measurementIndex == 2 && measurement != 0) { - m_totalEnergyProduced = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Total energy produced" << m_totalEnergyProduced << "kWh"; - } else if (measurementIndex == 21 && measurement != 0) { - m_energyConsumedPhaseA = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Energy consumed phase A" << m_energyConsumedPhaseA << "kWh"; - } else if (measurementIndex == 41 && measurement != 0) { - m_energyConsumedPhaseB = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Energy consumed phase B" << m_energyConsumedPhaseB << "kWh"; - } else if (measurementIndex == 61 && measurement != 0) { - m_energyConsumedPhaseC = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Energy consumed phase C" << m_energyConsumedPhaseC << "kWh"; - } else if (measurementIndex == 22 && measurement != 0) { - m_energyProducedPhaseA = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Energy produced phase A" << m_energyProducedPhaseA << "kWh"; - } else if (measurementIndex == 42 && measurement != 0) { - m_energyProducedPhaseB = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Energy produced phase B" << m_energyProducedPhaseB << "kWh"; - } else if (measurementIndex == 62 && measurement != 0) { - m_energyProducedPhaseC = measurement / 3600000.0; - qCDebug(dcSma()) << "Meter: Energy produced phase C" << m_energyProducedPhaseC << "kWh"; - } else { -// qCDebug(dcSma()) << "Meter: --> Channel:" << measurementChannel << "Index:" << measurementIndex << "Type:" << measurmentType << "Rate:" << measurmentTariff; -// qCDebug(dcSma()) << "Meter: Value:" << measurement; - } - - } 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)); - - qCDebug(dcSma()) << "Meter: Software version" << m_softwareVersion; - } else if (measurementChannel == 0 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) { - // 00 00 00 00 - //qCDebug(dcSma()) << "Meter: End of data reached."; - } - } - - // Save the current timestamp for reachable evaluation - m_lastSeenTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000; - evaluateReachable(); - - emit valuesUpdated(); -} diff --git a/sma/speedwiremeter.h b/sma/speedwiremeter.h deleted file mode 100644 index 0599cf3b..00000000 --- a/sma/speedwiremeter.h +++ /dev/null @@ -1,126 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2021, 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 SPEEDWIREMETER_H -#define SPEEDWIREMETER_H - -#include -#include -#include - -#include "speedwireinterface.h" - -class SpeedwireMeter : public QObject -{ - Q_OBJECT -public: - explicit SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr); - - bool initialize(); - bool initialized() const; - - bool reachable() const; - - double currentPower() const; - double totalEnergyProduced() const; - double totalEnergyConsumed() const; - - double energyConsumedPhaseA() const; - double energyConsumedPhaseB() const; - double energyConsumedPhaseC() const; - - double energyProducedPhaseA() const; - double energyProducedPhaseB() const; - double energyProducedPhaseC() const; - - double currentPowerPhaseA() const; - double currentPowerPhaseB() const; - double currentPowerPhaseC() const; - - double voltagePhaseA() const; - double voltagePhaseB() const; - double voltagePhaseC() const; - - double amperePhaseA() const; - double amperePhaseB() const; - double amperePhaseC() const; - - QString softwareVersion() const; - - -signals: - void reachableChanged(bool reachable); - void valuesUpdated(); - -private: - SpeedwireInterface *m_interface = nullptr; - QHostAddress m_address; - bool m_initialized = false; - quint16 m_modelId = 0; - quint32 m_serialNumber = 0; - - QTimer m_timer; - bool m_reachable = false; - qint64 m_lastSeenTimestamp = 0; - - double m_currentPower = 0; - double m_totalEnergyProduced = 0; - double m_totalEnergyConsumed = 0; - - double m_energyConsumedPhaseA = 0; - double m_energyConsumedPhaseB = 0; - double m_energyConsumedPhaseC = 0; - - double m_energyProducedPhaseA = 0; - double m_energyProducedPhaseB = 0; - double m_energyProducedPhaseC = 0; - - double m_currentPowerPhaseA = 0; - double m_currentPowerPhaseB = 0; - double m_currentPowerPhaseC = 0; - - double m_voltagePhaseA = 0; - double m_voltagePhaseB = 0; - double m_voltagePhaseC = 0; - - double m_amperePhaseA = 0; - double m_amperePhaseB = 0; - double m_amperePhaseC = 0; - - QString m_softwareVersion; - - -private slots: - void evaluateReachable(); - void processData(const QByteArray &data); - -}; - -#endif // SPEEDWIREMETER_H diff --git a/sma/sunnywebbox.cpp b/sma/sunnywebbox.cpp deleted file mode 100644 index d148ea27..00000000 --- a/sma/sunnywebbox.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, 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 "sunnywebbox.h" -#include "extern-plugininfo.h" - -#include "QJsonDocument" -#include "QJsonObject" -#include "QJsonArray" - -SunnyWebBox::SunnyWebBox(NetworkAccessManager *networkAccessManager, const QHostAddress &hostAddress, QObject *parrent) : - QObject(parrent), - m_hostAddresss(hostAddress), - m_networkManager(networkAccessManager) -{ - qCDebug(dcSma()) << "SunnyWebBox: Creating Sunny Web Box connection"; -} - -SunnyWebBox::~SunnyWebBox() -{ - qCDebug(dcSma()) << "SunnyWebBox: Deleting Sunny Web Box connection"; -} - -QString SunnyWebBox::getPlantOverview() -{ - return sendMessage(m_hostAddresss, "GetPlantOverview"); -} - -QString SunnyWebBox::getDevices() -{ - return sendMessage(m_hostAddresss, "GetDevices"); -} - -QString SunnyWebBox::getProcessDataChannels(const QString &deviceId) -{ - QJsonObject params; - params["device"] = deviceId; - return sendMessage(m_hostAddresss, "GetProcessDataChannels", params); -} - -QString SunnyWebBox::getProcessData(const QStringList &deviceKeys) -{ - QJsonObject paramsObj; - QJsonArray devicesArray; - foreach (const QString &key, deviceKeys) { - QJsonObject deviceObj; - deviceObj["key"] = key; - devicesArray.append(deviceObj); - } - paramsObj["devices"] = devicesArray; - return sendMessage(m_hostAddresss, "GetProcessData", paramsObj); -} - -QString SunnyWebBox::getParameterChannels(const QString &deviceKey) -{ - QJsonObject paramsObj; - QJsonArray devicesArray; - QJsonObject deviceObj; - deviceObj["key"] = deviceKey; - devicesArray.append(deviceObj); - paramsObj["devices"] = devicesArray; - return sendMessage(m_hostAddresss, "GetParameterChannels", paramsObj); -} - -QString SunnyWebBox::getParameters(const QStringList &deviceKeys) -{ - QJsonObject paramsObj; - QJsonArray devicesArray; - foreach (const QString &key, deviceKeys) { - QJsonObject deviceObj; - deviceObj["key"] = key; - devicesArray.append(deviceObj); - } - paramsObj["devices"] = devicesArray; - return sendMessage(m_hostAddresss, "GetParameter", paramsObj); -} - -QString SunnyWebBox::setParameters(const QString &deviceKey, const QHash &channels) -{ - QJsonObject paramsObj; - QJsonArray devicesArray; - QJsonObject deviceObj; - deviceObj["key"] = deviceKey; - QJsonArray channelsArray; - foreach (const QString &key, channels.keys()) { - QJsonObject channelObj; - channelObj["meta"] = key; - channelObj["value"] = channels.value(key).toString(); - channelsArray.append(channelObj); - } - deviceObj["channels"] = channelsArray; - devicesArray.append(deviceObj); - paramsObj["devices"] = devicesArray; - return sendMessage(m_hostAddresss, "SetParameter", paramsObj); -} - -QHostAddress SunnyWebBox::hostAddress() const -{ - return m_hostAddresss; -} - -void SunnyWebBox::setHostAddress(const QHostAddress &address) -{ - qCDebug(dcSma()) << "SunnyWebBox: Setting host address to" << address.toString(); - m_hostAddresss = address; -} - -QString SunnyWebBox::macAddress() const -{ - return m_macAddress; -} - -void SunnyWebBox::setMacAddress(const QString &macAddress) -{ - m_macAddress = macAddress; -} - -QNetworkReply *SunnyWebBox::sendRequest(const QHostAddress &address, const QString &procedure, const QJsonObject ¶ms, const QString &requestId) -{ - qCDebug(dcSma()) << "SunnyWebBox: Send message to" << address.toString() << "Procedure:" << procedure << "Params:" << params; - - QString finalRequestId = requestId; - if (finalRequestId.isEmpty()) - finalRequestId = generateRequestId(); - - QJsonDocument doc; - QJsonObject obj; - obj["format"] = "JSON"; - obj["id"] = requestId; - obj["proc"] = procedure; - obj["version"] = "1.0"; - - if (!params.isEmpty()) { - obj.insert("params", params); - } - doc.setObject(obj); - - QUrl url; - url.setHost(address.toString()); - url.setPath("/rpc"); - url.setPort(80); - url.setScheme("http"); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); - QByteArray data = doc.toJson(QJsonDocument::JsonFormat::Compact); - data.prepend("RPC="); - return m_networkManager->post(request, data); -} - -QString SunnyWebBox::generateRequestId() -{ - return QUuid::createUuid().toString().remove('{').remove('-').left(14); -} - -void SunnyWebBox::parseMessage(const QString &messageId, const QString &messageType, const QVariantMap &result) -{ - if (messageType == "GetPlantOverview") { - Overview overview; - QVariantList overviewList = result.value("overview").toList(); - qCDebug(dcSma()) << "SunnyWebBox: GetPlantOverview"; - foreach (const QVariant &value, overviewList) { - QVariantMap map = value.toMap(); - - if (map["meta"].toString() == "GriPwr") { - overview.power = map["value"].toString().toInt(); - QString unit = map["unit"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Power" << overview.power << unit; - } else if (map["meta"].toString() == "GriEgyTdy") { - overview.dailyYield = map["value"].toString().toDouble(); - QString unit = map["unit"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Daily yield" << overview.dailyYield << unit; - } else if (map["meta"].toString() == "GriEgyTot") { - overview.totalYield = map["value"].toString().toDouble(); - QString unit = map["unit"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Total yield" << overview.totalYield << unit; - } else if (map["meta"].toString() == "OpStt") { - overview.status = map["value"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Status" << overview.status; - } else if (map["meta"].toString() == "Msg") { - overview.error = map["value"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Error" << overview.error; - } - } - emit plantOverviewReceived(messageId, overview); - - } else if (messageType == "GetDevices") { - QList devices; - QVariantList deviceList = result.value("devices").toList(); - qCDebug(dcSma()) << "SunnyWebBox: GetDevices" << result.value("totalDevicesReturned").toInt(); - foreach (const QVariant &value, deviceList) { - Device device; - QVariantMap map = value.toMap(); - device.name = map["name"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Name" << device.name; - device.key = map["key"].toString(); - qCDebug(dcSma()) << "SunnyWebBox: - Key" << device.key; - QVariantList childrenList = map["children"].toList(); - foreach (const QVariant &childValue, childrenList) { - Device child; - QVariantMap childMap = childValue.toMap(); - device.name = childMap["name"].toString(); - device.key = childMap["key"].toString(); - device.childrens.append(child); - } - devices.append(device); - } - if (!devices.isEmpty()) { - emit devicesReceived(messageId, devices); - } - } else if (messageType == "GetProcessDataChannels" || - messageType == "GetProDataChannels") { - foreach (const QString &deviceKey, result.keys()) { - QStringList processDataChannels = result.value(deviceKey).toStringList(); - if (!processDataChannels.isEmpty()) - emit processDataChannelsReceived(messageId, deviceKey, processDataChannels); - } - } else if (messageType == "GetProcessData") { - QVariantList devicesList = result.value("devices").toList(); - qCDebug(dcSma()) << "SunnyWebBox: GetProcessData response received"; - foreach (const QVariant &value, devicesList) { - - QString key = value.toMap().value("key").toString(); - QVariantList channelsList = value.toMap().value("channels").toList(); - QHash channels; - foreach (const QVariant &channel, channelsList) { - channels.insert(channel.toMap().value("meta").toString(), channel.toMap().value("value")); - } - emit processDataReceived(messageId, key, channels); - } - } else if (messageType == "GetParameterChannels") { - foreach (const QString &deviceKey, result.keys()) { - QStringList parameterChannels = result.value(deviceKey).toStringList(); - if (!parameterChannels.isEmpty()) - emit parameterChannelsReceived(messageId, deviceKey, parameterChannels); - } - } else if (messageType == "GetParameter"|| messageType == "SetParameter") { - QVariantList devicesList = result.value("devices").toList(); - foreach (const QVariant &value, devicesList) { - - QString key = value.toMap().value("key").toString(); - QVariantList channelsList = value.toMap().value("channels").toList(); - QList parameters; - foreach (const QVariant &channel, channelsList) { - Parameter parameter; - parameter.meta = channel.toMap().value("meta").toString(); - parameter.name = channel.toMap().value("name").toString(); - parameter.unit = channel.toMap().value("unit").toString(); - parameter.min = channel.toMap().value("min").toDouble(); - parameter.max = channel.toMap().value("max").toDouble(); - parameter.value = channel.toMap().value("value").toDouble(); - parameters.append(parameter); - } - emit parametersReceived(messageId, key, parameters); - } - } else { - qCWarning(dcSma()) << "SunnyWebBox: Unknown message type" << messageType; - } -} - -void SunnyWebBox::setConnectionStatus(bool connected) -{ - if (m_connected != connected) { - qCDebug(dcSma()) << "SunnyWebBox: Connection status changed" << connected; - m_connected = connected; - emit connectedChanged(m_connected); - } -} - -QString SunnyWebBox::sendMessage(const QHostAddress &address, const QString &procedure) -{ - return sendMessage(address, procedure, QJsonObject()); -} - -QString SunnyWebBox::sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject ¶ms) -{ - QString requestId = generateRequestId(); - QNetworkReply *reply = sendRequest(m_hostAddresss, procedure, params, requestId); - connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, this, [this, address, requestId, reply]{ - - if (reply->error() != QNetworkReply::NoError) { - setConnectionStatus(false); - return; - } - setConnectionStatus(true); - - QByteArray data = reply->readAll(); - qCDebug(dcSma()) << "SunnyWebBox: Received reply" << data; - - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(data, &error); - if (error.error != QJsonParseError::NoError) { - qCWarning(dcSma()) << "SunnyWebBox: Could not parse JSON" << error.errorString(); - return; - } - if (!doc.isObject()) { - qCWarning(dcSma()) << "SunnyWebBox: JSON is not an Object"; - return; - } - QVariantMap map = doc.toVariant().toMap(); - if (map["version"] != "1.0") { - qCWarning(dcSma()) << "SunnyWebBox: API version not supported" << map["version"]; - return; - } - - if (map.contains("proc") && map.contains("result")) { - QString requestType = map["proc"].toString(); - QString requestId = map["id"].toString(); - QVariantMap result = map.value("result").toMap(); - parseMessage(requestId, requestType, result); - } else if (map.contains("proc") && map.contains("error")) { - } else { - qCWarning(dcSma()) << "SunnyWebBox: Missing proc or result value"; - } - }); - return requestId; -} - diff --git a/sma/sunnywebbox.h b/sma/sunnywebbox.h deleted file mode 100644 index 9981aeb6..00000000 --- a/sma/sunnywebbox.h +++ /dev/null @@ -1,120 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, 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 SUNNYWEBBOX_H -#define SUNNYWEBBOX_H - -#include "integrations/thing.h" -#include "network/networkaccessmanager.h" - -#include -#include -#include -#include - -class SunnyWebBox : public QObject -{ - Q_OBJECT - -public: - struct Overview { - int power; - double dailyYield; - int totalYield; - QString status; - QString error; - }; - - struct Device { - QString key; - QString name; - QList childrens; - }; - - struct Channel { - QString meta; - QString name; - QVariant value; - QString unit; - }; - - struct Parameter { - QString meta; - QString name; - QString unit; - double min; - double max; - double value; - }; - - explicit SunnyWebBox(NetworkAccessManager *networkAccessManager, const QHostAddress &hostAddress, QObject *parrent = 0); - ~SunnyWebBox(); - - QString getPlantOverview(); // Returns an object with the following plant data: PAC, E-TODAY, E-TOTAL, MODE, ERROR - QString getDevices(); // Returns a hierarchical list of all detected plant devices. - QString getProcessDataChannels(const QString &deviceKey); //Returns a list with the meta names of the available process data channels for a particular device type. - QString getProcessData(const QStringList &deviceKeys); //Returns process data for up to 5 devices per request. - QString getParameterChannels(const QString &deviceKey); //Returns a list with the meta names of the available parameter channels for a particular device type - QString getParameters(const QStringList &deviceKeys); //Returns the parameter values of up to 5 devices - QString setParameters(const QString &deviceKeys, const QHash &channels); //Sets parameter values - - QHostAddress hostAddress() const; - void setHostAddress(const QHostAddress &address); - - QString macAddress() const; - void setMacAddress(const QString &macAddress); - - QNetworkReply *sendRequest(const QHostAddress &address, const QString &procedure, const QJsonObject ¶ms = QJsonObject(), const QString &requestId = QString()); - - static QString generateRequestId(); - -private: - bool m_connected = false; - QHostAddress m_hostAddresss; - QString m_macAddress; - NetworkAccessManager *m_networkManager = nullptr; - - QString sendMessage(const QHostAddress &address, const QString &procedure); - QString sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject ¶ms); - void parseMessage(const QString &messageId, const QString &messageType, const QVariantMap &result); - void setConnectionStatus(bool connected); - -signals: - void connectedChanged(bool connected); - - void plantOverviewReceived(const QString &messageId, Overview overview); - void devicesReceived(const QString &messageId, QList devices); - void processDataChannelsReceived(const QString &messageId, const QString &deviceKey, QStringList processDataChanels); - void processDataReceived(const QString &messageId, const QString &deviceKey, const QHash &channels); - void parameterChannelsReceived(const QString &messageId, const QString &deviceKey, QStringList parameterChannels); - void parametersReceived(const QString &messageId, const QString &deviceKey, const QList ¶meters); -}; - -#endif // SUNNYWEBBOX_H diff --git a/sma/sunnywebboxdiscovery.cpp b/sma/sunnywebboxdiscovery.cpp deleted file mode 100644 index f9a1f76f..00000000 --- a/sma/sunnywebboxdiscovery.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 "sunnywebboxdiscovery.h" -#include "sunnywebbox.h" - -#include "extern-plugininfo.h" - -#include - -SunnyWebBoxDiscovery::SunnyWebBoxDiscovery(NetworkAccessManager *networkAccessManager, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) : - QObject(parent), - m_networkAccessManager(networkAccessManager), - m_networkDeviceDiscovery(networkDeviceDiscovery) -{ - -} - -void SunnyWebBoxDiscovery::startDiscovery() -{ - // Clean up - m_discoveryResults.clear(); - m_verifiedNetworkDeviceInfos.clear(); - - m_startDateTime = QDateTime::currentDateTime(); - - qCInfo(dcSma()) << "Discovery: SunnyWebBox: Starting network discovery..."; - m_discoveryReply = m_networkDeviceDiscovery->discover(); - - // Test any network device beeing discovered - connect(m_discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &SunnyWebBoxDiscovery::checkNetworkDevice); - - // When the network discovery has finished, we process the rest and give some time to finish the pending replies - connect(m_discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ - // The network device discovery is done - m_discoveredNetworkDeviceInfos = m_discoveryReply->networkDeviceInfos(); - m_discoveryReply->deleteLater(); - m_discoveryReply = nullptr; - - // Check if all network device infos have been verified - foreach (const NetworkDeviceInfo &networkDeviceInfo, m_discoveredNetworkDeviceInfos) { - if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) - continue; - - checkNetworkDevice(networkDeviceInfo); - } - - // If there might be some response after the grace period time, - // we don't care any more since there might just waiting for some timeouts... - // If there would be a device, if would have responded. - QTimer::singleShot(3000, this, [this](){ - qCDebug(dcSma()) << "Discovery: SunnyWebBox: Grace period timer triggered."; - finishDiscovery(); - }); - }); -} - -NetworkDeviceInfos SunnyWebBoxDiscovery::discoveryResults() const -{ - return m_discoveryResults; -} - -void SunnyWebBoxDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo) -{ - if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) - return; - - m_verifiedNetworkDeviceInfos.append(networkDeviceInfo); - - // Make a simple request and verify if it worked and the expected data gets returned. - SunnyWebBox webBox(m_networkAccessManager, networkDeviceInfo.address(), this); - QNetworkReply *reply = webBox.sendRequest(networkDeviceInfo.address(), "GetPlantOverview"); - m_pendingReplies.append(reply); - connect(reply, &QNetworkReply::finished, this, [=](){ - m_pendingReplies.removeAll(reply); - reply->deleteLater(); - - // Check HTTP reply - if (reply->error() != QNetworkReply::NoError) { - qCDebug(dcSma()) << "Discovery: SunnyWebBox: Checked" << networkDeviceInfo.address().toString() - << "and a HTTP error occurred:" << reply->errorString() << "Continue..."; - return; - } - - QByteArray data = reply->readAll(); - - // Check JSON - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - if (error.error != QJsonParseError::NoError) { - qCDebug(dcSma()) << "Discovery: SunnyWebBox: Checked" << networkDeviceInfo.address().toString() - << "and received invalid JSON data:" << error.errorString() << "Continue..."; - return; - } - - if (!jsonDoc.isObject()) { - qCDebug(dcSma()) << "Discovery: SunnyWebBox: Response JSON is not an Object" << networkDeviceInfo.address().toString() << "Continue..."; - return; - } - - QVariantMap map = jsonDoc.toVariant().toMap(); - if (map["version"] != "1.0") { - qCDebug(dcSma()) << "Discovery: SunnyWebBox: API version not supported on" << networkDeviceInfo.address().toString() << "Continue...";; - return; - } - - if (map.contains("proc") && map.contains("result")) { - // Ok, seems to be a Sunny WebBox we are talking to...add to the discovery results... - qCDebug(dcSma()) << "Discovery: SunnyWebBox: --> Found Sunny WebBox on" << networkDeviceInfo; - m_discoveryResults.append(networkDeviceInfo); - } else { - qCDebug(dcSma()) << "Discovery: SunnyWebBox: Missing proc or result value in response from" << networkDeviceInfo.address().toString() << "Continue..."; - return; - } - }); -} - -void SunnyWebBoxDiscovery::cleanupPendingReplies() -{ - foreach (QNetworkReply *reply, m_pendingReplies) { - reply->abort(); - } -} - -void SunnyWebBoxDiscovery::finishDiscovery() -{ - qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch(); - qCInfo(dcSma()) << "Discovery: SunnyWebBox: Finished the discovery process. Found" << m_discoveryResults.count() - << "Sunny WebBoxes in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); - - cleanupPendingReplies(); - emit discoveryFinished(); -} diff --git a/sma/sunnywebboxdiscovery.h b/sma/sunnywebboxdiscovery.h deleted file mode 100644 index 066e4cd5..00000000 --- a/sma/sunnywebboxdiscovery.h +++ /dev/null @@ -1,71 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 SUNNYWEBBOXDISCOVERY_H -#define SUNNYWEBBOXDISCOVERY_H - -#include - -#include -#include - -class SunnyWebBoxDiscovery : public QObject -{ - Q_OBJECT -public: - explicit SunnyWebBoxDiscovery(NetworkAccessManager *networkAccessManager, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); - - void startDiscovery(); - - NetworkDeviceInfos discoveryResults() const; - -signals: - void discoveryFinished(); - -private slots: - void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo); - void cleanupPendingReplies(); - void finishDiscovery(); - -private: - NetworkAccessManager *m_networkAccessManager = nullptr; - NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; - NetworkDeviceDiscoveryReply *m_discoveryReply = nullptr; - - NetworkDeviceInfos m_discoveryResults; - NetworkDeviceInfos m_discoveredNetworkDeviceInfos; - NetworkDeviceInfos m_verifiedNetworkDeviceInfos; - - QDateTime m_startDateTime; - QList m_pendingReplies; - -}; - -#endif // SUNNYWEBBOXDISCOVERY_H diff --git a/sma/translations/b8442bbf-9d3f-4aa2-9443-b3a31ae09bac-en_US.ts b/sma/translations/b8442bbf-9d3f-4aa2-9443-b3a31ae09bac-en_US.ts deleted file mode 100644 index 861aab46..00000000 --- a/sma/translations/b8442bbf-9d3f-4aa2-9443-b3a31ae09bac-en_US.ts +++ /dev/null @@ -1,309 +0,0 @@ - - - - - IntegrationPluginSma - - - Unable to discover devices in your network. - - - - - - Unable to discover the network. - - - - - Please enter the password of your inverter. If no password has been explicitly set, leave it empty to use the default password for SMA inverters. - - - - - The password can not be longer than 12 characters. - - - - - Failed to log in with the given password. Please try again. - - - - - sma - - - - - Connected - The name of the StateType ({aaff72c3-c70a-4a2f-bed1-89f38cebe442}) of ThingClass speedwireInverter ----------- -The name of the StateType ({35733d27-4fe0-439a-be71-7c1597481659}) of ThingClass speedwireMeter ----------- -The name of the StateType ({c05e6a1a-252c-4f2b-8b31-09cf113d01c1}) of ThingClass sunnyWebBox - - - - - - Current phase A - The name of the StateType ({2a6c59ca-853a-47d6-96fb-0c85edf32f52}) of ThingClass speedwireInverter ----------- -The name of the StateType ({45bbdbef-1832-4870-bff5-299e580fb4da}) of ThingClass speedwireMeter - - - - - - Current phase B - The name of the StateType ({4db96fec-737c-4c4b-bf07-5ef2fd62508a}) of ThingClass speedwireInverter ----------- -The name of the StateType ({b3a4fdd2-b6b8-4c58-9da3-2084ad414022}) of ThingClass speedwireMeter - - - - - - Current phase C - The name of the StateType ({0f23fb0e-a440-4ac2-9aff-896bc65feb2c}) of ThingClass speedwireInverter ----------- -The name of the StateType ({b3655188-3854-4336-ae3c-61d3bda6fc4d}) of ThingClass speedwireMeter - - - - - - - Current power - The name of the StateType ({d7ceb482-5df8-4c0c-82bd-62ce7ba22c43}) of ThingClass speedwireInverter ----------- -The name of the StateType ({d4ac7f37-e30a-44e4-93cb-ad16df18b8f1}) of ThingClass speedwireMeter ----------- -The name of the StateType ({ff4ff872-2f0f-4ca4-9fe2-220eeaf16cc2}) of ThingClass sunnyWebBox - - - - - Current power phase A - The name of the StateType ({c5d09c63-7461-4fb8-a6fe-bc7aa919be30}) of ThingClass speedwireMeter - - - - - Current power phase B - The name of the StateType ({c52d4422-b521-4804-a7a7-c4398e91e760}) of ThingClass speedwireMeter - - - - - Current power phase C - The name of the StateType ({555e892c-3ca7-4100-9832-6ac13b87eb04}) of ThingClass speedwireMeter - - - - - DC power MPP1 - The name of the StateType ({b366f680-6134-488b-8362-b1b824a8daca}) of ThingClass speedwireInverter - - - - - DC power MPP2 - The name of the StateType ({87d9b654-5558-47a3-9db9-ffd7c23b4774}) of ThingClass speedwireInverter - - - - - Day energy produced - The name of the StateType ({16f34c5c-8dbb-4dcc-9faa-4b782d57226c}) of ThingClass sunnyWebBox - - - - - Energy consumed phase A - The name of the StateType ({b4ff2c71-f81d-4904-bbac-0c0c6e8a5a33}) of ThingClass speedwireMeter - - - - - Energy consumed phase B - The name of the StateType ({c4e5f569-ac5d-4761-a898-888880bfd59f}) of ThingClass speedwireMeter - - - - - Energy consumed phase C - The name of the StateType ({aabc02d7-8dc3-4637-8bf2-dc2e0e737ad3}) of ThingClass speedwireMeter - - - - - Energy produced phase A - The name of the StateType ({754c3b67-768a-47f7-99d8-f66c198f0835}) of ThingClass speedwireMeter - - - - - Energy produced phase B - The name of the StateType ({7eb08c45-24cf-40ce-be28-f3564f087672}) of ThingClass speedwireMeter - - - - - Energy produced phase C - The name of the StateType ({1eb2bf01-5ec6-42e5-b348-ac1e95199d14}) of ThingClass speedwireMeter - - - - - Energy produced today - The name of the StateType ({e8bc8f81-e5c5-4900-b429-93fcaa262fcb}) of ThingClass speedwireInverter - - - - - Error - The name of the StateType ({4e64f9ca-7e5a-4897-8035-6f2ae88fde89}) of ThingClass sunnyWebBox - - - - - - Firmware version - The name of the StateType ({6d76cc7b-9e00-4561-be7b-4e2a6b8f7b66}) of ThingClass speedwireInverter ----------- -The name of the StateType ({a685393c-8b7e-42c5-bb41-f9907c074626}) of ThingClass speedwireMeter - - - - - Frequency - The name of the StateType ({fdccf5de-7413-4480-9ca0-1151665dede8}) of ThingClass speedwireInverter - - - - - - - Host address - The name of the ParamType (ThingClass: speedwireInverter, Type: thing, ID: {c8098d53-69eb-4d0b-9f07-e43c4a0ea9a9}) ----------- -The name of the ParamType (ThingClass: speedwireMeter, Type: thing, ID: {d90193e6-a996-4e49-bf6d-564d596d7e74}) ----------- -The name of the ParamType (ThingClass: sunnyWebBox, Type: thing, ID: {864d4162-e3ce-48b8-b8ac-c1b971b52d42}) - - - - - - - MAC address - The name of the ParamType (ThingClass: speedwireInverter, Type: thing, ID: {7df0ab60-0f11-4495-8e0d-508ba2b6d858}) ----------- -The name of the ParamType (ThingClass: speedwireMeter, Type: thing, ID: {2780eab7-1f1c-4cc7-a789-a8790329ca9e}) ----------- -The name of the ParamType (ThingClass: sunnyWebBox, Type: thing, ID: {03f32361-4e13-4597-a346-af8d16a986b3}) - - - - - Mode - The name of the StateType ({1974550b-6059-4b0e-83f4-70177e20dac3}) of ThingClass sunnyWebBox - - - - - - Model ID - The name of the ParamType (ThingClass: speedwireInverter, Type: thing, ID: {d9892f74-5b93-4c98-8da2-72aca033273a}) ----------- -The name of the ParamType (ThingClass: speedwireMeter, Type: thing, ID: {abdc114d-1fac-4454-8b82-871ed5cdf28c}) - - - - - SMA - The name of the plugin sma ({b8442bbf-9d3f-4aa2-9443-b3a31ae09bac}) - - - - - SMA Energy Meter - The name of the ThingClass ({0c5097af-e136-4430-9fb4-0ccbb30c3e1c}) - - - - - SMA Inverter - The name of the ThingClass ({b63a0669-f2ac-4769-abea-e14cafb2309a}) - - - - - SMA Solar Technology AG - The name of the vendor ({16d5a4a3-36d5-46c0-b7dd-df166ddf5981}) - - - - - - Serial number - The name of the ParamType (ThingClass: speedwireInverter, Type: thing, ID: {e42242b4-2811-47f9-b42b-b150ed233217}) ----------- -The name of the ParamType (ThingClass: speedwireMeter, Type: thing, ID: {7c81a0c5-9bc6-43bb-a01a-4de5fe656bba}) - - - - - Sunny WebBox - The name of the ThingClass ({49304127-ce9b-45dd-8511-05030a4ac003}) - - - - - Total energy consumed - The name of the StateType ({4fb0a4c1-18ed-4d02-b6d0-c07e9b96a56d}) of ThingClass speedwireMeter - - - - - - - Total energy produced - The name of the StateType ({51cadd66-2cf1-485a-a2a9-191d11abfbd1}) of ThingClass speedwireInverter ----------- -The name of the StateType ({76ca68d8-6781-4d2a-8663-440aec40b4de}) of ThingClass speedwireMeter ----------- -The name of the StateType ({0bb4e227-7e38-49ca-9b32-ce4621c9305b}) of ThingClass sunnyWebBox - - - - - - Voltage phase A - The name of the StateType ({6ef4eb16-a3d6-4bc9-972d-5e7cb81173a5}) of ThingClass speedwireInverter ----------- -The name of the StateType ({44ee2491-8376-41cd-a21d-185c736152ec}) of ThingClass speedwireMeter - - - - - - Voltage phase B - The name of the StateType ({d9a5768b-1bf5-4933-810d-84dd7a688f71}) of ThingClass speedwireInverter ----------- -The name of the StateType ({56ae3555-f874-4c2d-8833-17573dce477a}) of ThingClass speedwireMeter - - - - - - Voltage phase C - The name of the StateType ({fc168dc6-eecf-40b4-b214-3e28da0dbb12}) of ThingClass speedwireInverter ----------- -The name of the StateType ({51cbb29b-29f0-480a-9d7d-b8f4e6a205ae}) of ThingClass speedwireMeter - - - -