From 16eb2fdd614cefad19e809341e13f8dbc325cbb5 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 29 Jan 2021 00:54:58 +0100 Subject: [PATCH 1/2] Anel: Automatically update configuration when the IP changes --- anel/anel.pro | 2 + anel/discovery.cpp | 65 +++++ anel/discovery.h | 30 +++ anel/integrationpluginanel.cpp | 409 +++++++++++++++++--------------- anel/integrationpluginanel.h | 10 +- anel/integrationpluginanel.json | 44 +--- 6 files changed, 330 insertions(+), 230 deletions(-) create mode 100644 anel/discovery.cpp create mode 100644 anel/discovery.h diff --git a/anel/anel.pro b/anel/anel.pro index 27067883..3119f15c 100644 --- a/anel/anel.pro +++ b/anel/anel.pro @@ -5,7 +5,9 @@ QT += network TARGET = $$qtLibraryTarget(nymea_integrationpluginanel) SOURCES += \ + discovery.cpp \ integrationpluginanel.cpp \ HEADERS += \ + discovery.h \ integrationpluginanel.h \ diff --git a/anel/discovery.cpp b/anel/discovery.cpp new file mode 100644 index 00000000..d7732b8e --- /dev/null +++ b/anel/discovery.cpp @@ -0,0 +1,65 @@ +#include "discovery.h" + +#include +#include +#include + +#include "extern-plugininfo.h" + +Discovery::Discovery(QObject *parent) : QObject(parent) +{ + +} + +void Discovery::discover() +{ + QUdpSocket *searchSocket = new QUdpSocket(this); + + // Note: This will fail, and it's not a problem, but it is required to force the socket to stick to IPv4... + searchSocket->bind(QHostAddress::AnyIPv4, 30303); + + QString discoveryString = "Durchsuchen: Wer ist da?"; + qint64 len = searchSocket->writeDatagram(discoveryString.toUtf8(), QHostAddress("255.255.255.255"), 30303); + if (len != discoveryString.length()) { + searchSocket->deleteLater(); + qCWarning(dcAnelElektronik()) << "Error sending discovery"; + QTimer::singleShot(0, this, [=](){ + emit finished(true); + }); + return; + } + + QTimer::singleShot(2000, this, [this, searchSocket](){ + while(searchSocket->hasPendingDatagrams()) { + char buffer[1024]; + QHostAddress senderAddress; + int len = searchSocket->readDatagram(buffer, 1024, &senderAddress); + QByteArray data = QByteArray::fromRawData(buffer, len); + qCDebug(dcAnelElektronik()) << "Have datagram:" << data; + if (!data.startsWith("NET-CONTROL")) { + qCDebug(dcAnelElektronik()) << "Failed to parse discovery datagram from" << senderAddress << data; + continue; + } + QStringList parts = QString(data).split("\r\n"); + if (parts.count() != 4) { + qCDebug(dcAnelElektronik()) << "Failed to parse discovery datagram from" << senderAddress << data; + continue; + } + qCDebug(dcAnelElektronik()) << "Found NET-CONTROL:" << senderAddress << parts.at(0) << parts.at(1) << parts.at(2) << parts.at(3) << senderAddress.protocol(); + Result result; + result.name = parts.at(2); + result.macAddress = parts.at(1); + result.ipAddress = senderAddress.toString(); + result.port = parts.at(3).toInt(); + + m_results.insert(result.macAddress, result); + } + emit finished(false); + searchSocket->deleteLater(); + }); +} + +QHash Discovery::results() const +{ + return m_results; +} diff --git a/anel/discovery.h b/anel/discovery.h new file mode 100644 index 00000000..9401c90d --- /dev/null +++ b/anel/discovery.h @@ -0,0 +1,30 @@ +#ifndef DISCOVERY_H +#define DISCOVERY_H + +#include +#include + +class Discovery : public QObject +{ + Q_OBJECT +public: + struct Result { + QString name; + QString macAddress; + QString ipAddress; + int port; + }; + explicit Discovery(QObject *parent = nullptr); + + void discover(); + + QHash results() const; + +signals: + void finished(bool error); + +private: + QHash m_results; +}; + +#endif // DISCOVERY_H diff --git a/anel/integrationpluginanel.cpp b/anel/integrationpluginanel.cpp index 7bef8b19..49af37c0 100644 --- a/anel/integrationpluginanel.cpp +++ b/anel/integrationpluginanel.cpp @@ -134,29 +134,31 @@ Example reply for HOME and PRO: #include "integrationpluginanel.h" #include "plugininfo.h" #include "plugintimer.h" +#include "discovery.h" #include #include #include #include +QHash connectedStateTypeIdMap = { + {netPwrCtlHomeThingClassId, netPwrCtlHomeConnectedStateTypeId}, + {netPwrCtlProThingClassId, netPwrCtlProConnectedStateTypeId}, + {netPwrCtlAdvThingClassId, netPwrCtlAdvConnectedStateTypeId}, + {netPwrCtlHutThingClassId, netPwrCtlHutConnectedStateTypeId}, + {socketThingClassId, socketConnectedStateTypeId} +}; + +QHash macAddressParamTypeIdMap = { + {netPwrCtlHomeThingClassId, netPwrCtlHomeThingMacAddressParamTypeId}, + {netPwrCtlProThingClassId, netPwrCtlProThingMacAddressParamTypeId}, + {netPwrCtlAdvThingClassId, netPwrCtlAdvThingMacAddressParamTypeId}, + {netPwrCtlHutThingClassId, netPwrCtlHutThingMacAddressParamTypeId} +}; + IntegrationPluginAnel::IntegrationPluginAnel() { - m_connectedStateTypeIdMap.insert(netPwrCtlHomeThingClassId, netPwrCtlHomeConnectedStateTypeId); - m_connectedStateTypeIdMap.insert(netPwrCtlProThingClassId, netPwrCtlProConnectedStateTypeId); - m_connectedStateTypeIdMap.insert(netPwrCtlAdvThingClassId, netPwrCtlAdvConnectedStateTypeId); - m_connectedStateTypeIdMap.insert(netPwrCtlHutThingClassId, netPwrCtlHutConnectedStateTypeId); - m_connectedStateTypeIdMap.insert(socketThingClassId, socketConnectedStateTypeId); - m_ipAddressParamTypeIdMap.insert(netPwrCtlHomeThingClassId, netPwrCtlHomeThingIpAddressParamTypeId); - m_ipAddressParamTypeIdMap.insert(netPwrCtlProThingClassId, netPwrCtlProThingIpAddressParamTypeId); - m_ipAddressParamTypeIdMap.insert(netPwrCtlAdvThingClassId, netPwrCtlAdvThingIpAddressParamTypeId); - m_ipAddressParamTypeIdMap.insert(netPwrCtlHutThingClassId, netPwrCtlHutThingIpAddressParamTypeId); - - m_portParamTypeIdMap.insert(netPwrCtlHomeThingClassId, netPwrCtlHomeThingPortParamTypeId); - m_portParamTypeIdMap.insert(netPwrCtlProThingClassId, netPwrCtlProThingPortParamTypeId); - m_portParamTypeIdMap.insert(netPwrCtlAdvThingClassId, netPwrCtlAdvThingPortParamTypeId); - m_portParamTypeIdMap.insert(netPwrCtlHutThingClassId, netPwrCtlHutThingPortParamTypeId); } IntegrationPluginAnel::~IntegrationPluginAnel() @@ -165,66 +167,70 @@ IntegrationPluginAnel::~IntegrationPluginAnel() void IntegrationPluginAnel::init() { + m_discovery = new Discovery(this); + // Every time the discovery finish, we'll check if we need to update cached ip/port + connect(m_discovery, &Discovery::finished, this, [=](bool error){ + if (error) { + return; + } + foreach (Thing *thing, myThings()) { + if (!macAddressParamTypeIdMap.contains(thing->thingClassId())) { + continue; + } + QString macAddress = thing->paramValue(macAddressParamTypeIdMap.value(thing->thingClassId())).toString(); + + // Upgrading configured things from old version where the MAC param was still holding an IP address instead + if (!QHostAddress(macAddress).isNull()) { + qCDebug(dcAnelElektronik()) << "Upgrading configuration for:" << thing->name(); + foreach (const Discovery::Result &result, m_discovery->results()) { + if (result.ipAddress == macAddress) { + macAddress = result.macAddress; + thing->setParamValue(macAddressParamTypeIdMap.value(thing->thingClassId()), macAddress); + } + } + } + + + if (m_discovery->results().contains(macAddress)) { + Discovery::Result result = m_discovery->results().value(macAddress); + qCDebug(dcAnelElektronik()) << "Updating IP address for" << thing->name() << "to" << result.ipAddress << ":" << result.port; + pluginStorage()->beginGroup(thing->id().toString()); + pluginStorage()->setValue("cachedAddress", result.ipAddress); + pluginStorage()->setValue("cachedPort", result.port); + pluginStorage()->endGroup(); + } + } + }); } void IntegrationPluginAnel::discoverThings(ThingDiscoveryInfo *info) { - QUdpSocket *searchSocket = new QUdpSocket(this); + connect(m_discovery, &Discovery::finished, info, [=](bool error){ + if (error) { + qCWarning(dcAnelElektronik()) << "Error sending discovery"; + //: Error discovering devices + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error sending data to the network.")); + return; + } + foreach (const Discovery::Result &result, m_discovery->results()) { + ThingDescriptor d(info->thingClassId(), result.name, result.ipAddress); + ParamTypeId macAddressParamTypeId = macAddressParamTypeIdMap.value(info->thingClassId()); + ParamList params; + params << Param(macAddressParamTypeId, result.macAddress); - // Note: This will fail, and it's not a problem, but it is required to force the socket to stick to IPv4... - searchSocket->bind(QHostAddress::AnyIPv4, 30303); - - QString discoveryString = "Durchsuchen: Wer ist da?"; - qint64 len = searchSocket->writeDatagram(discoveryString.toUtf8(), QHostAddress("255.255.255.255"), 30303); - if (len != discoveryString.length()) { - searchSocket->deleteLater(); - qCWarning(dcAnelElektronik()) << "Error sending discovery"; - //: Error discovering devices - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error sending data to the network.")); - return; - } - - QTimer::singleShot(2000, info, [this, searchSocket, info](){ - while(searchSocket->hasPendingDatagrams()) { - char buffer[1024]; - QHostAddress senderAddress; - int len = searchSocket->readDatagram(buffer, 1024, &senderAddress); - QByteArray data = QByteArray::fromRawData(buffer, len); - qCDebug(dcAnelElektronik()) << "Have datagram:" << data; - if (!data.startsWith("NET-CONTROL")) { - qCDebug(dcAnelElektronik()) << "Failed to parse discovery datagram from" << senderAddress << data; - continue; - } - QStringList parts = QString(data).split("\r\n"); - if (parts.count() != 4) { - qCDebug(dcAnelElektronik()) << "Failed to parse discovery datagram from" << senderAddress << data; - continue; - } - qCDebug(dcAnelElektronik()) << "Found NET-CONTROL:" << senderAddress << parts.at(2) << parts.at(3) << senderAddress.protocol(); - - ParamTypeId ipAddressParamTypeId = m_ipAddressParamTypeIdMap.value(info->thingClassId()); - ParamTypeId portParamTypeId = m_portParamTypeIdMap.value(info->thingClassId()); - - bool existing = false; - foreach (Thing *existingDev, myThings()) { - if (existingDev->thingClassId() == info->thingClassId() && existingDev->paramValue(ipAddressParamTypeId).toString() == senderAddress.toString()) { - existing = true; + d.setParams(params); + foreach (Thing *existingThing, myThings().filterByThingClassId(info->thingClassId())) { + if (existingThing->paramValue(macAddressParamTypeId).toString() == result.macAddress) { + qCDebug(dcAnelElektronik()) << "Already have" << result.macAddress << result.ipAddress << "in configured things."; + d.setThingId(existingThing->id()); + break; } } - if (existing) { - qCDebug(dcAnelElektronik()) << "Already have" << senderAddress << "in configured things. Skipping..."; - continue; - } - ThingDescriptor d(info->thingClassId(), parts.at(2), senderAddress.toString()); - ParamList params; - params << Param(ipAddressParamTypeId, senderAddress.toString()); - params << Param(portParamTypeId, parts.at(3).toInt()); - d.setParams(params); info->addThingDescriptor(d); } info->finish(Thing::ThingErrorNoError); - searchSocket->deleteLater(); }); + m_discovery->discover(); } void IntegrationPluginAnel::startPairing(ThingPairingInfo *info) @@ -234,8 +240,10 @@ void IntegrationPluginAnel::startPairing(ThingPairingInfo *info) void IntegrationPluginAnel::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &password) { - QString ipAddress = info->params().paramValue(m_ipAddressParamTypeIdMap.value(info->thingClassId())).toString(); - int port = info->params().paramValue(m_portParamTypeIdMap.value(info->thingClassId())).toInt(); + QString macAddress = info->params().paramValue(macAddressParamTypeIdMap.value(info->thingClassId())).toString(); + + QString ipAddress = m_discovery->results().value(macAddress).ipAddress; + int port = m_discovery->results().value(macAddress).port; QNetworkRequest request; request.setUrl(QUrl(QString("http://%1:%2/strg.cfg").arg(ipAddress).arg(port))); @@ -243,9 +251,11 @@ void IntegrationPluginAnel::confirmPairing(ThingPairingInfo *info, const QString qCDebug(dcAnelElektronik()) << "ConfirmPairing fetching:" << request.url() << request.rawHeader("Authorization"); QNetworkReply *reply = hardwareManager()->networkManager()->get(request); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, info, [this, info, reply, username, password](){ + connect(reply, &QNetworkReply::finished, info, [=](){ if (reply->error() == QNetworkReply::NoError) { pluginStorage()->beginGroup(info->thingId().toString()); + pluginStorage()->setValue("cachedAddress", ipAddress); + pluginStorage()->setValue("cachedPort", port); pluginStorage()->setValue("username", username); pluginStorage()->setValue("password", password); pluginStorage()->endGroup(); @@ -285,12 +295,23 @@ void IntegrationPluginAnel::setupThing(ThingSetupInfo *info) info->finish(Thing::ThingErrorThingClassNotFound); } +void IntegrationPluginAnel::postSetupThing(Thing *thing) +{ + Q_UNUSED(thing) + if (!m_discoverTimer) { + m_discoverTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); + connect(m_discoverTimer, &PluginTimer::timeout, m_discovery, &Discovery::discover); + } +} + void IntegrationPluginAnel::thingRemoved(Thing *thing) { qCDebug(dcAnelElektronik) << "Device removed" << thing->name(); if (myThings().isEmpty()) { hardwareManager()->pluginTimerManager()->unregisterTimer(m_pollTimer); m_pollTimer = nullptr; + hardwareManager()->pluginTimerManager()->unregisterTimer(m_discoverTimer); + m_discoverTimer = nullptr; } } @@ -304,10 +325,9 @@ void IntegrationPluginAnel::executeAction(ThingActionInfo *info) Thing *parentDevice = myThings().findById(thing->parentId()); - QString ipAddress = parentDevice->paramValue(m_ipAddressParamTypeIdMap.value(parentDevice->thingClassId())).toString(); - int port = parentDevice->paramValue(m_portParamTypeIdMap.value(parentDevice->thingClassId())).toInt(); - pluginStorage()->beginGroup(parentDevice->id().toString()); + QString ipAddress = pluginStorage()->value("cachedAddress").toString(); + int port = pluginStorage()->value("cachedPort").toInt(); QString username = pluginStorage()->value("username").toString(); QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); @@ -351,10 +371,10 @@ void IntegrationPluginAnel::refreshStates() void IntegrationPluginAnel::setConnectedState(Thing *thing, bool connected) { - thing->setStateValue(m_connectedStateTypeIdMap.value(thing->thingClassId()), connected); + thing->setStateValue(connectedStateTypeIdMap.value(thing->thingClassId()), connected); foreach (Thing *child, myThings()) { if (child->parentId() == thing->id()) { - child->setStateValue(m_connectedStateTypeIdMap.value(child->thingClassId()), connected); + child->setStateValue(connectedStateTypeIdMap.value(child->thingClassId()), connected); } } } @@ -363,71 +383,77 @@ void IntegrationPluginAnel::setupHomeProDevice(ThingSetupInfo *info) { Thing *thing = info->thing(); - QString ipAddress = thing->paramValue(m_ipAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - int port = thing->paramValue(m_portParamTypeIdMap.value(thing->thingClassId())).toInt(); + QString macAddress = thing->paramValue(macAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - pluginStorage()->beginGroup(thing->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); + // Run a discovery and wait for it to finish before setting it up. + m_discovery->discover(); + connect(m_discovery, &Discovery::finished, info, [=](){ + pluginStorage()->beginGroup(thing->id().toString()); + QString ipAddress = pluginStorage()->value("cachedAddress").toString(); + int port = pluginStorage()->value("cachedPort").toInt(); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); - QNetworkRequest request; - request.setUrl(QUrl(QString("http://%1:%2/strg.cfg").arg(ipAddress).arg(port))); - request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username).arg(password).toUtf8().toBase64()); - qCDebug(dcAnelElektronik()) << "SetupDevice fetching:" << request.url() << request.rawHeader("Authorization") << username << password; - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, info, [this, info, reply](){ - StateTypeId connectedStateTypeId = m_connectedStateTypeIdMap.value(info->thing()->thingClassId()); - if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcAnelElektronik()) << "Error fetching state for" << info->thing()->name() << reply->error() << reply->errorString(); - info->thing()->setStateValue(connectedStateTypeId, false); - //: Error setting up thing - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The thing rejected our connection. Please check the configured network ports.")); - return; - } - info->thing()->setStateValue(connectedStateTypeId, true); - - QByteArray data = reply->readAll(); - - QStringList parts = QString(data).split(';'); - - int startIndex = parts.indexOf("end") - 58; - if (startIndex < 0 || !parts.at(startIndex).startsWith("NET-PWRCTRL") || parts.length() < 60) { - qCWarning(dcAnelElektronik()) << "Bad data from panel:" << data << "Length:" << parts.length(); - //: Error setting up thing - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unexpected data received from NET-PWRCTL device. Perhaps it's running an old firmware?")); - return; - } - - // At this point we're done with gathering information about the panel. Setup defintely succeeded for the gateway thing - info->finish(Thing::ThingErrorNoError); - - // If we haven't set up childs for this gateway yet, let's do it now - foreach (Thing *child, myThings()) { - if (child->parentId() == info->thing()->id()) { - // Already have childs for this panel. We're done here + QNetworkRequest request; + request.setUrl(QUrl(QString("http://%1:%2/strg.cfg").arg(ipAddress).arg(port))); + request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username).arg(password).toUtf8().toBase64()); + qCDebug(dcAnelElektronik()) << "SetupDevice fetching:" << request.url() << request.rawHeader("Authorization") << username << password; + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [this, info, reply](){ + StateTypeId connectedStateTypeId = connectedStateTypeIdMap.value(info->thing()->thingClassId()); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcAnelElektronik()) << "Error fetching state for" << info->thing()->name() << reply->error() << reply->errorString(); + info->thing()->setStateValue(connectedStateTypeId, false); + //: Error setting up thing + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The thing rejected our connection. Please check the configured network ports.")); return; } - } + info->thing()->setStateValue(connectedStateTypeId, true); - // Lets add the child devices now - int childs = -1; - QString type = parts.at(startIndex + 8); - if (type == "H") { - childs = 3; - } else { - childs = 8; - } + QByteArray data = reply->readAll(); + + QStringList parts = QString(data).split(';'); + + int startIndex = parts.indexOf("end") - 58; + if (startIndex < 0 || !parts.at(startIndex).startsWith("NET-PWRCTRL") || parts.length() < 60) { + qCWarning(dcAnelElektronik()) << "Bad data from panel:" << data << "Length:" << parts.length(); + //: Error setting up thing + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unexpected data received from NET-PWRCTL device. Perhaps it's running an old firmware?")); + return; + } + + // At this point we're done with gathering information about the panel. Setup defintely succeeded for the gateway thing + info->finish(Thing::ThingErrorNoError); + + // If we haven't set up childs for this gateway yet, let's do it now + foreach (Thing *child, myThings()) { + if (child->parentId() == info->thing()->id()) { + // Already have childs for this panel. We're done here + return; + } + } + + // Lets add the child devices now + int childs = -1; + QString type = parts.at(startIndex + 8); + if (type == "H") { + childs = 3; + } else { + childs = 8; + } + + QList descriptorList; + for (int i = 0; i < childs; i++) { + QString deviceName = parts.at(startIndex + 10 + i); + ThingDescriptor d(socketThingClassId, deviceName, info->thing()->name(), info->thing()->id()); + d.setParams(ParamList() << Param(socketThingNumberParamTypeId, i)); + descriptorList << d; + } + emit autoThingsAppeared(descriptorList); + }); - QList descriptorList; - for (int i = 0; i < childs; i++) { - QString deviceName = parts.at(startIndex + 10 + i); - ThingDescriptor d(socketThingClassId, deviceName, info->thing()->name(), info->thing()->id()); - d.setParams(ParamList() << Param(socketThingNumberParamTypeId, i)); - descriptorList << d; - } - emit autoThingsAppeared(descriptorList); }); } @@ -435,71 +461,75 @@ void IntegrationPluginAnel::setupAdvDevice(ThingSetupInfo *info) { Thing *thing = info->thing(); - QString ipAddress = thing->paramValue(m_ipAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - int port = thing->paramValue(m_portParamTypeIdMap.value(thing->thingClassId())).toInt(); + QString macAddress = thing->paramValue(macAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - pluginStorage()->beginGroup(thing->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); + // Run a discovery and wait for it to finish before trying to connect + m_discovery->discover(); + connect(m_discovery, &Discovery::finished, info, [=](){ + pluginStorage()->beginGroup(thing->id().toString()); + QString ipAddress = pluginStorage()->value("cachedAddress").toString(); + int port = pluginStorage()->value("cachedPort").toInt(); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); - QNetworkRequest request; - request.setUrl(QUrl(QString("http://%1:%2/strg.cfg").arg(ipAddress).arg(port))); - request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username).arg(password).toUtf8().toBase64()); - qCDebug(dcAnelElektronik()) << "SetupDevice fetching:" << request.url() << request.rawHeader("Authorization"); - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, info, [this, info, reply](){ - StateTypeId connectedStateTypeId = m_connectedStateTypeIdMap.value(info->thing()->thingClassId()); - if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcAnelElektronik()) << "Error fetching state for" << info->thing()->name() << reply->error() << reply->errorString(); - info->thing()->setStateValue(connectedStateTypeId, false); - //: Error setting up thing - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The thing rejected our connection. Please check the configured network ports.")); - return; - } - info->thing()->setStateValue(connectedStateTypeId, true); - - QByteArray data = reply->readAll(); - - QStringList parts = QString(data).split(';'); - - int startIndex = parts.indexOf("end") - 40; - if (startIndex < 0 || parts.length() < 58) { - qCWarning(dcAnelElektronik()) << "Bad data from panel:" << data << "Length:" << parts.length(); - //: Error setting up thing - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unexpected data received from NET-PWRCTL device. Perhaps it's running an old firmware?")); - return; - } - - // At this point we're done with gathering information about the panel. Setup defintely succeeded for the gateway thing - info->finish(Thing::ThingErrorNoError); - - // If we haven't set up childs for this gateway yet, let's do it now - foreach (Thing *child, myThings()) { - if (child->parentId() == info->thing()->id()) { - // Already have childs for this panel. We're done here + QNetworkRequest request; + request.setUrl(QUrl(QString("http://%1:%2/strg.cfg").arg(ipAddress).arg(port))); + request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username).arg(password).toUtf8().toBase64()); + qCDebug(dcAnelElektronik()) << "SetupDevice fetching:" << request.url() << request.rawHeader("Authorization"); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [this, info, reply]() { + StateTypeId connectedStateTypeId = connectedStateTypeIdMap.value(info->thing()->thingClassId()); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcAnelElektronik()) << "Error fetching state for" << info->thing()->name() << reply->error() << reply->errorString(); + info->thing()->setStateValue(connectedStateTypeId, false); + //: Error setting up thing + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The thing rejected our connection. Please check the configured network ports.")); return; } - } + info->thing()->setStateValue(connectedStateTypeId, true); - QList descriptorList; - for (int i = 0; i < 8; i++) { - QString deviceName = parts.at(startIndex + (i * 5)); - ThingDescriptor d(socketThingClassId, deviceName, info->thing()->name(), info->thing()->id()); - d.setParams(ParamList() << Param(socketThingNumberParamTypeId, i)); - descriptorList << d; - } - emit autoThingsAppeared(descriptorList); + QByteArray data = reply->readAll(); + + QStringList parts = QString(data).split(';'); + + int startIndex = parts.indexOf("end") - 40; + if (startIndex < 0 || parts.length() < 58) { + qCWarning(dcAnelElektronik()) << "Bad data from panel:" << data << "Length:" << parts.length(); + //: Error setting up thing + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unexpected data received from NET-PWRCTL device. Perhaps it's running an old firmware?")); + return; + } + + // At this point we're done with gathering information about the panel. Setup defintely succeeded for the gateway thing + info->finish(Thing::ThingErrorNoError); + + // If we haven't set up childs for this gateway yet, let's do it now + foreach (Thing *child, myThings()) { + if (child->parentId() == info->thing()->id()) { + // Already have childs for this panel. We're done here + return; + } + } + + QList descriptorList; + for (int i = 0; i < 8; i++) { + QString deviceName = parts.at(startIndex + (i * 5)); + ThingDescriptor d(socketThingClassId, deviceName, info->thing()->name(), info->thing()->id()); + d.setParams(ParamList() << Param(socketThingNumberParamTypeId, i)); + descriptorList << d; + } + emit autoThingsAppeared(descriptorList); + }); }); } void IntegrationPluginAnel::refreshHomePro(Thing *thing) { - QString ipAddress = thing->paramValue(m_ipAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - int port = thing->paramValue(m_portParamTypeIdMap.value(thing->thingClassId())).toInt(); - pluginStorage()->beginGroup(thing->id().toString()); + QString ipAddress = pluginStorage()->value("cachedAddress").toString(); + int port = pluginStorage()->value("cachedPort").toInt(); QString username = pluginStorage()->value("username").toString(); QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); @@ -553,10 +583,9 @@ void IntegrationPluginAnel::refreshHomePro(Thing *thing) void IntegrationPluginAnel::refreshAdv(Thing *thing) { - QString ipAddress = thing->paramValue(m_ipAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - int port = thing->paramValue(m_portParamTypeIdMap.value(thing->thingClassId())).toInt(); - pluginStorage()->beginGroup(thing->id().toString()); + QString ipAddress = pluginStorage()->value("cachedAddress").toString(); + int port = pluginStorage()->value("cachedPort").toInt(); QString username = pluginStorage()->value("username").toString(); QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); @@ -605,10 +634,9 @@ void IntegrationPluginAnel::refreshAdv(Thing *thing) void IntegrationPluginAnel::refreshAdvTemp(Thing *thing) { - QString ipAddress = thing->paramValue(m_ipAddressParamTypeIdMap.value(thing->thingClassId())).toString(); - int port = thing->paramValue(m_portParamTypeIdMap.value(thing->thingClassId())).toInt(); - pluginStorage()->beginGroup(thing->id().toString()); + QString ipAddress = pluginStorage()->value("cachedAddress").toString(); + int port = pluginStorage()->value("cachedPort").toInt(); QString username = pluginStorage()->value("username").toString(); QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); @@ -670,6 +698,5 @@ void IntegrationPluginAnel::refreshAdvTemp(Thing *thing) qCWarning(dcAnelElektronik()) << "Error fetching light intensity value:" << data; } } - }); } diff --git a/anel/integrationpluginanel.h b/anel/integrationpluginanel.h index e43cbfa1..71715ee3 100644 --- a/anel/integrationpluginanel.h +++ b/anel/integrationpluginanel.h @@ -38,6 +38,7 @@ #include class PluginTimer; +class Discovery; class IntegrationPluginAnel: public IntegrationPlugin { @@ -55,12 +56,15 @@ public: 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; void executeAction(ThingActionInfo *info) override; private slots: void refreshStates(); + + private: void setConnectedState(Thing *thing, bool connected); @@ -72,11 +76,11 @@ private: void refreshAdvTemp(Thing *thing); private: + Discovery *m_discovery = nullptr; PluginTimer *m_pollTimer = nullptr; + PluginTimer *m_discoverTimer = nullptr; - QHash m_connectedStateTypeIdMap; - QHash m_ipAddressParamTypeIdMap; - QHash m_portParamTypeIdMap; + QHash m_ipCache; }; #endif // INTEGRATIONPLUGINANEL_H diff --git a/anel/integrationpluginanel.json b/anel/integrationpluginanel.json index 39108b3a..a6bea4c1 100644 --- a/anel/integrationpluginanel.json +++ b/anel/integrationpluginanel.json @@ -18,16 +18,9 @@ "paramTypes": [ { "id": "1e273e10-3ea0-4337-a221-3b8e26c6e7dc", - "name":"ipAddress", - "displayName": "IP address", + "name":"macAddress", + "displayName": "MAC address", "type": "QString" - }, - { - "id": "81704e09-d283-49d1-9e3f-9c06f8b98d84", - "name": "port", - "displayName": "Web control Port", - "type": "int", - "defaultValue": 80 } ], "stateTypes": [ @@ -51,17 +44,10 @@ "paramTypes": [ { "id": "b1cf9a4f-9c2a-4ab4-a920-46f0b8a8b988", - "name":"ipAddress", - "displayName": "IP address", + "name":"macAddress", + "displayName": "MAC address", "type": "QString" }, - { - "id": "31540acc-949f-454f-9987-982e97ce7d21", - "name": "port", - "displayName": "Web control Port", - "type": "int", - "defaultValue": 80 - }, { "id": "f9cae7eb-a534-404f-b041-6e5a6720494e", "name": "username", @@ -105,17 +91,10 @@ "paramTypes": [ { "id": "5d98ead0-e445-492c-821c-ae169af648e4", - "name":"ipAddress", - "displayName": "IP address", + "name":"macAddress", + "displayName": "MAC address", "type": "QString" }, - { - "id": "1913912f-2579-4e8a-9e23-af1de4a2ef72", - "name": "port", - "displayName": "Web control Port", - "type": "int", - "defaultValue": 80 - }, { "id": "0eeaa6f0-7232-4d6e-8637-6e21f58a2018", "name": "username", @@ -159,17 +138,10 @@ "paramTypes": [ { "id": "fb34919c-3ca0-47b3-a14a-7986b5ce24e2", - "name":"ipAddress", - "displayName": "IP address", + "name":"macAddress", + "displayName": "MAC address", "type": "QString" }, - { - "id": "35010dff-dde9-4acf-a60b-cf92b7edc2a5", - "name": "port", - "displayName": "Web control Port", - "type": "int", - "defaultValue": 80 - }, { "id": "3bf3db56-4fe4-4c87-a5cd-7c1f1acb6408", "name": "username", From 8fb2dc44bbb67d6065ae8740c37b1000f1fbc4fe Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 14 Feb 2021 14:57:53 +0100 Subject: [PATCH 2/2] Update translations --- ...e5b64-20e4-42bd-b86b-989b84afc22a-en_US.ts | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/anel/translations/7a3e5b64-20e4-42bd-b86b-989b84afc22a-en_US.ts b/anel/translations/7a3e5b64-20e4-42bd-b86b-989b84afc22a-en_US.ts index f138d009..0268d7c6 100644 --- a/anel/translations/7a3e5b64-20e4-42bd-b86b-989b84afc22a-en_US.ts +++ b/anel/translations/7a3e5b64-20e4-42bd-b86b-989b84afc22a-en_US.ts @@ -42,28 +42,6 @@ The name of the plugin anelElektronik ({7a3e5b64-20e4-42bd-b86b-989b84afc22a})The name of the ThingClass ({d70433ac-9738-49ca-932f-6d3e20bcc6d4}) - - IP address - The name of the ParamType (ThingClass: netPwrCtlHut, Type: thing, ID: {fb34919c-3ca0-47b3-a14a-7986b5ce24e2}) ----------- -The name of the ParamType (ThingClass: netPwrCtlAdv, Type: thing, ID: {5d98ead0-e445-492c-821c-ae169af648e4}) ----------- -The name of the ParamType (ThingClass: netPwrCtlPro, Type: thing, ID: {b1cf9a4f-9c2a-4ab4-a920-46f0b8a8b988}) ----------- -The name of the ParamType (ThingClass: netPwrCtlHome, Type: thing, ID: {1e273e10-3ea0-4337-a221-3b8e26c6e7dc}) - - - - Web control Port - The name of the ParamType (ThingClass: netPwrCtlHut, Type: thing, ID: {35010dff-dde9-4acf-a60b-cf92b7edc2a5}) ----------- -The name of the ParamType (ThingClass: netPwrCtlAdv, Type: thing, ID: {1913912f-2579-4e8a-9e23-af1de4a2ef72}) ----------- -The name of the ParamType (ThingClass: netPwrCtlPro, Type: thing, ID: {31540acc-949f-454f-9987-982e97ce7d21}) ----------- -The name of the ParamType (ThingClass: netPwrCtlHome, Type: thing, ID: {81704e09-d283-49d1-9e3f-9c06f8b98d84}) - - Username The name of the ParamType (ThingClass: netPwrCtlHut, Type: thing, ID: {3bf3db56-4fe4-4c87-a5cd-7c1f1acb6408}) @@ -210,5 +188,16 @@ The name of the StateType ({47329958-c33f-478f-b2a0-910abd150da8}) of ThingClass The name of the ActionType ({47329958-c33f-478f-b2a0-910abd150da8}) of ThingClass socket + + MAC address + The name of the ParamType (ThingClass: netPwrCtlHut, Type: thing, ID: {fb34919c-3ca0-47b3-a14a-7986b5ce24e2}) +---------- +The name of the ParamType (ThingClass: netPwrCtlAdv, Type: thing, ID: {5d98ead0-e445-492c-821c-ae169af648e4}) +---------- +The name of the ParamType (ThingClass: netPwrCtlPro, Type: thing, ID: {b1cf9a4f-9c2a-4ab4-a920-46f0b8a8b988}) +---------- +The name of the ParamType (ThingClass: netPwrCtlHome, Type: thing, ID: {1e273e10-3ea0-4337-a221-3b8e26c6e7dc}) + +