diff --git a/debian/control b/debian/control
index dfc04795..26aa8f15 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..dabce807
--- /dev/null
+++ b/goecharger/README.md
@@ -0,0 +1,25 @@
+# go-eCharger
+
+nymea plugin 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 other 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..a8122ee7
--- /dev/null
+++ b/goecharger/goecharger.pro
@@ -0,0 +1,11 @@
+include(../plugins.pri)
+
+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..9a76348e
--- /dev/null
+++ b/goecharger/integrationplugingoecharger.cpp
@@ -0,0 +1,534 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "network/networkdevicediscovery.h"
+
+#include
+#include
+#include
+#include
+#include
+
+// API documentation: https://github.com/goecharger/go-eCharger-API-v1
+
+IntegrationPluginGoECharger::IntegrationPluginGoECharger()
+{
+
+}
+
+void IntegrationPluginGoECharger::discoverThings(ThingDiscoveryInfo *info)
+{
+ if (!hardwareManager()->networkDeviceDiscovery()->available()) {
+ qCWarning(dcGoECharger()) << "The network discovery is not available on this platform.";
+ info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
+ return;
+ }
+
+ // Perform a network device discovery and filter for "go-eCharger" hosts
+ NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
+ connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
+ foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
+
+ qCDebug(dcGoECharger()) << "Checking discovered" << networkDeviceInfo;
+ // Filter by hostname
+ if (!networkDeviceInfo.hostName().contains("go-eCharger"))
+ continue;
+
+ // We need also the mac address
+ if (networkDeviceInfo.macAddress().isEmpty())
+ continue;
+
+ QString title;
+ if (networkDeviceInfo.hostName().isEmpty()) {
+ title = networkDeviceInfo.address().toString();
+ } else {
+ title = "go-eCharger (" + networkDeviceInfo.address().toString() + ")";
+ }
+
+ QString description;
+ if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
+ description = networkDeviceInfo.macAddress();
+ } else {
+ description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")";
+ }
+
+ ThingDescriptor descriptor(goeHomeThingClassId, title, description);
+ ParamList params;
+ params << Param(goeHomeThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
+ params << Param(goeHomeThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
+ descriptor.setParams(params);
+
+ // Check if we already have set up this device
+ Things existingThings = myThings().filterByParam(goeHomeThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
+ if (existingThings.count() == 1) {
+ qCDebug(dcGoECharger()) << "This go-eCharger already exists in the system" << networkDeviceInfo;
+ descriptor.setThingId(existingThings.first()->id());
+ }
+
+ info->addThingDescriptor(descriptor);
+ }
+
+ 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, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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();
+
+ if (thing->thingClassId() != goeHomeThingClassId) {
+ info->finish(Thing::ThingErrorThingClassNotFound);
+ return;
+ }
+
+ if (!thing->stateValue(goeHomeConnectedStateTypeId).toBool()) {
+ qCWarning(dcGoECharger()) << thing << "failed to execute action. The device seems not to be connected.";
+ info->finish(Thing::ThingErrorHardwareNotAvailable);
+ return;
+ }
+
+ if (thing->stateValue(goeHomeSerialNumberStateTypeId).toString().isEmpty()) {
+ qCDebug(dcGoECharger()) << "Could not execute action because the serial number is missing.";
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ if (action.actionTypeId() == goeHomePowerActionTypeId) {
+ bool power = action.paramValue(goeHomePowerActionPowerParamTypeId).toBool();
+ qCDebug(dcGoECharger()) << "Setting charging allowed to" << power;
+ // Set the allow value
+ QString configuration = QString("alw=%1").arg(power ? 1: 0);
+ sendActionRequest(thing, info, configuration);
+ return;
+ } else if (action.actionTypeId() == goeHomeMaxChargingCurrentActionTypeId) {
+ int maxChargingCurrent = action.paramValue(goeHomeMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt();
+ qCDebug(dcGoECharger()) << "Setting max charging current to" << maxChargingCurrent << "A";
+ // Set the allow value
+ QString configuration = QString("ama=%1").arg(maxChargingCurrent);
+ sendActionRequest(thing, info, configuration);
+ return;
+ } else if (action.actionTypeId() == goeHomeCloudActionTypeId) {
+ bool enabled = action.paramValue(goeHomeCloudActionCloudParamTypeId).toBool();
+ qCDebug(dcGoECharger()) << "Set cloud" << (enabled ? "enabled" : "disabled");
+ // Set the allow value
+ QString configuration = QString("cdi=%1").arg(enabled ? 1: 0);
+ sendActionRequest(thing, info, configuration);
+ return;
+ } else if (action.actionTypeId() == goeHomeLedBrightnessActionTypeId) {
+ quint8 brightness = action.paramValue(goeHomeLedBrightnessActionLedBrightnessParamTypeId).toUInt();
+ qCDebug(dcGoECharger()) << "Set led brightnss to" << brightness << "/" << 255;
+ // Set the allow value
+ QString configuration = QString("lbr=%1").arg(brightness);
+ sendActionRequest(thing, info, configuration);
+ return;
+ } else if (action.actionTypeId() == goeHomeLedEnergySaveActionTypeId) {
+ bool enabled = action.paramValue(goeHomeLedEnergySaveActionLedEnergySaveParamTypeId).toBool();
+ qCDebug(dcGoECharger()) << "Set led energy saving" << (enabled ? "enabled" : "disabled");
+ // Set the allow value
+ QString configuration = QString("lse=%1").arg(enabled ? 1: 0);
+ sendActionRequest(thing, info, configuration);
+ return;
+ } else {
+ info->finish(Thing::ThingErrorActionTypeNotFound);
+ }
+}
+
+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;
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGoECharger()) << "Failed to parse status data for thing " << thing->name() << qUtf8Printable(payload) << error.errorString();
+ return;
+ }
+
+ QString serialNumber = thing->stateValue(goeHomeSerialNumberStateTypeId).toString();
+ if (topic == QString("go-eCharger/%1/status").arg(serialNumber)) {
+ update(thing, jsonDoc.toVariant().toMap());
+ } else {
+ qCDebug(dcGoECharger()) << "Unhandled topic publish received:" << topic << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Compact));
+ }
+}
+
+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;
+ }
+
+ QVariantList temperatureSensorList = statusMap.value("tma").toList();
+ if (temperatureSensorList.count() == 4) {
+ thing->setStateValue(goeHomeTemperatureSensor1StateTypeId, temperatureSensorList.at(0).toDouble());
+ thing->setStateValue(goeHomeTemperatureSensor2StateTypeId, temperatureSensorList.at(1).toDouble());
+ thing->setStateValue(goeHomeTemperatureSensor3StateTypeId, temperatureSensorList.at(2).toDouble());
+ thing->setStateValue(goeHomeTemperatureSensor4StateTypeId, temperatureSensorList.at(3).toDouble());
+ }
+
+ thing->setStateValue(goeHomeTotalEnergyConsumedStateTypeId, 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(goeHomeAdapterConnectedStateTypeId, (statusMap.value("adi").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(goeHomeLedBrightnessStateTypeId, statusMap.value("lbr").toUInt());
+ thing->setStateValue(goeHomeLedEnergySaveStateTypeId, statusMap.value("lse").toBool());
+ 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::sendActionRequest(Thing *thing, ThingActionInfo *info, const QString &configuration)
+{
+ // Lets use rest here since we get a reply on the rest request. For using MQTT publish to topic "go-eCharger//cmd/req"
+ QNetworkRequest request = buildConfigurationRequest(QHostAddress(thing->paramValue(goeHomeThingIpAddressParamTypeId).toString()), configuration);
+ QNetworkReply *reply = hardwareManager()->networkManager()->sendCustomRequest(request, "SET");
+ connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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;
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+ update(thing, jsonDoc.toVariant().toMap());
+ });
+}
+
+void IntegrationPluginGoECharger::setupMqttChannel(ThingSetupInfo *info, const QHostAddress &address, const QVariantMap &statusMap)
+{
+ Thing *thing = info->thing();
+ QString serialNumber = statusMap.value("sse").toString();
+ QString clientId = QString("go-eCharger:%1:%2").arg(serialNumber).arg(statusMap.value("rbc").toInt());
+ QString statusTopic = QString("go-eCharger/%1/status").arg(serialNumber);
+ QString commandTopic = QString("go-eCharger/%1/cmd/req").arg(serialNumber);
+ qCDebug(dcGoECharger()) << "Setting up mqtt channel for" << thing << address.toString() << statusTopic << commandTopic;
+
+ MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(clientId, address, {statusTopic, commandTopic});
+ 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, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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, reply, &QNetworkReply::deleteLater);
+ connect(reply, &QNetworkReply::finished, info, [=](){
+ 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..f5cc8dff
--- /dev/null
+++ b/goecharger/integrationplugingoecharger.h
@@ -0,0 +1,104 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 sendActionRequest(Thing *thing, ThingActionInfo *info, 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..d27255dc
--- /dev/null
+++ b/goecharger/integrationplugingoecharger.json
@@ -0,0 +1,230 @@
+{
+ "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": ["Discovery", "User"],
+ "interfaces": ["evcharger", "smartmeterconsumer", "connectable"],
+ "paramTypes": [
+ {
+ "id": "4342b72c-99d0-41a5-abc6-ea6c1cc1352c",
+ "name":"ipAddress",
+ "displayName": "IP address",
+ "type": "QString"
+ },
+ {
+ "id": "0e30e30f-ad96-417e-b739-cac85f75de39",
+ "name":"macAddress",
+ "displayName": "MAC 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",
+ "suggestLogging": true
+ },
+ {
+ "id": "d80e1ed8-c3ae-4b68-bf86-21b4d7b2b201",
+ "name": "access",
+ "displayName": "Access",
+ "displayNameEvent": "Access changed",
+ "type": "QString",
+ "possibleValues": [
+ "Open",
+ "RFID",
+ "Automatic"
+ ],
+ "defaultValue": "Open",
+ "suggestLogging": true
+ },
+ {
+ "id": "8a7ab9f1-0143-494c-98ee-69f94125fe42",
+ "name": "power",
+ "displayName": "Allow charging",
+ "type": "bool",
+ "displayNameAction": "Allow charging",
+ "displayNameEvent": "Allow charging 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,
+ "suggestLogging": true
+ },
+ {
+ "id": "08b107bc-1284-455d-9e5a-6a1c3adc389f",
+ "name": "updateAvailable",
+ "displayName": "Update available",
+ "displayNameEvent": "Update available changed",
+ "type": "bool",
+ "defaultValue": false,
+ "suggestLogging": true
+ },
+ {
+ "id": "d557e59e-ca22-4aff-bf80-dfee44db0f69",
+ "name": "adapterConnected",
+ "displayName": "Adapter connected",
+ "displayNameEvent": "Adapter connected changed",
+ "type": "bool",
+ "defaultValue": false,
+ "suggestLogging": true
+ },
+ {
+ "id": "d8f5abb6-5db3-4040-8829-553b1d881ce4",
+ "name": "totalEnergyConsumed",
+ "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,
+ "suggestLogging": true
+ },
+ {
+ "id": "b06479d5-7a38-4fbd-867e-e55bdb54651b",
+ "name": "ledBrightness",
+ "displayName": "Led brightness",
+ "displayNameAction": "Set led brightness",
+ "displayNameEvent": "Led brightness changed",
+ "type": "int",
+ "minValue": 0,
+ "maxValue": 255,
+ "defaultValue": 255,
+ "writable": true
+ },
+ {
+ "id": "048a4c98-3ee4-4d02-ad48-6d70f31fce8c",
+ "name": "ledEnergySave",
+ "displayName": "Led energy saving enabled",
+ "displayNameAction": "Set led energy saving enabled",
+ "displayNameEvent": "Led energy saving enabled enabled changed",
+ "type": "bool",
+ "defaultValue": true,
+ "writable": true
+ },
+ {
+ "id": "2bf1ebf1-0d8c-4209-ad35-4114d9861832",
+ "name": "temperatureSensor1",
+ "displayName": "Temperature 1",
+ "displayNameEvent": "Temperature 1 changed",
+ "type": "double",
+ "unit": "DegreeCelsius",
+ "defaultValue": 0.0,
+ "suggestLogging": true
+ },
+ {
+ "id": "558e273a-4028-495a-902a-e4e932a0ae24",
+ "name": "temperatureSensor2",
+ "displayName": "Temperature 2",
+ "displayNameEvent": "Temperature 2 changed",
+ "type": "double",
+ "unit": "DegreeCelsius",
+ "defaultValue": 0.0,
+ "suggestLogging": true
+ },
+ {
+ "id": "dbf8a5dc-b8f5-437a-ac0c-c4cf8a09aacb",
+ "name": "temperatureSensor3",
+ "displayName": "Temperature 3",
+ "displayNameEvent": "Temperature 3 changed",
+ "type": "double",
+ "unit": "DegreeCelsius",
+ "defaultValue": 0.0,
+ "suggestLogging": true
+ },
+ {
+ "id": "1953e29f-fe28-4016-9b05-f4baf4c311ff",
+ "name": "temperatureSensor4",
+ "displayName": "Temperature 4",
+ "displayNameEvent": "Temperature 4 changed",
+ "type": "double",
+ "unit": "DegreeCelsius",
+ "defaultValue": 0.0,
+ "suggestLogging": true
+ },
+ {
+ "id": "5d18b48d-b886-409e-ab2e-336d9c94a55c",
+ "name": "firmwareVersion",
+ "displayName": "Firmware version",
+ "displayNameEvent": "Firmware version changed",
+ "type": "QString",
+ "defaultValue": "",
+ "cached": true
+ },
+ {
+ "id": "8ecdf24b-daca-4b7a-98b5-3236f1e6ad85",
+ "name": "serialNumber",
+ "displayName": "Serial number",
+ "displayNameEvent": "Serial number changed",
+ "type": "QString",
+ "defaultValue": "",
+ "cached": true
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
+
+
+
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 e86833fb..226fd80f 100644
--- a/nymea-plugins.pro
+++ b/nymea-plugins.pro
@@ -23,6 +23,7 @@ PLUGIN_DIRS = \
fronius \
genericelements \
genericthings \
+ goecharger \
gpio \
i2cdevices \
httpcommander \