diff --git a/fronius/fronius.pro b/fronius/fronius.pro
index e730ebf0..84e9ebd3 100644
--- a/fronius/fronius.pro
+++ b/fronius/fronius.pro
@@ -3,11 +3,13 @@ include(../plugins.pri)
QT += network
SOURCES += \
+ froniusdiscovery.cpp \
froniusnetworkreply.cpp \
froniussolarconnection.cpp \
integrationpluginfronius.cpp \
HEADERS += \
+ froniusdiscovery.h \
froniusnetworkreply.h \
froniussolarconnection.h \
integrationpluginfronius.h \
diff --git a/fronius/froniusdiscovery.cpp b/fronius/froniusdiscovery.cpp
new file mode 100644
index 00000000..2eeeb5e8
--- /dev/null
+++ b/fronius/froniusdiscovery.cpp
@@ -0,0 +1,137 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2023, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* 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 "froniusdiscovery.h"
+
+#include "extern-plugininfo.h"
+
+#include
+#include
+
+FroniusDiscovery::FroniusDiscovery(NetworkAccessManager *networkManager, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent):
+ QObject(parent),
+ m_networkManager(networkManager),
+ m_networkDeviceDiscovery{networkDeviceDiscovery}
+{
+ m_gracePeriodTimer.setSingleShot(true);
+ m_gracePeriodTimer.setInterval(3000);
+ connect(&m_gracePeriodTimer, &QTimer::timeout, this, [this](){
+ qCDebug(dcFronius()) << "Discovery: Grace period timer triggered.";
+ finishDiscovery();
+ });
+
+}
+
+void FroniusDiscovery::startDiscovery()
+{
+ qCInfo(dcFronius()) << "Discovery: Searching for Fronius solar devices in the network...";
+ NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
+
+ connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &FroniusDiscovery::checkNetworkDevice);
+
+ connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
+ qCDebug(dcFronius()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
+ m_gracePeriodTimer.start();
+ discoveryReply->deleteLater();
+ });
+}
+
+QList FroniusDiscovery::discoveryResults() const
+{
+ return m_discoveryResults;
+}
+
+void FroniusDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
+{
+ qCDebug(dcFronius()) << "Checking network device:" << networkDeviceInfo;
+
+ FroniusSolarConnection *connection = new FroniusSolarConnection(m_networkManager, networkDeviceInfo.address(), this);
+ m_connections.append(connection);
+
+ FroniusNetworkReply *reply = connection->getVersion();
+ connect(reply, &FroniusNetworkReply::finished, this, [=] {
+ QByteArray data = reply->networkReply()->readAll();
+ if (reply->networkReply()->error() != QNetworkReply::NoError) {
+ if (reply->networkReply()->error() == QNetworkReply::ContentNotFoundError) {
+ qCInfo(dcFronius()) << "The device does not reply to our requests. Please verify that the Fronius Solar API is enabled on the device.";
+ } else {
+ qCDebug(dcFronius()) << "device" << networkDeviceInfo.address() << "is not reachable.";
+ }
+ cleanupConnection(connection);
+ return;
+ }
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcFronius()) << "Failed to parse JSON data" << data << ":" << error.errorString() << data;
+ cleanupConnection(connection);
+ return;
+ }
+
+ QVariantMap versionResponseMap = jsonDoc.toVariant().toMap();
+ if (!versionResponseMap.contains("CompatibilityRange")) {
+ qCInfo(dcFronius()) << "Unexpected JSON reply. PRobably not a Fronius device.";
+ cleanupConnection(connection);
+ return;
+ }
+ qCDebug(dcFronius()) << "Compatibility version" << versionResponseMap.value("CompatibilityRange").toString();
+
+ // Knwon version with broken JSON API. Still allowing to discover so the user will get a proper error message during setup
+ if (versionResponseMap.value("CompatibilityRange").toString() == "1.6-2") {
+ qCWarning(dcFronius()) << "The Fronius data logger has a version which is known to have a broken JSON API firmware.";
+ }
+
+ m_discoveryResults.append(networkDeviceInfo);
+ cleanupConnection(connection);
+ });
+}
+
+void FroniusDiscovery::cleanupConnection(FroniusSolarConnection *connection)
+{
+ m_connections.removeAll(connection);
+ connection->deleteLater();
+}
+
+void FroniusDiscovery::finishDiscovery()
+{
+ qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
+
+ foreach (FroniusSolarConnection *connection, m_connections) {
+ cleanupConnection(connection);
+ }
+
+ qCInfo(dcFronius()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count()
+ << "Fronius devices in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
+ m_gracePeriodTimer.stop();
+
+ emit discoveryFinished();
+
+}
diff --git a/fronius/froniusdiscovery.h b/fronius/froniusdiscovery.h
new file mode 100644
index 00000000..6b7b3249
--- /dev/null
+++ b/fronius/froniusdiscovery.h
@@ -0,0 +1,70 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2023, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* 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 FRONIUSDISCOVERY_H
+#define FRONIUSDISCOVERY_H
+
+#include
+#include
+
+#include
+#include "froniussolarconnection.h"
+
+class FroniusDiscovery : public QObject
+{
+ Q_OBJECT
+public:
+ explicit FroniusDiscovery(NetworkAccessManager *networkManager, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
+
+ void startDiscovery();
+
+ QList discoveryResults() const;
+
+signals:
+ void discoveryFinished();
+
+private:
+ NetworkAccessManager *m_networkManager = nullptr;
+ NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
+
+ QTimer m_gracePeriodTimer;
+ QDateTime m_startDateTime;
+
+ QList m_connections;
+
+ QList m_discoveryResults;
+
+ void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
+ void cleanupConnection(FroniusSolarConnection *connection);
+
+ void finishDiscovery();
+};
+
+#endif // FRONIUSDISCOVERY_H
diff --git a/fronius/integrationpluginfronius.cpp b/fronius/integrationpluginfronius.cpp
index f46949a9..738f04a2 100644
--- a/fronius/integrationpluginfronius.cpp
+++ b/fronius/integrationpluginfronius.cpp
@@ -31,6 +31,7 @@
#include "plugininfo.h"
#include "plugintimer.h"
#include "integrationpluginfronius.h"
+#include "froniusdiscovery.h"
#include "network/networkaccessmanager.h"
#include "network/networkdevicediscovery.h"
@@ -56,20 +57,15 @@ void IntegrationPluginFronius::discoverThings(ThingDiscoveryInfo *info)
}
qCInfo(dcFronius()) << "Starting network discovery...";
- NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
- connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
- connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, info, [=](){
+ FroniusDiscovery *discovery = new FroniusDiscovery(hardwareManager()->networkManager(), hardwareManager()->networkDeviceDiscovery(), info);
+ connect(discovery, &FroniusDiscovery::discoveryFinished, info, [=](){
ThingDescriptors descriptors;
- qCDebug(dcFronius()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices";
- foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
+ qCDebug(dcFronius()) << "Discovery finished. Found" << discovery->discoveryResults().count() << "devices";
+ foreach (const NetworkDeviceInfo &networkDeviceInfo, discovery->discoveryResults()) {
qCDebug(dcFronius()) << networkDeviceInfo;
if (networkDeviceInfo.macAddress().isNull())
continue;
- // Hostname or MAC manufacturer must include Fronius
- if (!(networkDeviceInfo.macAddressManufacturer().toLower().contains("fronius") || networkDeviceInfo.hostName().toLower().contains("fronius")))
- continue;
-
QString title;
if (networkDeviceInfo.hostName().isEmpty()) {
title += "Fronius Solar";