Use network-monitor for keba wallboxes

master
Simon Stürz 2022-05-24 17:10:13 +02:00
parent 212ffb1377
commit 99a1985958
5 changed files with 225 additions and 282 deletions

View File

@ -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<KeContact *>(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);
}
}

View File

@ -31,8 +31,8 @@
#ifndef INTEGRATIONPLUGINKEBA_H
#define INTEGRATIONPLUGINKEBA_H
#include <integrations/integrationplugin.h>
#include <plugintimer.h>
#include <integrations/integrationplugin.h>
#include <network/networkdevicediscovery.h>
#include "kecontact.h"
@ -68,26 +68,26 @@ public:
private:
PluginTimer *m_updateTimer = nullptr;
PluginTimer *m_reconnectTimer = nullptr;
KeContactDataLayer *m_kebaDataLayer = nullptr;
QHash<ThingId, KeContact *> m_kebaDevices;
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
QHash<ThingId, int> m_lastSessionId;
QHash<QUuid, ThingActionInfo *> m_asyncActions;
KebaDiscovery *m_runningDiscovery = nullptr;
QHash<ThingClassId, ParamTypeId> m_macAddressParamTypeIds;
QHash<ThingClassId, ParamTypeId> m_ipAddressParamTypeIds;
QHash<ThingClassId, ParamTypeId> m_modelParamTypeIds;
QHash<ThingClassId, ParamTypeId> 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);

View File

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

View File

@ -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"));
}

View File

@ -64,10 +64,18 @@ private:
KeContactDataLayer *m_kebaDataLayer = nullptr;
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
QTimer m_responseTimer;
NetworkDeviceInfos m_networkDeviceInfos;
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
QList<KebaDiscoveryResult> m_results;
bool alreadyDiscovered(const QHostAddress &address);
void cleanup();
private slots:
void sendReportRequest(const NetworkDeviceInfo &networkDeviceInfo);
};
#endif // KEBADISCOVERY_H