diff --git a/debian/control b/debian/control
index 3fdbb21d..5b7039f0 100644
--- a/debian/control
+++ b/debian/control
@@ -219,6 +219,15 @@ Description: nymea integration plugin for ESPuino
This package contains the nymea integration plugin for ESPuino devices.
+Package: nymea-plugin-evbox
+Architecture: any
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+Description: nymea integration plugin for EVBox
+ This package contains the nymea integration plugin for EVBox wallboxes
+ implementing the Protocol Max v4.
+
+
Package: nymea-plugin-fastcom
Architecture: any
Depends: ${misc:Depends},
diff --git a/debian/nymea-plugin-evbox.install.in b/debian/nymea-plugin-evbox.install.in
new file mode 100644
index 00000000..1973c152
--- /dev/null
+++ b/debian/nymea-plugin-evbox.install.in
@@ -0,0 +1,2 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginevbox.so
+evbox/translations/*qm usr/share/nymea/translations/
diff --git a/evbox/README.md b/evbox/README.md
new file mode 100644
index 00000000..fed95b89
--- /dev/null
+++ b/evbox/README.md
@@ -0,0 +1,17 @@
+# EVBox
+
+This integration allows nymea to control EVBox wallboxes supporting the Protocol Max v4.
+
+## Supported things
+
+Generally, all EVBox wallboxes supporting the Protocol Max v4 are supported. We've tested it with
+
+* Elvi
+
+## Requirements
+
+The EVBox Protocol Max v4 is based on a RS485 connection. This means, a RS485 port, either onboard or via USB adapter, is required on the nymea system.
+The wallbox must be configured to not be cloud controlled, in order to accept commands on the RS485 port.
+
+
+
diff --git a/evbox/evbox-logo-blue.png b/evbox/evbox-logo-blue.png
new file mode 100644
index 00000000..0ba3785e
Binary files /dev/null and b/evbox/evbox-logo-blue.png differ
diff --git a/evbox/evbox.pro b/evbox/evbox.pro
new file mode 100644
index 00000000..2126f7ef
--- /dev/null
+++ b/evbox/evbox.pro
@@ -0,0 +1,9 @@
+include(../plugins.pri)
+
+QT += network serialport
+
+SOURCES += \
+ integrationpluginevbox.cpp \
+
+HEADERS += \
+ integrationpluginevbox.h \
diff --git a/evbox/integrationpluginevbox.cpp b/evbox/integrationpluginevbox.cpp
new file mode 100644
index 00000000..9f1188f0
--- /dev/null
+++ b/evbox/integrationpluginevbox.cpp
@@ -0,0 +1,348 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 .
+*
+* 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 "integrationpluginevbox.h"
+#include "plugininfo.h"
+#include "plugintimer.h"
+
+#include
+#include
+#include
+
+#define STX 0x02
+#define ETX 0x03
+
+IntegrationPluginEVBox::IntegrationPluginEVBox()
+{
+
+}
+
+IntegrationPluginEVBox::~IntegrationPluginEVBox()
+{
+}
+
+void IntegrationPluginEVBox::discoverThings(ThingDiscoveryInfo *info)
+{
+ // Create the list of available serial interfaces
+
+ foreach(QSerialPortInfo port, QSerialPortInfo::availablePorts()) {
+
+ qCDebug(dcEVBox()) << "Found serial port:" << port.portName();
+ QString description = port.portName() + " " + port.manufacturer() + " " + port.description();
+ ThingDescriptor thingDescriptor(info->thingClassId(), "EVBox Elvi", description);
+ ParamList parameters;
+ foreach (Thing *existingThing, myThings()) {
+ if (existingThing->paramValue(evboxThingSerialPortParamTypeId).toString() == port.portName()) {
+ thingDescriptor.setThingId(existingThing->id());
+ break;
+ }
+ }
+ parameters.append(Param(evboxThingSerialPortParamTypeId, port.portName()));
+ thingDescriptor.setParams(parameters);
+ info->addThingDescriptor(thingDescriptor);
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginEVBox::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ QString interface = thing->paramValue(evboxThingSerialPortParamTypeId).toString();
+ QSerialPort *serialPort = new QSerialPort(interface, info->thing());
+
+ serialPort->setBaudRate(QSerialPort::Baud38400);
+ serialPort->setDataBits(QSerialPort::Data8);
+ serialPort->setStopBits(QSerialPort::OneStop);
+ serialPort->setParity(QSerialPort::NoParity);
+
+ connect(serialPort, &QSerialPort::readyRead, thing, [=]() {
+ thing->setStateValue(evboxConnectedStateTypeId, true);
+ QByteArray data = serialPort->readAll();
+// qCDebug(dcEVBox()) << "Data received from serial port:" << data;
+ m_inputBuffers[thing].append(data);
+ processInputBuffer(thing);
+ });
+
+ connect(serialPort, static_cast(&QSerialPort::error), thing, [=](){
+ qCWarning(dcEVBox()) << "Serial Port error" << serialPort->error() << serialPort->errorString();
+ if (serialPort->error() != QSerialPort::NoError) {
+ if (serialPort->isOpen()) {
+ serialPort->close();
+ }
+ thing->setStateValue(evboxConnectedStateTypeId, false);
+ QTimer::singleShot(1000, this, [=](){
+ serialPort->open(QSerialPort::ReadWrite);
+ });
+ }
+ });
+
+ if (!serialPort->open(QSerialPort::ReadWrite)) {
+ qCWarning(dcEVBox()) << "Unable to open serial port";
+ info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to open the RS485 port. Please make sure the RS485 adapter is connected properly."));
+ return;
+ }
+
+ m_serialPorts.insert(thing, serialPort);
+
+ m_pendingSetups.insert(thing, info);
+ connect(info, &ThingSetupInfo::finished, this, [=](){
+ m_pendingSetups.remove(thing);
+ });
+ QTimer::singleShot(2000, info, [=](){
+ qCDebug(dcEVBox()) << "Timeout during setup";
+ delete m_serialPorts.take(info->thing());
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The EVBox is not responding."));
+ });
+
+
+ sendCommand(thing, Command69, 1);
+}
+
+void IntegrationPluginEVBox::thingRemoved(Thing *thing)
+{
+ m_timers.remove(thing);
+ delete m_serialPorts.take(thing);
+}
+
+void IntegrationPluginEVBox::executeAction(ThingActionInfo *info)
+{
+ Thing *thing = info->thing();
+
+ if (info->action().actionTypeId() == evboxPowerActionTypeId) {
+ bool power = info->action().paramValue(evboxPowerActionPowerParamTypeId).toBool();
+ sendCommand(info->thing(), Command69, power ? info->thing()->stateValue(evboxMaxChargingCurrentStateTypeId).toUInt() : 0);
+ } else if (info->action().actionTypeId() == evboxMaxChargingCurrentActionTypeId) {
+ int maxChargingCurrent = info->action().paramValue(evboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toInt();
+ sendCommand(info->thing(), Command69, maxChargingCurrent);
+ }
+
+ m_pendingActions[thing].append(info);
+ connect(info, &ThingActionInfo::finished, this, [=](){
+ m_pendingActions[thing].removeAll(info);
+ });
+
+}
+
+bool IntegrationPluginEVBox::sendCommand(Thing *thing, Command command, quint16 maxChargingCurrent)
+{
+ QByteArray commandData;
+
+ commandData += "80"; // Dst addr
+ commandData += "A0"; // Sender address
+ commandData += QString::number(command);
+ commandData += QString("%1").arg(maxChargingCurrent * 10, 4, 10, QChar('0'));
+ commandData += QString("%1").arg(maxChargingCurrent * 10, 4, 10, QChar('0'));
+ commandData += QString("%1").arg(maxChargingCurrent * 10, 4, 10, QChar('0'));
+ commandData += "003C"; // Timeout (60 sec)
+ // If we fail to refresh the wallbox after the timeout, it shall turn off, which is what we'll use as default
+ // when we don't know what its set to (as we can't read it).
+ // Hence we do *not* cache the power and maxChargingCurrent states for this one
+ commandData += QString("%1").arg(0, 4, 10, QChar('0'));
+ commandData += QString("%1").arg(0, 4, 10, QChar('0'));
+ commandData += QString("%1").arg(0, 4, 10, QChar('0'));
+
+ commandData += createChecksum(commandData);
+
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << static_cast(STX);
+ stream.writeRawData(commandData.data(), commandData.length());
+ stream << static_cast(ETX);
+
+ qCDebug(dcEVBox()) << "Writing data:" << data << "->" << data.toHex();
+ QSerialPort *serialPort = m_serialPorts.value(thing);
+ qint64 count = serialPort->write(data);
+ if (count == data.length()) {
+ m_waitingForResponses[thing] = true;
+ }
+ return count == data.length();
+}
+
+QByteArray IntegrationPluginEVBox::createChecksum(const QByteArray &data) const
+{
+ QDataStream checksumStream(data);
+ quint8 sum = 0;
+ quint8 xOr = 0;
+ while (!checksumStream.atEnd()) {
+ quint8 byte;
+ checksumStream >> byte;
+ sum += byte;
+ xOr ^= byte;
+ }
+ return QString("%1%2").arg(sum,2,16, QChar('0')).arg(xOr,2,16, QChar('0')).toUpper().toLocal8Bit();
+}
+
+void IntegrationPluginEVBox::processInputBuffer(Thing *thing)
+{
+ QByteArray packet;
+ QDataStream inputStream(m_inputBuffers.value(thing));
+ QDataStream outputStream(&packet, QIODevice::WriteOnly);
+ bool startFound = false, endFound = false;
+
+ while (!inputStream.atEnd()) {
+ quint8 byte;
+ inputStream >> byte;
+ if (!startFound) {
+ if (byte == STX) {
+ startFound = true;
+ continue;
+ } else {
+ qCWarning(dcEVBox()) << "Discarding byte not matching start of frame 0x" + QString::number(byte, 16);
+ continue;
+ }
+ }
+
+ if (byte == ETX) {
+ endFound = true;
+ break;
+ }
+
+ outputStream << byte;
+ }
+
+ if (startFound && endFound) {
+ m_inputBuffers[thing].remove(0, packet.length() + 2);
+ } else {
+// qCDebug(dcEVBox()) << "Data is incomplete... Waiting for more...";
+ return;
+ }
+
+ if (packet.length() < 2) { // In practice it'll be longer, but let's make sure we won't crash checking the checksum on erraneous data
+ qCWarning(dcEVBox()) << "Packet is too short. Discarding packet...";
+ return;
+ }
+
+ qCDebug(dcEVBox()) << "Packet received:" << packet;
+
+ QByteArray checksum = createChecksum(packet.left(packet.length() - 4));
+ if (checksum != packet.right(4)) {
+ qCWarning(dcEVBox()) << "Checksum mismatch for incoming packet:" << packet << "Given checksum:" << packet.right(4) << "Expected:" << checksum;
+ return;
+ }
+
+ // We received something valid... Assuming the last command we've sent is OK.
+ // There's no way to properly match a response to a command, so...
+ if (m_pendingSetups.contains(thing)) {
+ qCDebug(dcEVBox()) << "Finishing setup";
+
+ // Can't use a pluginTimer because it may collide with data on the wire, so we're
+ // manually re-starting the timer whenever we receive something.
+ QTimer *timer = new QTimer(thing);
+ m_timers.insert(thing, timer);
+ timer->setInterval(1000);
+
+ connect(timer, &QTimer::timeout, thing, [=](){
+ thing->setStateValue(evboxConnectedStateTypeId, !m_waitingForResponses[thing]);
+
+ if (thing->stateValue(evboxPowerStateTypeId).toBool()) {
+ sendCommand(thing, Command69, thing->stateValue(evboxMaxChargingCurrentStateTypeId).toDouble());
+ } else {
+ sendCommand(thing, Command69, 0);
+ }
+ });
+
+ m_pendingSetups.take(thing)->finish(Thing::ThingErrorNoError);
+ }
+ if (!m_pendingActions.value(thing).isEmpty()) {
+ ThingActionInfo *info = m_pendingActions.value(thing).first();
+ if (info->action().actionTypeId() == evboxPowerActionTypeId) {
+ thing->setStateValue(evboxPowerStateTypeId, info->action().paramValue(evboxPowerActionPowerParamTypeId));
+ } else if (info->action().actionTypeId() == evboxMaxChargingCurrentActionTypeId) {
+ thing->setStateValue(evboxMaxChargingCurrentStateTypeId, info->action().paramValue(evboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId));
+ }
+ info->finish(Thing::ThingErrorNoError);
+ }
+
+ processDataPacket(thing, packet);
+}
+
+void IntegrationPluginEVBox::processDataPacket(Thing *thing, const QByteArray &packet)
+{
+
+ // The data is a mess of hex and dec values... So do not wonder about the weird from/to hex mess in here...
+ QDataStream stream(QByteArray::fromHex(packet));
+
+ quint8 from, to, commandId, wallboxCount;
+ quint16 minPollInterval, maxChargingCurrent;
+ stream >> from >> to >> commandId >> minPollInterval >> maxChargingCurrent >> wallboxCount;
+
+ commandId = QString::number(commandId, 16).toInt();
+
+ qCDebug(dcEVBox()) << QString("From: %1, To: %2, CMD: %3, MinPollInterval: %4, maxChargingCurrent: %5, Wallbox data count: %6")
+ .arg(from).arg(to).arg(commandId).arg(minPollInterval).arg(maxChargingCurrent).arg(wallboxCount);
+
+ if (commandId != Command69) {
+ qCWarning(dcEVBox()) << "Only command 69 is implemented! Adjust response parsing if sending other commands.";
+ return;
+ }
+
+ m_waitingForResponses[thing] = false;
+
+ // Command 69 would give a list of wallboxes (they can be chained apparently) but we only support a single one for now
+// for (int i = 0; i < wallboxCount; i++) {
+
+ if (wallboxCount > 0) {
+ quint16 minChargingCurrent, chargingCurrentL1, chargingCurrentL2, chargingCurrentL3, cosinePhiL1, cosinePhiL2, cosinePhiL3, totalEnergyConsumed;
+ stream >> minChargingCurrent >> chargingCurrentL1 >> chargingCurrentL2 >> chargingCurrentL3 >> cosinePhiL1 >> cosinePhiL2 >> cosinePhiL3 >> totalEnergyConsumed;
+
+ qCDebug(dcEVBox()) << QString("Min current: %1, actual current L1: %2, L2: %3, L3: %4, Total energy: %5")
+ .arg(minChargingCurrent).arg(chargingCurrentL1).arg(chargingCurrentL2).arg(chargingCurrentL3).arg(totalEnergyConsumed);
+
+ thing->setStateMinMaxValues(evboxMaxChargingCurrentStateTypeId, minChargingCurrent / 10, maxChargingCurrent / 10);
+
+ double currentPower = (chargingCurrentL1 + chargingCurrentL2 + chargingCurrentL3) * 23;
+ thing->setStateValue(evboxCurrentPowerStateTypeId, currentPower);
+
+ thing->setStateValue(evboxTotalEnergyConsumedStateTypeId, totalEnergyConsumed / 1000.0);
+
+ thing->setStateValue(evboxChargingStateTypeId, currentPower > 0);
+
+ int phaseCount = 0;
+ if (chargingCurrentL1 > 0) {
+ phaseCount++;
+ }
+ if (chargingCurrentL2 > 0) {
+ phaseCount++;
+ }
+ if (chargingCurrentL3 > 0) {
+ phaseCount++;
+ }
+ // If all phases are on 0, we aren't charging and don't know how may phases are used...
+ // so only updating the count if we actually do know that at least one is charging.
+ if (phaseCount > 0) {
+ thing->setStateValue(evboxPhaseCountStateTypeId, phaseCount);
+ }
+ }
+
+ m_timers.value(thing)->start();
+}
+
diff --git a/evbox/integrationpluginevbox.h b/evbox/integrationpluginevbox.h
new file mode 100644
index 00000000..fe663d55
--- /dev/null
+++ b/evbox/integrationpluginevbox.h
@@ -0,0 +1,83 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 .
+*
+* 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 INTEGRATIONPLUGINEVBOX_H
+#define INTEGRATIONPLUGINEVBOX_H
+
+#include "integrations/integrationplugin.h"
+
+#include "extern-plugininfo.h"
+
+#include
+
+class QSerialPort;
+
+class IntegrationPluginEVBox: public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginevbox.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ enum Command {
+ Command68 = 68,
+ Command69 = 69
+ };
+ Q_ENUM(Command)
+
+ explicit IntegrationPluginEVBox();
+ ~IntegrationPluginEVBox();
+
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void thingRemoved(Thing *thing) override;
+ void executeAction(ThingActionInfo *info) override;
+
+private:
+ bool sendCommand(Thing *thing, Command command, quint16 maxChargingCurrent);
+
+ QByteArray createChecksum(const QByteArray &data) const;
+
+ void processInputBuffer(Thing *thing);
+ void processDataPacket(Thing *thing, const QByteArray &packet);
+
+private:
+ QHash m_serialPorts;
+ QHash m_pendingSetups;
+ QHash> m_pendingActions;
+
+ QHash m_inputBuffers;
+
+ QHash m_timers;
+ QHash m_waitingForResponses;
+};
+
+#endif // INTEGRATIONPLUGINEVBOX_H
diff --git a/evbox/integrationpluginevbox.json b/evbox/integrationpluginevbox.json
new file mode 100644
index 00000000..ec9a1959
--- /dev/null
+++ b/evbox/integrationpluginevbox.json
@@ -0,0 +1,97 @@
+{
+ "name": "EVBox",
+ "displayName": "EVBox",
+ "id": "3362ac5c-5e2f-43c0-b3fc-70a98773e119",
+ "vendors": [
+ {
+ "name": "evbox",
+ "displayName": "EVBox",
+ "id": "435d8843-887a-4642-b2f5-cd27d18bdb95",
+ "thingClasses": [
+ {
+ "id": "d73a14e3-10af-47bc-9bc7-a5ff6e52f72c",
+ "name": "evbox",
+ "displayName": "Elvi",
+ "createMethods": ["discovery"],
+ "setupMethod": "justadd",
+ "interfaces": [ "evcharger", "connectable" ],
+ "paramTypes": [
+ {
+ "id": "bce7c412-c19a-4e60-a11f-fe8308408abf",
+ "name":"serialPort",
+ "displayName": "Serial port",
+ "type": "QString"
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "5ef06038-9fa9-4d5d-8d9b-0375b8aa343a",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "defaultValue": false,
+ "cached": false
+ },
+ {
+ "id": "e3ed9334-68bf-47eb-bd9a-d9a800529bce",
+ "name": "power",
+ "displayName": "Charging enabled",
+ "displayNameAction": "Enable/disable charging",
+ "type": "bool",
+ "defaultValue": false,
+ "writable": true,
+ "cached": false
+ },
+ {
+ "id": "cc9ae86d-fc86-473f-ae90-d9eb20d7a011",
+ "name": "maxChargingCurrent",
+ "displayName": "Maximum charging current",
+ "displayNameAction": "Set maximum charging current",
+ "type": "uint",
+ "writable": true,
+ "unit": "Ampere",
+ "minValue": "6",
+ "maxValue": "22",
+ "defaultValue": 6,
+ "cached": false
+ },
+ {
+ "id": "8d3c80b7-f1f1-48de-8b7a-f99b9bc688b7",
+ "name": "currentPower",
+ "displayName": "Current power consumption",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0,
+ "cached": false
+ },
+ {
+ "id": "9fd15d14-c228-4af6-85af-4cb171d6f9f0",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total consumed energy",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "6439fdba-dc03-454f-bc33-0f6e2619d2ab",
+ "name": "charging",
+ "displayName": "Charging",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "1120abe3-1878-4301-a701-014b24fd1e41",
+ "name": "phaseCount",
+ "displayName": "Used phases",
+ "type": "uint",
+ "minValue": 1,
+ "maxValue": 3,
+ "defaultValue": 1
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/evbox/meta.json b/evbox/meta.json
new file mode 100644
index 00000000..a0d966b3
--- /dev/null
+++ b/evbox/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "EVBox",
+ "tagline": "Integrates EVBox wallboxes with nymea.",
+ "icon": "evbox-logo-blue.png",
+ "stability": "consumer",
+ "offline": true,
+ "technologies": [
+ "serial-port"
+ ],
+ "categories": [
+ "energy"
+ ]
+}
diff --git a/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts b/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts
new file mode 100644
index 00000000..0d41216c
--- /dev/null
+++ b/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts
@@ -0,0 +1,101 @@
+
+
+
+
+ EVBox
+
+
+ Charging
+ The name of the StateType ({6439fdba-dc03-454f-bc33-0f6e2619d2ab}) of ThingClass evbox
+
+
+
+
+
+ Charging enabled
+ The name of the ParamType (ThingClass: evbox, ActionType: power, ID: {e3ed9334-68bf-47eb-bd9a-d9a800529bce})
+----------
+The name of the StateType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass evbox
+
+
+
+
+ Connected
+ The name of the StateType ({5ef06038-9fa9-4d5d-8d9b-0375b8aa343a}) of ThingClass evbox
+
+
+
+
+ Current power consumption
+ The name of the StateType ({8d3c80b7-f1f1-48de-8b7a-f99b9bc688b7}) of ThingClass evbox
+
+
+
+
+
+ EVBox
+ The name of the vendor ({435d8843-887a-4642-b2f5-cd27d18bdb95})
+----------
+The name of the plugin EVBox ({3362ac5c-5e2f-43c0-b3fc-70a98773e119})
+
+
+
+
+ Elvi
+ The name of the ThingClass ({d73a14e3-10af-47bc-9bc7-a5ff6e52f72c})
+
+
+
+
+ Enable/disable charging
+ The name of the ActionType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass evbox
+
+
+
+
+
+ Maximum charging current
+ The name of the ParamType (ThingClass: evbox, ActionType: maxChargingCurrent, ID: {cc9ae86d-fc86-473f-ae90-d9eb20d7a011})
+----------
+The name of the StateType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass evbox
+
+
+
+
+ Serial port
+ The name of the ParamType (ThingClass: evbox, Type: thing, ID: {bce7c412-c19a-4e60-a11f-fe8308408abf})
+
+
+
+
+ Set maximum charging current
+ The name of the ActionType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass evbox
+
+
+
+
+ Total consumed energy
+ The name of the StateType ({9fd15d14-c228-4af6-85af-4cb171d6f9f0}) of ThingClass evbox
+
+
+
+
+ Used phases
+ The name of the StateType ({1120abe3-1878-4301-a701-014b24fd1e41}) of ThingClass evbox
+
+
+
+
+ IntegrationPluginEVBox
+
+
+ Unable to open the RS485 port. Please make sure the RS485 adapter is connected properly.
+
+
+
+
+ The EVBox is not responding.
+
+
+
+
diff --git a/nymea-plugins.pro b/nymea-plugins.pro
index f6d3babf..7c714647 100644
--- a/nymea-plugins.pro
+++ b/nymea-plugins.pro
@@ -22,6 +22,7 @@ PLUGIN_DIRS = \
elgato \
eq-3 \
espuino \
+ evbox \
fastcom \
flowercare \
fronius \