New plugin: Mennekes HCC3 and ECU wallboxes

This commit is contained in:
Michael Zanetti 2022-08-08 14:19:24 +02:00
parent 7c61c7ea8b
commit 4e0d9a9090
20 changed files with 2113 additions and 2 deletions

12
debian/control vendored
View File

@ -136,7 +136,17 @@ Section: libs
Depends: ${shlibs:Depends},
${misc:Depends}
Description: nymea.io plugin for Kostal Solar inverters
This package will install the nymea.io plugin for Kostal Solar inverters
This package contains the nymea.io plugin for Kostal Solar inverters
Package: nymea-plugin-mennekes
Architecture: any
Multi-Arch: same
Section: libs
Depends: ${shlibs:Depends},
${misc:Depends}
Description: nymea.io plugin for Mennekes wallboxes
This package contains the nymea.io plugin for Mennekes wallboxes
Package: nymea-plugin-modbuscommander

View File

@ -0,0 +1,2 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginmennekes.so
mennekes/translations/*qm usr/share/nymea/translations/

View File

@ -79,7 +79,7 @@ def writePropertyGetSetMethodImplementationsTcp(fileDescriptor, className, regis
def writePropertyUpdateMethodImplementationsTcp(fileDescriptor, className, registerDefinitions):
for registerDefinition in registerDefinitions:
if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
if not 'readSchedule' in registerDefinition or registerDefinition['readSchedule'] == 'init':
continue
propertyName = registerDefinition['id']

Binary file not shown.

23
mennekes/README.md Normal file
View File

@ -0,0 +1,23 @@
# MENNEKES
Connects nymea to a MENNEKES wallboxes. Currently supported models are:
* Amtron Xtra
* Amtron Premium
* Amtron Professional
* Amtron Charge Control
* Amedio Professional
# Requirements
nymea uses the Modbus TCP connection to connect to the wallbox.
> The Modbus TCP connection needs to be enabled manually on the Wallbox.
For the Amtron Charge Control and Premium models, log in to the wallbox's web interface as operator. The login credentials can be obtained
from the user manual of the wallbox. Once logged in, navigate to the Load Management tab and set the Modbus TCP Server to On.
## More
It is highly recommended to update the wallbox to the latest firmware which can be downloaded from [here](https://www.chargeupyourday.de/services/software-updates/).

View File

@ -0,0 +1,302 @@
{
"className": "AmtronECU",
"protocol": "TCP",
"endianness": "BigEndian",
"errorLimitUntilNotReachable": 20,
"checkReachableRegister": "cpSignalState",
"enums": [
{
"name": "CPSignalState",
"values": [
{
"key": "A",
"value": 1
},
{
"key": "B",
"value": 2
},
{
"key": "C",
"value": 3
},
{
"key": "D",
"value": 4
},
{
"key": "E",
"value": 5
}
]
}
],
"blocks": [
{
"id": "consumptions",
"readSchedule": "update",
"registers": [
{
"id": "meterEnergyL1",
"address": 200,
"size": 2,
"type": "uint32",
"unit": "Wh",
"registerType": "holdingRegister",
"description": "Meter energy L1",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterEnergyL2",
"address": 202,
"size": 2,
"type": "uint32",
"unit": "Wh",
"registerType": "holdingRegister",
"description": "Meter energy L2",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterEnergyL3",
"address": 204,
"size": 2,
"type": "uint32",
"unit": "Wh",
"registerType": "holdingRegister",
"description": "Meter energy L3",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterPowerL1",
"address": 206,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter power L1",
"unit": "W",
"defaultValue": 0,
"access": "RO"
},
{
"id": "meterPowerL2",
"address": 208,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter power L2",
"unit": "W",
"defaultValue": 0,
"access": "RO"
},
{
"id": "meterPowerL3",
"address": 210,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter power L3",
"unit": "W",
"defaultValue": 0,
"access": "RO"
},
{
"id": "meterCurrentL1",
"address": 212,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter current L1",
"unit": "mA",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterCurrentL2",
"address": 214,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter current L2",
"unit": "mA",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterCurrentL3",
"address": 216,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter current L3",
"unit": "mA",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterTotoalEnergy",
"address": 218,
"size": 2,
"type": "uint32",
"unit": "Wh",
"registerType": "holdingRegister",
"description": "Meter total energy",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterTotalPower",
"address": 220,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter totoal power",
"unit": "W",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterVoltageL1",
"address": 222,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter voltage L1",
"unit": "V",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterVoltageL2",
"address": 224,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter voltage L2",
"unit": "V",
"defaultValue": "0",
"access": "RO"
},
{
"id": "meterVoltageL3",
"address": 226,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Meter voltage L3",
"unit": "V",
"defaultValue": "0",
"access": "RO"
}
]
}
],
"registers": [
{
"id": "firmwareVersion",
"address": 100,
"size": 2,
"type": "uint32",
"readSchedule": "init",
"registerType": "holdingRegister",
"description": "Firmware version",
"defaultValue": 0,
"access": "RO"
},
{
"id": "cpSignalState",
"address": 122,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "CP signal state",
"enum": "CPSignalState",
"defaultValue": "CPSignalStateA",
"access": "RO"
},
{
"id": "cpAvailability",
"address": 124,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Charge Point availability",
"defaultValue": 0,
"access": "RW"
},
{
"id": "model",
"address": 142,
"size": 10,
"type": "string",
"readSchedule": "init",
"registerType": "holdingRegister",
"description": "Device model",
"access": "RO"
},
{
"id": "signalledCurrent",
"address": 706,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Signalled current to EV",
"unit": "A",
"defaultValue": "0",
"access": "RO"
},
{
"id": "minCurrentLimit",
"address": 712,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Minimum current limit",
"unit": "A",
"defaultValue": "0",
"access": "RO"
},
{
"id": "maxCurrentLimit",
"address": 715,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Maximum current limit",
"unit": "A",
"defaultValue": "0",
"access": "RO"
},
{
"id": "chargedEnergy",
"address": 716,
"size": 2,
"type": "uint32",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Charged energy for current session",
"unit": "Wh",
"defaultValue": "0",
"access": "RO"
},
{
"id": "hemsCurrentLimit",
"address": 1000,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "HEMS current limit",
"unit": "A",
"defaultValue": "0",
"access": "RW"
}
]
}

View File

@ -0,0 +1,368 @@
{
"className": "AmtronHCC3",
"protocol": "TCP",
"endianness": "LittleEndian",
"errorLimitUntilNotReachable": 20,
"checkReachableRegister": "customerCurrentLimitation",
"enums": [
{
"name": "CPSignalState",
"values": [
{
"key": "A1",
"value": 1
},
{
"key": "A2",
"value": 2
},
{
"key": "B1",
"value": 3
},
{
"key": "B2",
"value": 4
},
{
"key": "C1",
"value": 5
},
{
"key": "C2",
"value": 6
},
{
"key": "D1",
"value": 7
},
{
"key": "D2",
"value": 8
}
]
},
{
"name": "PPState",
"values": [
{
"key": "Illegal",
"value": 0
},
{
"key": "Open",
"value": 1
},
{
"key": "13A",
"value": 2
},
{
"key": "20A",
"value": 3
},
{
"key": "32A",
"value": 4
}
]
},
{
"name": "ChargeState",
"values": [
{
"key": "Pause",
"value": 1
},
{
"key": "Continue",
"value": 2
},
{
"key": "Terminate",
"value": 3
},
{
"key": "Start",
"value": 4
}
]
},
{
"name": "HCC3ErrorCode",
"values": [
{
"key": "NoError",
"value": 0
},
{
"key": "InstallationFault",
"value": 10
},
{
"key": "ControllerFault",
"value": 11
},
{
"key": "Misconfiguration",
"value": 12
},
{
"key": "Overtemperature",
"value": 13
},
{
"key": "MirrorContactorError",
"value": 14
},
{
"key": "InvalidDeviceTime",
"value": 15
},
{
"key": "EnergyManagerConnectionError",
"value": 16
},
{
"key": "DeviceStartup",
"value": 30
},
{
"key": "InternalTestNotPassed",
"value": 31
},
{
"key": "HMINoConnection",
"value": 32
},
{
"key": "BadlyPluggedCable",
"value": 50
},
{
"key": "WrongCable",
"value": 51
},
{
"key": "DefectCable",
"value": 52
},
{
"key": "ACUCommunicationError",
"value": 100
},
{
"key": "NotPolledByACU",
"value": 101
},
{
"key": "Maintenance",
"value": 102
},
{
"key": "Disabled",
"value": 103
},
{
"key": "UnknownError",
"value": 255
}
]
},
{
"name": "AmtronState",
"values": [
{
"key": "Idle",
"value": 0
},
{
"key": "StandByAuthorize",
"value": 1
},
{
"key": "StandbyConnect",
"value": 2
},
{
"key": "Charging",
"value": 3
},
{
"key": "Paused",
"value": 4
},
{
"key": "Terminated",
"value": 5
},
{
"key": "Error",
"value": 6
}
]
}
],
"blocks": [
{
"id": "states",
"readSchedule": "update",
"registers": [
{
"id": "cpSignalState",
"address": 770,
"size": 1,
"type": "uint16",
"registerType": "inputRegister",
"description": "CP signal state",
"enum": "CPSignalState",
"defaultValue": "CPSignalStateA1",
"access": "RO"
},
{
"id": "ppState",
"address": 771,
"size": 1,
"type": "uint16",
"registerType": "inputRegister",
"description": "PP state",
"enum": "PPState",
"defaultValue": "PPStateIllegal",
"access": "RO"
},
{
"id": "hcc3ErrorCode",
"address": 772,
"size": 1,
"type": "uint16",
"registerType": "inputRegister",
"description": "HCC3 Error Code",
"enum": "HCC3ErrorCode",
"defaultValue": "HCC3ErrorCodeNoError",
"access": "RO"
},
{
"id": "amtronState",
"address": 773,
"size": 1,
"type": "uint16",
"registerType": "inputRegister",
"description": "AMTRON state",
"enum": "AmtronState",
"defaultValue": "AmtronStateIdle",
"access": "RO"
}
]
},
{
"id": "maxValues",
"readSchedule": "update",
"registers": [
{
"id": "phaseCount",
"address": 776,
"size": 1,
"type": "uint16",
"registerType": "inputRegister",
"description": "Phase count",
"defaultValue": "0",
"access": "RO"
},
{
"id": "ratedCurrent",
"address": 777,
"size": 1,
"type": "uint16",
"unit": "A",
"registerType": "inputRegister",
"description": "Rated Current",
"defaultValue": "0",
"access": "RO"
},
{
"id": "installationCurrent",
"address": 778,
"size": 1,
"type": "uint16",
"registerType": "inputRegister",
"description": "Installation current",
"access": "RO"
}
]
},
{
"id": "consumptions",
"readSchedule": "update",
"registers": [
{
"id": "chargingSessionMeter",
"address": 781,
"size": 2,
"type": "uint32",
"unut": "Wh",
"registerType": "inputRegister",
"description": "Charging session meter count",
"defaultValue": "0",
"access": "RO"
},
{
"id": "actualPowerConsumption",
"address": 783,
"size": 2,
"type": "uint32",
"registerType": "inputRegister",
"description": "Actual power consumption",
"unit": "W",
"defaultValue": "0",
"access": "RO"
}
]
}
],
"registers": [
{
"id": "serialNumber",
"address": 779,
"size": 2,
"type": "uint32",
"readSchedule": "init",
"registerType": "inputRegister",
"description": "Serial number",
"unit": "",
"defaultValue": 0,
"access": "RO"
},
{
"id": "name",
"address": 785,
"size": 11,
"type": "string",
"readSchedule": "init",
"registerType": "inputRegister",
"description": "Wallbox name",
"access": "RO"
},
{
"id": "customerCurrentLimitation",
"address": 1024,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "holdingRegister",
"description": "Customer Current Limitation",
"unit": "A",
"defaultValue": "0",
"access": "RW"
},
{
"id": "changeChargeState",
"address": 1025,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Change charge state",
"enum": "ChargeState",
"access": "WO"
}
]
}

View File

@ -0,0 +1,135 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "amtronecudiscovery.h"
#include "extern-plugininfo.h"
AmtronECUDiscovery::AmtronECUDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
QObject{parent},
m_networkDeviceDiscovery{networkDeviceDiscovery}
{
m_gracePeriodTimer.setSingleShot(true);
m_gracePeriodTimer.setInterval(3000);
connect(&m_gracePeriodTimer, &QTimer::timeout, this, [this](){
qCDebug(dcMennekes()) << "Discovery: Grace period timer triggered.";
finishDiscovery();
});
}
void AmtronECUDiscovery::startDiscovery()
{
qCInfo(dcMennekes()) << "Discovery: Searching for AMTRON wallboxes in the network...";
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &AmtronECUDiscovery::checkNetworkDevice);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
qCDebug(dcMennekes()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
m_gracePeriodTimer.start();
discoveryReply->deleteLater();
});
}
QList<AmtronECUDiscovery::Result> AmtronECUDiscovery::discoveryResults() const
{
return m_discoveryResults;
}
void AmtronECUDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
if (networkDeviceInfo.macAddressManufacturer() != "GIGA-BYTE TECHNOLOGY CO.,LTD.") {
return;
}
int port = 502;
int slaveId = 0xff;
qCDebug(dcMennekes()) << "Checking network device:" << networkDeviceInfo << "Port:" << port << "Slave ID:" << slaveId;
AmtronECUModbusTcpConnection *connection = new AmtronECUModbusTcpConnection(networkDeviceInfo.address(), port, slaveId, this);
m_connections.append(connection);
connect(connection, &AmtronECUModbusTcpConnection::reachableChanged, this, [=](bool reachable){
if (!reachable) {
cleanupConnection(connection);
return;
}
connect(connection, &AmtronECUModbusTcpConnection::initializationFinished, this, [=](bool success){
if (!success) {
qCDebug(dcMennekes()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString();
cleanupConnection(connection);
return;
}
Result result;
result.firmwareVersion = connection->firmwareVersion();
result.model = connection->model();
result.networkDeviceInfo = networkDeviceInfo;
m_discoveryResults.append(result);
qCDebug(dcMennekes()) << "Discovery: Found wallbox with firmware version:" << result.firmwareVersion << result.networkDeviceInfo;
cleanupConnection(connection);
});
if (!connection->initialize()) {
qCDebug(dcMennekes()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString();
cleanupConnection(connection);
}
});
connect(connection, &AmtronECUModbusTcpConnection::checkReachabilityFailed, this, [=](){
qCDebug(dcMennekes()) << "Discovery: Checking reachability failed on" << networkDeviceInfo.address().toString();
cleanupConnection(connection);
});
connection->connectDevice();
}
void AmtronECUDiscovery::cleanupConnection(AmtronECUModbusTcpConnection *connection)
{
m_connections.removeAll(connection);
connection->disconnectDevice();
connection->deleteLater();
}
void AmtronECUDiscovery::finishDiscovery()
{
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
// Cleanup any leftovers...we don't care any more
foreach (AmtronECUModbusTcpConnection *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");
m_gracePeriodTimer.stop();
emit discoveryFinished();
}

View File

@ -0,0 +1,75 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef AMTRONECUDISCOVERY_H
#define AMTRONECUDISCOVERY_H
#include <QObject>
#include <QTimer>
#include <network/networkdevicediscovery.h>
#include "amtronecumodbustcpconnection.h"
class AmtronECUDiscovery : public QObject
{
Q_OBJECT
public:
explicit AmtronECUDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
struct Result {
QString firmwareVersion;
QString model;
NetworkDeviceInfo networkDeviceInfo;
};
void startDiscovery();
QList<Result> discoveryResults() const;
signals:
void discoveryFinished();
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
QTimer m_gracePeriodTimer;
QDateTime m_startDateTime;
QList<AmtronECUModbusTcpConnection *> m_connections;
QList<Result> m_discoveryResults;
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupConnection(AmtronECUModbusTcpConnection *connection);
void finishDiscovery();
};
#endif // AMTRONECUDISCOVERY_H

View File

@ -0,0 +1,143 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "amtronhcc3discovery.h"
#include "extern-plugininfo.h"
AmtronHCC3Discovery::AmtronHCC3Discovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
QObject{parent},
m_networkDeviceDiscovery{networkDeviceDiscovery}
{
m_gracePeriodTimer.setSingleShot(true);
m_gracePeriodTimer.setInterval(3000);
connect(&m_gracePeriodTimer, &QTimer::timeout, this, [this](){
qCDebug(dcMennekes()) << "Discovery: Grace period timer triggered.";
finishDiscovery();
});
}
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::finished, this, [=](){
qCDebug(dcMennekes()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
m_gracePeriodTimer.start();
discoveryReply->deleteLater();
});
}
QList<AmtronHCC3Discovery::AmtronDiscoveryResult> AmtronHCC3Discovery::discoveryResults() const
{
return m_discoveryResults;
}
void AmtronHCC3Discovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
if (networkDeviceInfo.macAddressManufacturer() != "GIGA-BYTE TECHNOLOGY CO.,LTD.") {
return;
}
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);
m_connections.append(connection);
connect(connection, &AmtronHCC3ModbusTcpConnection::reachableChanged, this, [=](bool reachable){
if (!reachable) {
// Disconnected ... done with this connection
cleanupConnection(connection);
return;
}
// 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();
cleanupConnection(connection);
return;
}
AmtronDiscoveryResult result;
result.wallboxName = connection->name();
result.serialNumber = connection->serialNumber();
result.networkDeviceInfo = networkDeviceInfo;
m_discoveryResults.append(result);
qCDebug(dcMennekes()) << "Discovery: --> Found" << result.wallboxName
<< "Serial number:" << result.serialNumber
<< result.networkDeviceInfo;
// Done with this connection
cleanupConnection(connection);
});
if (!connection->initialize()) {
qCDebug(dcMennekes()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.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();
cleanupConnection(connection);
});
// Try to connect, maybe it works, maybe not...
connection->connectDevice();
}
void AmtronHCC3Discovery::cleanupConnection(AmtronHCC3ModbusTcpConnection *connection)
{
m_connections.removeAll(connection);
connection->disconnectDevice();
connection->deleteLater();
}
void AmtronHCC3Discovery::finishDiscovery()
{
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
// 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");
m_gracePeriodTimer.stop();
emit discoveryFinished();
}

View File

@ -0,0 +1,75 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef AMTRONHCC3DISCOVERY_H
#define AMTRONHCC3DISCOVERY_H
#include <QObject>
#include <QTimer>
#include <network/networkdevicediscovery.h>
#include "amtronhcc3modbustcpconnection.h"
class AmtronHCC3Discovery : public QObject
{
Q_OBJECT
public:
explicit AmtronHCC3Discovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
typedef struct AmtronDiscoveryResult {
QString wallboxName;
QString serialNumber;
NetworkDeviceInfo networkDeviceInfo;
} AmtronDiscoveryResult;
void startDiscovery();
QList<AmtronDiscoveryResult> discoveryResults() const;
signals:
void discoveryFinished();
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
QTimer m_gracePeriodTimer;
QDateTime m_startDateTime;
QList<AmtronHCC3ModbusTcpConnection *> m_connections;
QList<AmtronDiscoveryResult> m_discoveryResults;
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupConnection(AmtronHCC3ModbusTcpConnection *connection);
void finishDiscovery();
};
#endif // AMTRONHCC3DISCOVERY_H

View File

@ -0,0 +1,520 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginmennekes.h"
#include "plugininfo.h"
#include "amtronecudiscovery.h"
#include "amtronhcc3discovery.h"
#include <network/networkdevicediscovery.h>
#include <hardwaremanager.h>
IntegrationPluginMennekes::IntegrationPluginMennekes()
{
}
void IntegrationPluginMennekes::discoverThings(ThingDiscoveryInfo *info)
{
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcMennekes()) << "The network discovery is not available on this platform.";
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
return;
}
if (info->thingClassId() == amtronECUThingClassId) {
AmtronECUDiscovery *discovery = new AmtronECUDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
connect(discovery, &AmtronECUDiscovery::discoveryFinished, info, [=](){
foreach (const AmtronECUDiscovery::Result &result, discovery->discoveryResults()) {
QString name = "AMTRON Charge Control/Professional";
QString description = result.model.isEmpty() ? result.networkDeviceInfo.address().toString() :
result.model + " (" + result.networkDeviceInfo.address().toString() + ")";
if (result.model.startsWith("CC")) {
name = "AMTRON Charge Control";
} else if (result.model.startsWith("P")) {
name = "AMTRON Professional";
}
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());
// 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);
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
discovery->startDiscovery();
} else if (info->thingClassId() == amtronHCC3ThingClassId) {
AmtronHCC3Discovery *discovery = new AmtronHCC3Discovery(hardwareManager()->networkDeviceDiscovery(), info);
connect(discovery, &AmtronHCC3Discovery::discoveryFinished, info, [=](){
foreach (const AmtronHCC3Discovery::AmtronDiscoveryResult &result, discovery->discoveryResults()) {
ThingDescriptor descriptor(amtronHCC3ThingClassId, result.wallboxName, "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString());
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());
// 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);
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
discovery->startDiscovery();
}
}
void IntegrationPluginMennekes::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcMennekes()) << "Setup" << thing << thing->params();
if (thing->thingClassId() == amtronECUThingClassId) {
if (m_amtronECUConnections.contains(thing)) {
qCDebug(dcMennekes()) << "Reconfiguring existing thing" << thing->name();
m_amtronECUConnections.take(thing)->deleteLater();
if (m_monitors.contains(thing)) {
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
}
MacAddress macAddress = MacAddress(thing->paramValue(amtronECUThingMacAddressParamTypeId).toString());
if (!macAddress.isValid()) {
qCWarning(dcMennekes()) << "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."));
return;
}
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
m_monitors.insert(thing, monitor);
QHostAddress address = monitor->networkDeviceInfo().address();
if (address.isNull()) {
qCWarning(dcMennekes()) << "Cannot set up thing. The host address is not known yet...";
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;
}
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
if (m_monitors.contains(thing)) {
qCDebug(dcMennekes()) << "Unregistering monitor because setup has been aborted.";
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
});
if (monitor->reachable()) {
setupAmtronECUConnection(info);
} else {
qCDebug(dcMennekes()) << "Waiting for the network monitor to get reachable before continue to set up the connection" << thing->name() << address.toString() << "...";
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
if (reachable) {
qCDebug(dcMennekes()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continue setup...";
setupAmtronECUConnection(info);
}
});
}
return;
}
if (info->thing()->thingClassId() == amtronHCC3ThingClassId) {
if (m_amtronHCC3Connections.contains(thing)) {
qCDebug(dcMennekes()) << "Reconfiguring existing thing" << thing->name();
m_amtronHCC3Connections.take(thing)->deleteLater();
if (m_monitors.contains(thing)) {
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
}
MacAddress macAddress = MacAddress(thing->paramValue(amtronHCC3ThingMacAddressParamTypeId).toString());
if (!macAddress.isValid()) {
qCWarning(dcMennekes()) << "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."));
return;
}
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
m_monitors.insert(thing, monitor);
QHostAddress address = monitor->networkDeviceInfo().address();
if (address.isNull()) {
qCWarning(dcMennekes()) << "Cannot set up thing. The host address is not known yet...";
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;
}
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
if (m_monitors.contains(thing)) {
qCDebug(dcMennekes()) << "Unregistering monitor because setup has been aborted.";
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
});
if (monitor->reachable()) {
setupAmtronHCC3Connection(info);
} else {
qCDebug(dcMennekes()) << "Waiting for the network monitor to get reachable before continue to set up the connection" << thing->name() << address.toString() << "...";
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
if (reachable) {
qCDebug(dcMennekes()) << "The monitor for thing setup" << thing->name() << "is now reachable. Continue setup...";
setupAmtronHCC3Connection(info);
}
});
}
return;
}
}
void IntegrationPluginMennekes::postSetupThing(Thing *thing)
{
Q_UNUSED(thing)
if (!m_pluginTimer) {
qCDebug(dcMennekes()) << "Starting plugin timer...";
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
foreach(AmtronECUModbusTcpConnection *connection, m_amtronECUConnections) {
qCDebug(dcMennekes()) << "Updating connection" << connection->hostAddress().toString();
connection->update();
}
foreach(AmtronHCC3ModbusTcpConnection *connection, m_amtronHCC3Connections) {
qCDebug(dcMennekes()) << "Updating connection" << connection->hostAddress().toString();
connection->update();
}
});
m_pluginTimer->start();
}
}
void IntegrationPluginMennekes::executeAction(ThingActionInfo *info)
{
if (info->thing()->thingClassId() == amtronECUThingClassId) {
AmtronECUModbusTcpConnection *amtronECUConnection = m_amtronECUConnections.value(info->thing());
if (info->action().actionTypeId() == amtronECUPowerActionTypeId) {
bool power = info->action().paramValue(amtronECUPowerActionPowerParamTypeId).toBool();
QModbusReply *reply = amtronECUConnection->setHemsCurrentLimit(power ? info->thing()->stateValue(amtronECUMaxChargingCurrentStateTypeId).toUInt() : 0);
connect(reply, &QModbusReply::finished, info, [info, reply, power](){
if (reply->error() == QModbusDevice::NoError) {
info->thing()->setStateValue(amtronECUPowerStateTypeId, power);
info->finish(Thing::ThingErrorNoError);
} else {
qCWarning(dcMennekes()) << "Error setting cp availability:" << reply->error() << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure);
}
});
}
if (info->action().actionTypeId() == amtronECUMaxChargingCurrentActionTypeId) {
int maxChargingCurrent = info->action().paramValue(amtronECUMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toInt();
QModbusReply *reply = amtronECUConnection->setHemsCurrentLimit(maxChargingCurrent);
connect(reply, &QModbusReply::finished, info, [info, reply, maxChargingCurrent](){
if (reply->error() == QModbusDevice::NoError) {
info->thing()->setStateValue(amtronECUMaxChargingCurrentStateTypeId, maxChargingCurrent);
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareFailure);
}
});
}
}
}
void IntegrationPluginMennekes::thingRemoved(Thing *thing)
{
if (thing->thingClassId() == amtronECUThingClassId && m_amtronECUConnections.contains(thing)) {
AmtronECUModbusTcpConnection *connection = m_amtronECUConnections.take(thing);
delete connection;
}
if (thing->thingClassId() == amtronHCC3ThingClassId && m_amtronHCC3Connections.contains(thing)) {
AmtronHCC3ModbusTcpConnection *connection = m_amtronHCC3Connections.take(thing);
delete connection;
}
// Unregister related hardware resources
if (m_monitors.contains(thing))
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
if (myThings().isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
void IntegrationPluginMennekes::updateECUPhaseCount(Thing *thing)
{
AmtronECUModbusTcpConnection *amtronECUConnection = m_amtronECUConnections.value(thing);
int phaseCount = 0;
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)
if (amtronECUConnection->meterCurrentL1() > 500) {
phaseCount++;
}
if (amtronECUConnection->meterCurrentL2() > 500) {
phaseCount++;
}
if (amtronECUConnection->meterCurrentL3() > 500) {
phaseCount++;
}
qCDebug(dcMennekes()) << "Actively charging phases:" << phaseCount;
if (phaseCount == 0) {
if (amtronECUConnection->meterCurrentL1() > 0) {
phaseCount++;
}
if (amtronECUConnection->meterCurrentL2() > 0) {
phaseCount++;
}
if (amtronECUConnection->meterCurrentL3() > 0) {
phaseCount++;
}
qCDebug(dcMennekes()) << "Connected phases:" << phaseCount;
}
thing->setStateValue(amtronECUPhaseCountStateTypeId, phaseCount);
}
void IntegrationPluginMennekes::setupAmtronECUConnection(ThingSetupInfo *info)
{
Thing *thing = info->thing();
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
qCDebug(dcMennekes()) << "Setting up amtron wallbox on" << address.toString();
AmtronECUModbusTcpConnection *amtronECUConnection = new AmtronECUModbusTcpConnection(address, 502, 0xff, this);
connect(info, &ThingSetupInfo::aborted, amtronECUConnection, &ModbusTCPMaster::deleteLater);
// 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->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();
}
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::reachableChanged, thing, [thing, amtronECUConnection](bool reachable){
qCDebug(dcMennekes()) << "Reachable changed to" << reachable << "for" << thing;
if (reachable) {
amtronECUConnection->initialize();
} else {
thing->setStateValue(amtronECUConnectedStateTypeId, false);
}
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::initializationFinished, thing, [=](bool success){
if (!thing->setupComplete())
return;
if (success) {
thing->setStateValue(amtronECUConnectedStateTypeId, true);
} else {
thing->setStateValue(amtronECUConnectedStateTypeId, false);
// Try once to reconnect the device
amtronECUConnection->reconnectDevice();
}
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::initializationFinished, info, [=](bool success){
if (!success) {
qCWarning(dcMennekes()) << "Connection init finished with errors" << thing->name() << amtronECUConnection->hostAddress().toString();
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
amtronECUConnection->deleteLater();
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error communicating with the wallbox."));
return;
}
qCDebug(dcMennekes()) << "Connection init finished successfully" << amtronECUConnection;
m_amtronECUConnections.insert(thing, amtronECUConnection);
info->finish(Thing::ThingErrorNoError);
thing->setStateValue(amtronECUConnectedStateTypeId, true);
amtronECUConnection->update();
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::updateFinished, thing, [this, amtronECUConnection, thing](){
qCDebug(dcMennekes()) << "Amtron ECU update finished:" << thing->name() << amtronECUConnection;
updateECUPhaseCount(thing);
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::cpSignalStateChanged, thing, [thing](AmtronECUModbusTcpConnection::CPSignalState cpSignalState) {
qCDebug(dcMennekes()) << "CP signal state changed" << cpSignalState;
thing->setStateValue(amtronECUPluggedInStateTypeId, cpSignalState >= AmtronECUModbusTcpConnection::CPSignalStateB);
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::signalledCurrentChanged, thing, [](quint16 signalledCurrent) {
qCDebug(dcMennekes()) << "Signalled current changed:" << signalledCurrent;
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::minCurrentLimitChanged, thing, [thing](quint16 minCurrentLimit) {
qCDebug(dcMennekes()) << "min current limit changed:" << minCurrentLimit;
thing->setStateMinValue(amtronECUMaxChargingCurrentStateTypeId, minCurrentLimit);
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::maxCurrentLimitChanged, thing, [thing](quint16 maxCurrentLimit) {
qCDebug(dcMennekes()) << "max current limit changed:" << maxCurrentLimit;
thing->setStateMaxValue(amtronECUMaxChargingCurrentStateTypeId, maxCurrentLimit);
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::hemsCurrentLimitChanged, thing, [thing](quint16 hemsCurrentLimit) {
qCDebug(dcMennekes()) << "HEMS current limit changed:" << hemsCurrentLimit;
if (hemsCurrentLimit == 0) {
thing->setStateValue(amtronECUPowerStateTypeId, false);
} else {
thing->setStateValue(amtronECUPowerStateTypeId, true);
thing->setStateValue(amtronECUMaxChargingCurrentStateTypeId, hemsCurrentLimit);
}
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::meterTotoalEnergyChanged, thing, [thing](quint32 meterTotalEnergy) {
qCDebug(dcMennekes()) << "meter total energy changed:" << meterTotalEnergy;
thing->setStateValue(amtronECUTotalEnergyConsumedStateTypeId, qRound(meterTotalEnergy / 10.0) / 100.0); // rounded to 2 as it changes on every update
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::meterTotalPowerChanged, thing, [thing](quint32 meterTotalPower) {
qCDebug(dcMennekes()) << "meter total power changed:" << meterTotalPower;
thing->setStateValue(amtronECUCurrentPowerStateTypeId, meterTotalPower);
thing->setStateValue(amtronECUChargingStateTypeId, meterTotalPower > 0);
});
connect(amtronECUConnection, &AmtronECUModbusTcpConnection::chargedEnergyChanged, thing, [thing](quint32 chargedEnergy) {
qCDebug(dcMennekes()) << "charged energy changed:" << chargedEnergy;
thing->setStateValue(amtronECUSessionEnergyStateTypeId, qRound(chargedEnergy / 10.0) / 100.0); // rounded to 2 as it changes on every update
});
amtronECUConnection->connectDevice();
}
void IntegrationPluginMennekes::setupAmtronHCC3Connection(ThingSetupInfo *info)
{
Thing *thing = info->thing();
QHostAddress address = m_monitors.value(thing)->networkDeviceInfo().address();
qCDebug(dcMennekes()) << "Setting up amtron wallbox on" << address.toString();
AmtronHCC3ModbusTcpConnection *amtronHCC3Connection = new AmtronHCC3ModbusTcpConnection(address, 502, 0xff, this);
connect(info, &ThingSetupInfo::aborted, amtronHCC3Connection, &ModbusTCPMaster::deleteLater);
// 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->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();
}
});
connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::reachableChanged, thing, [thing, amtronHCC3Connection](bool reachable){
qCDebug(dcMennekes()) << "Reachable changed to" << reachable << "for" << thing;
if (reachable) {
amtronHCC3Connection->initialize();
} else {
thing->setStateValue(amtronHCC3ConnectedStateTypeId, false);
}
});
connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::initializationFinished, thing, [=](bool success){
if (!thing->setupComplete())
return;
if (success) {
thing->setStateValue(amtronHCC3ConnectedStateTypeId, true);
} else {
thing->setStateValue(amtronHCC3ConnectedStateTypeId, false);
// Try once to reconnect the device
amtronHCC3Connection->reconnectDevice();
}
});
connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::initializationFinished, info, [=](bool success){
if (!success) {
qCWarning(dcMennekes()) << "Connection init finished with errors" << thing->name() << amtronHCC3Connection->hostAddress().toString();
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
amtronHCC3Connection->deleteLater();
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error communicating with the wallbox."));
return;
}
qCDebug(dcMennekes()) << "Connection init finished successfully" << amtronHCC3Connection;
m_amtronHCC3Connections.insert(thing, amtronHCC3Connection);
info->finish(Thing::ThingErrorNoError);
thing->setStateValue(amtronHCC3ConnectedStateTypeId, true);
amtronHCC3Connection->update();
});
amtronHCC3Connection->connectDevice();
}
bool IntegrationPluginMennekes::ensureAmtronECUVersion(AmtronECUModbusTcpConnection *connection, const QString &version)
{
QByteArray deviceVersion = QByteArray::fromHex(QByteArray::number(connection->firmwareVersion(), 16));
return deviceVersion >= version;
}

View File

@ -0,0 +1,77 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTEGRATIONPLUGINMENNEKES_H
#define INTEGRATIONPLUGINMENNEKES_H
#include <plugintimer.h>
#include <integrations/integrationplugin.h>
#include <network/networkdevicemonitor.h>
#include "extern-plugininfo.h"
#include "amtronecumodbustcpconnection.h"
#include "amtronhcc3modbustcpconnection.h"
class IntegrationPluginMennekes: public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginmennekes.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginMennekes();
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
void thingRemoved(Thing *thing) override;
private slots:
void updateECUPhaseCount(Thing *thing);
private:
void setupAmtronECUConnection(ThingSetupInfo *info);
void setupAmtronHCC3Connection(ThingSetupInfo *info);
bool ensureAmtronECUVersion(AmtronECUModbusTcpConnection *connection, const QString &version);
PluginTimer *m_pluginTimer = nullptr;
QHash<Thing *, AmtronECUModbusTcpConnection *> m_amtronECUConnections;
QHash<Thing *, AmtronHCC3ModbusTcpConnection *> m_amtronHCC3Connections;
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
};
#endif // INTEGRATIONPLUGINMENNEKES_H

View File

@ -0,0 +1,184 @@
{
"name": "Mennekes",
"displayName": "Mennekes",
"id": "c7c3c65c-a0cc-4ab1-90d8-4ad05bfcdc38",
"vendors": [
{
"name": "mennekes",
"displayName": "MENNEKES",
"id": "7c585571-e3a3-458c-a598-e11f510cbc10",
"thingClasses": [
{
"name": "amtronECU",
"displayName": "AMTRON Charge Control/Professional",
"id": "fb48e067-2237-4eaf-8d2c-681d406395fc",
"createMethods": ["discovery", "user"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable"],
"paramTypes": [
{
"id": "0b9c1466-5eb9-4b25-9450-513e2484a3b4",
"name":"macAddress",
"displayName": "MAC address",
"type": "QString",
"inputType": "MacAddress",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "352be84a-f5c6-434d-8a92-a4065a24ff0a",
"name": "connected",
"displayName": "Connected",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "c93d7377-8a4a-4c35-9876-1032d8b309e3",
"name": "pluggedIn",
"displayName": "Plugged in",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "c8c812c6-dd56-425c-8dd1-8dd621bd3a11",
"name": "charging",
"displayName": "Charging",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "a51d0beb-f4fd-4279-85b5-b436b283a86e",
"name": "phaseCount",
"displayName": "Connected phases",
"type": "uint",
"minValue": 1,
"maxValue": 3,
"defaultValue": 1
},
{
"id": "8b2eb039-b4e3-49ae-94fc-a8b825fd8d9b",
"name": "currentPower",
"displayName": "Active power",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "5b8bfdf0-eaa6-41d0-8f2f-119b06aed181",
"name": "totalEnergyConsumed",
"displayName": "Total consumed energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.0,
"cached": true
},
{
"id": "53dc845a-a397-4c90-890b-fd50512666f4",
"name": "power",
"displayName": "Charging enabled",
"displayNameAction": "Set charging enabled",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "fb12ff61-f88a-4bfc-930f-a4a55b342d1b",
"name": "maxChargingCurrent",
"displayName": "Maximum charging current",
"displayNameAction": "Set maximum charging current",
"type": "uint",
"unit": "Ampere",
"minValue": 6,
"maxValue": 32,
"defaultValue": 6,
"writable": true
},
{
"id": "2ce6b363-5b8d-4703-8376-611a0e573f71",
"name": "sessionEnergy",
"displayName": "Session energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
}
]
},
{
"name": "amtronHCC3",
"displayName": "AMTRON XTRA/Premium",
"id": "01995c4f-a7b5-4bdd-9333-486390ff75fd",
"createMethods": ["discovery", "user"],
"interfaces": ["evcharger", "smartmeterconsumer", "connectable"],
"paramTypes": [
{
"id": "6112045e-e472-4475-bc11-f373e9c39cdd",
"name":"macAddress",
"displayName": "MAC address",
"type": "QString",
"inputType": "MacAddress",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "899e270f-7666-44b3-8509-0dad43ac9c4c",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "96b9c121-3caf-44fe-8380-483b9b40dbd9",
"name": "currentPower",
"displayName": "Active power",
"displayNameEvent": "Active power changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "3d1384fc-8b46-42b0-b043-23279d8c7665",
"name": "totalEnergyConsumed",
"displayName": "Total consumed energy",
"displayNameEvent": "Total consumed energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.0,
"cached": true
},
{
"id": "130585ca-14e9-45b7-87d7-c0d935228104",
"name": "power",
"displayName": "Charging enabled",
"displayNameEvent": "Charging enabled changed",
"displayNameAction": "Set charging enabled",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "87b4e9cb-e57b-461a-90f9-207bb5dab44c",
"name": "maxChargingCurrent",
"displayName": "Maximum charging current",
"displayNameEvent": "Maximum charging current changed",
"displayNameAction": "Set maximum charging current",
"type": "uint",
"unit": "Ampere",
"minValue": 6,
"maxValue": 32,
"defaultValue": 6,
"writable": true
}
]
}
]
}
]
}

17
mennekes/mennekes.pro Normal file
View File

@ -0,0 +1,17 @@
include(../plugins.pri)
# Generate modbus connection
MODBUS_CONNECTIONS += amtron-ecu-registers.json \
amtron-hcc3-registers.json
#MODBUS_TOOLS_CONFIG += VERBOSE
include(../modbus.pri)
HEADERS += \
amtronecudiscovery.h \
amtronhcc3discovery.h \
integrationpluginmennekes.h
SOURCES += \
amtronecudiscovery.cpp \
amtronhcc3discovery.cpp \
integrationpluginmennekes.cpp

1
mennekes/mennekes.svg Normal file
View File

@ -0,0 +1 @@
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" viewBox="0 0 294.3 71.5" xml:space="preserve" enable-background="new 0 0 294.3 71.5"><style type="text/css">.st0{fill:#fff}.st1{fill:#d9073a}</style><g><path d="M3,55.4h3.6l4.8,12.8l4.7-12.8h3.6v15.4h-2.2V57.5h0l-5.1,13.3h-2.2L5.3,57.5h0v13.3H3V55.4z"/><path d="M29.4,64.4l-6-8.9H26l4.6,6.9l4.5-6.9h2.6l-6,8.9v6.4h-2.2V64.4z"/><path d="M53,55.4h4c3.1,0,5.8,1.1,5.8,4.6c0,3.4-2.7,4.6-5.4,4.6h-2.2v6.1H53V55.4z M55.2,62.7h2.2c1.4,0,3.1-0.7,3.1-2.7 c0-1.9-2-2.6-3.4-2.6h-1.9V62.7z"/><path d="M72.4,55.2c4.7,0,7.4,3.4,7.4,7.9c0,4.7-2.7,7.9-7.4,7.9c-4.7,0-7.4-3.3-7.4-7.9C65,58.6,67.8,55.2,72.4,55.2z M72.4,69.1 c3.5,0,5.1-2.9,5.1-6c0-3.1-1.7-6-5.1-6c-3.4,0-5.1,2.9-5.1,6C67.4,66.2,68.9,69.1,72.4,69.1z"/><path d="M81.2,55.4h2.4l3.7,13.2h0l3.3-13.2h3.1L97,68.6h0l3.7-13.2h2.3l-4.7,15.4h-2.8l-3.3-13.4h0l-3.4,13.4h-2.9L81.2,55.4z"/><path d="M106.3,55.4h8.6v1.9h-6.4v4.5h5.8v1.9h-5.8v5.1h6.4v1.9h-8.6V55.4z"/><path d="M119.1,55.4h3.8c3,0,6,0.5,6,4.2c0,1.9-1.4,3.2-3.2,3.5v0c0.7,0.3,1,0.6,1.5,1.5l2.9,6.1h-2.5l-2.4-5.3 c-0.7-1.5-1.4-1.5-2.6-1.5h-1.3v6.8h-2.2V55.4z M121.3,62h1.9c2.1,0,3.3-0.9,3.3-2.4c0-1.4-1.1-2.3-3.1-2.3h-2.1V62z"/><path d="M157.6,58c-1-0.6-2-0.9-3.2-0.9c-3.5,0-5.9,2.6-5.9,6c0,3.6,2.6,6,5.8,6c1.1,0,2.6-0.3,3.3-0.7l0.1,2.1 c-1.2,0.5-2.6,0.5-3.5,0.5c-4.9,0-8.1-3.1-8.1-8c0-4.7,3.4-7.9,8.1-7.9c1.2,0,2.4,0.2,3.5,0.7L157.6,58z"/><path d="M167.5,55.2c4.7,0,7.4,3.4,7.4,7.9c0,4.7-2.7,7.9-7.4,7.9c-4.7,0-7.4-3.3-7.4-7.9C160.1,58.6,162.9,55.2,167.5,55.2z M167.5,69.1c3.5,0,5.1-2.9,5.1-6c0-3.1-1.7-6-5.1-6c-3.4,0-5.1,2.9-5.1,6C162.5,66.2,164,69.1,167.5,69.1z"/><path d="M178.9,55.4h3l6.8,12.4h0V55.4h2.2v15.4h-2.8l-7-12.6h0v12.6h-2.2V55.4z"/><path d="M196.3,55.4h3l6.8,12.4h0V55.4h2.2v15.4h-2.8l-7-12.6h0v12.6h-2.2V55.4z"/><path d="M213.7,55.4h8.6v1.9h-6.4v4.5h5.8v1.9h-5.8v5.1h6.4v1.9h-8.6V55.4z"/><path d="M237,58c-1-0.6-2-0.9-3.2-0.9c-3.5,0-5.9,2.6-5.9,6c0,3.6,2.6,6,5.8,6c1.1,0,2.6-0.3,3.3-0.7l0.1,2.1 c-1.2,0.5-2.6,0.5-3.5,0.5c-4.9,0-8.1-3.1-8.1-8c0-4.7,3.4-7.9,8.1-7.9c1.2,0,2.4,0.2,3.5,0.7L237,58z"/><path d="M244.1,57.4h-4.8v-1.9H251v1.9h-4.8v13.4h-2.2V57.4z"/><path d="M254.3,55.4h2.2v15.4h-2.2V55.4z"/><path d="M268.1,55.2c4.7,0,7.4,3.4,7.4,7.9c0,4.7-2.7,7.9-7.4,7.9c-4.7,0-7.4-3.3-7.4-7.9C260.7,58.6,263.4,55.2,268.1,55.2z M268.1,69.1c3.5,0,5.1-2.9,5.1-6c0-3.1-1.7-6-5.1-6c-3.4,0-5.1,2.9-5.1,6C263,66.2,264.6,69.1,268.1,69.1z"/><path d="M279.7,55.4h3l6.8,12.4h0V55.4h2.2v15.4h-2.8l-7-12.6h0v12.6h-2.2V55.4z"/></g><polygon points="81.4,1.2 71.9,28.6 71.8,28.6 62.2,1.2 52.9,1.2 52.9,37.4 59.6,37.4 59.6,10.3 59.7,10.3 68.9,37.4 74.6,37.4 83.8,10.3 83.9,10.3 84,10.3 84,37.4 90.7,37.4 90.7,1.2"/><polygon points="96.7,1.2 96.7,37.4 116.1,37.4 116.1,31.1 103.4,31.1 103.4,22.6 115.6,22.6 115.6,16.3 103.4,16.3 103.4,7.5 116.1,7.5 116.1,1.2"/><polygon points="144.9,1.2 144.9,27.5 144.8,27.5 129.9,1.2 122.2,1.2 122.2,37.4 128.9,37.4 128.9,11.1 129,11.1 144.1,37.4 151.6,37.4 151.6,1.2"/><polygon points="180.3,1.2 180.3,27.5 180.2,27.5 165.3,1.2 157.7,1.2 157.7,37.4 164.4,37.4 164.4,11.1 164.5,11.1 179.6,37.4 187,37.4 187,1.2"/><polygon points="193.1,1.2 193.1,37.4 212.5,37.4 212.5,31.1 199.7,31.1 199.7,22.6 212,22.6 212,16.3 199.7,16.3 199.7,7.5 212.5,7.5 212.5,1.2"/><polygon points="236.5,1.2 225.3,16.9 225.2,16.9 225.2,1.2 218.5,1.2 218.5,37.4 225.2,37.4 225.2,21.5 225.3,21.5 236.5,37.4 244.4,37.4 230.8,19 244.4,1.2"/><polygon points="247.2,1.2 247.2,37.4 266.6,37.4 266.6,31.1 253.9,31.1 253.9,22.6 266.1,22.6 266.1,16.3 253.9,16.3 253.9,7.5 266.6,7.5 266.6,1.2"/><path d="M269.9,27c0.4,8,7.1,11.1,11.8,11.1c5.7,0,11.7-3.7,11.7-11.5c0-7.9-6.5-10.2-9.5-11.1c-3.7-1.2-6-1.9-6-5 c0-2.1,1.6-3.7,3.7-3.7c1.8,0,3.8,0.8,4.1,4.1h6.8c-0.3-7.4-6.5-10.3-10.7-10.3c-6.1,0-10.6,4.5-10.6,10.3c0,7.6,5.8,9.3,8.6,10.4 c4.5,1.7,6.9,2.4,6.9,6.1c0,2.7-2.3,4.5-5,4.5c-2.1,0-4.4-1.2-4.8-4.8H269.9z"/><polygon class="st0" points="1.6,37.4 16.3,22.9 16.3,33.2 37.8,7.9 37.8,1.2 22.7,17.2 22.7,5.5 1.6,30.7"/><polygon class="st1" points="17.5,30.7 17.5,19.5 1.6,37.4 37.8,37.4 37.8,7.9"/><polygon class="st1" points="1.6,1.2 1.6,30.7 21.8,7.9 21.8,19.1 37.8,1.2"/></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

13
mennekes/meta.json Normal file
View File

@ -0,0 +1,13 @@
{
"title": "MENNEKES",
"tagline": "Connect MENNEKES wallboxes to nymea.",
"icon": "mennekes.svg",
"stability": "consumer",
"offline": true,
"technologies": [
"network"
],
"categories": [
"energy"
]
}

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>IntegrationPluginMennekes</name>
<message>
<location filename="../integrationpluginmennekes.cpp" line="48"/>
<source>The network device discovery is not available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginmennekes.cpp" line="127"/>
<location filename="../integrationpluginmennekes.cpp" line="177"/>
<source>The MAC address is not known. Please reconfigure the thing.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginmennekes.cpp" line="138"/>
<location filename="../integrationpluginmennekes.cpp" line="188"/>
<source>The host address is not known yet. Trying later again.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginmennekes.cpp" line="372"/>
<location filename="../integrationpluginmennekes.cpp" line="497"/>
<source>Error communicating with the wallbox.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Mennekes</name>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="53"/>
<source>AMTRON Charge Control/Professional</source>
<extracomment>The name of the ThingClass ({fb48e067-2237-4eaf-8d2c-681d406395fc})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="56"/>
<source>AMTRON XTRA/Premium</source>
<extracomment>The name of the ThingClass ({01995c4f-a7b5-4bdd-9333-486390ff75fd})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="59"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="62"/>
<source>Active power</source>
<extracomment>The name of the StateType ({96b9c121-3caf-44fe-8380-483b9b40dbd9}) of ThingClass amtronHCC3
----------
The name of the StateType ({8b2eb039-b4e3-49ae-94fc-a8b825fd8d9b}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="65"/>
<source>Charging</source>
<extracomment>The name of the StateType ({c8c812c6-dd56-425c-8dd1-8dd621bd3a11}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="68"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="71"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="74"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="77"/>
<source>Charging enabled</source>
<extracomment>The name of the ParamType (ThingClass: amtronHCC3, ActionType: power, ID: {130585ca-14e9-45b7-87d7-c0d935228104})
----------
The name of the StateType ({130585ca-14e9-45b7-87d7-c0d935228104}) of ThingClass amtronHCC3
----------
The name of the ParamType (ThingClass: amtronECU, ActionType: power, ID: {53dc845a-a397-4c90-890b-fd50512666f4})
----------
The name of the StateType ({53dc845a-a397-4c90-890b-fd50512666f4}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="80"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="83"/>
<source>Connected</source>
<extracomment>The name of the StateType ({899e270f-7666-44b3-8509-0dad43ac9c4c}) of ThingClass amtronHCC3
----------
The name of the StateType ({352be84a-f5c6-434d-8a92-a4065a24ff0a}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="86"/>
<source>Connected phases</source>
<extracomment>The name of the StateType ({a51d0beb-f4fd-4279-85b5-b436b283a86e}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="89"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="92"/>
<source>MAC address</source>
<extracomment>The name of the ParamType (ThingClass: amtronHCC3, Type: thing, ID: {6112045e-e472-4475-bc11-f373e9c39cdd})
----------
The name of the ParamType (ThingClass: amtronECU, Type: thing, ID: {0b9c1466-5eb9-4b25-9450-513e2484a3b4})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="95"/>
<source>MENNEKES</source>
<extracomment>The name of the vendor ({7c585571-e3a3-458c-a598-e11f510cbc10})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="98"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="101"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="104"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="107"/>
<source>Maximum charging current</source>
<extracomment>The name of the ParamType (ThingClass: amtronHCC3, ActionType: maxChargingCurrent, ID: {87b4e9cb-e57b-461a-90f9-207bb5dab44c})
----------
The name of the StateType ({87b4e9cb-e57b-461a-90f9-207bb5dab44c}) of ThingClass amtronHCC3
----------
The name of the ParamType (ThingClass: amtronECU, ActionType: maxChargingCurrent, ID: {fb12ff61-f88a-4bfc-930f-a4a55b342d1b})
----------
The name of the StateType ({fb12ff61-f88a-4bfc-930f-a4a55b342d1b}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="110"/>
<source>Mennekes</source>
<extracomment>The name of the plugin Mennekes ({c7c3c65c-a0cc-4ab1-90d8-4ad05bfcdc38})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="113"/>
<source>Plugged in</source>
<extracomment>The name of the StateType ({c93d7377-8a4a-4c35-9876-1032d8b309e3}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="116"/>
<source>Session energy</source>
<extracomment>The name of the StateType ({2ce6b363-5b8d-4703-8376-611a0e573f71}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="119"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="122"/>
<source>Set charging enabled</source>
<extracomment>The name of the ActionType ({130585ca-14e9-45b7-87d7-c0d935228104}) of ThingClass amtronHCC3
----------
The name of the ActionType ({53dc845a-a397-4c90-890b-fd50512666f4}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="125"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="128"/>
<source>Set maximum charging current</source>
<extracomment>The name of the ActionType ({87b4e9cb-e57b-461a-90f9-207bb5dab44c}) of ThingClass amtronHCC3
----------
The name of the ActionType ({fb12ff61-f88a-4bfc-930f-a4a55b342d1b}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="131"/>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/mennekes/plugininfo.h" line="134"/>
<source>Total consumed energy</source>
<extracomment>The name of the StateType ({3d1384fc-8b46-42b0-b043-23279d8c7665}) of ThingClass amtronHCC3
----------
The name of the StateType ({5b8bfdf0-eaa6-41d0-8f2f-119b06aed181}) of ThingClass amtronECU</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -12,6 +12,7 @@ PLUGIN_DIRS = \
idm \
inepro \
kostal \
mennekes \
modbuscommander \
mtec \
mypv \