From 9afe65dd4f9b2958e2676aa7be6eb500dc396a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 16 Dec 2024 15:37:24 +0100 Subject: [PATCH] Mennekes: Update to networkdevice interface --- mennekes/amtronecudiscovery.cpp | 34 +-- mennekes/amtronecudiscovery.h | 12 +- mennekes/amtronhcc3discovery.cpp | 33 +-- mennekes/amtronhcc3discovery.h | 12 +- mennekes/integrationpluginmennekes.cpp | 286 ++++++++++-------------- mennekes/integrationpluginmennekes.json | 20 +- 6 files changed, 188 insertions(+), 209 deletions(-) diff --git a/mennekes/amtronecudiscovery.cpp b/mennekes/amtronecudiscovery.cpp index 9838ac9..9993034 100644 --- a/mennekes/amtronecudiscovery.cpp +++ b/mennekes/amtronecudiscovery.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. @@ -50,12 +50,12 @@ void AmtronECUDiscovery::startDiscovery() NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover(); - connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &AmtronECUDiscovery::checkNetworkDevice); - + connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &AmtronECUDiscovery::checkNetworkDevice); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ qCDebug(dcMennekes()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices"; + m_networkDeviceInfos = discoveryReply->networkDeviceInfos(); m_gracePeriodTimer.start(); - discoveryReply->deleteLater(); }); } @@ -64,16 +64,16 @@ QList AmtronECUDiscovery::discoveryResults() const return m_discoveryResults; } -void AmtronECUDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo) +void AmtronECUDiscovery::checkNetworkDevice(const QHostAddress &address) { int port = 502; int slaveId = 0xff; - qCDebug(dcMennekes()) << "Discovery: Checking network device:" << networkDeviceInfo << "Port:" << port << "Slave ID:" << slaveId; + qCDebug(dcMennekes()) << "Discovery: Checking network device:" << address.toString() << "Port:" << port << "Slave ID:" << slaveId; - AmtronECU *connection = new AmtronECU(networkDeviceInfo.address(), port, slaveId, this); + AmtronECU *connection = new AmtronECU(address, port, slaveId, this); m_connections.append(connection); - connect(connection, &AmtronECU::reachableChanged, this, [=](bool reachable){ + connect(connection, &AmtronECU::reachableChanged, this, [this, connection, address](bool reachable){ if (!reachable) { cleanupConnection(connection); return; @@ -81,7 +81,7 @@ void AmtronECUDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi connect(connection, &AmtronECU::initializationFinished, this, [=](bool success){ if (!success) { - qCDebug(dcMennekes()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString(); + qCDebug(dcMennekes()) << "Discovery: Initialization failed on" << address.toString(); cleanupConnection(connection); return; } @@ -90,18 +90,18 @@ void AmtronECUDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi result.detectedVersion = connection->detectedVersion(); result.firmwareVersion = QString::fromUtf8(QByteArray::fromHex(QByteArray::number(connection->firmwareVersion(), 16))); result.model = connection->model(); - result.networkDeviceInfo = networkDeviceInfo; + result.address = address; // Note: the model is only known in firmware >= 5.22 // Some useres have to stay on 5.12 due to calibration law which is not available on 5.22 switch(connection->detectedVersion()) { case AmtronECU::VersionOld: - qCDebug(dcMennekes()) << "Discovery: Found wallbox with old firmware version:" << result.firmwareVersion << result.networkDeviceInfo; + qCDebug(dcMennekes()) << "Discovery: Found wallbox with old firmware version:" << result.firmwareVersion << address.toString(); m_discoveryResults.append(result); break; case AmtronECU::VersionNew: if (connection->model().isEmpty()) { - qCDebug(dcMennekes()) << "Discovery: Firmware version is >= 5.22 but the model could not be fetched. Skipping" << networkDeviceInfo.address(); + qCDebug(dcMennekes()) << "Discovery: Firmware version is >= 5.22 but the model could not be fetched. Skipping" << address.toString(); break; } @@ -109,7 +109,7 @@ void AmtronECUDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi m_discoveryResults.append(result); break; case AmtronECU::VersionUnknown: - qCDebug(dcMennekes()) << "Discovery: Firmware version or model invalid. Skipping" << networkDeviceInfo.address(); + qCDebug(dcMennekes()) << "Discovery: Firmware version or model invalid. Skipping" << address.toString(); break; } @@ -117,13 +117,13 @@ void AmtronECUDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDevi }); if (!connection->initialize()) { - qCDebug(dcMennekes()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString(); + qCDebug(dcMennekes()) << "Discovery: Unable to initialize connection on" << address.toString(); cleanupConnection(connection); } }); connect(connection, &AmtronECU::checkReachabilityFailed, this, [=](){ - qCDebug(dcMennekes()) << "Discovery: Checking reachability failed on" << networkDeviceInfo.address().toString(); + qCDebug(dcMennekes()) << "Discovery: Checking reachability failed on" << address.toString(); cleanupConnection(connection); }); @@ -141,6 +141,10 @@ void AmtronECUDiscovery::finishDiscovery() { qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch(); + // Fill in all network device infos we have + for (int i = 0; i < m_discoveryResults.count(); i++) + m_discoveryResults[i].networkDeviceInfo = m_networkDeviceInfos.get(m_discoveryResults.at(i).address); + // Cleanup any leftovers...we don't care any more foreach (AmtronECU *connection, m_connections) cleanupConnection(connection); diff --git a/mennekes/amtronecudiscovery.h b/mennekes/amtronecudiscovery.h index 0082492..85ea831 100644 --- a/mennekes/amtronecudiscovery.h +++ b/mennekes/amtronecudiscovery.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. @@ -42,14 +42,16 @@ class AmtronECUDiscovery : public QObject { Q_OBJECT public: - explicit AmtronECUDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); struct Result { AmtronECU::Version detectedVersion; QString firmwareVersion; QString model; + QHostAddress address; NetworkDeviceInfo networkDeviceInfo; }; + explicit AmtronECUDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); + void startDiscovery(); QList discoveryResults() const; @@ -59,15 +61,13 @@ signals: private: NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; - QTimer m_gracePeriodTimer; QDateTime m_startDateTime; - + NetworkDeviceInfos m_networkDeviceInfos; QList m_connections; - QList m_discoveryResults; - void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo); + void checkNetworkDevice(const QHostAddress &address); void cleanupConnection(AmtronECU *connection); void finishDiscovery(); diff --git a/mennekes/amtronhcc3discovery.cpp b/mennekes/amtronhcc3discovery.cpp index bd5c7d8..b9a1442 100644 --- a/mennekes/amtronhcc3discovery.cpp +++ b/mennekes/amtronhcc3discovery.cpp @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2022, nymea GmbH +* Copyright 2013 - 2024, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -48,12 +48,12 @@ void AmtronHCC3Discovery::startDiscovery() qCInfo(dcMennekes()) << "Discovery: Searching for AMTRON wallboxes in the network..."; NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover(); - connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &AmtronHCC3Discovery::checkNetworkDevice); - + connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &AmtronHCC3Discovery::checkNetworkDevice); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ qCDebug(dcMennekes()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices"; + m_networkDeviceInfos = discoveryReply->networkDeviceInfos(); m_gracePeriodTimer.start(); - discoveryReply->deleteLater(); }); } @@ -62,13 +62,13 @@ QList AmtronHCC3Discovery::discovery return m_discoveryResults; } -void AmtronHCC3Discovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo) +void AmtronHCC3Discovery::checkNetworkDevice(const QHostAddress &address) { int port = 502; int slaveId = 0xff; - qCDebug(dcMennekes()) << "Checking network device:" << networkDeviceInfo << "Port:" << port << "Slave ID:" << slaveId; - AmtronHCC3ModbusTcpConnection *connection = new AmtronHCC3ModbusTcpConnection(networkDeviceInfo.address(), port, slaveId, this); + qCDebug(dcMennekes()) << "Checking network device:" << address << "Port:" << port << "Slave ID:" << slaveId; + AmtronHCC3ModbusTcpConnection *connection = new AmtronHCC3ModbusTcpConnection(address, port, slaveId, this); m_connections.append(connection); connect(connection, &AmtronHCC3ModbusTcpConnection::reachableChanged, this, [=](bool reachable){ @@ -81,12 +81,12 @@ void AmtronHCC3Discovery::checkNetworkDevice(const NetworkDeviceInfo &networkDev // Modbus TCP connected...ok, let's try to initialize it! connect(connection, &AmtronHCC3ModbusTcpConnection::initializationFinished, this, [=](bool success){ if (!success) { - qCDebug(dcMennekes()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString(); + qCDebug(dcMennekes()) << "Discovery: Initialization failed on" << address.toString(); cleanupConnection(connection); return; } if (connection->serialNumber() == 0 || connection->name().isEmpty()) { - qCDebug(dcMennekes()) << "Serial number or name invalid. Skipping" << networkDeviceInfo.address(); + qCDebug(dcMennekes()) << "Serial number or name invalid. Skipping" << address.toString(); cleanupConnection(connection); return; } @@ -94,27 +94,26 @@ void AmtronHCC3Discovery::checkNetworkDevice(const NetworkDeviceInfo &networkDev AmtronDiscoveryResult result; result.wallboxName = connection->name(); result.serialNumber = connection->serialNumber(); - result.networkDeviceInfo = networkDeviceInfo; + result.address = address; m_discoveryResults.append(result); qCDebug(dcMennekes()) << "Discovery: --> Found" << result.wallboxName << "Serial number:" << result.serialNumber - << result.networkDeviceInfo; - + << result.address.toString(); // Done with this connection cleanupConnection(connection); }); if (!connection->initialize()) { - qCDebug(dcMennekes()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString(); + qCDebug(dcMennekes()) << "Discovery: Unable to initialize connection on" << address.toString(); cleanupConnection(connection); } }); // If check reachability failed...skip this host... connect(connection, &AmtronHCC3ModbusTcpConnection::checkReachabilityFailed, this, [=](){ - qCDebug(dcMennekes()) << "Discovery: Checking reachability failed on" << networkDeviceInfo.address().toString(); + qCDebug(dcMennekes()) << "Discovery: Checking reachability failed on" << address.toString(); cleanupConnection(connection); }); @@ -133,12 +132,16 @@ void AmtronHCC3Discovery::finishDiscovery() { qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch(); + // Fill in all network device infos we have + for (int i = 0; i < m_discoveryResults.count(); i++) + m_discoveryResults[i].networkDeviceInfo = m_networkDeviceInfos.get(m_discoveryResults.at(i).address); + // Cleanup any leftovers...we don't care any more foreach (AmtronHCC3ModbusTcpConnection *connection, m_connections) cleanupConnection(connection); qCInfo(dcMennekes()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() - << "AMTRON wallboxes in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); + << "AMTRON HCC3 wallboxes in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); m_gracePeriodTimer.stop(); emit discoveryFinished(); diff --git a/mennekes/amtronhcc3discovery.h b/mennekes/amtronhcc3discovery.h index 2d5b8d4..1489d32 100644 --- a/mennekes/amtronhcc3discovery.h +++ b/mennekes/amtronhcc3discovery.h @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2022, nymea GmbH +* Copyright 2013 - 2024, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -42,13 +42,15 @@ class AmtronHCC3Discovery : public QObject { Q_OBJECT public: - explicit AmtronHCC3Discovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); typedef struct AmtronDiscoveryResult { QString wallboxName; QString serialNumber; + QHostAddress address; NetworkDeviceInfo networkDeviceInfo; } AmtronDiscoveryResult; + explicit AmtronHCC3Discovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); + void startDiscovery(); QList discoveryResults() const; @@ -58,15 +60,13 @@ signals: private: NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; - QTimer m_gracePeriodTimer; QDateTime m_startDateTime; - + NetworkDeviceInfos m_networkDeviceInfos; QList m_connections; - QList m_discoveryResults; - void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo); + void checkNetworkDevice(const QHostAddress &address); void cleanupConnection(AmtronHCC3ModbusTcpConnection *connection); void finishDiscovery(); diff --git a/mennekes/integrationpluginmennekes.cpp b/mennekes/integrationpluginmennekes.cpp index 2a09c00..41f1115 100644 --- a/mennekes/integrationpluginmennekes.cpp +++ b/mennekes/integrationpluginmennekes.cpp @@ -77,22 +77,25 @@ void IntegrationPluginMennekes::discoverThings(ThingDiscoveryInfo *info) ThingDescriptor descriptor(amtronECUThingClassId, name, description); qCDebug(dcMennekes()) << "Discovered:" << descriptor.title() << descriptor.description(); - // Check if we already have set up this device - Things existingThings = myThings().filterByParam(amtronECUThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); - if (existingThings.count() == 1) { - qCDebug(dcMennekes()) << "This wallbox already exists in the system:" << result.networkDeviceInfo; - descriptor.setThingId(existingThings.first()->id()); - } - ParamList params; - params << Param(amtronECUThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); + params << Param(amtronECUThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress()); + params << Param(amtronECUThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName()); + params << Param(amtronECUThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress()); // Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults... descriptor.setParams(params); + + // Check if we already have set up this device + Thing *existingThing = myThings().findByParams(params); + if (existingThing) { + qCDebug(dcMennekes()) << "This wallbox already exists in the system:" << result.networkDeviceInfo; + descriptor.setThingId(existingThing->id()); + } info->addThingDescriptor(descriptor); } info->finish(Thing::ThingErrorNoError); }); + discovery->startDiscovery(); } else if (info->thingClassId() == amtronHCC3ThingClassId) { @@ -111,26 +114,32 @@ void IntegrationPluginMennekes::discoverThings(ThingDiscoveryInfo *info) continue; } - ThingDescriptor descriptor(amtronHCC3ThingClassId, result.wallboxName, "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString()); + QString description = "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString(); + ThingDescriptor descriptor(amtronHCC3ThingClassId, result.wallboxName, description); qCDebug(dcMennekes()) << "Discovered:" << descriptor.title() << descriptor.description(); - // Check if we already have set up this device - Things existingThings = myThings().filterByParam(amtronHCC3ThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); - if (existingThings.count() == 1) { - qCDebug(dcMennekes()) << "This wallbox already exists in the system:" << result.networkDeviceInfo; - descriptor.setThingId(existingThings.first()->id()); - } - ParamList params; - params << Param(amtronHCC3ThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); + params << Param(amtronHCC3ThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress()); + params << Param(amtronHCC3ThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName()); + params << Param(amtronHCC3ThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress()); // Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults... descriptor.setParams(params); + + // Check if we already have set up this device + Thing *existingThing = myThings().findByParams(params); + if (existingThing) { + qCDebug(dcMennekes()) << "This wallbox already exists in the system:" << result.networkDeviceInfo; + descriptor.setThingId(existingThing->id()); + } + info->addThingDescriptor(descriptor); } info->finish(Thing::ThingErrorNoError); }); + discovery->startDiscovery(); + } else if (info->thingClassId() == amtronCompact20ThingClassId) { AmtronCompact20Discovery *discovery = new AmtronCompact20Discovery(hardwareManager()->modbusRtuResource(), info); connect(discovery, &AmtronCompact20Discovery::discoveryFinished, info, [this, info, discovery](bool modbusMasterAvailable){ @@ -180,55 +189,38 @@ void IntegrationPluginMennekes::setupThing(ThingSetupInfo *info) } } - MacAddress macAddress = MacAddress(thing->paramValue(amtronECUThingMacAddressParamTypeId).toString()); - QHostAddress address = QHostAddress(thing->paramValue(amtronECUThingAddressParamTypeId).toString()); + qCDebug(dcMennekes()) << "Setting up MAC address based ModbusTcp connection for" << thing; + NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing); + m_monitors.insert(thing, monitor); - if (macAddress.isNull() && address.isNull()) { - qCWarning(dcMennekes()) << "There is no MAC address and no host address configured in the thing params. Cannot set up thing" << thing; - info->finish(Thing::ThingErrorMissingParameter, QT_TR_NOOP("Failed to set up the connection to the EV charger. Please specify either manually the IP address or get the MAC address using the network discovery.")); - return; - } + // Make sure the monitor gets cleaned up when setup gets aborted + connect(info, &ThingSetupInfo::aborted, monitor, [this, thing](){ + if (m_monitors.contains(thing)) { + qCDebug(dcMennekes()) << "Unregistering monitor because setup has been aborted."; + hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); + } + }); - if (!macAddress.isNull()) { - // ########### MAC based connection - qCDebug(dcMennekes()) << "Setting up MAC address based ModbusTcp connection for" << thing; - NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress); - m_monitors.insert(thing, monitor); - - // Make sure the monitor gets cleaned up when setup gets aborted - connect(info, &ThingSetupInfo::aborted, monitor, [this, thing](){ - if (m_monitors.contains(thing)) { - qCDebug(dcMennekes()) << "Unregistering monitor because setup has been aborted."; - hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); - } - }); - - // If this is the initial setup, wait for the monitor to be reachable and make sure - // we have an IP address, otherwise let the monitor do his work - if (info->isInitialSetup()) { - // Continue with setup only if we know that the network device is reachable - if (monitor->reachable()) { - setupAmtronECUConnection(info); - } else { - // otherwise wait until we reach the networkdevice before setting up the device - qCDebug(dcMennekes()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable."; - connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){ - if (reachable) { - qCDebug(dcMennekes()) << "Network device" << thing->name() << "is now reachable. Continue with the setup..."; - setupAmtronECUConnection(info); - } - }); - } - } else { - // Continue setup with monitor... + // If this is the initial setup, wait for the monitor to be reachable and make sure + // we have an IP address, otherwise let the monitor do his work + if (info->isInitialSetup()) { + // Continue with setup only if we know that the network device is reachable + if (monitor->reachable()) { setupAmtronECUConnection(info); + } else { + // otherwise wait until we reach the networkdevice before setting up the device + qCDebug(dcMennekes()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable."; + connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){ + if (reachable) { + qCDebug(dcMennekes()) << "Network device" << thing->name() << "is now reachable. Continue with the setup..."; + setupAmtronECUConnection(info); + } + }); } } else { - // ########### IP based connection - qCDebug(dcMennekes()) << "Setting up IP address based ModbusTcp connection for" << thing; + // Continue setup with monitor... setupAmtronECUConnection(info); } - return; } @@ -242,52 +234,36 @@ void IntegrationPluginMennekes::setupThing(ThingSetupInfo *info) } } - MacAddress macAddress = MacAddress(thing->paramValue(amtronHCC3ThingMacAddressParamTypeId).toString()); - QHostAddress address = QHostAddress(thing->paramValue(amtronHCC3ThingAddressParamTypeId).toString()); + qCDebug(dcMennekes()) << "Setting up MAC address based ModbusTcp connection for" << thing; + NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing); + m_monitors.insert(thing, monitor); - if (macAddress.isNull() && address.isNull()) { - qCWarning(dcMennekes()) << "There is no MAC address and no host address configured in the thing params. Cannot set up thing" << thing; - info->finish(Thing::ThingErrorMissingParameter, QT_TR_NOOP("Failed to set up the connection to the EV charger. Please specify either manually the IP address or get the MAC address using the network discovery.")); - return; - } + // Make sure the monitor gets cleaned up when setup gets aborted + connect(info, &ThingSetupInfo::aborted, monitor, [this, thing](){ + if (m_monitors.contains(thing)) { + qCDebug(dcMennekes()) << "Unregistering monitor because setup has been aborted."; + hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); + } + }); - if (!macAddress.isNull()) { - // ########### MAC based connection - qCDebug(dcMennekes()) << "Setting up MAC address based ModbusTcp connection for" << thing; - NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress); - m_monitors.insert(thing, monitor); - - // Make sure the monitor gets cleaned up when setup gets aborted - connect(info, &ThingSetupInfo::aborted, monitor, [this, thing](){ - if (m_monitors.contains(thing)) { - qCDebug(dcMennekes()) << "Unregistering monitor because setup has been aborted."; - hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); - } - }); - - // If this is the initial setup, wait for the monitor to be reachable and make sure - // we have an IP address, otherwise let the monitor do his work - if (info->isInitialSetup()) { - // Continue with setup only if we know that the network device is reachable - if (monitor->reachable()) { - setupAmtronHCC3Connection(info); - } else { - // otherwise wait until we reach the networkdevice before setting up the device - qCDebug(dcMennekes()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable."; - connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){ - if (reachable) { - qCDebug(dcMennekes()) << "Network device" << thing->name() << "is now reachable. Continue with the setup..."; - setupAmtronHCC3Connection(info); - } - }); - } - } else { - // Continue setup with monitor... + // If this is the initial setup, wait for the monitor to be reachable and make sure + // we have an IP address, otherwise let the monitor do his work + if (info->isInitialSetup()) { + // Continue with setup only if we know that the network device is reachable + if (monitor->reachable()) { setupAmtronHCC3Connection(info); + } else { + // otherwise wait until we reach the networkdevice before setting up the device + qCDebug(dcMennekes()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable."; + connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){ + if (reachable) { + qCDebug(dcMennekes()) << "Network device" << thing->name() << "is now reachable. Continue with the setup..."; + setupAmtronHCC3Connection(info); + } + }); } } else { - // ########### IP based connection - qCDebug(dcMennekes()) << "Setting up IP address based ModbusTcp connection for" << thing; + // Continue setup with monitor... setupAmtronHCC3Connection(info); } @@ -541,7 +517,10 @@ void IntegrationPluginMennekes::updateECUPhaseCount(Thing *thing) { AmtronECU *amtronECUConnection = m_amtronECUConnections.value(thing); int phaseCount = 0; - qCDebug(dcMennekes()) << "Phases: L1" << amtronECUConnection->meterCurrentL1() << "L2" << amtronECUConnection->meterCurrentL2() << "L3" << amtronECUConnection->meterCurrentL3(); + qCDebug(dcMennekes()) << "Phases: L1" << amtronECUConnection->meterCurrentL1() + << "L2" << amtronECUConnection->meterCurrentL2() + << "L3" << amtronECUConnection->meterCurrentL3(); + // the current idles on some 5 - 10 mA when not charging... // We want to detect the phases we're actually charging on. Checking the current flow for that if it's > 500mA // If no phase is charging, let's count all phases that are not 0 instead (to determine how many phases are connected at the wallbox) @@ -597,46 +576,35 @@ void IntegrationPluginMennekes::updateCompact20PhaseCount(Thing *thing) void IntegrationPluginMennekes::setupAmtronECUConnection(ThingSetupInfo *info) { Thing *thing = info->thing(); + NetworkDeviceMonitor *monitor = m_monitors.value(thing); - // Set up depending on the available params, mac can only be filled in by discovery (ro param), - // the ip could be used as static manual config for VPN networks or WAN IP's - QHostAddress address; - if (m_monitors.contains(thing)) { - address = m_monitors.value(thing)->networkDeviceInfo().address(); - } else { - address = QHostAddress(thing->paramValue(amtronECUThingAddressParamTypeId).toString()); - if (address.isNull()) { - qCWarning(dcMennekes()) << "Could not set up the Amtron ECU connection. The IP address is not valid."; - info->finish(Thing::ThingErrorHardwareNotAvailable); - return; - } + QHostAddress address = monitor->networkDeviceInfo().address(); + if (address.isNull()) { + qCWarning(dcMennekes()) << "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; } qCDebug(dcMennekes()) << "Creating Amtron ECU connection for" << address.toString(); AmtronECU *amtronECUConnection = new AmtronECU(address, 502, 0xff, this); connect(info, &ThingSetupInfo::aborted, amtronECUConnection, &ModbusTcpMaster::deleteLater); - if (m_monitors.contains(thing)) { + // Reconnect on monitor reachable changed + connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ + qCDebug(dcMennekes()) << "Network device monitor reachable changed for" << thing->name() << reachable; + if (!thing->setupComplete()) + return; - // Only relevant for MAC based connections... - - // Reconnect on monitor reachable changed - NetworkDeviceMonitor *monitor = m_monitors.value(thing); - connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ - qCDebug(dcMennekes()) << "Network device monitor reachable changed for" << thing->name() << reachable; - if (!thing->setupComplete()) - return; - - if (reachable && !thing->stateValue("connected").toBool()) { - amtronECUConnection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address()); - amtronECUConnection->connectDevice(); - } else if (!reachable) { - // Note: We disable autoreconnect explicitly and we will - // connect the device once the monitor says it is reachable again - amtronECUConnection->disconnectDevice(); - } - }); - } + if (reachable && !thing->stateValue("connected").toBool()) { + amtronECUConnection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address()); + amtronECUConnection->connectDevice(); + } else if (!reachable) { + // Note: We disable autoreconnect explicitly and we will + // connect the device once the monitor says it is reachable again + amtronECUConnection->disconnectDevice(); + } + }); // Init during setup connect(amtronECUConnection, &AmtronECU::initializationFinished, info, [this, thing, amtronECUConnection, info](bool success){ @@ -793,46 +761,34 @@ void IntegrationPluginMennekes::setupAmtronECUConnection(ThingSetupInfo *info) void IntegrationPluginMennekes::setupAmtronHCC3Connection(ThingSetupInfo *info) { Thing *thing = info->thing(); + NetworkDeviceMonitor *monitor = m_monitors.value(thing); - // Set up depending on the available params, mac can only be filled in by discovery (ro param), - // the ip could be used as static manual config for VPN networks or WAN IP's - QHostAddress address; - if (m_monitors.contains(thing)) { - address = m_monitors.value(thing)->networkDeviceInfo().address(); - } else { - address = QHostAddress(thing->paramValue(amtronHCC3ThingAddressParamTypeId).toString()); - if (address.isNull()) { - qCWarning(dcMennekes()) << "Could not set up the Amtron HCC3 connection. The IP address is not valid."; - info->finish(Thing::ThingErrorHardwareNotAvailable); - return; - } + QHostAddress address = monitor->networkDeviceInfo().address(); + if (address.isNull()) { + qCWarning(dcMennekes()) << "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; } qCDebug(dcMennekes()) << "Creating Amtron HHC3 connection for" << address.toString(); AmtronHCC3ModbusTcpConnection *amtronHCC3Connection = new AmtronHCC3ModbusTcpConnection(address, 502, 0xff, this); connect(info, &ThingSetupInfo::aborted, amtronHCC3Connection, &ModbusTcpMaster::deleteLater); - if (m_monitors.contains(thing)) { + connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ + qCDebug(dcMennekes()) << "Network device monitor reachable changed for" << thing->name() << reachable; + if (!thing->setupComplete()) + return; - // Only relevant for MAC based connections... - - // Reconnect on monitor reachable changed - NetworkDeviceMonitor *monitor = m_monitors.value(thing); - connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ - qCDebug(dcMennekes()) << "Network device monitor reachable changed for" << thing->name() << reachable; - if (!thing->setupComplete()) - return; - - if (reachable && !thing->stateValue("connected").toBool()) { - amtronHCC3Connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address()); - amtronHCC3Connection->connectDevice(); - } else if (!reachable) { - // Note: We disable autoreconnect explicitly and we will - // connect the device once the monitor says it is reachable again - amtronHCC3Connection->disconnectDevice(); - } - }); - } + if (reachable && !thing->stateValue("connected").toBool()) { + amtronHCC3Connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address()); + amtronHCC3Connection->connectDevice(); + } else if (!reachable) { + // Note: We disable autoreconnect explicitly and we will + // connect the device once the monitor says it is reachable again + amtronHCC3Connection->disconnectDevice(); + } + }); // Init during setup connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::initializationFinished, info, [=](bool success){ diff --git a/mennekes/integrationpluginmennekes.json b/mennekes/integrationpluginmennekes.json index 2a9f9ca..bd3057f 100644 --- a/mennekes/integrationpluginmennekes.json +++ b/mennekes/integrationpluginmennekes.json @@ -13,7 +13,7 @@ "displayName": "AMTRON Charge Control/Professional", "id": "fb48e067-2237-4eaf-8d2c-681d406395fc", "createMethods": ["discovery", "user"], - "interfaces": ["evcharger", "smartmeterconsumer", "connectable"], + "interfaces": ["evcharger", "smartmeterconsumer", "connectable", "networkdevice"], "paramTypes": [ { "id": "36d11ea0-524a-43d2-b157-f69a744ac5d8", @@ -23,6 +23,14 @@ "inputType": "IPv4Address", "defaultValue": "" }, + { + "id": "26c08902-37f8-448a-9c1f-e2c563a98461", + "name": "hostName", + "displayName": "Host name", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "" + }, { "id": "0b9c1466-5eb9-4b25-9450-513e2484a3b4", "name":"macAddress", @@ -129,7 +137,7 @@ "displayName": "AMTRON XTRA/Premium", "id": "01995c4f-a7b5-4bdd-9333-486390ff75fd", "createMethods": ["discovery", "user"], - "interfaces": ["evcharger", "smartmeterconsumer", "connectable"], + "interfaces": ["evcharger", "smartmeterconsumer", "connectable", "networkdevice"], "paramTypes": [ { "id": "0dae61b0-5d2d-49df-bdac-93aec86b2085", @@ -139,6 +147,14 @@ "inputType": "IPv4Address", "defaultValue": "" }, + { + "id": "8f597d67-7ba1-410e-9232-476ac37f5b4b", + "name": "hostName", + "displayName": "Host name", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "" + }, { "id": "6112045e-e472-4475-bc11-f373e9c39cdd", "name":"macAddress",