From 0d79c3ac1238ebeb0b1f7c7326a56b5e28e135ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 19 Dec 2024 11:24:05 +0100 Subject: [PATCH] Webasto: Update to networkdevice interface --- webasto/integrationpluginwebasto.cpp | 252 +++++++++++++++++--------- webasto/integrationpluginwebasto.h | 4 +- webasto/integrationpluginwebasto.json | 65 +++++-- webasto/webasto.cpp | 10 +- webasto/webasto.h | 4 +- webasto/webastodiscovery.cpp | 82 +++++---- webasto/webastodiscovery.h | 12 +- 7 files changed, 273 insertions(+), 156 deletions(-) diff --git a/webasto/integrationpluginwebasto.cpp b/webasto/integrationpluginwebasto.cpp index 153e9965..38a02f97 100644 --- a/webasto/integrationpluginwebasto.cpp +++ b/webasto/integrationpluginwebasto.cpp @@ -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(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; diff --git a/webasto/integrationpluginwebasto.h b/webasto/integrationpluginwebasto.h index cb7b51bf..f7fd6d28 100644 --- a/webasto/integrationpluginwebasto.h +++ b/webasto/integrationpluginwebasto.h @@ -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 @@ class IntegrationPluginWebasto : public IntegrationPlugin QHash m_webastoLiveConnections; QHash m_webastoNextConnections; QHash m_evc04Connections; + QHash m_monitors; void setupWebastoNextConnection(ThingSetupInfo *info); @@ -88,7 +89,6 @@ class IntegrationPluginWebasto : public IntegrationPlugin 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); diff --git a/webasto/integrationpluginwebasto.json b/webasto/integrationpluginwebasto.json index d26ae787..571f4b52 100644 --- a/webasto/integrationpluginwebasto.json +++ b/webasto/integrationpluginwebasto.json @@ -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", diff --git a/webasto/webasto.cpp b/webasto/webasto.cpp index a89a1e08..11168bb5 100644 --- a/webasto/webasto.cpp +++ b/webasto/webasto.cpp @@ -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() diff --git a/webasto/webasto.h b/webasto/webasto.h index e79b5d6f..43f2706d 100644 --- a/webasto/webasto.h +++ b/webasto/webasto.h @@ -114,8 +114,8 @@ class Webasto : public QObject 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(); diff --git a/webasto/webastodiscovery.cpp b/webasto/webastodiscovery.cpp index 5ee81f94..0b752de9 100644 --- a/webasto/webastodiscovery.cpp +++ b/webasto/webastodiscovery.cpp @@ -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::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); diff --git a/webasto/webastodiscovery.h b/webasto/webastodiscovery.h index 459ca031..98d02db0 100644 --- a/webasto/webastodiscovery.h +++ b/webasto/webastodiscovery.h @@ -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 #include - #include "webastonextmodbustcpconnection.h" class WebastoDiscovery : public QObject @@ -50,6 +49,7 @@ class WebastoDiscovery : public QObject typedef struct Result { QString productName; Type type; + QHostAddress address; NetworkDeviceInfo networkDeviceInfo; } Result; @@ -64,14 +64,14 @@ class WebastoDiscovery : public QObject private: NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; + QDateTime m_startDateTime; + NetworkDeviceInfos m_networkDeviceInfos; QList m_connections; - + QList m_temporaryResults; QList m_results; - QDateTime m_startDateTime; - - void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo); + void checkNetworkDevice(const QHostAddress &address); void cleanupConnection(WebastoNextModbusTcpConnection *connection); void finishDiscovery();