Anel: Automatically update configuration when the IP changes

This commit is contained in:
Michael Zanetti 2021-01-29 00:54:58 +01:00
parent cdff51655f
commit 16eb2fdd61
6 changed files with 330 additions and 230 deletions

View File

@ -5,7 +5,9 @@ QT += network
TARGET = $$qtLibraryTarget(nymea_integrationpluginanel)
SOURCES += \
discovery.cpp \
integrationpluginanel.cpp \
HEADERS += \
discovery.h \
integrationpluginanel.h \

65
anel/discovery.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "discovery.h"
#include <QUdpSocket>
#include <QTimer>
#include <QMetaObject>
#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<QString, Discovery::Result> Discovery::results() const
{
return m_results;
}

30
anel/discovery.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef DISCOVERY_H
#define DISCOVERY_H
#include <QObject>
#include <QHash>
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<QString, Result> results() const;
signals:
void finished(bool error);
private:
QHash<QString, Result> m_results;
};
#endif // DISCOVERY_H

View File

@ -134,29 +134,31 @@ Example reply for HOME and PRO:
#include "integrationpluginanel.h"
#include "plugininfo.h"
#include "plugintimer.h"
#include "discovery.h"
#include <network/networkaccessmanager.h>
#include <QNetworkReply>
#include <QAuthenticator>
#include <QUrlQuery>
QHash<ThingClassId, StateTypeId> connectedStateTypeIdMap = {
{netPwrCtlHomeThingClassId, netPwrCtlHomeConnectedStateTypeId},
{netPwrCtlProThingClassId, netPwrCtlProConnectedStateTypeId},
{netPwrCtlAdvThingClassId, netPwrCtlAdvConnectedStateTypeId},
{netPwrCtlHutThingClassId, netPwrCtlHutConnectedStateTypeId},
{socketThingClassId, socketConnectedStateTypeId}
};
QHash<ThingClassId, ParamTypeId> 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<ThingDescriptor> 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<ThingDescriptor> 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<ThingDescriptor> 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<ThingDescriptor> 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;
}
}
});
}

View File

@ -38,6 +38,7 @@
#include <QNetworkAccessManager>
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<ThingClassId, StateTypeId> m_connectedStateTypeIdMap;
QHash<ThingClassId, ParamTypeId> m_ipAddressParamTypeIdMap;
QHash<ThingClassId, ParamTypeId> m_portParamTypeIdMap;
QHash<QString, QHostAddress> m_ipCache;
};
#endif // INTEGRATIONPLUGINANEL_H

View File

@ -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",