New plugin: EVBox

master
Michael Zanetti 2022-11-17 11:16:40 +01:00
parent effe59e9b9
commit a72f421db5
11 changed files with 680 additions and 0 deletions

9
debian/control vendored
View File

@ -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},

2
debian/nymea-plugin-evbox.install.in vendored Normal file
View File

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

17
evbox/README.md Normal file
View File

@ -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.

BIN
evbox/evbox-logo-blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

9
evbox/evbox.pro Normal file
View File

@ -0,0 +1,9 @@
include(../plugins.pri)
QT += network serialport
SOURCES += \
integrationpluginevbox.cpp \
HEADERS += \
integrationpluginevbox.h \

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginevbox.h"
#include "plugininfo.h"
#include "plugintimer.h"
#include <QSerialPortInfo>
#include <QSerialPort>
#include <QDataStream>
#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<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&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<quint8>(STX);
stream.writeRawData(commandData.data(), commandData.length());
stream << static_cast<quint8>(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();
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTEGRATIONPLUGINEVBOX_H
#define INTEGRATIONPLUGINEVBOX_H
#include "integrations/integrationplugin.h"
#include "extern-plugininfo.h"
#include <QTimer>
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<Thing*, QSerialPort*> m_serialPorts;
QHash<Thing*, ThingSetupInfo*> m_pendingSetups;
QHash<Thing*, QList<ThingActionInfo*>> m_pendingActions;
QHash<Thing*, QByteArray> m_inputBuffers;
QHash<Thing*, QTimer*> m_timers;
QHash<Thing*, bool> m_waitingForResponses;
};
#endif // INTEGRATIONPLUGINEVBOX_H

View File

@ -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
}
]
}
]
}
]
}

13
evbox/meta.json Normal file
View File

@ -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"
]
}

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>EVBox</name>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="40"/>
<source>Charging</source>
<extracomment>The name of the StateType ({6439fdba-dc03-454f-bc33-0f6e2619d2ab}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="43"/>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="46"/>
<source>Charging enabled</source>
<extracomment>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</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="49"/>
<source>Connected</source>
<extracomment>The name of the StateType ({5ef06038-9fa9-4d5d-8d9b-0375b8aa343a}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="52"/>
<source>Current power consumption</source>
<extracomment>The name of the StateType ({8d3c80b7-f1f1-48de-8b7a-f99b9bc688b7}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="55"/>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="58"/>
<source>EVBox</source>
<extracomment>The name of the vendor ({435d8843-887a-4642-b2f5-cd27d18bdb95})
----------
The name of the plugin EVBox ({3362ac5c-5e2f-43c0-b3fc-70a98773e119})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="61"/>
<source>Elvi</source>
<extracomment>The name of the ThingClass ({d73a14e3-10af-47bc-9bc7-a5ff6e52f72c})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="64"/>
<source>Enable/disable charging</source>
<extracomment>The name of the ActionType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="67"/>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="70"/>
<source>Maximum charging current</source>
<extracomment>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</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="73"/>
<source>Serial port</source>
<extracomment>The name of the ParamType (ThingClass: evbox, Type: thing, ID: {bce7c412-c19a-4e60-a11f-fe8308408abf})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="76"/>
<source>Set maximum charging current</source>
<extracomment>The name of the ActionType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="79"/>
<source>Total consumed energy</source>
<extracomment>The name of the StateType ({9fd15d14-c228-4af6-85af-4cb171d6f9f0}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-Desktop-Debug/evbox/plugininfo.h" line="82"/>
<source>Used phases</source>
<extracomment>The name of the StateType ({1120abe3-1878-4301-a701-014b24fd1e41}) of ThingClass evbox</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>IntegrationPluginEVBox</name>
<message>
<location filename="../integrationpluginevbox.cpp" line="101"/>
<source>Unable to open the RS485 port. Please make sure the RS485 adapter is connected properly.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginevbox.cpp" line="114"/>
<source>The EVBox is not responding.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -22,6 +22,7 @@ PLUGIN_DIRS = \
elgato \
eq-3 \
espuino \
evbox \
fastcom \
flowercare \
fronius \