diff --git a/debian/control b/debian/control
index ea89a128..ea0402f3 100644
--- a/debian/control
+++ b/debian/control
@@ -376,6 +376,21 @@ Description: nymea.io plugin for gpio
This package will install the nymea.io plugin for gpio
+Package: nymea-plugin-goecharger
+Architecture: any
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+ nymea-plugins-translations,
+Description: nymea.io plugin for the go-eCharger wallbox
+ The nymea daemon is a plugin based IoT (Internet of Things) server. The
+ server works like a translator for devices, things and services and
+ allows them to interact.
+ With the powerful rule engine you are able to connect any device available
+ in the system and create individual scenes and behaviors for your environment.
+ .
+ This package will install the nymea.io plugin for the go-eCharger wallbox
+
+
Package: nymea-plugin-homeconnect
Architecture: any
Depends: ${shlibs:Depends},
diff --git a/debian/nymea-plugin-goecharger.install.in b/debian/nymea-plugin-goecharger.install.in
new file mode 100644
index 00000000..d959940d
--- /dev/null
+++ b/debian/nymea-plugin-goecharger.install.in
@@ -0,0 +1 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationplugingoecharger.so
diff --git a/goecharger/README.md b/goecharger/README.md
new file mode 100644
index 00000000..2fc4590b
--- /dev/null
+++ b/goecharger/README.md
@@ -0,0 +1,25 @@
+# go-eCharger
+
+nymea plug-in for go-eCharger smart wallbox for electic vehicles.
+
+Once you connect to the go-eCharger, nymea will configure the wallbox to use MQTT and send information to nymea.
+Please make sure no ther service is using the custom MQTT server in the local network, otherwise they will exclude each other, depending who comes first.
+
+## Supported Things
+
+* go-eCharger Home
+
+## Requirements
+
+* The package "nymea-plugin-goecharger" must be installed.
+* The device must be in the same local area network as nymea.
+* The Firmware version has to be at least `030.00`.
+
+## Developer documentation
+
+The documentation of the API can be found [here](https://github.com/goecharger/go-eCharger-API-v1).
+
+## More
+
+https://go-e.co/
+
diff --git a/goecharger/go-e-logo.png b/goecharger/go-e-logo.png
new file mode 100644
index 00000000..1a9f7f3f
Binary files /dev/null and b/goecharger/go-e-logo.png differ
diff --git a/goecharger/goecharger.pro b/goecharger/goecharger.pro
new file mode 100644
index 00000000..a1bad035
--- /dev/null
+++ b/goecharger/goecharger.pro
@@ -0,0 +1,13 @@
+include(../plugins.pri)
+
+TARGET = $$qtLibraryTarget(nymea_integrationplugingoecharger)
+
+QT += network
+
+PKGCONFIG += nymea-mqtt
+
+SOURCES += \
+ integrationplugingoecharger.cpp \
+
+HEADERS += \
+ integrationplugingoecharger.h \
diff --git a/goecharger/integrationplugingoecharger.cpp b/goecharger/integrationplugingoecharger.cpp
new file mode 100644
index 00000000..07a17cdb
--- /dev/null
+++ b/goecharger/integrationplugingoecharger.cpp
@@ -0,0 +1,381 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2021, 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 .
+*
+* 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 "plugininfo.h"
+#include "integrationplugingoecharger.h"
+
+#include
+#include
+#include
+#include
+#include
+
+// Modbus documentation: https://github.com/goecharger/go-eCharger-API-v1
+
+IntegrationPluginGoECharger::IntegrationPluginGoECharger()
+{
+
+}
+
+void IntegrationPluginGoECharger::discoverThings(ThingDiscoveryInfo *info)
+{
+ // TODO: perform nmap discovery and filter for "go-eCharger" hosts
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginGoECharger::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ if (thing->thingClassId() == goeHomeThingClassId) {
+ QHostAddress address = QHostAddress(thing->paramValue(goeHomeThingIpAddressParamTypeId).toString());
+ QUrl requestUrl;
+ requestUrl.setScheme("http");
+ requestUrl.setHost(address.toString());
+ requestUrl.setPath("/status");
+
+ QNetworkRequest request(requestUrl);
+ QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+ connect(reply, &QNetworkReply::finished, thing, [=](){
+ if (reply->error() != QNetworkReply::NoError) {
+ qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable."));
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString();
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data."));
+ return;
+ }
+
+ qCDebug(dcGoECharger()) << "Received" << qUtf8Printable(jsonDoc.toJson());
+ // Verify mqtt client and set it up
+ setupMqttChannel(info, address, jsonDoc.toVariant().toMap());
+ });
+ return;
+ }
+
+ Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
+}
+
+
+void IntegrationPluginGoECharger::thingRemoved(Thing *thing)
+{
+ if (m_channels.contains(thing)) {
+ hardwareManager()->mqttProvider()->releaseChannel(m_channels.take(thing));
+ }
+}
+
+void IntegrationPluginGoECharger::executeAction(ThingActionInfo *info)
+{
+ //Thing *thing = info->thing();
+ //Action action = info->action();
+
+
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginGoECharger::onClientConnected(MqttChannel *channel)
+{
+ Thing *thing = m_channels.key(channel);
+ if (!thing) {
+ qCWarning(dcGoECharger()) << "Received a client connect for an unknown thing. Ignoring the event.";
+ return;
+ }
+
+ qCDebug(dcGoECharger()) << thing << "connected";
+ thing->setStateValue(goeHomeConnectedStateTypeId, true);
+}
+
+void IntegrationPluginGoECharger::onClientDisconnected(MqttChannel *channel)
+{
+ Thing *thing = m_channels.key(channel);
+ if (!thing) {
+ qCWarning(dcGoECharger()) << "Received a client disconnect for an unknown thing. Ignoring the event.";
+ return;
+ }
+
+ qCDebug(dcGoECharger()) << thing << "connected";
+ thing->setStateValue(goeHomeConnectedStateTypeId, false);
+}
+
+void IntegrationPluginGoECharger::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload)
+{
+ Thing *thing = m_channels.key(channel);
+ if (!thing) {
+ qCWarning(dcGoECharger()) << "Received a MQTT client publish from an unknown thing. Ignoring the event.";
+ return;
+ }
+
+ qCDebug(dcGoECharger()) << thing << "publish received" << topic << qUtf8Printable(payload);
+}
+
+void IntegrationPluginGoECharger::update(Thing *thing, const QVariantMap &statusMap)
+{
+ if (thing->thingClassId() == goeHomeThingClassId) {
+ // Parse status map and update states...
+ CarState carState = static_cast(statusMap.value("car").toUInt());
+ switch (carState) {
+ case CarStateReadyNoCar:
+ thing->setStateValue(goeHomeCarStatusStateTypeId, "Ready but no vehicle connected");
+ break;
+ case CarStateCharging:
+ thing->setStateValue(goeHomeCarStatusStateTypeId, "Vehicle loads");
+ break;
+ case CarStateWaitForCar:
+ thing->setStateValue(goeHomeCarStatusStateTypeId, "Waiting for vehicle");
+ break;
+ case CarStateChargedCarConnected:
+ thing->setStateValue(goeHomeCarStatusStateTypeId, "Charging finished and vehicle still connected");
+ break;
+ }
+
+ Access accessStatus = static_cast(statusMap.value("ast").toUInt());
+ switch (accessStatus) {
+ case AccessOpen:
+ thing->setStateValue(goeHomeAccessStateTypeId, "Open");
+ break;
+ case AccessRfid:
+ thing->setStateValue(goeHomeAccessStateTypeId, "RFID");
+ break;
+ case AccessAuto:
+ thing->setStateValue(goeHomeAccessStateTypeId, "Automatic");
+ break;
+ }
+
+ thing->setStateValue(goeHomeTotalEnergyStateTypeId, statusMap.value("eto").toUInt() / 10.0);
+ thing->setStateValue(goeHomeChargeEnergyStateTypeId, statusMap.value("dws").toUInt() / 360000.0);
+ thing->setStateValue(goeHomePowerStateTypeId, (statusMap.value("alw").toUInt() == 0 ? false : true));
+ thing->setStateValue(goeHomeUpdateAvailableStateTypeId, (statusMap.value("upd").toUInt() == 0 ? false : true));
+ thing->setStateValue(goeHomeCloudStateTypeId, (statusMap.value("cdi").toUInt() == 0 ? false : true));
+ thing->setStateValue(goeHomeFirmwareVersionStateTypeId, statusMap.value("fwv").toString());
+ thing->setStateValue(goeHomeMaxChargingCurrentStateTypeId, statusMap.value("ama").toUInt());
+ thing->setStateValue(goeHomeSerialNumberStateTypeId, statusMap.value("sse").toString());
+ }
+}
+
+QNetworkRequest IntegrationPluginGoECharger::buildConfigurationRequest(const QHostAddress &address, const QString &configuration)
+{
+ QUrl requestUrl;
+ requestUrl.setScheme("http");
+ requestUrl.setHost(address.toString());
+ requestUrl.setPath("/mqtt");
+ QUrlQuery query;
+ query.addQueryItem("payload", configuration);
+ requestUrl.setQuery(query);
+ return QNetworkRequest(requestUrl);
+}
+
+void IntegrationPluginGoECharger::setupMqttChannel(ThingSetupInfo *info, const QHostAddress &address, const QVariantMap &statusMap)
+{
+ Thing *thing = info->thing();
+ QString statusTopic = QString("go-eCharger/%1/status").arg(statusMap.value("sse").toString());
+ qCDebug(dcGoECharger()) << "Setting up mqtt channel for" << thing << address.toString() << statusTopic;
+
+ MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(thing->id().toString(), address, {statusTopic});
+ if (!channel) {
+ qCWarning(dcGoECharger()) << "Failed to create MQTT channel for" << thing;
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error creating MQTT channel. Please check MQTT server settings."));
+ return;
+ }
+
+ m_channels.insert(thing, channel);
+ connect(channel, &MqttChannel::clientConnected, this, &IntegrationPluginGoECharger::onClientConnected);
+ connect(channel, &MqttChannel::clientDisconnected, this, &IntegrationPluginGoECharger::onClientDisconnected);
+ connect(channel, &MqttChannel::publishReceived, this, &IntegrationPluginGoECharger::onPublishReceived);
+
+ // Configure the mqtt server on the go-e
+ QNetworkRequest request = buildConfigurationRequest(address, QString("mcs=%1").arg(channel->serverAddress().toString()));
+ qCDebug(dcGoECharger()) << "Configure nymea mqtt server address on" << thing << request.url().toString();
+ QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET");
+ connect(reply, &QNetworkReply::finished, thing, [=](){
+ reply->deleteLater();
+ if (reply->error() != QNetworkReply::NoError) {
+ qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable."));
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString();
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data."));
+ return;
+ }
+
+ // Verify response matches the requsted value
+ if (jsonDoc.toVariant().toMap().value("mcs").toString() != channel->serverAddress().toString()) {
+ qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server address" << channel->serverAddress().toString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox."));
+ return;
+ } else {
+ qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->serverAddress().toString();
+ }
+
+ QNetworkRequest request = buildConfigurationRequest(address, QString("mcp=%1").arg(channel->serverPort()));
+ qCDebug(dcGoECharger()) << "Configure nymea mqtt server port on" << thing << request.url().toString();
+ QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET");
+ connect(reply, &QNetworkReply::finished, thing, [=](){
+ reply->deleteLater();
+ if (reply->error() != QNetworkReply::NoError) {
+ qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable."));
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString();
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data."));
+ return;
+ }
+
+ // Verify response matches the requsted value
+ if (jsonDoc.toVariant().toMap().value("mcp").toUInt() != channel->serverPort()) {
+ qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server port" << channel->serverPort();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox."));
+ return;
+ } else {
+ qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->serverPort();
+ }
+
+ QNetworkRequest request = buildConfigurationRequest(address, QString("mcu=%1").arg(channel->username()));
+ qCDebug(dcGoECharger()) << "Configure nymea mqtt server user name on" << thing << request.url().toString();
+ QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET");
+ connect(reply, &QNetworkReply::finished, thing, [=](){
+ reply->deleteLater();
+ if (reply->error() != QNetworkReply::NoError) {
+ qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable."));
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString();
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data."));
+ return;
+ }
+
+ // Verify response matches the requsted value
+ if (jsonDoc.toVariant().toMap().value("mcu").toString() != channel->username()) {
+ qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server username" << channel->username();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox."));
+ return;
+ } else {
+ qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->username();
+ }
+
+ QNetworkRequest request = buildConfigurationRequest(address, QString("mck=%1").arg(channel->password()));
+ qCDebug(dcGoECharger()) << "Configure nymea mqtt server password on" << thing << request.url().toString();
+ QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET");
+ connect(reply, &QNetworkReply::finished, thing, [=](){
+ reply->deleteLater();
+ if (reply->error() != QNetworkReply::NoError) {
+ qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable."));
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString();
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data."));
+ return;
+ }
+
+ // Verify response matches the requsted value
+ if (jsonDoc.toVariant().toMap().value("mck").toString() != channel->password()) {
+ qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested server password" << channel->password();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox."));
+ return;
+ } else {
+ qCDebug(dcGoECharger()) << "Configured successfully MQTT server" << thing << channel->password();
+ }
+
+ QNetworkRequest request = buildConfigurationRequest(address, QString("mce=1"));
+ qCDebug(dcGoECharger()) << "Enable custom mqtt server on" << thing << request.url().toString();
+ QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET");
+ connect(reply, &QNetworkReply::finished, thing, [=](){
+ reply->deleteLater();
+ if (reply->error() != QNetworkReply::NoError) {
+ qCWarning(dcGoECharger()) << "HTTP status reply returned error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The wallbox does not seem to be reachable."));
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(data) << error.errorString();
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The wallbox returned invalid data."));
+ return;
+ }
+
+ // Verify response matches the requsted value
+ QVariantMap statusMap = jsonDoc.toVariant().toMap();
+ if (statusMap.value("mce").toInt() != 1) {
+ qCWarning(dcGoECharger()) << "Configured MQTT server but the response does not match with requested value 1";
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error while configuring MQTT settings on the wallbox."));
+ return;
+ } else {
+ qCDebug(dcGoECharger()) << "Configured successfully MQTT server enabled" << thing;
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+ qCDebug(dcGoECharger()) << "Configuration of MQTT for" << thing << "finished successfully";
+ // Update states...
+ update(thing, statusMap);
+ });
+ });
+ });
+ });
+ });
+}
+
+
diff --git a/goecharger/integrationplugingoecharger.h b/goecharger/integrationplugingoecharger.h
new file mode 100644
index 00000000..e0e1c6ca
--- /dev/null
+++ b/goecharger/integrationplugingoecharger.h
@@ -0,0 +1,102 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2021, 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 .
+*
+* 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 INTEGRATIONPLUGINGOECHARGER_H
+#define INTEGRATIONPLUGINGOECHARGER_H
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+class IntegrationPluginGoECharger: public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugingoecharger.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ enum CarState {
+ CarStateReadyNoCar = 1,
+ CarStateCharging = 2,
+ CarStateWaitForCar = 3,
+ CarStateChargedCarConnected = 4
+ };
+ Q_ENUM(CarState)
+
+ enum Access {
+ AccessOpen = 0,
+ AccessRfid = 1,
+ AccessAuto = 2
+ };
+ Q_ENUM(Access)
+
+ enum ErrorCode {
+ ErrorCodeResidualCurrentCircuitBreaker = 1,
+ ErrorCodePhase = 3,
+ ErrorCodeNoGround = 8,
+ ErrorCodeInternalError = 10
+ };
+ Q_ENUM(ErrorCode)
+
+ enum CableLockMode {
+ CableLockModeLockWhileCareConnected = 0,
+ CableLockModeUnlockAfterCharging = 1,
+ CableLockModeAlwaysLock = 2
+ };
+ Q_ENUM(CableLockMode)
+
+ explicit IntegrationPluginGoECharger();
+
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void thingRemoved(Thing *thing) override;
+ void executeAction(ThingActionInfo *info) override;
+
+private:
+ QHash m_channels;
+
+ void update(Thing *thing, const QVariantMap &statusMap);
+ QNetworkRequest buildConfigurationRequest(const QHostAddress &address, const QString &configuration);
+ void setupMqttChannel(ThingSetupInfo *info, const QHostAddress &address, const QVariantMap &statusMap);
+
+private slots:
+ void onClientConnected(MqttChannel* channel);
+ void onClientDisconnected(MqttChannel* channel);
+ void onPublishReceived(MqttChannel* channel, const QString &topic, const QByteArray &payload);
+
+};
+
+#endif // INTEGRATIONPLUGINGOECHARGER_H
+
diff --git a/goecharger/integrationplugingoecharger.json b/goecharger/integrationplugingoecharger.json
new file mode 100644
index 00000000..fb20793e
--- /dev/null
+++ b/goecharger/integrationplugingoecharger.json
@@ -0,0 +1,156 @@
+{
+ "name": "GoECharger",
+ "displayName": "go-eCharger",
+ "id": "a1dfca21-3f41-4a67-bc8c-c8b333411bd9",
+ "vendors": [
+ {
+ "name": "goE",
+ "displayName": "go-e",
+ "id": "c2cf9998-3584-489f-8d82-68a0baed2064",
+ "thingClasses": [
+ {
+ "name": "goeHome",
+ "displayName": "go-eCharger Home",
+ "id": "3b663d51-fdb5-4944-b409-c07f7933877e",
+ "createMethods": ["User"],
+ "interfaces": ["connectable"],
+ "paramTypes": [
+ {
+ "id": "4342b72c-99d0-41a5-abc6-ea6c1cc1352c",
+ "name":"ipAddress",
+ "displayName": "IP address",
+ "type": "QString"
+ }
+ ],
+ "stateTypes":[
+ {
+ "id": "a5afaad5-78bf-4cac-b98d-7eae31aac518",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "defaultValue": false,
+ "cached": false
+ },
+ {
+ "id": "c69053bc-3a53-4e76-868b-ccf0958e9e44",
+ "name": "carStatus",
+ "displayName": "Car state",
+ "displayNameEvent": "Car status changed",
+ "type": "QString",
+ "possibleValues": [
+ "Ready but no vehicle connected",
+ "Vehicle loads",
+ "Waiting for vehicle",
+ "Charging finished and vehicle still connected"
+ ],
+ "defaultValue": "Ready but no vehicle connected"
+ },
+ {
+ "id": "d80e1ed8-c3ae-4b68-bf86-21b4d7b2b201",
+ "name": "access",
+ "displayName": "Access",
+ "displayNameEvent": "Access changed",
+ "type": "QString",
+ "possibleValues": [
+ "Open",
+ "RFID",
+ "Automatic"
+ ],
+ "defaultValue": "Open"
+ },
+ {
+ "id": "8a7ab9f1-0143-494c-98ee-69f94125fe42",
+ "name": "power",
+ "displayName": "Charging",
+ "type": "bool",
+ "displayNameAction": "Start charging",
+ "displayNameEvent": "Charging status changed",
+ "defaultValue": false,
+ "writable": true
+ },
+ {
+ "id": "446fb786-bfbe-4938-963c-73d02184573f",
+ "name": "maxChargingCurrent",
+ "displayName": "Charging current",
+ "displayNameEvent": "Charging current changed",
+ "displayNameAction": "Set charging current",
+ "type": "double",
+ "unit": "Ampere",
+ "minValue": 6,
+ "maxValue": 32,
+ "defaultValue": 16,
+ "writable": true
+ },
+ {
+ "id": "ac849296-3f70-4b1b-aa30-127d774667bb",
+ "name": "cloud",
+ "displayName": "Cloud enabled",
+ "displayNameAction": "Set cloud enabled",
+ "displayNameEvent": "Cloud enabled changed",
+ "type": "bool",
+ "defaultValue": true,
+ "writable": true
+ },
+ {
+ "id": "08b107bc-1284-455d-9e5a-6a1c3adc389f",
+ "name": "updateAvailable",
+ "displayName": "Update available",
+ "displayNameEvent": "Update available changed",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "d8f5abb6-5db3-4040-8829-553b1d881ce4",
+ "name": "totalEnergy",
+ "displayName": "Total energy",
+ "displayNameEvent": "Total energy changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0.0
+ },
+ {
+ "id": "e8258831-ad89-4d27-b295-e8c10dd42b76",
+ "name": "chargeEnergy",
+ "displayName": "Charge energy",
+ "displayNameEvent": "Charge energy changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0.0
+ },
+ {
+ "id": "b06479d5-7a38-4fbd-867e-e55bdb54651b",
+ "name": "ledBrightness",
+ "displayName": "Led brightness",
+ "displayNameEvent": "Led brightness changed",
+ "type": "int",
+ "minValue": 0,
+ "maxValue": 255,
+ "defaultValue": 255
+ },
+ {
+ "id": "5d18b48d-b886-409e-ab2e-336d9c94a55c",
+ "name": "firmwareVersion",
+ "displayName": "Firmware version",
+ "displayNameEvent": "Firmware version changed",
+ "type": "QString",
+ "defaultValue": ""
+ },
+ {
+ "id": "8ecdf24b-daca-4b7a-98b5-3236f1e6ad85",
+ "name": "serialNumber",
+ "displayName": "Serial number",
+ "displayNameEvent": "Serial number changed",
+ "type": "QString",
+ "defaultValue": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
+
+
+
diff --git a/goecharger/meta.json b/goecharger/meta.json
new file mode 100644
index 00000000..376be697
--- /dev/null
+++ b/goecharger/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "go-eCharger",
+ "tagline": "Control and monitor the go-eCharger smart wallbox for electric vehicles.",
+ "icon": "go-e-logo.png",
+ "stability": "community",
+ "offline": true,
+ "technologies": [
+ "network"
+ ],
+ "categories": [
+ "energy"
+ ]
+}
diff --git a/goecharger/translations/a1dfca21-3f41-4a67-bc8c-c8b333411bd9-en_US.ts b/goecharger/translations/a1dfca21-3f41-4a67-bc8c-c8b333411bd9-en_US.ts
new file mode 100644
index 00000000..ffd1a6b3
--- /dev/null
+++ b/goecharger/translations/a1dfca21-3f41-4a67-bc8c-c8b333411bd9-en_US.ts
@@ -0,0 +1,275 @@
+
+
+
+
+ GoECharger
+
+
+
+ Access
+ The name of the ParamType (ThingClass: goeHome, EventType: access, ID: {d80e1ed8-c3ae-4b68-bf86-21b4d7b2b201})
+----------
+The name of the StateType ({d80e1ed8-c3ae-4b68-bf86-21b4d7b2b201}) of ThingClass goeHome
+
+
+
+
+ Access changed
+ The name of the EventType ({d80e1ed8-c3ae-4b68-bf86-21b4d7b2b201}) of ThingClass goeHome
+
+
+
+
+
+ Car state
+ The name of the ParamType (ThingClass: goeHome, EventType: carStatus, ID: {c69053bc-3a53-4e76-868b-ccf0958e9e44})
+----------
+The name of the StateType ({c69053bc-3a53-4e76-868b-ccf0958e9e44}) of ThingClass goeHome
+
+
+
+
+ Car status changed
+ The name of the EventType ({c69053bc-3a53-4e76-868b-ccf0958e9e44}) of ThingClass goeHome
+
+
+
+
+
+ Charge energy
+ The name of the ParamType (ThingClass: goeHome, EventType: chargeEnergy, ID: {e8258831-ad89-4d27-b295-e8c10dd42b76})
+----------
+The name of the StateType ({e8258831-ad89-4d27-b295-e8c10dd42b76}) of ThingClass goeHome
+
+
+
+
+ Charge energy changed
+ The name of the EventType ({e8258831-ad89-4d27-b295-e8c10dd42b76}) of ThingClass goeHome
+
+
+
+
+
+
+ Charging
+ The name of the ParamType (ThingClass: goeHome, ActionType: power, ID: {8a7ab9f1-0143-494c-98ee-69f94125fe42})
+----------
+The name of the ParamType (ThingClass: goeHome, EventType: power, ID: {8a7ab9f1-0143-494c-98ee-69f94125fe42})
+----------
+The name of the StateType ({8a7ab9f1-0143-494c-98ee-69f94125fe42}) of ThingClass goeHome
+
+
+
+
+
+
+ Charging current
+ The name of the ParamType (ThingClass: goeHome, ActionType: maxChargingCurrent, ID: {446fb786-bfbe-4938-963c-73d02184573f})
+----------
+The name of the ParamType (ThingClass: goeHome, EventType: maxChargingCurrent, ID: {446fb786-bfbe-4938-963c-73d02184573f})
+----------
+The name of the StateType ({446fb786-bfbe-4938-963c-73d02184573f}) of ThingClass goeHome
+
+
+
+
+ Charging current changed
+ The name of the EventType ({446fb786-bfbe-4938-963c-73d02184573f}) of ThingClass goeHome
+
+
+
+
+ Charging status changed
+ The name of the EventType ({8a7ab9f1-0143-494c-98ee-69f94125fe42}) of ThingClass goeHome
+
+
+
+
+
+
+ Cloud enabled
+ The name of the ParamType (ThingClass: goeHome, ActionType: cloud, ID: {ac849296-3f70-4b1b-aa30-127d774667bb})
+----------
+The name of the ParamType (ThingClass: goeHome, EventType: cloud, ID: {ac849296-3f70-4b1b-aa30-127d774667bb})
+----------
+The name of the StateType ({ac849296-3f70-4b1b-aa30-127d774667bb}) of ThingClass goeHome
+
+
+
+
+ Cloud enabled changed
+ The name of the EventType ({ac849296-3f70-4b1b-aa30-127d774667bb}) of ThingClass goeHome
+
+
+
+
+
+ Connected
+ The name of the ParamType (ThingClass: goeHome, EventType: connected, ID: {a5afaad5-78bf-4cac-b98d-7eae31aac518})
+----------
+The name of the StateType ({a5afaad5-78bf-4cac-b98d-7eae31aac518}) of ThingClass goeHome
+
+
+
+
+ Connected changed
+ The name of the EventType ({a5afaad5-78bf-4cac-b98d-7eae31aac518}) of ThingClass goeHome
+
+
+
+
+
+ Firmware version
+ The name of the ParamType (ThingClass: goeHome, EventType: firmwareVersion, ID: {5d18b48d-b886-409e-ab2e-336d9c94a55c})
+----------
+The name of the StateType ({5d18b48d-b886-409e-ab2e-336d9c94a55c}) of ThingClass goeHome
+
+
+
+
+ Firmware version changed
+ The name of the EventType ({5d18b48d-b886-409e-ab2e-336d9c94a55c}) of ThingClass goeHome
+
+
+
+
+ IP address
+ The name of the ParamType (ThingClass: goeHome, Type: thing, ID: {4342b72c-99d0-41a5-abc6-ea6c1cc1352c})
+
+
+
+
+
+ Led brightness
+ The name of the ParamType (ThingClass: goeHome, EventType: ledBrightness, ID: {b06479d5-7a38-4fbd-867e-e55bdb54651b})
+----------
+The name of the StateType ({b06479d5-7a38-4fbd-867e-e55bdb54651b}) of ThingClass goeHome
+
+
+
+
+ Led brightness changed
+ The name of the EventType ({b06479d5-7a38-4fbd-867e-e55bdb54651b}) of ThingClass goeHome
+
+
+
+
+
+ Serial number
+ The name of the ParamType (ThingClass: goeHome, EventType: serialNumber, ID: {8ecdf24b-daca-4b7a-98b5-3236f1e6ad85})
+----------
+The name of the StateType ({8ecdf24b-daca-4b7a-98b5-3236f1e6ad85}) of ThingClass goeHome
+
+
+
+
+ Serial number changed
+ The name of the EventType ({8ecdf24b-daca-4b7a-98b5-3236f1e6ad85}) of ThingClass goeHome
+
+
+
+
+ Set charging current
+ The name of the ActionType ({446fb786-bfbe-4938-963c-73d02184573f}) of ThingClass goeHome
+
+
+
+
+ Set cloud enabled
+ The name of the ActionType ({ac849296-3f70-4b1b-aa30-127d774667bb}) of ThingClass goeHome
+
+
+
+
+ Start charging
+ The name of the ActionType ({8a7ab9f1-0143-494c-98ee-69f94125fe42}) of ThingClass goeHome
+
+
+
+
+
+ Total energy
+ The name of the ParamType (ThingClass: goeHome, EventType: totalEnergy, ID: {d8f5abb6-5db3-4040-8829-553b1d881ce4})
+----------
+The name of the StateType ({d8f5abb6-5db3-4040-8829-553b1d881ce4}) of ThingClass goeHome
+
+
+
+
+ Total energy changed
+ The name of the EventType ({d8f5abb6-5db3-4040-8829-553b1d881ce4}) of ThingClass goeHome
+
+
+
+
+
+ Update available
+ The name of the ParamType (ThingClass: goeHome, EventType: updateAvailable, ID: {08b107bc-1284-455d-9e5a-6a1c3adc389f})
+----------
+The name of the StateType ({08b107bc-1284-455d-9e5a-6a1c3adc389f}) of ThingClass goeHome
+
+
+
+
+ Update available changed
+ The name of the EventType ({08b107bc-1284-455d-9e5a-6a1c3adc389f}) of ThingClass goeHome
+
+
+
+
+ go-e
+ The name of the vendor ({c2cf9998-3584-489f-8d82-68a0baed2064})
+
+
+
+
+ go-eCharger
+ The name of the plugin GoECharger ({a1dfca21-3f41-4a67-bc8c-c8b333411bd9})
+
+
+
+
+ go-eCharger Home
+ The name of the ThingClass ({3b663d51-fdb5-4944-b409-c07f7933877e})
+
+
+
+
+ IntegrationPluginGoECharger
+
+
+
+
+
+
+
+ The wallbox does not seem to be reachable.
+
+
+
+
+
+
+
+
+
+ The wallbox returned invalid data.
+
+
+
+
+ Error creating MQTT channel. Please check MQTT server settings.
+
+
+
+
+
+
+
+
+ Error while configuring MQTT settings on the wallbox.
+
+
+
+
diff --git a/nymea-plugins.pro b/nymea-plugins.pro
index e32bd80e..51a727f7 100644
--- a/nymea-plugins.pro
+++ b/nymea-plugins.pro
@@ -23,6 +23,7 @@ PLUGIN_DIRS = \
fronius \
genericelements \
genericthings \
+ goecharger \
gpio \
i2cdevices \
httpcommander \