Merge PR #204: Webasto: Update to networkdevice interface

This commit is contained in:
jenkins 2025-03-30 20:37:33 +02:00
commit c3ed7f481b
7 changed files with 275 additions and 158 deletions

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -67,7 +67,7 @@ void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info)
qCInfo(dcWebasto()) << "Start discovering webasto live in the local network...";
NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [this, discoveryReply, info](){
ThingDescriptors descriptors;
qCDebug(dcWebasto()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices";
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
@ -83,25 +83,38 @@ void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info)
}
QString description;
if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
description = networkDeviceInfo.macAddress();
} else {
description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")";
MacAddressInfo macInfo;
switch (networkDeviceInfo.monitorMode()) {
case NetworkDeviceInfo::MonitorModeMac:
macInfo = networkDeviceInfo.macAddressInfos().constFirst();
description = macInfo.macAddress().toString();
if (!macInfo.vendorName().isEmpty())
description += " - " + macInfo.vendorName();
break;
case NetworkDeviceInfo::MonitorModeHostName:
description = networkDeviceInfo.hostName();
break;
case NetworkDeviceInfo::MonitorModeIp:
description = "Interface: " + networkDeviceInfo.networkInterface().name();
break;
}
ThingDescriptor descriptor(webastoLiveThingClassId, title, description);
ParamList params;
params << Param(webastoLiveThingMacAddressParamTypeId, networkDeviceInfo.thingParamValueMacAddress());
params << Param(webastoLiveThingAddressParamTypeId, networkDeviceInfo.thingParamValueAddress());
params << Param(webastoLiveThingHostNameParamTypeId, networkDeviceInfo.thingParamValueHostName());
descriptor.setParams(params);
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(webastoLiveThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
if (existingThings.count() == 1) {
qCDebug(dcWebasto()) << "This thing already exists in the system." << existingThings.first() << networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
Thing *existingThing = myThings().findByParams(params);
if (existingThing) {
qCDebug(dcWebasto()) << "This thing already exists in the system:" << networkDeviceInfo;
descriptor.setThingId(existingThing->id());
}
ParamList params;
params << Param(webastoLiveThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
params << Param(webastoLiveThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
@ -117,7 +130,7 @@ void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info)
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
WebastoDiscovery *discovery = new WebastoDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
connect(discovery, &WebastoDiscovery::discoveryFinished, info, [=](){
connect(discovery, &WebastoDiscovery::discoveryFinished, info, [this, discovery, info](){
foreach (const WebastoDiscovery::Result &result, discovery->results()) {
QString title = "Webasto Next";
@ -125,25 +138,39 @@ void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info)
title.append(" (" + result.networkDeviceInfo.hostName() + ")");
}
QString description = result.networkDeviceInfo.address().toString();
if (result.networkDeviceInfo.macAddressManufacturer().isEmpty()) {
description += " " + result.networkDeviceInfo.macAddress();
} else {
description += " " + result.networkDeviceInfo.macAddress() + " (" + result.networkDeviceInfo.macAddressManufacturer() + ")";
QString description;
MacAddressInfo macInfo;
switch (result.networkDeviceInfo.monitorMode()) {
case NetworkDeviceInfo::MonitorModeMac:
macInfo = result.networkDeviceInfo.macAddressInfos().constFirst();
description = macInfo.macAddress().toString();
if (!macInfo.vendorName().isEmpty())
description += " - " + macInfo.vendorName();
break;
case NetworkDeviceInfo::MonitorModeHostName:
description = result.networkDeviceInfo.hostName();
break;
case NetworkDeviceInfo::MonitorModeIp:
description = "Interface: " + result.networkDeviceInfo.networkInterface().name();
break;
}
ThingDescriptor descriptor(webastoNextThingClassId, title, description);
ParamList params;
params << Param(webastoNextThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress());
params << Param(webastoNextThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName());
params << Param(webastoNextThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress());
descriptor.setParams(params);
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(webastoNextThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
if (existingThings.count() == 1) {
qCDebug(dcWebasto()) << "This thing already exists in the system." << existingThings.first() << result.networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
Thing *existingThing = myThings().findByParams(params);
if (existingThing) {
qCDebug(dcWebasto()) << "This thing already exists in the system:" << result.networkDeviceInfo;
descriptor.setThingId(existingThing->id());
}
ParamList params;
params << Param(webastoNextThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
@ -168,24 +195,29 @@ void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info)
ThingDescriptor descriptor(webastoUniteThingClassId, name, description);
qCDebug(dcWebasto()) << "Discovered:" << descriptor.title() << descriptor.description();
ParamList params;
params << Param(webastoUniteThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress());
params << Param(webastoUniteThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName());
params << Param(webastoUniteThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress());
descriptor.setParams(params);
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(webastoUniteThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
if (existingThings.count() == 1) {
qCDebug(dcWebasto()) << "This wallbox already exists in the system:" << result.networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
Thing *existingThing = myThings().findByParams(params);
if (existingThing) {
qCDebug(dcWebasto()) << "This thing already exists in the system:" << result.networkDeviceInfo;
descriptor.setThingId(existingThing->id());
}
ParamList params;
params << Param(webastoUniteThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
discovery->startDiscovery();
discovery->startDiscovery();
return;
}
Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
}
@ -199,25 +231,73 @@ void IntegrationPluginWebasto::setupThing(ThingSetupInfo *info)
if (m_webastoLiveConnections.contains(thing)) {
// Clean up after reconfiguration
m_webastoLiveConnections.take(thing)->deleteLater();
if (m_monitors.contains(thing)) {
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
}
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing);
if (!monitor) {
qCWarning(dcWebasto()) << "Unable to register monitor with the given params" << thing->params();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("Unable to set up the connection with this configuration, please reconfigure the connection."));
return;
}
m_monitors.insert(thing, monitor);
connect(info, &ThingSetupInfo::aborted, monitor, [this, thing](){
if (m_monitors.contains(thing)) {
qCDebug(dcWebasto()) << "Unregistering monitor because setup has been aborted.";
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
if (m_webastoLiveConnections.contains(thing)) {
qCDebug(dcWebasto()) << "Clean up connection because setup has been aborted.";
m_webastoLiveConnections.take(thing)->deleteLater();
}
});
QHostAddress address = monitor->networkDeviceInfo().address();
if (address.isNull()) {
qCWarning(dcWebasto()) << "Cannot set up thing. The host address is not known yet. Maybe it will be available in the next run...";
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The host address is not known yet. Trying later again."));
return;
}
QHostAddress address = QHostAddress(thing->paramValue(webastoLiveThingIpAddressParamTypeId).toString());
Webasto *webasto = new Webasto(address, 502, thing);
m_webastoLiveConnections.insert(thing, webasto);
connect(webasto, &Webasto::destroyed, this, [thing, this] {m_webastoLiveConnections.remove(thing);});
connect(webasto, &Webasto::connectionStateChanged, this, &IntegrationPluginWebasto::onConnectionChanged);
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [monitor, webasto, thing](bool reachable){
qCDebug(dcWebasto()) << "Network device monitor reachable changed for" << thing->name() << reachable;
if (!thing->setupComplete())
return;
if (reachable && !thing->stateValue("connected").toBool()) {
webasto->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address());
webasto->connectDevice();
} else if (!reachable) {
// Note: We disable autoreconnect explicitly and we will
// connect the device once the monitor says it is reachable again
webasto->modbusTcpMaster()->disconnectDevice();
}
});
connect(webasto, &Webasto::connectionStateChanged, this, [thing](bool state){
thing->setStateValue(webastoLiveConnectedStateTypeId, state);
});
connect(webasto, &Webasto::receivedRegister, this, &IntegrationPluginWebasto::onReceivedRegister);
connect(webasto, &Webasto::writeRequestError, this, &IntegrationPluginWebasto::onWriteRequestError);
connect(webasto, &Webasto::writeRequestExecuted, this, &IntegrationPluginWebasto::onWriteRequestExecuted);
if (!webasto->connectDevice()) {
qCWarning(dcWebasto()) << "Could not connect to device";
info->finish(Thing::ThingErrorSetupFailed);
}
connect(webasto, &Webasto::connectionStateChanged, info, [info] (bool connected) {
if (connected)
info->finish(Thing::ThingErrorNoError);
});
info->finish(Thing::ThingErrorNoError);
return;
}
@ -233,15 +313,14 @@ void IntegrationPluginWebasto::setupThing(ThingSetupInfo *info)
}
}
MacAddress macAddress = MacAddress(thing->paramValue(webastoNextThingMacAddressParamTypeId).toString());
if (!macAddress.isValid()) {
qCWarning(dcWebasto()) << "The configured mac address is not valid" << thing->params();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
// Create the monitor
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing);
if (!monitor) {
qCWarning(dcWebasto()) << "Unable to register monitor with the given params" << thing->params();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("Unable to set up the connection with this configuration, please reconfigure the connection."));
return;
}
// Create the monitor
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
m_monitors.insert(thing, monitor);
QHostAddress address = monitor->networkDeviceInfo().address();
@ -294,14 +373,13 @@ void IntegrationPluginWebasto::setupThing(ThingSetupInfo *info)
}
}
MacAddress macAddress = MacAddress(thing->paramValue(webastoUniteThingMacAddressParamTypeId).toString());
if (!macAddress.isValid()) {
qCWarning(dcWebasto()) << "The configured mac address is not valid" << thing->params();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing);
if (!monitor) {
qCWarning(dcWebasto()) << "Unable to register monitor with the given params" << thing->params();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("Unable to set up the connection with this configuration, please reconfigure the connection."));
return;
}
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
m_monitors.insert(thing, monitor);
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
@ -311,23 +389,28 @@ void IntegrationPluginWebasto::setupThing(ThingSetupInfo *info)
}
});
if (monitor->reachable()) {
setupEVC04Connection(info);
// If this is the first setup, the monitor must become reachable before we finish the setup
if (info->isInitialSetup()) {
// Wait for the monitor to be ready
if (monitor->reachable()) {
setupEVC04Connection(info);
} else {
qCDebug(dcWebasto()) << "Waiting for the network monitor to get reachable before continuing to set up the connection" << thing->name() << "...";
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
if (reachable) {
qCDebug(dcWebasto()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continuing setup on" << monitor->networkDeviceInfo().address().toString();
setupEVC04Connection(info);
}
});
}
} else {
qCDebug(dcWebasto()) << "Waiting for the network monitor to get reachable before continuing to set up the connection" << thing->name() << "...";
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
if (reachable) {
qCDebug(dcWebasto()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continuing setup on" << monitor->networkDeviceInfo().address().toString();
setupEVC04Connection(info);
}
});
// Not the first setup, just add and let the monitor do the check reachable work
setupEVC04Connection(info);
}
return;
}
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
void IntegrationPluginWebasto::postSetupThing(Thing *thing)
@ -936,17 +1019,6 @@ void IntegrationPluginWebasto::executeWebastoNextPowerAction(ThingActionInfo *in
});
}
void IntegrationPluginWebasto::onConnectionChanged(bool connected)
{
Webasto *connection = static_cast<Webasto *>(sender());
Thing *thing = m_webastoLiveConnections.key(connection);
if (!thing) {
qCWarning(dcWebasto()) << "On connection changed, thing not found for connection";
return;
}
thing->setStateValue(webastoLiveConnectedStateTypeId, connected);
}
void IntegrationPluginWebasto::onWriteRequestExecuted(const QUuid &requestId, bool success)
{
if (m_asyncActions.contains(requestId)) {
@ -1258,21 +1330,21 @@ void IntegrationPluginWebasto::setupEVC04Connection(ThingSetupInfo *info)
connect(evc04Connection, &EVC04ModbusTcpConnection::chargepointStateChanged, thing, [thing](EVC04ModbusTcpConnection::ChargePointState chargePointState) {
qCDebug(dcWebasto()) << "Chargepoint state changed" << thing->name() << chargePointState;
// switch (chargePointState) {
// case EVC04ModbusTcpConnection::ChargePointStateAvailable:
// case EVC04ModbusTcpConnection::ChargePointStatePreparing:
// case EVC04ModbusTcpConnection::ChargePointStateReserved:
// case EVC04ModbusTcpConnection::ChargePointStateUnavailable:
// case EVC04ModbusTcpConnection::ChargePointStateFaulted:
// thing->setStateValue(evc04PluggedInStateTypeId, false);
// break;
// case EVC04ModbusTcpConnection::ChargePointStateCharging:
// case EVC04ModbusTcpConnection::ChargePointStateSuspendedEVSE:
// case EVC04ModbusTcpConnection::ChargePointStateSuspendedEV:
// case EVC04ModbusTcpConnection::ChargePointStateFinishing:
// thing->setStateValue(evc04PluggedInStateTypeId, true);
// break;
// }
// switch (chargePointState) {
// case EVC04ModbusTcpConnection::ChargePointStateAvailable:
// case EVC04ModbusTcpConnection::ChargePointStatePreparing:
// case EVC04ModbusTcpConnection::ChargePointStateReserved:
// case EVC04ModbusTcpConnection::ChargePointStateUnavailable:
// case EVC04ModbusTcpConnection::ChargePointStateFaulted:
// thing->setStateValue(evc04PluggedInStateTypeId, false);
// break;
// case EVC04ModbusTcpConnection::ChargePointStateCharging:
// case EVC04ModbusTcpConnection::ChargePointStateSuspendedEVSE:
// case EVC04ModbusTcpConnection::ChargePointStateSuspendedEV:
// case EVC04ModbusTcpConnection::ChargePointStateFinishing:
// thing->setStateValue(evc04PluggedInStateTypeId, true);
// break;
// }
});
connect(evc04Connection, &EVC04ModbusTcpConnection::chargingStateChanged, thing, [thing](EVC04ModbusTcpConnection::ChargingState chargingState) {
qCDebug(dcWebasto()) << "Charging state changed:" << chargingState;

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -69,6 +69,7 @@ private:
QHash<Thing *, Webasto *> m_webastoLiveConnections;
QHash<Thing *, WebastoNextModbusTcpConnection *> m_webastoNextConnections;
QHash<Thing *, EVC04ModbusTcpConnection *> m_evc04Connections;
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
void setupWebastoNextConnection(ThingSetupInfo *info);
@ -88,7 +89,6 @@ private:
void executeWebastoUnitePhaseCountAction(ThingActionInfo *info);
private slots:
void onConnectionChanged(bool connected);
void onWriteRequestExecuted(const QUuid &requestId, bool success);
void onWriteRequestError(const QUuid &requestId, const QString &error);

View File

@ -12,23 +12,33 @@
"id": "48472124-3199-4827-990a-b72069bd5658",
"displayName": "Webasto Live",
"name": "webastoLive",
"createMethods": ["discovery"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable"],
"createMethods": ["discovery", "user"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable", "networkdevice"],
"paramTypes": [
{
"id": "51fa3ea8-e819-46ca-b975-1bee6285441c",
"name": "ipAddress",
"name": "address",
"displayName": "IP address",
"type": "QString",
"defaultValue": "0.0.0.0"
"inputType": "IPv4Address",
"defaultValue": ""
},
{
"id": "8ff1cc86-e0b4-4887-ae2a-95de0f9cb24c",
"name": "hostName",
"displayName": "Host name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": ""
},
{
"id": "4aa97965-fc1c-488a-92a6-848c214564bc",
"name": "macAddress",
"displayName": "MAC address",
"type": "QString",
"defaultValue": "",
"readOnly": true
"inputType": "MacAddress",
"readOnly": true,
"defaultValue": ""
}
],
"stateTypes":[
@ -200,8 +210,8 @@
"id": "1dddfbf4-a49d-4e28-8cbc-108547a369a2",
"displayName": "Webasto NEXT",
"name": "webastoNext",
"createMethods": ["discovery"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable"],
"createMethods": ["discovery", "user"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable", "networkdevice"],
"settingsTypes": [
{
"id": "5292e079-515c-47ae-9117-6a70d5c02566",
@ -224,13 +234,30 @@
}
],
"paramTypes": [
{
"id": "3e971569-dd28-4031-a5bc-86b58c9b9096",
"name": "address",
"displayName": "IP address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": ""
},
{
"id": "eddebfc7-28ee-479d-b528-6837ed974d27",
"name": "hostName",
"displayName": "Host name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": ""
},
{
"id": "882b662f-ec7c-4134-be31-5d36567b9fc2",
"name": "macAddress",
"displayName": "MAC address",
"type": "QString",
"defaultValue": "",
"readOnly": true
"readOnly": true,
"inputType": "MacAddress",
"defaultValue": ""
},
{
"id": "be5a0c50-f3ba-4562-b6c0-a0208e2ab118",
@ -437,8 +464,24 @@
"displayName": "Webasto Unite",
"id": "f7598439-a794-44d4-ae51-47ab40189d61",
"createMethods": ["discovery", "user"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable", "networkdevice"],
"paramTypes": [
{
"id": "41d713fb-4658-4797-ab0f-a978f5677eb9",
"name": "address",
"displayName": "IP address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": ""
},
{
"id": "fb98c75c-fb6b-4043-8794-d941017d471e",
"name": "hostName",
"displayName": "Host name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": ""
},
{
"id": "99aedef2-1b23-4ab8-bee4-7e8b57b8fa18",
"name":"macAddress",

View File

@ -50,15 +50,9 @@ Webasto::Webasto(const QHostAddress &address, uint port, QObject *parent) :
});
}
void Webasto::setAddress(const QHostAddress &address)
ModbusTcpMaster *Webasto::modbusTcpMaster() const
{
qCDebug(dcWebasto()) << "Webasto: set address" << address;
m_modbusConnection->setHostAddress(address);
}
QHostAddress Webasto::address() const
{
return m_modbusConnection->hostAddress();
return m_modbusConnection;
}
bool Webasto::connected()

View File

@ -114,8 +114,8 @@ public:
explicit Webasto(const QHostAddress &address, uint port = 502, QObject *parent = nullptr);
void setAddress(const QHostAddress &address);
QHostAddress address() const;
ModbusTcpMaster *modbusTcpMaster() const;
bool connected();
bool connectDevice();

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -40,21 +40,24 @@ WebastoDiscovery::WebastoDiscovery(NetworkDeviceDiscovery *networkDeviceDiscover
void WebastoDiscovery::startDiscovery()
{
// TODO: add parameter for searching WebastoNext or WebastoLive, for now the discovery searches only for WebastoNext
// TODO: add parameter for searching WebastoNext or WebastoLive, for now the discovery searches only for WebastoNext
qCInfo(dcWebasto()) << "Discovery: Starting to search for WebastoNext wallboxes in the network...";
m_startDateTime = QDateTime::currentDateTime();
qCInfo(dcWebasto()) << "Discovery: Starting to search for WebastoNext wallboxes in the network...";
m_networkDeviceInfos.clear();
m_temporaryResults.clear();
m_results.clear();
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &WebastoDiscovery::checkNetworkDevice);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &WebastoDiscovery::checkNetworkDevice);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [this, discoveryReply](){
qCDebug(dcWebasto()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
// Give the last connections added right before the network discovery finished a chance to check the device...
QTimer::singleShot(3000, this, [this](){
qCDebug(dcWebasto()) << "Discovery: Grace period timer triggered.";
finishDiscovery();
});
QTimer::singleShot(3000, this, &WebastoDiscovery::finishDiscovery);
});
}
@ -63,9 +66,9 @@ QList<WebastoDiscovery::Result> WebastoDiscovery::results() const
return m_results;
}
void WebastoDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
void WebastoDiscovery::checkNetworkDevice(const QHostAddress &address)
{
WebastoNextModbusTcpConnection *connection = new WebastoNextModbusTcpConnection(networkDeviceInfo.address(), 502, 1, this);
WebastoNextModbusTcpConnection *connection = new WebastoNextModbusTcpConnection(address, 502, 1, this);
m_connections.append(connection);
connect(connection, &WebastoNextModbusTcpConnection::reachableChanged, this, [=](bool reachable){
@ -77,7 +80,7 @@ void WebastoDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevice
// Read some well known registers to verify if the register exist and make sense...
QModbusReply *reply = connection->readCableState();
connect(reply, &QModbusReply::finished, this, [=](){
connect(reply, &QModbusReply::finished, this, [this, reply, connection, address](){
reply->deleteLater();
@ -92,12 +95,12 @@ void WebastoDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevice
quint16 rawValue = ModbusDataUtils::convertToUInt16(unit.values());
QMetaEnum valueEnum = WebastoNextModbusTcpConnection::staticMetaObject.enumerator(WebastoNextModbusTcpConnection::staticMetaObject.indexOfEnumerator("CableState"));
if (!valueEnum.valueToKey(rawValue)) {
qCDebug(dcWebasto()) << "Discovery: invalid enum value for cable state on connection on" << networkDeviceInfo.address().toString() << "Continue...";;
qCDebug(dcWebasto()) << "Discovery: invalid enum value for cable state on connection on" << address.toString() << "Continue...";;
cleanupConnection(connection);
}
QModbusReply *reply = connection->readChargerState();
connect(reply, &QModbusReply::finished, this, [=](){
connect(reply, &QModbusReply::finished, this, [this, reply, connection, address](){
reply->deleteLater();
@ -112,15 +115,14 @@ void WebastoDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevice
quint16 rawValue = ModbusDataUtils::convertToUInt16(unit.values());
QMetaEnum valueEnum = WebastoNextModbusTcpConnection::staticMetaObject.enumerator(WebastoNextModbusTcpConnection::staticMetaObject.indexOfEnumerator("ChargerState"));
if (!valueEnum.valueToKey(rawValue)) {
qCDebug(dcWebasto()) << "Discovery: invalid enum value for charger state on connection on" << networkDeviceInfo.address().toString() << "Continue...";;
qCDebug(dcWebasto()) << "Discovery: invalid enum value for charger state on connection on" << address.toString() << "Continue...";;
cleanupConnection(connection);
}
// Read some registers distributed over the range...
QModbusReply *reply = connection->readTotalActivePower();
connect(reply, &QModbusReply::finished, this, [=](){
connect(reply, &QModbusReply::finished, this, [this, reply, connection, address](){
reply->deleteLater();
@ -143,28 +145,13 @@ void WebastoDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevice
// All values good so far, let's assume this is a Webasto NEXT
// Final check if there is a hostname available for this network device, if so it shouls contain the string "NEXT_".
// This is neccessary since Wallboxes from Vestel EVC04 aka. Webasto Unite wallboxes would also match
// the creteria up to here get detected as positiv Webasto NEXT.
// Example hostname: NEXT-WS10XXXX
if (!networkDeviceInfo.hostName().isEmpty() &&
(!networkDeviceInfo.hostName().contains("NEXT_") || networkDeviceInfo.hostName().contains("VESTEL"))) {
qCDebug(dcWebasto()) << "Discovery: network device has a hostname and it does match kriteria for Webasto next:" << networkDeviceInfo.hostName() << "on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
return;
}
// Hostname verification also OK, let's assume this is a Webasto NEXT
// The final check regarding the hostname will be done when all network device infos are available in the finishDiscovery slot
Result result;
result.productName = "Webasto NEXT";
result.type = TypeWebastoNext;
result.networkDeviceInfo = networkDeviceInfo;
m_results.append(result);
qCDebug(dcWebasto()) << "Discovery: --> Found" << result.productName << result.networkDeviceInfo;
result.address = address;
m_temporaryResults.append(result);
// Done with this connection
cleanupConnection(connection);
@ -177,14 +164,14 @@ void WebastoDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevice
// If we get any error...skip this host...
connect(connection->modbusTcpMaster(), &ModbusTcpMaster::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
if (error != QModbusDevice::NoError) {
qCDebug(dcWebasto()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";;
qCDebug(dcWebasto()) << "Discovery: Connection error on" << address.toString() << "Continue...";;
cleanupConnection(connection);
}
});
// If check reachability failed...skip this host...
connect(connection, &WebastoNextModbusTcpConnection::checkReachabilityFailed, this, [=](){
qCDebug(dcWebasto()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
qCDebug(dcWebasto()) << "Discovery: Check reachability failed on" << address.toString() << "Continue...";;
cleanupConnection(connection);
});
@ -203,6 +190,27 @@ void WebastoDiscovery::finishDiscovery()
{
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
for (int i = 0; i < m_temporaryResults.count(); i++) {
// Fill in all network device infos we have
m_temporaryResults[i].networkDeviceInfo = m_networkDeviceInfos.get(m_temporaryResults.at(i).address);
// Final check if there is a hostname available for this network device, if so it shouls contain the string "NEXT_".
// This is neccessary since Wallboxes from Vestel EVC04 aka. Webasto Unite wallboxes would also match
// the creteria up to here get detected as positiv Webasto NEXT.
// Example hostname: NEXT-WS10XXXX
QString hostName = m_temporaryResults.at(i).networkDeviceInfo.hostName();
if (!hostName.isEmpty() && (!hostName.contains("NEXT_") || hostName.contains("VESTEL"))) {
qCDebug(dcWebasto()) << "Discovery: network device has a hostname and it does match kriteria for Webasto next:" << hostName << "on" << m_temporaryResults.at(i).networkDeviceInfo.address().toString() << "Continue...";;
continue;
}
// Hostname verification also OK, let's assume this is a Webasto NEXT
qCDebug(dcWebasto()) << "Discovery: --> Found" << m_temporaryResults.at(i).productName << m_temporaryResults.at(i).networkDeviceInfo;
m_results.append(m_temporaryResults.at(i));
}
// Cleanup any leftovers...we don't care any more
foreach (WebastoNextModbusTcpConnection *connection, m_connections)
cleanupConnection(connection);

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -34,7 +34,6 @@
#include <QObject>
#include <network/networkdevicediscovery.h>
#include "webastonextmodbustcpconnection.h"
class WebastoDiscovery : public QObject
@ -50,6 +49,7 @@ public:
typedef struct Result {
QString productName;
Type type;
QHostAddress address;
NetworkDeviceInfo networkDeviceInfo;
} Result;
@ -64,14 +64,14 @@ signals:
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
QList<WebastoNextModbusTcpConnection *> m_connections;
QList<Result> m_results;
QDateTime m_startDateTime;
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
NetworkDeviceInfos m_networkDeviceInfos;
QList<WebastoNextModbusTcpConnection *> m_connections;
QList<Result> m_temporaryResults;
QList<Result> m_results;
void checkNetworkDevice(const QHostAddress &address);
void cleanupConnection(WebastoNextModbusTcpConnection *connection);
void finishDiscovery();