Fix discovery cleanup and handle multiple discovery requests internally

This commit is contained in:
Simon Stürz 2021-12-15 08:24:38 +01:00
parent e70b4a679a
commit c1720851ab
2 changed files with 48 additions and 23 deletions

View File

@ -35,8 +35,6 @@
#include <QUdpSocket> #include <QUdpSocket>
#include <QTimeZone> #include <QTimeZone>
#include "kebadiscovery.h"
IntegrationPluginKeba::IntegrationPluginKeba() IntegrationPluginKeba::IntegrationPluginKeba()
{ {
@ -70,7 +68,7 @@ void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info)
} }
if (info->thingClassId() == wallboxThingClassId) { if (info->thingClassId() == wallboxThingClassId) {
// Create a discovery with the info as parent for auto deleting the object // Create a discovery with the info as parent for auto deleting the object once the discovery info is done
KebaDiscovery *discovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), info); KebaDiscovery *discovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), info);
connect(discovery, &KebaDiscovery::discoveryFinished, info, [=](){ connect(discovery, &KebaDiscovery::discoveryFinished, info, [=](){
foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) { foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) {
@ -138,7 +136,7 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info)
// Check if we have a keba with this ip, if reconfigure the object would already been removed from the hash // Check if we have a keba with this ip, if reconfigure the object would already been removed from the hash
foreach (KeContact *kebaConnect, m_kebaDevices.values()) { foreach (KeContact *kebaConnect, m_kebaDevices.values()) {
if (kebaConnect->address() == address) { if (kebaConnect->address() == address) {
qCWarning(dcKeba()) << "Failed to set up keba for host address" << address.toString() << "because there has already been configured a keba for this ip."; qCWarning(dcKeba()) << "Failed to set up keba for host address" << address.toString() << "because there has already been configured a keba for this IP.";
return info->finish(Thing::ThingErrorThingInUse, QT_TR_NOOP("Already configured for this IP address.")); return info->finish(Thing::ThingErrorThingInUse, QT_TR_NOOP("Already configured for this IP address."));
} }
} }
@ -151,12 +149,9 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info)
connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived); connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived);
connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived); connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived);
// TODO: first test the ip, verify serial number if responds // Make sure we receive data from the keba and the DIP switches are configured correctly
// if no response, rediscover, reassign ip in case if changes
connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) { connect(keba, &KeContact::reportOneReceived, info, [info, this, keba] (const KeContact::ReportOne &report) {
Thing *thing = info->thing(); Thing *thing = info->thing();
qCDebug(dcKeba()) << "Report one received for" << thing->name(); qCDebug(dcKeba()) << "Report one received for" << thing->name();
qCDebug(dcKeba()) << " - Firmware" << report.firmware; qCDebug(dcKeba()) << " - Firmware" << report.firmware;
qCDebug(dcKeba()) << " - Serial" << report.serialNumber; qCDebug(dcKeba()) << " - Serial" << report.serialNumber;
@ -187,8 +182,9 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info)
connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); // Clean up if the setup fails connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); // Clean up if the setup fails
connect(keba, &KeContact::destroyed, this, [thing, this]{ connect(keba, &KeContact::destroyed, this, [thing, this]{
m_kebaDevices.remove(thing->id()); m_kebaDevices.remove(thing->id());
// Setup failed, lets search the network, maybe the IP has changed...
searchNetworkDevices();
}); });
} else { } else {
qCWarning(dcKeba()) << "Could not setup thing: unhandled device class" << thing->thingClass(); qCWarning(dcKeba()) << "Could not setup thing: unhandled device class" << thing->thingClass();
info->finish(Thing::ThingErrorThingClassNotFound); info->finish(Thing::ThingErrorThingClassNotFound);
@ -213,7 +209,8 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing)
} }
// Try to find the mac address in case the user added the ip manually // Try to find the mac address in case the user added the ip manually
if (thing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty() || thing->paramValue(wallboxThingMacAddressParamTypeId).toString() == "00:00:00:00:00:00") { if (thing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty()
|| thing->paramValue(wallboxThingMacAddressParamTypeId).toString() == "00:00:00:00:00:00") {
searchNetworkDevices(); searchNetworkDevices();
} }
@ -240,19 +237,24 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing)
if (!m_reconnectTimer) { if (!m_reconnectTimer) {
m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(60 * 5); m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(60 * 5);
connect(m_reconnectTimer, &PluginTimer::timeout, this, [this] { connect(m_reconnectTimer, &PluginTimer::timeout, this, [this] {
bool startDiscoveryRequired = false;
// Only search for new network devices if there is one keba which is not connected // Only search for new network devices if there is one keba which is not connected
foreach (Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) { foreach (Thing *thing, myThings().filterByThingClassId(wallboxThingClassId)) {
KeContact *keba = m_kebaDevices.value(thing->id()); KeContact *keba = m_kebaDevices.value(thing->id());
if (!keba) { if (!keba) {
qCWarning(dcKeba()) << "No Keba connection found for" << thing->name(); qCWarning(dcKeba()) << "No Keba connection found for" << thing->name();
startDiscoveryRequired = true;
continue; continue;
} }
if (!keba->reachable()) { if (!keba->reachable()) {
searchNetworkDevices(); startDiscoveryRequired = true;
return; return;
} }
} }
if (startDiscoveryRequired)
searchNetworkDevices();
}); });
m_reconnectTimer->start(); m_reconnectTimer->start();
@ -262,7 +264,7 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing)
void IntegrationPluginKeba::thingRemoved(Thing *thing) void IntegrationPluginKeba::thingRemoved(Thing *thing)
{ {
qCDebug(dcKeba()) << "Deleting" << thing->name(); qCDebug(dcKeba()) << "Deleting" << thing->name();
if (thing->thingClassId() == wallboxThingClassId) { if (thing->thingClassId() == wallboxThingClassId && m_kebaDevices.contains(thing->id())) {
KeContact *keba = m_kebaDevices.take(thing->id()); KeContact *keba = m_kebaDevices.take(thing->id());
keba->deleteLater(); keba->deleteLater();
} }
@ -297,6 +299,13 @@ void IntegrationPluginKeba::executeAction(ThingActionInfo *info)
return info->finish(Thing::ThingErrorHardwareNotAvailable); return info->finish(Thing::ThingErrorHardwareNotAvailable);
} }
// Make sure wallbox is reachable
if (!keba->reachable()) {
qCWarning(dcKeba()) << "Failed to execute action. The wallbox seems not to be reachable" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
QUuid requestId; QUuid requestId;
if (action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId) { if (action.actionTypeId() == wallboxMaxChargingCurrentActionTypeId) {
int milliAmpere = action.paramValue(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt() * 1000; int milliAmpere = action.paramValue(wallboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt() * 1000;
@ -336,9 +345,6 @@ void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success)
{ {
if (m_asyncActions.contains(requestId)) { if (m_asyncActions.contains(requestId)) {
KeContact *keba = static_cast<KeContact *>(sender()); KeContact *keba = static_cast<KeContact *>(sender());
//keba->getReport2(); // Check if the state was actually set
Thing *thing = myThings().findById(m_kebaDevices.key(keba)); Thing *thing = myThings().findById(m_kebaDevices.key(keba));
if (!thing) { if (!thing) {
qCWarning(dcKeba()) << "On command executed: missing device object"; qCWarning(dcKeba()) << "On command executed: missing device object";
@ -420,23 +426,36 @@ void IntegrationPluginKeba::setDevicePlugState(Thing *thing, KeContact::PlugStat
void IntegrationPluginKeba::searchNetworkDevices() void IntegrationPluginKeba::searchNetworkDevices()
{ {
qCDebug(dcKeba()) << "Start searching for things..."; if (m_runningDiscovery) {
qCDebug(dcKeba()) << "Keba discovery already running.";
return;
}
KebaDiscovery *discovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), this); if (!m_kebaDataLayer) {
connect(discovery, &KebaDiscovery::discoveryFinished, this, [=](){ qCDebug(dcKeba()) << "Could not search wallboxes in the network. The data layer seems not to be available";
foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) { return;
}
qCDebug(dcKeba()) << "Start searching for wallboxes in the network...";
m_runningDiscovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), this);
connect(m_runningDiscovery, &KebaDiscovery::discoveryFinished, this, [=](){
foreach (const KebaDiscovery::KebaDiscoveryResult &result, m_runningDiscovery->discoveryResults()) {
foreach (Thing *existingThing, myThings().filterByThingClassId(wallboxThingClassId)) { foreach (Thing *existingThing, myThings().filterByThingClassId(wallboxThingClassId)) {
if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty()) { if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString().isEmpty()) {
//This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup
if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() == result.networkDeviceInfo.address().toString()) { if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() == result.networkDeviceInfo.address().toString()) {
qCDebug(dcKeba()) << "Keba Wallbox MAC Address has been discovered" << existingThing->name() << result.networkDeviceInfo.macAddress(); qCDebug(dcKeba()) << "Wallbox MAC address has been discovered" << existingThing->name() << result.networkDeviceInfo.macAddress();
existingThing->setParamValue(wallboxThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); existingThing->setParamValue(wallboxThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
} }
} else if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == result.networkDeviceInfo.macAddress()) { } else if (existingThing->paramValue(wallboxThingMacAddressParamTypeId).toString() == result.networkDeviceInfo.macAddress()) {
// We found the existing keba thing, lets check if the ip has changed // We found the existing keba thing, lets check if the ip has changed
if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() != result.networkDeviceInfo.address().toString()) { if (existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() != result.networkDeviceInfo.address().toString()) {
qCDebug(dcKeba()) << "Keba Wallbox IP Address has changed, from" << existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() << "to" << result.networkDeviceInfo.address().toString(); // Update the ip address of the thing.
// FIXME: as of now the thing manager does not store the changed param
qCDebug(dcKeba()) << "Wallbox IP Address has changed, from" << existingThing->paramValue(wallboxThingIpAddressParamTypeId).toString() << "to" << result.networkDeviceInfo.address().toString();
existingThing->setParamValue(wallboxThingIpAddressParamTypeId, result.networkDeviceInfo.address().toString()); existingThing->setParamValue(wallboxThingIpAddressParamTypeId, result.networkDeviceInfo.address().toString());
// Make sure the setup has already run for this thing, if not, the thingmanager will retry with the new ip every 15 seconds
KeContact *keba = m_kebaDevices.value(existingThing->id()); KeContact *keba = m_kebaDevices.value(existingThing->id());
if (keba) { if (keba) {
keba->setAddress(QHostAddress(result.networkDeviceInfo.address())); keba->setAddress(QHostAddress(result.networkDeviceInfo.address()));
@ -445,15 +464,19 @@ void IntegrationPluginKeba::searchNetworkDevices()
keba->getReport2(); keba->getReport2();
keba->getReport3(); keba->getReport3();
} else { } else {
qCWarning(dcKeba()) << "Could not update IP address since there is no keba connection for" << existingThing; qCWarning(dcKeba()) << "Could not update IP address since the keba connection has not been set up yet for" << existingThing;
} }
} else { } else {
qCDebug(dcKeba()) << "Keba Wallbox" << existingThing->name() << "IP address has not changed" << result.networkDeviceInfo.address().toString(); qCDebug(dcKeba()) << "Wallbox" << existingThing->name() << "IP address has not changed" << result.networkDeviceInfo.address().toString();
} }
break; break;
} }
} }
} }
// Clean up
m_runningDiscovery->deleteLater();
m_runningDiscovery = nullptr;
}); });
} }

View File

@ -36,6 +36,7 @@
#include <network/networkdevicediscovery.h> #include <network/networkdevicediscovery.h>
#include "kecontact.h" #include "kecontact.h"
#include "kebadiscovery.h"
#include "kecontactdatalayer.h" #include "kecontactdatalayer.h"
#include <QHash> #include <QHash>
@ -72,6 +73,7 @@ private:
QHash<ThingId, KeContact *> m_kebaDevices; QHash<ThingId, KeContact *> m_kebaDevices;
QHash<ThingId, int> m_lastSessionId; QHash<ThingId, int> m_lastSessionId;
QHash<QUuid, ThingActionInfo *> m_asyncActions; QHash<QUuid, ThingActionInfo *> m_asyncActions;
KebaDiscovery *m_runningDiscovery = nullptr;
void setDeviceState(Thing *device, KeContact::State state); void setDeviceState(Thing *device, KeContact::State state);
void setDevicePlugState(Thing *device, KeContact::PlugState plugState); void setDevicePlugState(Thing *device, KeContact::PlugState plugState);