diff --git a/keba/integrationpluginkeba.cpp b/keba/integrationpluginkeba.cpp index af0abf8e..a0d8b9f7 100644 --- a/keba/integrationpluginkeba.cpp +++ b/keba/integrationpluginkeba.cpp @@ -46,9 +46,6 @@ void IntegrationPluginKeba::init() m_macAddressParamTypeIds.insert(kebaThingClassId, kebaThingMacAddressParamTypeId); m_macAddressParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingMacAddressParamTypeId); - m_ipAddressParamTypeIds.insert(kebaThingClassId, kebaThingIpAddressParamTypeId); - m_ipAddressParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingIpAddressParamTypeId); - m_modelParamTypeIds.insert(kebaThingClassId, kebaThingModelParamTypeId); m_modelParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingModelParamTypeId); @@ -80,6 +77,7 @@ void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info) // 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); connect(discovery, &KebaDiscovery::discoveryFinished, info, [=](){ + foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) { KebaProductInfo productInformation(result.product); @@ -110,7 +108,6 @@ void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info) ParamList params; params << Param(m_macAddressParamTypeIds.value(discoveredThingClassId), result.networkDeviceInfo.macAddress()); - params << Param(m_ipAddressParamTypeIds.value(discoveredThingClassId), result.networkDeviceInfo.address().toString()); params << Param(m_modelParamTypeIds.value(discoveredThingClassId), result.product); params << Param(m_serialNumberParamTypeIds.value(discoveredThingClassId), result.serialNumber); descriptor.setParams(params); @@ -150,124 +147,44 @@ void IntegrationPluginKeba::setupThing(ThingSetupInfo *info) } } - QHostAddress address = QHostAddress(thing->paramValue(m_ipAddressParamTypeIds.value(thing->thingClassId())).toString()); - // 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()) { - 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."; - info->finish(Thing::ThingErrorThingInUse, QT_TR_NOOP("Already configured for this IP address.")); - return; - } + // Make sure we have a valid mac address, otherwise no monitor and not auto searching is possible + MacAddress macAddress = MacAddress(thing->paramValue(m_macAddressParamTypeIds.value(thing->thingClassId())).toString()); + if (macAddress.isNull()) { + qCWarning(dcKeba()) << "Failed to set up keba because the MAC address is not valid:" << thing->paramValue(m_macAddressParamTypeIds.value(thing->thingClassId())).toString() << macAddress.toString(); + info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not vaild. Please reconfigure the device to fix this.")); + return; } - KeContact *keba = new KeContact(address, m_kebaDataLayer, this); - connect(keba, &KeContact::reachableChanged, this, &IntegrationPluginKeba::onConnectionChanged); - connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted); - connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived); - connect(keba, &KeContact::reportThreeReceived, this, &IntegrationPluginKeba::onReportThreeReceived); - connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived); - connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived); - - connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); // Clean up if the setup fails - - // Make sure we receive data from the keba and the DIP switches are configured correctly - connect(keba, &KeContact::reportOneReceived, info, [=] (const KeContact::ReportOne &report) { - Thing *thing = info->thing(); - qCDebug(dcKeba()) << "Report one received for" << thing->name(); - qCDebug(dcKeba()) << " - Firmware" << report.firmware; - qCDebug(dcKeba()) << " - Serial" << report.serialNumber; - qCDebug(dcKeba()) << " - Product" << report.product; - qCDebug(dcKeba()) << " - Uptime" << report.seconds / 60 << "[min]"; - qCDebug(dcKeba()) << " - Com Module" << report.comModule; - qCDebug(dcKeba()) << " - DIP switch 1" << report.dipSw1; - qCDebug(dcKeba()) << " - DIP switch 2" << report.dipSw2; - - KebaProductInfo productInformation(report.product); - - if (thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString().isEmpty()) { - qCDebug(dcKeba()) << "Update serial number parameter for" << thing << "to" << report.serialNumber; - thing->setParamValue(m_serialNumberParamTypeIds.value(thing->thingClassId()), report.serialNumber); - } - - if (thing->paramValue(m_modelParamTypeIds.value(thing->thingClassId())).toString().isEmpty()) { - qCDebug(dcKeba()) << "Update model parameter for" << thing << "to" << report.product; - thing->setParamValue(m_modelParamTypeIds.value(thing->thingClassId()), report.product); - } - - // Verify the DIP switches and warn the user in case if wrong configuration - // For having UPD controll on the keba we need DIP Switch 1.3 enabled - KeContact::DipSwitchOneFlag dipSwOne(report.dipSw1); - qCDebug(dcKeba()) << dipSwOne; - if (!dipSwOne.testFlag(KeContact::DipSwitchOneSmartHomeInterface)) { - qCWarning(dcKeba()) << "Connected successfully to Keba but the DIP Switch for controlling it is not enabled."; - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The required communication interface is not enabled on this keba. Please make sure the DIP switch 1.3 is switched on and try again.")); + // Create a monitor so we always get the correct IP in the network and see if the device is reachable without polling on our own + NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress); + connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ + // Only if the setup has been finished + KeContact *keba = m_kebaDevices.value(thing->id()); + if (!keba) return; + + qCDebug(dcKeba()) << "Network device monitor reachable changed for" << thing->name() << reachable; + if (reachable) { + // Update address and refresh + keba->setAddress(monitor->networkDeviceInfo().address()); + refresh(thing, keba); } - - // Parse the product code and check if the model actually supports the UDP/Modbus communication - // Supported are: - // - The A series (german edition), no meter DE440 (green edition) - // - The B series (german edition), no meter DE440 - // - All C series - // - All X series - - if (productInformation.isValid()) { - - bool supported = false; - - qCDebug(dcKeba()) << "Product information are valid. Evaluating if model supports UDP/Modbus communication..."; - - switch (productInformation.series()) { - case KebaProductInfo::SeriesA: - if (productInformation.model() == "P30" && productInformation.germanEdition()) { - qCDebug(dcKeba()) << "The P30 A series german edition is supported (DE440 GREEN EDITION)"; - supported = true; - } - break; - case KebaProductInfo::SeriesB: - if (productInformation.model() == "P30" && productInformation.germanEdition()) { - qCDebug(dcKeba()) << "The P30 B series german edition is supported (DE440)"; - supported = true; - } - break; - case KebaProductInfo::SeriesC: - case KebaProductInfo::SeriesXWlan: - case KebaProductInfo::SeriesXWlan3G: - case KebaProductInfo::SeriesXWlan4G: - case KebaProductInfo::SeriesX3G: - case KebaProductInfo::SeriesX4G: - qCDebug(dcKeba()) << "The keba" << productInformation.series() << "is capable of communicating using UDP"; - supported = true; - break; - default: - break; - } - - if (!supported) { - qCWarning(dcKeba()) << "Connected successfully to Keba but this model" << productInformation.series() << "has no communication module."; - info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("This model does not support communication with smart devices.")); - return; - } - } else { - qCWarning(dcKeba()) << "Product information are not valid. Cannot determin if this model supports UDP/Modbus communication, assuming yes so let's try to init..."; - } - - m_kebaDevices.insert(thing->id(), keba); - info->finish(Thing::ThingErrorNoError); - qCDebug(dcKeba()) << "Setup finsihed successfully for" << thing << thing->params(); - - thing->setStateValue("connected", true); - thing->setStateValue("firmware", report.firmware); - thing->setStateValue("uptime", report.seconds / 60); }); - keba->getReport1(); - - connect(keba, &KeContact::destroyed, this, [this, thing]{ - m_kebaDevices.remove(thing->id()); - // Setup failed, lets search the network, maybe the IP has changed... - searchNetworkDevices(); - }); + // Continue with setup only if we know that the network device is reachable + m_monitors.insert(thing, monitor); + if (monitor->reachable()) { + setupKeba(info, monitor->networkDeviceInfo().address()); + } else { + // otherwise wait until we reach the networkdevice before setting up the device + qCDebug(dcKeba()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable."; + connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){ + if (reachable) { + qCDebug(dcKeba()) << "Network device" << thing->name() << "is now reachable. Continue with the setup..."; + setupKeba(info, monitor->networkDeviceInfo().address()); + } + }); + } } void IntegrationPluginKeba::postSetupThing(Thing *thing) @@ -278,18 +195,9 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) if (!keba) { qCWarning(dcKeba()) << "No Keba connection found for this thing while doing post setup."; return; - } else { - keba->getReport2(); - // No valid information if no meter - if (thing->thingClassId() != kebaSimpleThingClassId) - keba->getReport3(); } - // Try to find the mac address in case the user added the ip manually - if (thing->paramValue(m_macAddressParamTypeIds.value(thing->thingClassId())).toString().isEmpty() - || thing->paramValue(m_macAddressParamTypeIds.value(thing->thingClassId())).toString() == "00:00:00:00:00:00") { - searchNetworkDevices(); - } + refresh(thing, keba); if (!m_updateTimer) { m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); @@ -297,75 +205,47 @@ void IntegrationPluginKeba::postSetupThing(Thing *thing) foreach (const ThingId &thingId, m_kebaDevices.keys()) { KeContact *keba = m_kebaDevices.value(thingId); Thing *thing = myThings().findById(thingId); + if (!thing) + return; + if (!keba) { qCWarning(dcKeba()) << "No Keba connection found for" << thing->name(); return; } - keba->getReport2(); - keba->getReport3(); - if (thing->stateValue("activity").toString() == "Charging") { - keba->getReport1XX(100); - } + refresh(thing, keba); + } }); m_updateTimer->start(); } - - if (!m_reconnectTimer) { - m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer(60 * 5); - 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 - foreach (const ThingId &thingId, m_kebaDevices.keys()) { - KeContact *keba = m_kebaDevices.value(thingId); - Thing *thing = myThings().findById(thingId); - if (!keba) { - qCWarning(dcKeba()) << "No Keba connection found for" << thing->name(); - startDiscoveryRequired = true; - continue; - } - - if (!keba->reachable()) { - startDiscoveryRequired = true; - return; - } - } - - if (startDiscoveryRequired) - searchNetworkDevices(); - }); - - m_reconnectTimer->start(); - } } void IntegrationPluginKeba::thingRemoved(Thing *thing) { - qCDebug(dcKeba()) << "Deleting" << thing->name(); + qCDebug(dcKeba()) << "Removing" << thing->name(); if (m_kebaDevices.contains(thing->id())) { KeContact *keba = m_kebaDevices.take(thing->id()); keba->deleteLater(); } + if (m_monitors.contains(thing)) { + hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); + } + m_lastSessionId.remove(thing->id()); if (myThings().empty()) { - qCDebug(dcKeba()) << "Closing UDP Ports"; - m_kebaDataLayer->deleteLater(); - m_kebaDataLayer= nullptr; - qCDebug(dcKeba()) << "Stopping plugin timers ..."; - if (m_reconnectTimer) { - hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer); - m_reconnectTimer = nullptr; - } - if (m_updateTimer) { hardwareManager()->pluginTimerManager()->unregisterTimer(m_updateTimer); m_updateTimer = nullptr; } + + qCDebug(dcKeba()) << "Closing keba data layer..."; + m_kebaDataLayer->deleteLater(); + m_kebaDataLayer= nullptr; } } @@ -442,6 +322,115 @@ void IntegrationPluginKeba::executeAction(ThingActionInfo *info) connect(info, &ThingActionInfo::aborted, this, [requestId, this]{ m_asyncActions.remove(requestId); }); } +void IntegrationPluginKeba::setupKeba(ThingSetupInfo *info, const QHostAddress &address) +{ + Thing *thing = info->thing(); + KeContact *keba = new KeContact(address, m_kebaDataLayer, this); + connect(keba, &KeContact::reachableChanged, thing, [=](bool reachable){ + thing->setStateValue("connected", reachable); + }); + + connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted); + connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived); + connect(keba, &KeContact::reportThreeReceived, this, &IntegrationPluginKeba::onReportThreeReceived); + connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived); + connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived); + + // Clean up if the setup fails + connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater); + + // Make sure we receive data from the keba and the DIP switches are configured correctly + connect(keba, &KeContact::reportOneReceived, info, [=] (const KeContact::ReportOne &report) { + Thing *thing = info->thing(); + qCDebug(dcKeba()) << "Report one received for" << thing->name(); + qCDebug(dcKeba()) << " - Firmware" << report.firmware; + qCDebug(dcKeba()) << " - Serial" << report.serialNumber; + qCDebug(dcKeba()) << " - Product" << report.product; + qCDebug(dcKeba()) << " - Uptime" << report.seconds / 60 << "[min]"; + qCDebug(dcKeba()) << " - Com Module" << report.comModule; + qCDebug(dcKeba()) << " - DIP switch 1" << report.dipSw1; + qCDebug(dcKeba()) << " - DIP switch 2" << report.dipSw2; + + KebaProductInfo productInformation(report.product); + + if (thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString().isEmpty()) { + qCDebug(dcKeba()) << "Update serial number parameter for" << thing << "to" << report.serialNumber; + thing->setParamValue(m_serialNumberParamTypeIds.value(thing->thingClassId()), report.serialNumber); + } + + if (thing->paramValue(m_modelParamTypeIds.value(thing->thingClassId())).toString().isEmpty()) { + qCDebug(dcKeba()) << "Update model parameter for" << thing << "to" << report.product; + thing->setParamValue(m_modelParamTypeIds.value(thing->thingClassId()), report.product); + } + + // Verify the DIP switches and warn the user in case if wrong configuration + // For having UPD controll on the keba we need DIP Switch 1.3 enabled + KeContact::DipSwitchOneFlag dipSwOne(report.dipSw1); + qCDebug(dcKeba()) << dipSwOne; + if (!dipSwOne.testFlag(KeContact::DipSwitchOneSmartHomeInterface)) { + qCWarning(dcKeba()) << "Connected successfully to Keba but the DIP Switch for controlling it is not enabled."; + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The required communication interface is not enabled on this keba. Please make sure the DIP switch 1.3 is switched on and try again.")); + return; + } + + // Parse the product code and check if the model actually supports the UDP/Modbus communication + // Supported are: + // - The A series (german edition), no meter DE440 (green edition) + // - The B series (german edition), no meter DE440 + // - All C series + // - All X series + + if (productInformation.isValid()) { + bool supported = false; + qCDebug(dcKeba()) << "Product information are valid. Evaluating if model supports UDP/Modbus communication..."; + + switch (productInformation.series()) { + case KebaProductInfo::SeriesA: + if (productInformation.model() == "P30" && productInformation.germanEdition()) { + qCDebug(dcKeba()) << "The P30 A series german edition is supported (DE440 GREEN EDITION)"; + supported = true; + } + break; + case KebaProductInfo::SeriesB: + if (productInformation.model() == "P30" && productInformation.germanEdition()) { + qCDebug(dcKeba()) << "The P30 B series german edition is supported (DE440)"; + supported = true; + } + break; + case KebaProductInfo::SeriesC: + case KebaProductInfo::SeriesXWlan: + case KebaProductInfo::SeriesXWlan3G: + case KebaProductInfo::SeriesXWlan4G: + case KebaProductInfo::SeriesX3G: + case KebaProductInfo::SeriesX4G: + qCDebug(dcKeba()) << "The keba" << productInformation.series() << "is capable of communicating using UDP"; + supported = true; + break; + default: + break; + } + + if (!supported) { + qCWarning(dcKeba()) << "Connected successfully to Keba but this model" << productInformation.series() << "has no communication module."; + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("This model does not support communication with smart devices.")); + return; + } + } else { + qCWarning(dcKeba()) << "Product information are not valid. Cannot determin if this model supports UDP/Modbus communication, assuming yes so let's try to init..."; + } + + m_kebaDevices.insert(thing->id(), keba); + info->finish(Thing::ThingErrorNoError); + qCDebug(dcKeba()) << "Setup finsihed successfully for" << thing << thing->params(); + + thing->setStateValue("connected", true); + thing->setStateValue("firmware", report.firmware); + thing->setStateValue("uptime", report.seconds / 60); + }); + + keba->getReport1(); +} + void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success) { if (m_asyncActions.contains(requestId)) { @@ -534,79 +523,19 @@ void IntegrationPluginKeba::setDevicePlugState(Thing *thing, KeContact::PlugStat } } -void IntegrationPluginKeba::searchNetworkDevices() +void IntegrationPluginKeba::refresh(Thing *thing, KeContact *keba) { - if (m_runningDiscovery) { - qCDebug(dcKeba()) << "Keba discovery already running."; + if (m_monitors.contains(thing) && !m_monitors.value(thing)->reachable()) return; + + keba->getReport2(); + // No valid information if no meter + if (thing->thingClassId() != kebaSimpleThingClassId) { + keba->getReport3(); } - if (!m_kebaDataLayer) { - qCDebug(dcKeba()) << "Could not search keba wallboxes in the network. The data layer seems not to be available"; - return; - } - - qCDebug(dcKeba()) << "Start searching keba 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 (const ThingId &thingId, m_kebaDevices.keys()) { - Thing *existingThing = myThings().findById(thingId); - if (!existingThing) - continue; - - if (existingThing->paramValue(m_macAddressParamTypeIds.value(existingThing->thingClassId())).toString().isEmpty()) { - //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup - if (existingThing->paramValue(m_ipAddressParamTypeIds.value(existingThing->thingClassId())).toString() == result.networkDeviceInfo.address().toString()) { - qCDebug(dcKeba()) << "Wallbox MAC address has been discovered" << existingThing->name() << result.networkDeviceInfo.macAddress(); - existingThing->setParamValue(m_macAddressParamTypeIds.value(existingThing->thingClassId()), result.networkDeviceInfo.macAddress()); - } - } else if (existingThing->paramValue(m_macAddressParamTypeIds.value(existingThing->thingClassId())).toString() == result.networkDeviceInfo.macAddress()) { - // We found the existing keba thing, lets check if the ip has changed - if (existingThing->paramValue(m_ipAddressParamTypeIds.value(existingThing->thingClassId())).toString() != 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(m_ipAddressParamTypeIds.value(existingThing->thingClassId())).toString() - << "to" << result.networkDeviceInfo.address().toString(); - existingThing->setParamValue(m_ipAddressParamTypeIds.value(existingThing->thingClassId()), 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()); - if (keba) { - keba->setAddress(QHostAddress(result.networkDeviceInfo.address())); - - // Refresh - keba->getReport2(); - keba->getReport3(); - } else { - qCWarning(dcKeba()) << "Could not update IP address since the keba connection has not been set up yet for" << existingThing; - } - } else { - qCDebug(dcKeba()) << "Wallbox" << existingThing->name() << "IP address has not changed" << result.networkDeviceInfo.address().toString(); - } - break; - } - } - } - - // Clean up - m_runningDiscovery->deleteLater(); - m_runningDiscovery = nullptr; - }); -} - -void IntegrationPluginKeba::onConnectionChanged(bool status) -{ - KeContact *keba = static_cast(sender()); - Thing *thing = myThings().findById(m_kebaDevices.key(keba)); - if (!thing) { - qCDebug(dcKeba()) << "Received connected changed but the thing seems not to be setup yet."; - return; - } - - thing->setStateValue("connected", status); - if (!status) { - searchNetworkDevices(); + if (thing->stateValue("activity").toString() == "Charging") { + keba->getReport1XX(100); } } diff --git a/keba/integrationpluginkeba.h b/keba/integrationpluginkeba.h index 8ff90426..0e347a97 100644 --- a/keba/integrationpluginkeba.h +++ b/keba/integrationpluginkeba.h @@ -31,8 +31,8 @@ #ifndef INTEGRATIONPLUGINKEBA_H #define INTEGRATIONPLUGINKEBA_H -#include #include +#include #include #include "kecontact.h" @@ -68,26 +68,26 @@ public: private: PluginTimer *m_updateTimer = nullptr; - PluginTimer *m_reconnectTimer = nullptr; KeContactDataLayer *m_kebaDataLayer = nullptr; QHash m_kebaDevices; + QHash m_monitors; QHash m_lastSessionId; QHash m_asyncActions; KebaDiscovery *m_runningDiscovery = nullptr; QHash m_macAddressParamTypeIds; - QHash m_ipAddressParamTypeIds; QHash m_modelParamTypeIds; QHash m_serialNumberParamTypeIds; + void setupKeba(ThingSetupInfo *info, const QHostAddress &address); + void setDeviceState(Thing *device, KeContact::State state); void setDevicePlugState(Thing *device, KeContact::PlugState plugState); - void searchNetworkDevices(); + void refresh(Thing *thing, KeContact *keba); private slots: - void onConnectionChanged(bool status); void onCommandExecuted(QUuid requestId, bool success); void onReportTwoReceived(const KeContact::ReportTwo &reportTwo); void onReportThreeReceived(const KeContact::ReportThree &reportThree); diff --git a/keba/integrationpluginkeba.json b/keba/integrationpluginkeba.json index 160e844c..dc2b0445 100644 --- a/keba/integrationpluginkeba.json +++ b/keba/integrationpluginkeba.json @@ -15,14 +15,6 @@ "createMethods": ["discovery", "user"], "interfaces": ["evcharger", "smartmeterconsumer", "connectable"], "paramTypes":[ - { - "id": "730cd3d3-5f0e-4028-a8c2-ced7574f13f3", - "name": "ipAddress", - "displayName": "IP address", - "type": "QString", - "inputType": "IPv4Address", - "defaultValue":"0.0.0.0" - }, { "id": "c2df921d-ff8b-411c-9b1d-04a437d7dfa6", "name": "macAddress", @@ -401,14 +393,6 @@ "createMethods": ["discovery", "user"], "interfaces": ["evcharger", "connectable"], "paramTypes":[ - { - "id": "8324cad1-0d9d-4e48-b472-8c22eb7a1057", - "name": "ipAddress", - "displayName": "IP address", - "type": "QString", - "inputType": "IPv4Address", - "defaultValue":"0.0.0.0" - }, { "id": "e438179a-5202-4106-a622-d9e10a74fed9", "name": "macAddress", diff --git a/keba/kebadiscovery.cpp b/keba/kebadiscovery.cpp index d5f2937f..be73dc6a 100644 --- a/keba/kebadiscovery.cpp +++ b/keba/kebadiscovery.cpp @@ -41,10 +41,10 @@ KebaDiscovery::KebaDiscovery(KeContactDataLayer *kebaDataLayer, NetworkDeviceDis m_networkDeviceDiscovery(networkDeviceDiscovery) { // Timer for waiting if network devices responded to the "report 1 request" - m_responseTimer.setInterval(5000); + m_responseTimer.setInterval(2000); m_responseTimer.setSingleShot(true); connect(&m_responseTimer, &QTimer::timeout, this, [=](){ - qCDebug(dcKeba()) << "Discovery: Report response timeout. Found" << m_results.count() << "Keba Wallbox"; + qCInfo(dcKeba()) << "Discovery: Finished successfully. Found" << m_results.count() << "Keba Wallbox"; emit discoveryFinished(); }); @@ -73,48 +73,56 @@ KebaDiscovery::KebaDiscovery(KeContactDataLayer *kebaDataLayer, NetworkDeviceDis } if (dataMap.value("ID").toInt() != 1) { - qCDebug(dcKeba()) << "Discovery: Received valid Keba JSON data on data layer but this is not a report 1 message:" << qUtf8Printable(jsonDoc.toJson()); + qCDebug(dcKeba()) << "Discovery: Received valid Keba JSON data on data layer but this is not a report 1 we requested for:" << qUtf8Printable(jsonDoc.toJson()); return; } // We have received a report 1 datagram, let's add it to the result - foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) { - if (networkDeviceInfo.address() == address) { - KebaDiscoveryResult result; - result.networkDeviceInfo = networkDeviceInfo; - result.product = dataMap.value("Product").toString(); - result.serialNumber = dataMap.value("Serial").toString(); - result.firmwareVersion = dataMap.value("Firmware").toString(); - m_results.append(result); - qCDebug(dcKeba()) << "Discovery: -->" << networkDeviceInfo << networkDeviceInfo.macAddress() << result.product << result.serialNumber << result.firmwareVersion; - } + NetworkDeviceInfo networkDeviceInfo = m_verifiedNetworkDeviceInfos.get(address); + if (networkDeviceInfo.isValid()) { + KebaDiscoveryResult result; + result.networkDeviceInfo = networkDeviceInfo; + result.product = dataMap.value("Product").toString(); + result.serialNumber = dataMap.value("Serial").toString(); + result.firmwareVersion = dataMap.value("Firmware").toString(); + m_results.append(result); + qCDebug(dcKeba()) << "Discovery: -->" << networkDeviceInfo << networkDeviceInfo.macAddress() << result.product << result.serialNumber << result.firmwareVersion; } }); - } KebaDiscovery::~KebaDiscovery() { - qCDebug(dcKeba()) << "Discovery: Destroying object."; + qCDebug(dcKeba()) << "Discovery: Destructing"; } void KebaDiscovery::startDiscovery() { // Clean up - m_networkDeviceInfos.clear(); - m_results.clear(); + cleanup(); - qCDebug(dcKeba()) << "Discovery: Start discovering Keba Wallboxs..."; + qCInfo(dcKeba()) << "Discovery: Start searching for Keba wallboxes in the network..."; NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover(); + + // Check any already discovered infos.. + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + sendReportRequest(networkDeviceInfo); + } + + // Imedialty check any new device gets discovered + connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &KebaDiscovery::sendReportRequest); + + // Check what might be left on finished connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ qCDebug(dcKeba()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices"; m_networkDeviceInfos = discoveryReply->networkDeviceInfos(); - - // Send a report 1 request to all discovered network devices and see which one responds - qCDebug(dcKeba()) << "Discovery: Start sending \"report 1\" request to all discovered network devices"; - foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) - m_kebaDataLayer->write(networkDeviceInfo.address(), QByteArray("report 1\n")); - + qCDebug(dcKeba()) << "Discovery: Network discovery finished. Start finishing discovery..."; + // Send a report request to nework device info not sent already... + foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) { + if (!m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) { + sendReportRequest(networkDeviceInfo); + } + } m_responseTimer.start(); }); } @@ -134,3 +142,17 @@ bool KebaDiscovery::alreadyDiscovered(const QHostAddress &address) return false; } + +void KebaDiscovery::cleanup() +{ + m_networkDeviceInfos.clear(); + m_verifiedNetworkDeviceInfos.clear(); + m_results.clear(); +} + +void KebaDiscovery::sendReportRequest(const NetworkDeviceInfo &networkDeviceInfo) +{ + m_verifiedNetworkDeviceInfos.append(networkDeviceInfo); + m_kebaDataLayer->write(networkDeviceInfo.address(), QByteArray("report 1\n")); +} + diff --git a/keba/kebadiscovery.h b/keba/kebadiscovery.h index d7556944..df36c0dd 100644 --- a/keba/kebadiscovery.h +++ b/keba/kebadiscovery.h @@ -64,10 +64,18 @@ private: KeContactDataLayer *m_kebaDataLayer = nullptr; NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; QTimer m_responseTimer; + NetworkDeviceInfos m_networkDeviceInfos; + NetworkDeviceInfos m_verifiedNetworkDeviceInfos; QList m_results; bool alreadyDiscovered(const QHostAddress &address); + + void cleanup(); + +private slots: + void sendReportRequest(const NetworkDeviceInfo &networkDeviceInfo); + }; #endif // KEBADISCOVERY_H