Skip to content

Commit

Permalink
Merge PR #204: Webasto: Update to networkdevice interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jenkins committed Jan 9, 2025
2 parents e13f85f + 0d79c3a commit a8a05c4
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 156 deletions.
252 changes: 162 additions & 90 deletions webasto/integrationpluginwebasto.cpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions webasto/integrationpluginwebasto.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: [email protected]
*
* This file is part of nymea.
Expand Down Expand Up @@ -69,6 +69,7 @@ class IntegrationPluginWebasto : public IntegrationPlugin
QHash<Thing *, Webasto *> m_webastoLiveConnections;
QHash<Thing *, WebastoNextModbusTcpConnection *> m_webastoNextConnections;
QHash<Thing *, EVC04ModbusTcpConnection *> m_evc04Connections;

QHash<Thing *, NetworkDeviceMonitor *> m_monitors;

void setupWebastoNextConnection(ThingSetupInfo *info);
Expand All @@ -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);

Expand Down
65 changes: 54 additions & 11 deletions webasto/integrationpluginwebasto.json
Original file line number Diff line number Diff line change
Expand Up @@ -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":[
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
10 changes: 2 additions & 8 deletions webasto/webasto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions webasto/webasto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
82 changes: 45 additions & 37 deletions webasto/webastodiscovery.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: [email protected]
*
* This file is part of nymea.
Expand Down Expand Up @@ -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);
});
}

Expand All @@ -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){
Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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();

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

Expand All @@ -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);
Expand Down
12 changes: 6 additions & 6 deletions webasto/webastodiscovery.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2023, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: [email protected]
*
* This file is part of nymea.
Expand Down Expand Up @@ -34,7 +34,6 @@
#include <QObject>

#include <network/networkdevicediscovery.h>

#include "webastonextmodbustcpconnection.h"

class WebastoDiscovery : public QObject
Expand All @@ -50,6 +49,7 @@ class WebastoDiscovery : public QObject
typedef struct Result {
QString productName;
Type type;
QHostAddress address;
NetworkDeviceInfo networkDeviceInfo;
} Result;

Expand All @@ -64,14 +64,14 @@ class WebastoDiscovery : public QObject

private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
QDateTime m_startDateTime;

NetworkDeviceInfos m_networkDeviceInfos;
QList<WebastoNextModbusTcpConnection *> m_connections;

QList<Result> m_temporaryResults;
QList<Result> m_results;

QDateTime m_startDateTime;

void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void checkNetworkDevice(const QHostAddress &address);
void cleanupConnection(WebastoNextModbusTcpConnection *connection);

void finishDiscovery();
Expand Down

0 comments on commit a8a05c4

Please sign in to comment.