Add powersync-plugin-keba (fork) for FR P30 PV-Edition shutter
- Fork du plugin keba upstream, patch kebaproductinfo.cpp: la detection du courant comparait connectorTypeValue au lieu de connectorCurrentValue, faisant echouer isValid() sur le type Shutter (KC-P30-ESS...). - debian/: 2e paquet binaire avec Provides/Replaces/Conflicts nymea-plugin-keba, .install par paquet (routage des .so en multi-binaire). - .pro: backslash manquant, openmeteo etait tombe des SUBDIRS.
This commit is contained in:
parent
1df5433d61
commit
dafc2d0166
8
debian/changelog
vendored
8
debian/changelog
vendored
@ -1,3 +1,11 @@
|
||||
etm-powersync-plugins (1.15.0+etm2) trixie; urgency=medium
|
||||
|
||||
* Add powersync-plugin-keba: fork of the nymea KEBA plugin, patched for the
|
||||
French P30 PV-Edition with socket shutter (KC-P30-ESS...).
|
||||
* Provides/Replaces/Conflicts nymea-plugin-keba.
|
||||
|
||||
-- ETM-Schurig SARL <contact@etm-schurig.eu> Sun, 31 May 2026 12:00:00 +0200
|
||||
|
||||
etm-powersync-plugins (1.15.0+etm1) trixie; urgency=medium
|
||||
|
||||
* Initial ETM packaging of the openmeteo integration plugin.
|
||||
|
||||
13
debian/control
vendored
13
debian/control
vendored
@ -22,3 +22,16 @@ Description: PowerSync integration plugin for Open-Meteo weather data
|
||||
This package contains the nymea integration plugin to fetch weather and solar
|
||||
forecast data from the Open-Meteo online service, for use by the ETM PowerSync
|
||||
home energy management system.
|
||||
|
||||
Package: powersync-plugin-keba
|
||||
Architecture: any
|
||||
Section: libs
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
Provides: nymea-plugin-keba
|
||||
Replaces: nymea-plugin-keba
|
||||
Conflicts: nymea-plugin-keba
|
||||
Description: PowerSync integration plugin for KEBA KeContact wallboxes (ETM fork)
|
||||
Fork of the nymea KEBA plugin, patched to recognise the French KC-P30
|
||||
PV-Edition with socket shutter (KC-P30-ESS...), wrongly rejected by the
|
||||
upstream discovery. Uses the KEBA UDP protocol.
|
||||
|
||||
1
debian/powersync-plugin-keba.install
vendored
Normal file
1
debian/powersync-plugin-keba.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/*/nymea/plugins/libnymea_integrationpluginkeba.so
|
||||
1
debian/powersync-plugin-openmeteo.install
vendored
Normal file
1
debian/powersync-plugin-openmeteo.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/*/nymea/plugins/libnymea_integrationpluginopenmeteo.so
|
||||
@ -1,7 +1,8 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
PLUGIN_DIRS = \
|
||||
openmeteo \
|
||||
keba \
|
||||
openmeteo
|
||||
# eastron (à finir : pas de code)
|
||||
# waveshare (à normaliser : noms incohérents)
|
||||
|
||||
|
||||
31
keba/README.md
Normal file
31
keba/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Keba Wallbox
|
||||
|
||||
This plugin allows to control Keba KeContact EV-Charging stations.
|
||||
|
||||
## Supported Things
|
||||
|
||||
* KeContact Wallbox
|
||||
* P20 (certain models)
|
||||
* P30
|
||||
* c-series
|
||||
* x-series
|
||||
* PV Edition
|
||||
* BMW (certain models)
|
||||
* [Keba Deutschland Edition](https://a.storyblok.com/f/40131/x/fc59dc7bf7/datenblatt_deutschland_edition.pdf) (DE440)
|
||||
(by March 2022)
|
||||
|
||||
Please make sure that your model supports communication through the UDP protocol.
|
||||
The [product overview](https://www.keba.com/download/x/21634787f7/kecontact-p30_productoverview_en.pdf) helps to verify about your models capabilities.
|
||||
|
||||
## Requirements
|
||||
|
||||
* nymea and the wallbox are required to be in the same network.
|
||||
* UDP Port 7090 must not be blocked by a firewall or router.
|
||||
* The package "nymea-plugin-keba" must be installed.
|
||||
* KeContact P20 Charging station with network connection (LSA+ socket). Firmware version: `2.5` or higher.
|
||||
* KeContact P30 Charging station or BMW wallbox. Firmware version `3.05` of higher.
|
||||
* **Enabled UDP function with DIP-switch `DSW1.3 = ON`.**
|
||||
|
||||
## More information
|
||||
|
||||
https://www.keba.com/en/emobility/products/product-overview/product_overview
|
||||
782
keba/integrationpluginkeba.cpp
Normal file
782
keba/integrationpluginkeba.cpp
Normal file
@ -0,0 +1,782 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "integrationpluginkeba.h"
|
||||
#include "kebaproductinfo.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QUdpSocket>
|
||||
#include <QTimeZone>
|
||||
|
||||
IntegrationPluginKeba::IntegrationPluginKeba()
|
||||
{
|
||||
// KebaProductInfo bmw("BMW-10-EC240522-E1R");
|
||||
// KebaProductInfo ke("KC-P30-EC240122-E0R");
|
||||
// KebaProductInfo ge("KC-P30-EC220112-000-DE");
|
||||
// KebaProductInfo n("KC-P30-EC2404B2-M0A-GE");
|
||||
// KebaProductInfo pv("KC-P30-EC2204U2-E00-PV");
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::init()
|
||||
{
|
||||
m_macAddressParamTypeIds.insert(kebaThingClassId, kebaThingMacAddressParamTypeId);
|
||||
m_macAddressParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingMacAddressParamTypeId);
|
||||
|
||||
m_hostNameParamTypeIds.insert(kebaThingClassId, kebaThingHostNameParamTypeId);
|
||||
m_hostNameParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingHostNameParamTypeId);
|
||||
|
||||
m_addressParamTypeIds.insert(kebaThingClassId, kebaThingAddressParamTypeId);
|
||||
m_addressParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingAddressParamTypeId);
|
||||
|
||||
m_modelParamTypeIds.insert(kebaThingClassId, kebaThingModelParamTypeId);
|
||||
m_modelParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingModelParamTypeId);
|
||||
|
||||
m_serialNumberParamTypeIds.insert(kebaThingClassId, kebaThingSerialNumberParamTypeId);
|
||||
m_serialNumberParamTypeIds.insert(kebaSimpleThingClassId, kebaSimpleThingSerialNumberParamTypeId);
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
// Init data layer if not already created
|
||||
if (!m_kebaDataLayer){
|
||||
qCDebug(dcKeba()) << "Creating new Keba data layer...";
|
||||
m_kebaDataLayer= new KeContactDataLayer(this);
|
||||
if (!m_kebaDataLayer->init()) {
|
||||
m_kebaDataLayer->deleteLater();
|
||||
m_kebaDataLayer = nullptr;
|
||||
qCWarning(dcKeba()) << "Failed to create Keba data layer...";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The communication could not be established."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcKeba()) << "The network discovery does not seem to be available.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The network discovery is not available. Please enter the IP address manually."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
|
||||
KebaDiscovery *discovery = new KebaDiscovery(m_kebaDataLayer, hardwareManager()->networkDeviceDiscovery(), info);
|
||||
connect(discovery, &KebaDiscovery::discoveryFinished, info, [=](){
|
||||
|
||||
foreach (const KebaDiscovery::KebaDiscoveryResult &result, discovery->discoveryResults()) {
|
||||
|
||||
KebaProductInfo productInformation(result.product);
|
||||
if (!productInformation.isValid()) {
|
||||
qCWarning(dcKeba()) << "Discovered keba with invalid product information" << result.product;
|
||||
continue;
|
||||
}
|
||||
|
||||
ThingClassId discoveredThingClassId = kebaThingClassId;
|
||||
// Check if this is a keba without meter (aka simple)
|
||||
if (productInformation.meter() == KebaProductInfo::NoMeter) {
|
||||
discoveredThingClassId = kebaSimpleThingClassId;
|
||||
}
|
||||
|
||||
// Make sure we show only the result we searched for to prevent cross adding between normal and simple
|
||||
if (discoveredThingClassId != info->thingClassId())
|
||||
continue;
|
||||
|
||||
ThingDescriptor descriptor(discoveredThingClassId, productInformation.manufacturer() + " " + result.product, "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString());
|
||||
qCDebug(dcKeba()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||
|
||||
// Check if we already have set up this device
|
||||
Things existingThings = myThings().filterByParam(m_serialNumberParamTypeIds.value(discoveredThingClassId), result.serialNumber);
|
||||
if (existingThings.count() == 1) {
|
||||
qCDebug(dcKeba()) << "This keba already exists in the system!" << result.networkDeviceInfo;
|
||||
descriptor.setThingId(existingThings.first()->id());
|
||||
}
|
||||
|
||||
ParamList params;
|
||||
params << Param(m_macAddressParamTypeIds.value(discoveredThingClassId), result.networkDeviceInfo.thingParamValueMacAddress());
|
||||
params << Param(m_hostNameParamTypeIds.value(discoveredThingClassId), result.networkDeviceInfo.thingParamValueHostName());
|
||||
params << Param(m_addressParamTypeIds.value(discoveredThingClassId), result.networkDeviceInfo.thingParamValueAddress());
|
||||
params << Param(m_modelParamTypeIds.value(discoveredThingClassId), result.product);
|
||||
params << Param(m_serialNumberParamTypeIds.value(discoveredThingClassId), result.serialNumber);
|
||||
descriptor.setParams(params);
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
// Start the discovery process
|
||||
discovery->startDiscovery();
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
// Handle reconfigure
|
||||
if (myThings().contains(thing)) {
|
||||
KeContact *keba = m_kebaDevices.take(thing->id());
|
||||
if (keba) {
|
||||
qCDebug(dcKeba()) << "Reconfigure" << thing->name() << thing->params();
|
||||
delete keba;
|
||||
}
|
||||
|
||||
if (m_monitors.contains(thing)) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
|
||||
// Now continue with the normal setup..
|
||||
}
|
||||
|
||||
qCDebug(dcKeba()) << "Setting up" << thing->name() << thing->params();
|
||||
if (!m_kebaDataLayer){
|
||||
qCDebug(dcKeba()) << "Creating new Keba data layer...";
|
||||
m_kebaDataLayer = new KeContactDataLayer(this);
|
||||
if (!m_kebaDataLayer->init()) {
|
||||
m_kebaDataLayer->deleteLater();
|
||||
m_kebaDataLayer = nullptr;
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Error opening network port."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create a monitor so we always get the correct IP in the network and see if the device is reachable without polling on our own
|
||||
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing);
|
||||
// Make sure we have a valid mac address, otherwise no monitor and not auto searching is possible
|
||||
if (!monitor) {
|
||||
qCWarning(dcKeba()) << "Can not set up connection monitor with the given parameters:" << thing->params();
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("Unable to set up the connection with this configuration. Please reconfigure the connection."));
|
||||
return;
|
||||
}
|
||||
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
|
||||
// Only if the setup has been finished
|
||||
KeContact *keba = m_kebaDevices.value(thing->id());
|
||||
if (!keba)
|
||||
return;
|
||||
|
||||
qCDebug(dcKeba()) << "Network device monitor for" << thing->name() << (reachable ? "is now reachable" : "is not reachable any more" );
|
||||
if (reachable) {
|
||||
// Update address and refresh
|
||||
thing->setStateValue("hostAddress", monitor->networkDeviceInfo().address().toString());
|
||||
keba->setAddress(monitor->networkDeviceInfo().address());
|
||||
refresh(thing, keba);
|
||||
}
|
||||
});
|
||||
|
||||
// Continue with setup only if we know that the network device is reachable
|
||||
m_monitors.insert(thing, monitor);
|
||||
if (monitor->reachable()) {
|
||||
setupKeba(info, monitor->networkDeviceInfo().address());
|
||||
} else {
|
||||
// otherwise wait until we reach the networkdevice before setting up the device
|
||||
qCDebug(dcKeba()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable.";
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
|
||||
if (reachable) {
|
||||
qCDebug(dcKeba()) << "Network device" << thing->name() << "is now reachable. Continue with the setup...";
|
||||
setupKeba(info, monitor->networkDeviceInfo().address());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::postSetupThing(Thing *thing)
|
||||
{
|
||||
qCDebug(dcKeba()) << "Post setup" << thing->name();
|
||||
|
||||
KeContact *keba = m_kebaDevices.value(thing->id());
|
||||
if (!keba) {
|
||||
qCWarning(dcKeba()) << "No Keba connection found for this thing while doing post setup.";
|
||||
return;
|
||||
}
|
||||
|
||||
refresh(thing, keba);
|
||||
|
||||
if (!m_updateTimer) {
|
||||
m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
|
||||
connect(m_updateTimer, &PluginTimer::timeout, this, [this]() {
|
||||
foreach (const ThingId &thingId, m_kebaDevices.keys()) {
|
||||
KeContact *keba = m_kebaDevices.value(thingId);
|
||||
Thing *thing = myThings().findById(thingId);
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
if (!keba) {
|
||||
qCWarning(dcKeba()) << "No Keba connection found for" << thing->name();
|
||||
return;
|
||||
}
|
||||
|
||||
refresh(thing, keba);
|
||||
}
|
||||
});
|
||||
|
||||
m_updateTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::thingRemoved(Thing *thing)
|
||||
{
|
||||
qCDebug(dcKeba()) << "Removing" << thing->name();
|
||||
if (m_kebaDevices.contains(thing->id())) {
|
||||
KeContact *keba = m_kebaDevices.take(thing->id());
|
||||
keba->deleteLater();
|
||||
}
|
||||
|
||||
if (m_monitors.contains(thing)) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
|
||||
m_lastSessionId.remove(thing->id());
|
||||
|
||||
if (myThings().empty()) {
|
||||
qCDebug(dcKeba()) << "Stopping plugin timers ...";
|
||||
if (m_updateTimer) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_updateTimer);
|
||||
m_updateTimer = nullptr;
|
||||
}
|
||||
|
||||
qCDebug(dcKeba()) << "Closing keba data layer...";
|
||||
m_kebaDataLayer->deleteLater();
|
||||
m_kebaDataLayer= nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
Action action = info->action();
|
||||
|
||||
KeContact *keba = m_kebaDevices.value(thing->id());
|
||||
if (!keba) {
|
||||
qCWarning(dcKeba()) << "Device not properly initialized, Keba object missing";
|
||||
return info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
}
|
||||
|
||||
// Make sure keba is reachable
|
||||
if (!keba->reachable()) {
|
||||
qCWarning(dcKeba()) << "Failed to execute action. The keba seems not to be reachable" << thing;
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
QUuid requestId;
|
||||
if (thing->thingClassId() == kebaThingClassId) {
|
||||
if (action.actionTypeId() == kebaMaxChargingCurrentActionTypeId) {
|
||||
int milliAmpere = qRound(action.paramValue(kebaMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toDouble() * 1000);
|
||||
requestId = keba->setMaxAmpereGeneral(milliAmpere);
|
||||
} else if (action.actionTypeId() == kebaPowerActionTypeId) {
|
||||
requestId = keba->enableOutput(action.param(kebaPowerActionTypeId).value().toBool());
|
||||
} else if (action.actionTypeId() == kebaDisplayActionTypeId) {
|
||||
requestId = keba->displayMessage(action.param(kebaDisplayActionMessageParamTypeId).value().toByteArray());
|
||||
} else if (action.actionTypeId() == kebaOutputX2ActionTypeId) {
|
||||
requestId = keba->setOutputX2(action.param(kebaOutputX2ActionOutputX2ParamTypeId).value().toBool());
|
||||
} else if (action.actionTypeId() == kebaFailsafeModeActionTypeId) {
|
||||
int timeout = 0;
|
||||
if (action.param(kebaFailsafeModeActionFailsafeModeParamTypeId).value().toBool()) {
|
||||
timeout = 60;
|
||||
}
|
||||
requestId = keba->setFailsafe(timeout, 0, false);
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Unhandled ActionTypeId:" << action.actionTypeId();
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
} else if (thing->thingClassId() == kebaSimpleThingClassId) {
|
||||
if (action.actionTypeId() == kebaSimpleMaxChargingCurrentActionTypeId) {
|
||||
int milliAmpere = qRound(action.paramValue(kebaSimpleMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toDouble() * 1000);
|
||||
requestId = keba->setMaxAmpereGeneral(milliAmpere);
|
||||
} else if (action.actionTypeId() == kebaSimplePowerActionTypeId) {
|
||||
requestId = keba->enableOutput(action.param(kebaSimplePowerActionTypeId).value().toBool());
|
||||
} else if (action.actionTypeId() == kebaSimpleDisplayActionTypeId) {
|
||||
requestId = keba->displayMessage(action.param(kebaSimpleDisplayActionMessageParamTypeId).value().toByteArray());
|
||||
} else if (action.actionTypeId() == kebaSimpleOutputX2ActionTypeId) {
|
||||
requestId = keba->setOutputX2(action.param(kebaSimpleOutputX2ActionOutputX2ParamTypeId).value().toBool());
|
||||
} else if (action.actionTypeId() == kebaSimpleFailsafeModeActionTypeId) {
|
||||
int timeout = 0;
|
||||
if (action.param(kebaSimpleFailsafeModeActionFailsafeModeParamTypeId).value().toBool()) {
|
||||
timeout = 60;
|
||||
}
|
||||
requestId = keba->setFailsafe(timeout, 0, false);
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Unhandled ActionTypeId:" << action.actionTypeId();
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If the keba returns an invalid uuid, something went wrong
|
||||
if (requestId.isNull()) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
m_asyncActions.insert(requestId, info);
|
||||
connect(info, &ThingActionInfo::aborted, this, [requestId, this]{ m_asyncActions.remove(requestId); });
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::setupKeba(ThingSetupInfo *info, const QHostAddress &address)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
KeContact *keba = new KeContact(address, m_kebaDataLayer, this);
|
||||
connect(keba, &KeContact::reachableChanged, thing, [=](bool reachable){
|
||||
thing->setStateValue("connected", reachable);
|
||||
if (!reachable) {
|
||||
thing->setStateValue("voltagePhaseA", 0);
|
||||
thing->setStateValue("voltagePhaseB", 0);
|
||||
thing->setStateValue("voltagePhaseC", 0);
|
||||
thing->setStateValue("currentPhaseA", 0);
|
||||
thing->setStateValue("currentPhaseB", 0);
|
||||
thing->setStateValue("currentPhaseC", 0);
|
||||
thing->setStateValue("currentPower", 0);
|
||||
thing->setStateValue("powerFactor", 0);
|
||||
}
|
||||
});
|
||||
|
||||
connect(keba, &KeContact::commandExecuted, this, &IntegrationPluginKeba::onCommandExecuted);
|
||||
connect(keba, &KeContact::reportTwoReceived, this, &IntegrationPluginKeba::onReportTwoReceived);
|
||||
connect(keba, &KeContact::reportThreeReceived, this, &IntegrationPluginKeba::onReportThreeReceived);
|
||||
connect(keba, &KeContact::report1XXReceived, this, &IntegrationPluginKeba::onReport1XXReceived);
|
||||
connect(keba, &KeContact::broadcastReceived, this, &IntegrationPluginKeba::onBroadcastReceived);
|
||||
|
||||
// Clean up if the setup fails
|
||||
connect(info, &ThingSetupInfo::aborted, keba, &KeContact::deleteLater);
|
||||
|
||||
// Make sure we receive data from the keba and the DIP switches are configured correctly
|
||||
connect(keba, &KeContact::reportOneReceived, info, [=] (const KeContact::ReportOne &report) {
|
||||
Thing *thing = info->thing();
|
||||
qCDebug(dcKeba()) << "Report one received for" << thing->name();
|
||||
qCDebug(dcKeba()) << " - Firmware" << report.firmware;
|
||||
qCDebug(dcKeba()) << " - Serial" << report.serialNumber;
|
||||
qCDebug(dcKeba()) << " - Product" << report.product;
|
||||
qCDebug(dcKeba()) << " - Uptime" << report.seconds / 60 << "[min]";
|
||||
qCDebug(dcKeba()) << " - Com Module" << report.comModule;
|
||||
qCDebug(dcKeba()) << " - DIP switch 1" << report.dipSw1;
|
||||
qCDebug(dcKeba()) << " - DIP switch 2" << report.dipSw2;
|
||||
|
||||
KebaProductInfo productInformation(report.product);
|
||||
|
||||
if (thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString().isEmpty()) {
|
||||
qCDebug(dcKeba()) << "Update serial number parameter for" << thing << "to" << report.serialNumber;
|
||||
thing->setParamValue(m_serialNumberParamTypeIds.value(thing->thingClassId()), report.serialNumber);
|
||||
}
|
||||
|
||||
if (thing->paramValue(m_modelParamTypeIds.value(thing->thingClassId())).toString().isEmpty()) {
|
||||
qCDebug(dcKeba()) << "Update model parameter for" << thing << "to" << report.product;
|
||||
thing->setParamValue(m_modelParamTypeIds.value(thing->thingClassId()), report.product);
|
||||
}
|
||||
|
||||
// Verify the DIP switches and warn the user in case if wrong configuration
|
||||
// For having UPD controll on the keba we need DIP Switch 1.3 enabled
|
||||
KeContact::DipSwitchOneFlag dipSwOne(report.dipSw1);
|
||||
qCDebug(dcKeba()) << dipSwOne;
|
||||
if (!dipSwOne.testFlag(KeContact::DipSwitchOneSmartHomeInterface)) {
|
||||
qCWarning(dcKeba()) << "Connected successfully to Keba but the DIP Switch for controlling it is not enabled.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The required communication interface is not enabled on this keba. Please make sure the DIP switch 1.3 is switched on and try again."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the product code and check if the model actually supports the UDP/Modbus communication
|
||||
// Supported are:
|
||||
// - The A series (german edition), no meter DE440 (green edition)
|
||||
// - The B series (german edition), no meter DE440
|
||||
// - All C series
|
||||
// - All X series
|
||||
|
||||
if (productInformation.isValid()) {
|
||||
bool supported = false;
|
||||
qCDebug(dcKeba()) << "Product information are valid. Evaluating if model supports UDP/Modbus communication...";
|
||||
|
||||
switch (productInformation.series()) {
|
||||
case KebaProductInfo::SeriesA:
|
||||
if (productInformation.model() == "P30" && productInformation.germanEdition()) {
|
||||
qCDebug(dcKeba()) << "The P30 A series german edition is supported (DE440 GREEN EDITION)";
|
||||
supported = true;
|
||||
}
|
||||
break;
|
||||
case KebaProductInfo::SeriesB:
|
||||
if (productInformation.model() == "P30" && productInformation.germanEdition()) {
|
||||
qCDebug(dcKeba()) << "The P30 B series german edition is supported (DE440)";
|
||||
supported = true;
|
||||
}
|
||||
break;
|
||||
case KebaProductInfo::SeriesC:
|
||||
case KebaProductInfo::SeriesXWlan:
|
||||
case KebaProductInfo::SeriesXWlan3G:
|
||||
case KebaProductInfo::SeriesXWlan4G:
|
||||
case KebaProductInfo::SeriesX3G:
|
||||
case KebaProductInfo::SeriesX4G:
|
||||
case KebaProductInfo::SeriesSpecial:
|
||||
qCDebug(dcKeba()) << "The keba" << productInformation.series() << "is capable of communicating using UDP";
|
||||
supported = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!supported) {
|
||||
qCWarning(dcKeba()) << "Connected successfully to Keba but this model" << productInformation.series() << "has no communication module.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("This model does not support communication with smart devices."));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Product information are not valid. Cannot determin if this model supports UDP/Modbus communication, assuming yes so let's try to init...";
|
||||
}
|
||||
|
||||
m_kebaDevices.insert(thing->id(), keba);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
qCDebug(dcKeba()) << "Setup finsihed successfully for" << thing << thing->params();
|
||||
|
||||
thing->setStateValue("connected", true);
|
||||
thing->setStateValue("hostAddress", address.toString());
|
||||
thing->setStateValue("firmware", report.firmware);
|
||||
thing->setStateValue("uptime", report.seconds / 60);
|
||||
|
||||
if (thing->thingClassId() == kebaSimpleThingClassId) {
|
||||
thing->setStateValue(kebaSimplePhaseCountStateTypeId, thing->setting(kebaSimpleThingClassId));
|
||||
}
|
||||
|
||||
connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingsTypeId, const QVariant &value){
|
||||
if (settingsTypeId == kebaSimpleSettingsPhaseCountParamTypeId) {
|
||||
thing->setStateValue(kebaSimplePhaseCountStateTypeId, value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
keba->getReport1();
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::onCommandExecuted(QUuid requestId, bool success)
|
||||
{
|
||||
if (m_asyncActions.contains(requestId)) {
|
||||
KeContact *keba = static_cast<KeContact *>(sender());
|
||||
Thing *thing = myThings().findById(m_kebaDevices.key(keba));
|
||||
if (!thing) {
|
||||
qCWarning(dcKeba()) << "On command executed: missing device object";
|
||||
return;
|
||||
}
|
||||
|
||||
ThingActionInfo *info = m_asyncActions.take(requestId);
|
||||
if (success) {
|
||||
qCDebug(dcKeba()) << "Action execution finished successfully. Request ID:" << requestId.toString();
|
||||
|
||||
if (thing->thingClassId() == kebaThingClassId) {
|
||||
// Set the value to the state so we don't have to wait for the report 2 response
|
||||
if (info->action().actionTypeId() == kebaMaxChargingCurrentActionTypeId) {
|
||||
double value = info->action().paramValue(kebaMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toDouble();
|
||||
info->thing()->setStateValue("maxChargingCurrent", value);
|
||||
} else if (info->action().actionTypeId() == kebaPowerActionTypeId) {
|
||||
info->thing()->setStateValue("power", info->action().paramValue(kebaPowerActionTypeId).toBool());
|
||||
}
|
||||
} else if (thing->thingClassId() == kebaSimpleThingClassId) {
|
||||
// Set the value to the state so we don't have to wait for the report 2 response
|
||||
if (info->action().actionTypeId() == kebaSimpleMaxChargingCurrentActionTypeId) {
|
||||
double value = info->action().paramValue(kebaSimpleMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toDouble();
|
||||
info->thing()->setStateValue("maxChargingCurrent", value);
|
||||
} else if (info->action().actionTypeId() == kebaPowerActionTypeId) {
|
||||
info->thing()->setStateValue("power", info->action().paramValue(kebaSimplePowerActionTypeId).toBool());
|
||||
}
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Action execution finished with error. Request ID:" << requestId.toString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::setDeviceState(Thing *thing, KeContact::State state)
|
||||
{
|
||||
switch (state) {
|
||||
case KeContact::StateStarting:
|
||||
thing->setStateValue("activity", "Starting");
|
||||
break;
|
||||
case KeContact::StateNotReady:
|
||||
thing->setStateValue("activity", "Not ready for charging");
|
||||
break;
|
||||
case KeContact::StateReady:
|
||||
thing->setStateValue("activity", "Ready for charging");
|
||||
break;
|
||||
case KeContact::StateCharging:
|
||||
thing->setStateValue("activity", "Charging");
|
||||
break;
|
||||
case KeContact::StateError:
|
||||
thing->setStateValue("activity", "Error");
|
||||
break;
|
||||
case KeContact::StateAuthorizationRejected:
|
||||
thing->setStateValue("activity", "Authorization rejected");
|
||||
break;
|
||||
}
|
||||
|
||||
thing->setStateValue("charging", state == KeContact::StateCharging);
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::setDevicePlugState(Thing *thing, KeContact::PlugState plugState)
|
||||
{
|
||||
switch (plugState) {
|
||||
case KeContact::PlugStateUnplugged:
|
||||
thing->setStateValue("plugState", "Unplugged");
|
||||
break;
|
||||
case KeContact::PlugStatePluggedOnChargingStation:
|
||||
thing->setStateValue("plugState", "Plugged in charging station");
|
||||
break;
|
||||
case KeContact::PlugStatePluggedOnChargingStationAndPluggedOnEV:
|
||||
thing->setStateValue("plugState", "Plugged in on EV");
|
||||
break;
|
||||
case KeContact::PlugStatePluggedOnChargingStationAndPlugLocked:
|
||||
thing->setStateValue("plugState", "Plugged in and locked");
|
||||
break;
|
||||
case KeContact::PlugStatePluggedOnChargingStationAndPlugLockedAndPluggedOnEV:
|
||||
thing->setStateValue("plugState", "Plugged in on EV and locked");
|
||||
break;
|
||||
}
|
||||
|
||||
if (plugState >= 5) {
|
||||
thing->setStateValue("pluggedIn", true);
|
||||
} else {
|
||||
thing->setStateValue("pluggedIn", false);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::refresh(Thing *thing, KeContact *keba)
|
||||
{
|
||||
if (m_monitors.contains(thing) && !m_monitors.value(thing)->reachable())
|
||||
return;
|
||||
|
||||
keba->getReport2();
|
||||
// No valid information if no meter
|
||||
if (thing->thingClassId() != kebaSimpleThingClassId) {
|
||||
keba->getReport3();
|
||||
}
|
||||
|
||||
if (thing->stateValue("activity").toString() == "Charging") {
|
||||
keba->getReport1XX(100);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::onReportTwoReceived(const KeContact::ReportTwo &reportTwo)
|
||||
{
|
||||
KeContact *keba = static_cast<KeContact *>(sender());
|
||||
Thing *thing = myThings().findById(m_kebaDevices.key(keba));
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
qCDebug(dcKeba()) << "Report 2 received for" << thing->name() << "Serial number:" << thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString();
|
||||
qCDebug(dcKeba()) << " - State:" << reportTwo.state;
|
||||
qCDebug(dcKeba()) << " - Error 1:" << reportTwo.error1;
|
||||
qCDebug(dcKeba()) << " - Error 2:" << reportTwo.error2;
|
||||
qCDebug(dcKeba()) << " - Plug:" << reportTwo.plugState;
|
||||
qCDebug(dcKeba()) << " - Enable sys:" << reportTwo.enableSys;
|
||||
qCDebug(dcKeba()) << " - Enable user:" << reportTwo.enableUser;
|
||||
qCDebug(dcKeba()) << " - Max curr:" << reportTwo.maxCurrent;
|
||||
qCDebug(dcKeba()) << " - Max curr %:" << reportTwo.maxCurrentPercentage;
|
||||
qCDebug(dcKeba()) << " - Curr HW:" << reportTwo.currentHardwareLimitation;
|
||||
qCDebug(dcKeba()) << " - Curr User:" << reportTwo.currentUser;
|
||||
qCDebug(dcKeba()) << " - Curr FS:" << reportTwo.currentFailsafe;
|
||||
qCDebug(dcKeba()) << " - Tmo FS:" << reportTwo.timeoutFailsafe;
|
||||
qCDebug(dcKeba()) << " - Curr timer:" << reportTwo.currTimer;
|
||||
qCDebug(dcKeba()) << " - Timeout CT:" << reportTwo.timeoutCt;
|
||||
qCDebug(dcKeba()) << " - Output:" << reportTwo.output;
|
||||
qCDebug(dcKeba()) << " - Input:" << reportTwo.input;
|
||||
qCDebug(dcKeba()) << " - Serial number:" << reportTwo.serialNumber;
|
||||
qCDebug(dcKeba()) << " - Uptime:" << reportTwo.seconds / 60 << "[min]";
|
||||
|
||||
if (reportTwo.serialNumber == thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString()) {
|
||||
setDeviceState(thing, reportTwo.state);
|
||||
setDevicePlugState(thing, reportTwo.plugState);
|
||||
|
||||
thing->setStateValue("power", reportTwo.enableUser);
|
||||
thing->setStateValue("error1", reportTwo.error1);
|
||||
thing->setStateValue("error2", reportTwo.error2);
|
||||
thing->setStateValue("systemEnabled", reportTwo.enableSys);
|
||||
|
||||
thing->setStateValue("maxChargingCurrent", reportTwo.currentUser);
|
||||
thing->setStateValue("maxChargingCurrentPercent", reportTwo.maxCurrentPercentage);
|
||||
thing->setStateValue("maxChargingCurrentHardware", reportTwo.currentHardwareLimitation);
|
||||
|
||||
// Set the state limits according to the hardware limits
|
||||
if (reportTwo.currentHardwareLimitation > 0) {
|
||||
thing->setStateMaxValue("maxChargingCurrent", reportTwo.currentHardwareLimitation);
|
||||
} else {
|
||||
// If we have no limit given, reset to the statetype limit
|
||||
thing->setStateMaxValue("maxChargingCurrent", thing->thingClass().stateTypes().findByName("maxChargingCurrent").maxValue());
|
||||
}
|
||||
thing->setStateValue("outputX2", reportTwo.output);
|
||||
thing->setStateValue("input", reportTwo.input);
|
||||
|
||||
thing->setStateValue("uptime", reportTwo.seconds / 60);
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Received report but the serial number didn't match";
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::onReportThreeReceived(const KeContact::ReportThree &reportThree)
|
||||
{
|
||||
KeContact *keba = static_cast<KeContact *>(sender());
|
||||
Thing *thing = myThings().findById(m_kebaDevices.key(keba));
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
qCDebug(dcKeba()) << "Report 3 received for" << thing->name() << "Serial number:" << thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString();
|
||||
qCDebug(dcKeba()) << " - Current phase 1:" << reportThree.currentPhase1 << "[A]";
|
||||
qCDebug(dcKeba()) << " - Current phase 2:" << reportThree.currentPhase2 << "[A]";
|
||||
qCDebug(dcKeba()) << " - Current phase 3:" << reportThree.currentPhase3 << "[A]";
|
||||
qCDebug(dcKeba()) << " - Voltage phase 1:" << reportThree.voltagePhase1 << "[V]";
|
||||
qCDebug(dcKeba()) << " - Voltage phase 2:" << reportThree.voltagePhase2 << "[V]";
|
||||
qCDebug(dcKeba()) << " - Voltage phase 3:" << reportThree.voltagePhase3 << "[V]";
|
||||
qCDebug(dcKeba()) << " - Power consumption:" << reportThree.power << "[kW]";
|
||||
qCDebug(dcKeba()) << " - Energy session" << reportThree.energySession << "[kWh]";
|
||||
qCDebug(dcKeba()) << " - Energy total" << reportThree.energyTotal << "[kWh]";
|
||||
qCDebug(dcKeba()) << " - Serial number" << reportThree.serialNumber;
|
||||
qCDebug(dcKeba()) << " - Uptime" << reportThree.seconds / 60 << "[min]";
|
||||
|
||||
// Note: all these infos are from the meter...
|
||||
if (thing->thingClassId() == kebaSimpleThingClassId) {
|
||||
qCDebug(dcKeba()) << "Received report 3 from keba but this model has no meter. Ignoring report data...";
|
||||
return;
|
||||
}
|
||||
|
||||
if (reportThree.serialNumber == thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString()) {
|
||||
thing->setStateValue("currentPhaseA", reportThree.currentPhase1);
|
||||
thing->setStateValue("currentPhaseB", reportThree.currentPhase2);
|
||||
thing->setStateValue("currentPhaseC", reportThree.currentPhase3);
|
||||
thing->setStateValue("voltagePhaseA", reportThree.voltagePhase1);
|
||||
thing->setStateValue("voltagePhaseB", reportThree.voltagePhase2);
|
||||
thing->setStateValue("voltagePhaseC", reportThree.voltagePhase3);
|
||||
thing->setStateValue("currentPower", reportThree.power);
|
||||
thing->setStateValue("sessionEnergy", reportThree.energySession);
|
||||
thing->setStateValue("powerFactor", reportThree.powerFactor);
|
||||
thing->setStateValue("totalEnergyConsumed", reportThree.energyTotal);
|
||||
|
||||
// Check how many phases are actually charging, and update the phase count only if something happens on the phases (current or power)
|
||||
if (!(reportThree.currentPhase1 == 0 && reportThree.currentPhase2 == 0 && reportThree.currentPhase3 == 0)) {
|
||||
uint phaseCount = 0;
|
||||
if (reportThree.currentPhase1 != 0)
|
||||
phaseCount += 1;
|
||||
|
||||
if (reportThree.currentPhase2 != 0)
|
||||
phaseCount += 1;
|
||||
|
||||
if (reportThree.currentPhase3 != 0)
|
||||
phaseCount += 1;
|
||||
|
||||
thing->setStateValue("phaseCount", phaseCount);
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Received report but the serial number didn't match";
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::onReport1XXReceived(int reportNumber, const KeContact::Report1XX &report)
|
||||
{
|
||||
KeContact *keba = static_cast<KeContact *>(sender());
|
||||
Thing *thing = myThings().findById(m_kebaDevices.key(keba));
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
qCDebug(dcKeba()) << "Report" << reportNumber << "received for" << thing->name() << "Serial number:" << thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString();
|
||||
qCDebug(dcKeba()) << " - Session Id" << report.sessionId;
|
||||
qCDebug(dcKeba()) << " - Curr HW" << report.currHW;
|
||||
qCDebug(dcKeba()) << " - Energy start" << report.startEnergy;
|
||||
qCDebug(dcKeba()) << " - Energy present" << report.presentEnergy;
|
||||
qCDebug(dcKeba()) << " - Start time" << report.startTime << QDateTime::fromMSecsSinceEpoch(report.startTime * 1000).toString();
|
||||
qCDebug(dcKeba()) << " - End time" << report.endTime;
|
||||
qCDebug(dcKeba()) << " - Stop reason" << report.stopReason;
|
||||
qCDebug(dcKeba()) << " - RFID Tag" << report.rfidTag;
|
||||
qCDebug(dcKeba()) << " - RFID Class" << report.rfidClass;
|
||||
qCDebug(dcKeba()) << " - Serial number" << report.serialNumber;
|
||||
qCDebug(dcKeba()) << " - Uptime" << report.seconds;
|
||||
|
||||
// Note: all these infos are from the meter...
|
||||
if (thing->thingClassId() == kebaSimpleThingClassId) {
|
||||
qCDebug(dcKeba()) << "Received" << reportNumber << "from keba but this model has no meter. Ignoring report data...";
|
||||
return;
|
||||
}
|
||||
|
||||
if (reportNumber == 100) {
|
||||
// Report 100 is the current charging session
|
||||
if (report.endTime == 0) {
|
||||
// if the charing session is finished the end time will be set
|
||||
double duration = (report.seconds - report.startTime) / 60.00;
|
||||
thing->setStateValue("sessionTime", duration);
|
||||
} else {
|
||||
// Charging session is finished and copied to Report 101
|
||||
}
|
||||
|
||||
} else if (reportNumber == 101) {
|
||||
// Report 101 is the lastest finished session
|
||||
if (report.serialNumber == thing->paramValue(m_serialNumberParamTypeIds.value(thing->thingClassId())).toString()) {
|
||||
if (!m_lastSessionId.contains(thing->id())) {
|
||||
// This happens after reboot
|
||||
m_lastSessionId.insert(thing->id(), report.sessionId);
|
||||
} else {
|
||||
if (m_lastSessionId.value(thing->id()) != report.sessionId) {
|
||||
qCDebug(dcKeba()) << "New session id receivd";
|
||||
Event event;
|
||||
event.setEventTypeId(kebaChargingSessionFinishedEventTypeId);
|
||||
event.setThingId(thing->id());
|
||||
ParamList params;
|
||||
params << Param(kebaChargingSessionFinishedEventEnergyParamTypeId, report.presentEnergy);
|
||||
params << Param(kebaChargingSessionFinishedEventDurationParamTypeId, report.endTime);
|
||||
params << Param(kebaChargingSessionFinishedEventIdParamTypeId);
|
||||
event.setParams(params);
|
||||
emit emitEvent(event);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Received report but the serial number didn't match";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Received unhandled report" << reportNumber;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginKeba::onBroadcastReceived(KeContact::BroadcastType type, const QVariant &content)
|
||||
{
|
||||
KeContact *keba = static_cast<KeContact *>(sender());
|
||||
Thing *thing = myThings().findById(m_kebaDevices.key(keba));
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
qCDebug(dcKeba()) << "Broadcast received" << type << "value" << content;
|
||||
|
||||
switch (type) {
|
||||
case KeContact::BroadcastTypePlug:
|
||||
setDevicePlugState(thing, KeContact::PlugState(content.toInt()));
|
||||
break;
|
||||
case KeContact::BroadcastTypeInput:
|
||||
thing->setStateValue("input", (content.toInt() == 1));
|
||||
break;
|
||||
case KeContact::BroadcastTypeEPres:
|
||||
thing->setStateValue("sessionEnergy", content.toInt() / 10000.00);
|
||||
break;
|
||||
case KeContact::BroadcastTypeState:
|
||||
setDeviceState(thing, KeContact::State(content.toInt()));
|
||||
break;
|
||||
case KeContact::BroadcastTypeMaxCurr:
|
||||
//Current preset value via Control pilot in milliampere
|
||||
break;
|
||||
case KeContact::BroadcastTypeEnableSys:
|
||||
thing->setStateValue("systemEnabled", (content.toInt() != 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
94
keba/integrationpluginkeba.h
Normal file
94
keba/integrationpluginkeba.h
Normal file
@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef INTEGRATIONPLUGINKEBA_H
|
||||
#define INTEGRATIONPLUGINKEBA_H
|
||||
|
||||
#include <plugintimer.h>
|
||||
#include <integrations/integrationplugin.h>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "kecontact.h"
|
||||
#include "kebadiscovery.h"
|
||||
#include "kecontactdatalayer.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QUdpSocket>
|
||||
#include <QDateTime>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
class IntegrationPluginKeba : public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginkeba.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginKeba();
|
||||
|
||||
void init() override;
|
||||
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
|
||||
void postSetupThing(Thing* thing) override;
|
||||
void thingRemoved(Thing* thing) override;
|
||||
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_updateTimer = nullptr;
|
||||
KeContactDataLayer *m_kebaDataLayer = nullptr;
|
||||
|
||||
QHash<ThingId, KeContact *> m_kebaDevices;
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
QHash<ThingId, int> m_lastSessionId;
|
||||
QHash<QUuid, ThingActionInfo *> m_asyncActions;
|
||||
KebaDiscovery *m_runningDiscovery = nullptr;
|
||||
|
||||
QHash<ThingClassId, ParamTypeId> m_macAddressParamTypeIds;
|
||||
QHash<ThingClassId, ParamTypeId> m_hostNameParamTypeIds;
|
||||
QHash<ThingClassId, ParamTypeId> m_addressParamTypeIds;
|
||||
QHash<ThingClassId, ParamTypeId> m_modelParamTypeIds;
|
||||
QHash<ThingClassId, ParamTypeId> m_serialNumberParamTypeIds;
|
||||
|
||||
void setupKeba(ThingSetupInfo *info, const QHostAddress &address);
|
||||
|
||||
void setDeviceState(Thing *device, KeContact::State state);
|
||||
void setDevicePlugState(Thing *device, KeContact::PlugState plugState);
|
||||
|
||||
void refresh(Thing *thing, KeContact *keba);
|
||||
|
||||
private slots:
|
||||
void onCommandExecuted(QUuid requestId, bool success);
|
||||
void onReportTwoReceived(const KeContact::ReportTwo &reportTwo);
|
||||
void onReportThreeReceived(const KeContact::ReportThree &reportThree);
|
||||
void onReport1XXReceived(int reportNumber, const KeContact::Report1XX &report);
|
||||
void onBroadcastReceived(KeContact::BroadcastType type, const QVariant &content);
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINKEBA_H
|
||||
678
keba/integrationpluginkeba.json
Normal file
678
keba/integrationpluginkeba.json
Normal file
@ -0,0 +1,678 @@
|
||||
{
|
||||
"displayName": "Keba KeContact",
|
||||
"name": "Keba",
|
||||
"id": "9142b09f-30a9-43d0-9ede-2f8debe075ac",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "f7cda40b-829a-4675-abaa-485697430f5f",
|
||||
"displayName": "Keba",
|
||||
"name": "keba",
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "900dacec-cae7-4a37-95ba-501846368ea2",
|
||||
"name": "keba",
|
||||
"displayName": "Keba KeContact",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": ["evcharger", "smartmeterconsumer", "connectable", "networkdevice"],
|
||||
"paramTypes":[
|
||||
{
|
||||
"id": "c2df921d-ff8b-411c-9b1d-04a437d7dfa6",
|
||||
"name": "macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress",
|
||||
"defaultValue": "",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "0cc79bb7-3162-432c-a7bc-45f9b5542fbd",
|
||||
"name": "hostName",
|
||||
"displayName": "Host name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "22f70789-33c2-4183-8bca-0d3108b1c80b",
|
||||
"name": "address",
|
||||
"displayName": "IP address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "45255155-318b-4204-8ce6-2c106a56286d",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "a996c698-4831-4977-8979-f76f78ac7da8",
|
||||
"name": "model",
|
||||
"displayName": "Product name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "ce813458-d7d8-4f40-9648-dba4c41e92f0",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "83ed0774-2a91-434d-b03c-d920d02f2981",
|
||||
"name": "power",
|
||||
"displayName": "Charging enabled",
|
||||
"displayNameEvent": "Charging enabled changed",
|
||||
"displayNameAction": "Set charging enabled",
|
||||
"type": "bool",
|
||||
"writable": true,
|
||||
"defaultValue": false,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "e5631593-f486-47cb-9951-b7597d0b769b",
|
||||
"name": "systemEnabled",
|
||||
"displayName": "System enabled",
|
||||
"displayNameEvent": "System enabled changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "6713b2e7-41b3-4596-a304-3065726bdbe4",
|
||||
"name": "phaseCount",
|
||||
"displayName": "Number of connected phases",
|
||||
"displayNameEvent": "Number of connected phases changed",
|
||||
"type": "uint",
|
||||
"minValue": 1,
|
||||
"maxValue": 3,
|
||||
"defaultValue": 1
|
||||
},
|
||||
{
|
||||
"id": "539e5602-6dd9-465d-9705-3bb59bcf8982",
|
||||
"name": "activity",
|
||||
"displayName": "Activity",
|
||||
"displayNameEvent": "Activity changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
},
|
||||
{
|
||||
"id": "3b4d29f3-3101-47ad-90fd-269b6348783b",
|
||||
"name": "plugState",
|
||||
"displayName": "Plug state",
|
||||
"displayNameEvent": "Plug state changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
},
|
||||
{
|
||||
"id": "6c227717-f420-4dcd-bd52-49973715603b",
|
||||
"name": "pluggedIn",
|
||||
"displayName": "Car plugged in",
|
||||
"displayNameEvent": "Car plugged in changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "c9785626-2501-478d-8c18-c42ad5d9a269",
|
||||
"name": "charging",
|
||||
"displayName": "Charging",
|
||||
"displayNameEvent": "Charging changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "593656f0-babf-4308-8767-68f34e10fb15",
|
||||
"name": "maxChargingCurrent",
|
||||
"displayName": "Maximal charging current",
|
||||
"displayNameEvent": "Maximal charging current changed",
|
||||
"displayNameAction": "Set maximal charging current",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 6,
|
||||
"minValue": 6,
|
||||
"maxValue": 32,
|
||||
"stepSize": 0.001,
|
||||
"writable": true,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "3c7b83a0-0e42-47bf-9788-dde6aab5ceea",
|
||||
"name": "maxChargingCurrentPercent",
|
||||
"displayName": "Maximal charging current in percent",
|
||||
"displayNameEvent": "Maximal charging current percentage changed",
|
||||
"type": "uint",
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 100,
|
||||
"minValue": 0,
|
||||
"maxValue": 100
|
||||
},
|
||||
{
|
||||
"id": "33e2ed95-f01e-44db-8156-34d124a8ecc8",
|
||||
"name": "maxChargingCurrentHardware",
|
||||
"displayName": "Maximal hardware charging current",
|
||||
"displayNameEvent": "Maximal hardware charging current changed",
|
||||
"type": "uint",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 32,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"displayNameEvent": "Voltage phase A changed",
|
||||
"type": "int",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "c8344ca5-21ac-4cd1-8f4b-e5ed202c5862",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"displayNameEvent": "Voltage phase B changed",
|
||||
"type": "int",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "5f01e86c-0943-4849-a01a-db441916ebd5",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"displayNameEvent": "Voltage phase C changed",
|
||||
"type": "int",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "31ec17b0-11e3-4332-92b0-fea821cf024f",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"displayNameEvent": "Current phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0.00,
|
||||
"suggestLogging": true,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"displayNameEvent": "Current phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0.00,
|
||||
"suggestLogging": true,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "da838dc8-85f0-4e55-b4b5-cb93a43b373d",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"displayNameEvent": "Current phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0.00,
|
||||
"suggestLogging": true,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "7af9e93b-099d-4d9d-a480-9c0f66aecd8b",
|
||||
"name": "currentPower",
|
||||
"displayName": "Power consumption",
|
||||
"displayNameEvent": "Power consumtion changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
"suggestLogging": true,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "889c3c9a-96b4-4408-bd9a-d79e36ed9296",
|
||||
"name": "powerFactor",
|
||||
"displayName": "Power factor",
|
||||
"displayNameEvent": "Power factor changed",
|
||||
"type": "double",
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 0.00,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1d30ce60-2ea0-450f-817e-5c88f59ebfbf",
|
||||
"name": "sessionId",
|
||||
"displayName": "Session ID",
|
||||
"displayNameEvent": "Session ID changed",
|
||||
"type": "uint",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "a6f35ea0-aaea-438b-b818-6d161762611e",
|
||||
"name": "sessionTime",
|
||||
"displayName": "Session time",
|
||||
"displayNameEvent": "Session time changed",
|
||||
"type": "int",
|
||||
"unit": "Minutes",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "8e277efe-21ef-4536-bfc0-901b32d44d7c",
|
||||
"name": "sessionEnergy",
|
||||
"displayName": "Session energy",
|
||||
"displayNameEvent": "Session energy changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0,
|
||||
"suggestLogging": true,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "41e179b3-29a2-43ec-b537-023a527081e8",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Total energy consumed",
|
||||
"displayNameEvent": "Total energy consumption changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "96b2d176-6460-4109-8824-3af4679c6573",
|
||||
"name": "outputX2",
|
||||
"displayName": "Output X2",
|
||||
"displayNameEvent": "Output X2 changed",
|
||||
"displayNameAction": "Set output X2",
|
||||
"type": "bool",
|
||||
"writable": true,
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "ba600276-8b36-4404-b8ec-415245e5bc15",
|
||||
"name": "input",
|
||||
"displayName": "Input",
|
||||
"displayNameEvent": "Input changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "3421ecf9-c95f-4dc1-ad0c-144e9b6ae056",
|
||||
"name": "uptime",
|
||||
"displayName": "Uptime",
|
||||
"displayNameEvent": "Uptime changed",
|
||||
"type": "int",
|
||||
"unit": "Minutes",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "b44bc948-1234-4f87-9a22-bfb6de09df4d",
|
||||
"name": "error1",
|
||||
"displayName": "Error 1",
|
||||
"displayNameEvent": "Error 1 changed",
|
||||
"type": "int",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "afca201a-5213-43fe-bfec-cae6ce7509d2",
|
||||
"name": "error2",
|
||||
"displayName": "Error 2",
|
||||
"displayNameEvent": "Error 2 changed",
|
||||
"type": "int",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f1758c5c-2c02-41cb-93ec-b778a3c78d28",
|
||||
"name": "failsafeMode",
|
||||
"displayName": "Failsafe mode",
|
||||
"displayNameEvent": "Failsafe mode changed",
|
||||
"displayNameAction": "Set failsafe mode",
|
||||
"writable": true,
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "e941ace5-fb7f-4dc2-b3f2-188233f4e934",
|
||||
"name": "firmware",
|
||||
"displayName": "Firmware",
|
||||
"displayNameEvent": "Firmware changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "11501a4b-8b83-4b92-be3c-a714d507f158",
|
||||
"name": "hostAddress",
|
||||
"displayName": "Host address",
|
||||
"displayNameEvent": "Host address changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "http://127.0.0.1"
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "158b1a8f-fde9-4191-bf42-4ece5fe582e6",
|
||||
"name": "display",
|
||||
"displayName": "Display",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "4e69a761-f4f1-42d0-83db-380894a86ebc",
|
||||
"name": "message",
|
||||
"displayName": "Display message",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"id": "dac02c37-f051-481a-ae99-1de0885ef37a",
|
||||
"name": "chargingSessionFinished",
|
||||
"displayName": "Charging session finished",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "33446eae-f2cc-4cf2-af29-b3a45e4b91c0",
|
||||
"name": "id",
|
||||
"displayName": "ID",
|
||||
"type": "int",
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"id": "60494d6f-853b-42b8-894e-108a52ed6feb",
|
||||
"name": "duration",
|
||||
"displayName": "Duration",
|
||||
"type": "int",
|
||||
"unit": "Minutes",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "c8de58b6-b671-4fee-b552-d2c14a37a769",
|
||||
"name": "energy",
|
||||
"displayName": "Energy",
|
||||
"type": "double",
|
||||
"defaultValue": 0.00,
|
||||
"unit": "KiloWattHour"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "c5bca9d2-2a17-40c4-8bb2-ba89783a6dd1",
|
||||
"name": "kebaSimple",
|
||||
"displayName": "KeConnect German Edition",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": ["evcharger", "connectable", "networkdevice"],
|
||||
"paramTypes":[
|
||||
{
|
||||
"id": "e438179a-5202-4106-a622-d9e10a74fed9",
|
||||
"name": "macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue":"",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "96104d2b-2fd3-4f20-bc8c-7b873d0b0f9e",
|
||||
"name": "hostName",
|
||||
"displayName": "Host name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "a0c60511-4082-4109-9a9f-383076680c4f",
|
||||
"name": "address",
|
||||
"displayName": "IP address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "6f732eb9-1711-4da0-a9a4-abcfa19f5e34",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue":"",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "5e49d289-9e32-47a8-8b30-43cb949695c8",
|
||||
"name": "model",
|
||||
"displayName": "Product name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue":"",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"settingsTypes": [
|
||||
{
|
||||
"id": "42f32882-ae1c-455e-a044-3f589056a148",
|
||||
"name": "phaseCount",
|
||||
"displayName": "Phase count",
|
||||
"type": "uint",
|
||||
"minValue": 1,
|
||||
"maxValue": 3,
|
||||
"defaultValue": 3
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "995f2ccf-2082-434e-a46d-c506862e6d6a",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "63f84293-62aa-420d-bc0d-cc48618c6526",
|
||||
"name": "power",
|
||||
"displayName": "Charging enabled",
|
||||
"displayNameEvent": "Charging enabled changed",
|
||||
"displayNameAction": "Set charging enabled",
|
||||
"type": "bool",
|
||||
"writable": true,
|
||||
"defaultValue": false,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "8ade4b68-e44e-425c-87ea-a35d176f337d",
|
||||
"name": "systemEnabled",
|
||||
"displayName": "System enabled",
|
||||
"displayNameEvent": "System enabled changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "2473e39b-9641-4236-be56-e706d7797161",
|
||||
"name": "phaseCount",
|
||||
"displayName": "Number of connected phases",
|
||||
"displayNameEvent": "Number of connected phases changed",
|
||||
"type": "uint",
|
||||
"minValue": 1,
|
||||
"maxValue": 3,
|
||||
"defaultValue": 3
|
||||
},
|
||||
{
|
||||
"id": "955ffd64-42f6-4000-94c5-c7f862daa438",
|
||||
"name": "activity",
|
||||
"displayName": "Activity",
|
||||
"displayNameEvent": "Activity changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
},
|
||||
{
|
||||
"id": "82aa0d67-eea6-4a5e-b7ab-2848a4012490",
|
||||
"name": "plugState",
|
||||
"displayName": "Plug state",
|
||||
"displayNameEvent": "Plug state changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
},
|
||||
{
|
||||
"id": "faf68cc9-f014-4db5-94fa-0f10a0b85fb1",
|
||||
"name": "pluggedIn",
|
||||
"displayName": "Car plugged in",
|
||||
"displayNameEvent": "Car plugged in changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "38affdf2-f62e-458c-b738-8db81aa13790",
|
||||
"name": "charging",
|
||||
"displayName": "Charging",
|
||||
"displayNameEvent": "Charging changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "2a72ad9e-96bd-4281-afb7-ce4f5c6f5052",
|
||||
"name": "maxChargingCurrent",
|
||||
"displayName": "Maximal charging current",
|
||||
"displayNameEvent": "Maximal charging current changed",
|
||||
"displayNameAction": "Set maximal charging current",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 6,
|
||||
"minValue": 6,
|
||||
"maxValue": 32,
|
||||
"stepSize": 0.001,
|
||||
"writable": true,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "33631b7f-a675-4625-8095-31e09e03a010",
|
||||
"name": "maxChargingCurrentPercent",
|
||||
"displayName": "Maximal charging current in percent",
|
||||
"displayNameEvent": "Maximal charging current percentage changed",
|
||||
"type": "uint",
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 100,
|
||||
"minValue": 0,
|
||||
"maxValue": 100
|
||||
},
|
||||
{
|
||||
"id": "f94a2381-28a8-478e-ac44-0902a5be8885",
|
||||
"name": "maxChargingCurrentHardware",
|
||||
"displayName": "Maximal hardware charging current",
|
||||
"displayNameEvent": "Maximal hardware charging current changed",
|
||||
"type": "uint",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 32,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "043ea799-4348-44f9-985d-bee2ba280957",
|
||||
"name": "outputX2",
|
||||
"displayName": "Output X2",
|
||||
"displayNameEvent": "Output X2 changed",
|
||||
"displayNameAction": "Set output X2",
|
||||
"type": "bool",
|
||||
"writable": true,
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "0ca0921d-5516-44fb-9483-242d9bb7a2d0",
|
||||
"name": "input",
|
||||
"displayName": "Input",
|
||||
"displayNameEvent": "Input changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "2cffff03-63b2-468d-b2ef-a4741401d7c8",
|
||||
"name": "uptime",
|
||||
"displayName": "Uptime",
|
||||
"displayNameEvent": "Uptime changed",
|
||||
"type": "int",
|
||||
"unit": "Minutes",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "8380c340-84ee-4d62-84b0-7c5738ab66bc",
|
||||
"name": "error1",
|
||||
"displayName": "Error 1",
|
||||
"displayNameEvent": "Error 1 changed",
|
||||
"type": "int",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "afe287a2-35e2-4762-a6bf-79d7c31d32ab",
|
||||
"name": "error2",
|
||||
"displayName": "Error 2",
|
||||
"displayNameEvent": "Error 2 changed",
|
||||
"type": "int",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "bfad6a1a-40e0-4b32-9f42-09efd5a7e94c",
|
||||
"name": "failsafeMode",
|
||||
"displayName": "Failsafe mode",
|
||||
"displayNameEvent": "Failsafe mode changed",
|
||||
"displayNameAction": "Set failsafe mode",
|
||||
"writable": true,
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "d473770e-c5b4-4845-8215-0dea304ea202",
|
||||
"name": "firmware",
|
||||
"displayName": "Firmware",
|
||||
"displayNameEvent": "Firmware changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "eef1d569-f383-4980-8cb8-06ccc5069b0b",
|
||||
"name": "hostAddress",
|
||||
"displayName": "Host address",
|
||||
"displayNameEvent": "Host address changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "http://127.0.0.1"
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "e756c842-bec5-42ee-a28b-280d48e834b1",
|
||||
"name": "display",
|
||||
"displayName": "Display",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "ec14a880-0546-431c-ab4e-578d56ecbfb9",
|
||||
"name": "message",
|
||||
"displayName": "Display message",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventTypes": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
17
keba/keba.pro
Normal file
17
keba/keba.pro
Normal file
@ -0,0 +1,17 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
QT *= network
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginkeba.cpp \
|
||||
kebadiscovery.cpp \
|
||||
kebaproductinfo.cpp \
|
||||
kecontact.cpp \
|
||||
kecontactdatalayer.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginkeba.h \
|
||||
kebadiscovery.h \
|
||||
kebaproductinfo.h \
|
||||
kecontact.h \
|
||||
kecontactdatalayer.h
|
||||
115
keba/keba.svg
Normal file
115
keba/keba.svg
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
width="600"
|
||||
height="155"
|
||||
id="svg2"
|
||||
xml:space="preserve"><defs
|
||||
id="defs5" /><g
|
||||
transform="matrix(1.25,0,0,-1.25,-85.3175,208.42905)"
|
||||
id="g11"><path
|
||||
d="M 248.41317,92.867352 L 274.56135,92.867352 L 298.73517,164.22283 L 272.70861,164.22283 L 248.41317,92.867352"
|
||||
id="path31"
|
||||
style="fill:#50b848;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 300.82874,149.20063 L 337.95028,149.20063 L 343.06498,164.22284 L 305.95537,164.22284 L 300.82874,149.20063"
|
||||
id="path35"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 281.59793,92.867352 L 319.11052,92.867352 L 324.22523,107.88956 L 286.72932,107.88956 L 281.59793,92.867352"
|
||||
id="path39"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 291.23837,121.03518 L 326.2115,121.03518 L 331.3262,136.055 L 296.32208,136.055 L 291.23837,121.03518"
|
||||
id="path43"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 194.09478,164.22283 L 168.06821,164.22283 L 143.77278,92.867353 L 169.92095,92.867353 L 194.09478,164.22283"
|
||||
id="path47"
|
||||
style="fill:#50b848;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 201.06698,92.867352 L 224.23694,92.867352 L 210.16853,129.86728 L 245.29665,164.22283 L 223.21639,164.22283 L 188.15265,130.02943 L 201.06698,92.867352"
|
||||
id="path51"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 351.60617,92.867352 L 377.75673,92.867352 L 401.92817,164.22283 L 375.90161,164.22283 L 351.60617,92.867352"
|
||||
id="path55"
|
||||
style="fill:#50b848;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 394.85582,122.71385 L 402.56007,122.71385 C 411.58054,122.71385 417.16022,118.97261 415.13818,113.04003 C 413.12091,107.11222 405.72665,105.517 396.70379,105.517 L 388.96855,105.517 L 384.69795,92.864966 L 404.25543,92.864966 C 417.66096,92.864966 422.23916,94.448258 427.62569,98.401721 C 431.28348,101.06995 434.86735,105.65292 436.55317,111.24928 C 439.06879,119.58542 433.87302,127.53526 425.38905,128.93972 L 425.45581,129.13763 C 434.3976,131.41004 441.77041,135.55187 443.45385,145.6406 C 445.2279,156.26821 434.72189,164.22044 422.53006,164.22044 L 408.82409,164.22044 L 404.47719,150.97944 L 412.09559,150.97944 C 420.26004,150.97944 424.43049,147.13566 422.71128,142.09726 C 421.09699,137.35454 414.42761,135.3635 406.86882,135.3635 L 399.1288,135.3635 L 394.85582,122.71385"
|
||||
id="path59"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 506.66395,164.22283 L 480.63738,164.22283 L 456.34195,92.867353 L 482.49012,92.867353 L 506.66395,164.22283"
|
||||
id="path63"
|
||||
style="fill:#50b848;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 534.63625,162.57754 L 535.92148,162.57754 C 536.57483,162.57754 537.29017,162.44401 537.29017,161.60706 C 537.29017,160.60796 536.50568,160.56266 535.64965,160.56266 L 534.63625,160.56266 L 534.63625,162.57754 z M 533.66338,157.04555 L 534.63625,157.04555 L 534.63625,159.76624 L 535.66158,159.76624 L 537.36171,157.04555 L 538.40849,157.04555 L 536.62013,159.84016 C 537.55008,159.94269 538.26304,160.41959 538.26304,161.56175 C 538.26304,162.77784 537.56439,163.37157 536.07647,163.37157 L 533.66338,163.37157 L 533.66338,157.04555 z M 531.33375,160.23121 C 531.33375,162.82314 533.24848,164.81656 535.74742,164.81656 C 538.22012,164.81656 540.13485,162.82314 540.13485,160.23121 C 540.13485,157.58206 538.22012,155.58148 535.74742,155.58148 C 533.24848,155.58148 531.33375,157.58206 531.33375,160.23121 z M 530.36327,160.23121 C 530.36327,157.11709 532.81928,154.78507 535.74742,154.78507 C 538.66125,154.78507 541.10534,157.11709 541.10534,160.23121 C 541.10534,163.28573 538.66125,165.61536 535.74742,165.61536 C 532.81928,165.61536 530.36327,163.28573 530.36327,160.23121"
|
||||
id="path67"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 507.46752,145.22093 L 515.63197,120.31507 L 498.97163,120.31507 L 493.79731,104.69913 L 520.85874,104.69913 L 524.87659,92.838737 L 548.254,92.838737 L 522.10344,164.19421 L 513.90561,164.19421 L 507.46752,145.22093"
|
||||
id="path71"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 77.238709,66.671487 L 77.293552,66.671487 L 80.54121,57.58186 L 73.933824,57.58186 L 77.238709,66.671487 z M 68.253999,49.527097 L 70.929383,49.527097 L 73.115948,55.39291 L 81.36147,55.39291 L 83.490808,49.527097 L 86.354565,49.527097 L 78.738545,69.015428 L 75.872404,69.015428 L 68.253999,49.527097"
|
||||
id="path75"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 102.07066,63.638433 L 99.748179,63.638433 L 99.748179,55.664742 C 99.748179,53.125273 98.384258,51.241537 95.570575,51.241537 C 93.798909,51.241537 92.706818,52.362241 92.706818,54.081449 L 92.706818,63.638433 L 90.384338,63.638433 L 90.384338,54.353279 C 90.384338,51.322609 91.531271,49.193271 95.243902,49.193271 C 97.263554,49.193271 98.849231,50.013531 99.831636,51.763737 L 99.886479,51.763737 L 99.886479,49.524713 L 102.07066,49.524713 L 102.07066,63.638433"
|
||||
id="path79"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 111.28188,67.868494 L 108.9594,67.868494 L 108.9594,63.638432 L 106.56062,63.638432 L 106.56062,61.587781 L 108.9594,61.587781 L 108.9594,52.605456 C 108.9594,50.013529 109.91796,49.524712 112.32151,49.524712 L 114.09795,49.524712 L 114.09795,51.568209 L 113.0297,51.568209 C 111.58232,51.568209 111.28188,51.761351 111.28188,52.824828 L 111.28188,61.587781 L 114.09795,61.587781 L 114.09795,63.638432 L 111.28188,63.638432 L 111.28188,67.868494"
|
||||
id="path83"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 120.8508,56.566074 C 120.8508,60.00449 122.81561,61.919225 125.24778,61.919225 C 127.67517,61.919225 129.64237,60.00449 129.64237,56.566074 C 129.64237,53.153887 127.67517,51.241537 125.24778,51.241537 C 122.81561,51.241537 120.8508,53.153887 120.8508,56.566074 z M 118.3924,56.566074 C 118.3924,52.443313 120.76734,49.193271 125.24778,49.193271 C 129.72344,49.193271 132.09599,52.443313 132.09599,56.566074 C 132.09599,60.71268 129.72344,63.965106 125.24778,63.965106 C 120.76734,63.965106 118.3924,60.71268 118.3924,56.566074"
|
||||
id="path87"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 137.32515,49.524713 L 139.64525,49.524713 L 139.64525,58.31628 C 139.64525,59.377372 140.65388,61.919225 143.4151,61.919225 C 145.4896,61.919225 146.05949,60.605378 146.05949,58.778869 L 146.05949,49.524713 L 148.38197,49.524713 L 148.38197,58.31628 C 148.38197,60.498077 149.82935,61.919225 152.06837,61.919225 C 154.33124,61.919225 154.7986,60.524306 154.7986,58.778869 L 154.7986,49.524713 L 157.11869,49.524713 L 157.11869,59.870959 C 157.11869,62.789559 155.23496,63.965106 152.42366,63.965106 C 150.62099,63.965106 148.95424,63.063774 148.00045,61.563938 C 147.42579,63.283146 145.8425,63.965106 144.12568,63.965106 C 142.18471,63.965106 140.60142,63.144846 139.56417,61.563938 L 139.50933,61.563938 L 139.50933,63.638433 L 137.32515,63.638433 L 137.32515,49.524713"
|
||||
id="path91"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 175.61269,51.377452 C 175.31225,51.267766 175.09526,51.239152 174.9045,51.239152 C 174.16531,51.239152 174.16531,51.730355 174.16531,52.82483 L 174.16531,60.085562 C 174.16531,63.390448 171.40647,63.962722 168.86939,63.962722 C 165.72903,63.962722 163.02504,62.737101 162.88912,59.241457 L 165.2116,59.241457 C 165.32129,61.315953 166.76628,61.919225 168.73347,61.919225 C 170.2047,61.919225 171.87145,61.587783 171.87145,59.570515 C 171.87145,57.820309 169.68965,57.984838 167.12157,57.493636 C 164.7204,57.028663 162.34307,56.346702 162.34307,53.153887 C 162.34307,50.33782 164.44618,49.193271 167.01188,49.193271 C 168.97907,49.193271 170.69828,49.872847 171.98352,51.377452 C 171.98352,49.849002 172.74416,49.193271 173.94594,49.193271 C 174.68513,49.193271 175.20494,49.331571 175.61269,49.574787 L 175.61269,51.377452 z M 171.84522,54.436736 C 171.84522,53.153887 170.5886,51.239152 167.52931,51.239152 C 166.11293,51.239152 164.79909,51.787582 164.79909,53.316032 C 164.79909,55.03524 166.11293,55.58367 167.639,55.8555 C 169.19845,56.129715 170.94388,56.155944 171.84522,56.811675 L 171.84522,54.436736"
|
||||
id="path95"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 183.48385,67.868494 L 181.16137,67.868494 L 181.16137,63.638432 L 178.7602,63.638432 L 178.7602,61.587781 L 181.16137,61.587781 L 181.16137,52.605456 C 181.16137,50.013529 182.11993,49.524712 184.5211,49.524712 L 186.29276,49.524712 L 186.29276,51.568209 L 185.22929,51.568209 C 183.78429,51.568209 183.48385,51.761351 183.48385,52.824828 L 183.48385,61.587781 L 186.29276,61.587781 L 186.29276,63.638432 L 183.48385,63.638432 L 183.48385,67.868494"
|
||||
id="path99"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 193.81579,69.015428 L 191.49536,69.015428 L 191.49536,66.176946 L 193.81579,66.176946 L 193.81579,69.015428 z M 191.49569,49.524712 L 193.81612,49.524712 L 193.81612,63.638194 L 191.49569,63.638194 L 191.49569,49.524712 z"
|
||||
id="path103"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 201.60588,56.566074 C 201.60588,60.00449 203.57069,61.919225 206.00047,61.919225 C 208.43264,61.919225 210.39983,60.00449 210.39983,56.566074 C 210.39983,53.153887 208.43264,51.241537 206.00047,51.241537 C 203.57069,51.241537 201.60588,53.153887 201.60588,56.566074 z M 199.14987,56.566074 C 199.14987,52.443313 201.52481,49.193271 206.00047,49.193271 C 210.47852,49.193271 212.85346,52.443313 212.85346,56.566074 C 212.85346,60.71268 210.47852,63.965106 206.00047,63.965106 C 201.52481,63.965106 199.14987,60.71268 199.14987,56.566074"
|
||||
id="path107"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 218.08261,49.524713 L 220.40509,49.524713 L 220.40509,57.49602 C 220.40509,60.030719 221.7714,61.919225 224.58031,61.919225 C 226.35675,61.919225 227.44884,60.798521 227.44884,59.079313 L 227.44884,49.524713 L 229.77132,49.524713 L 229.77132,58.805098 C 229.77132,61.835768 228.622,63.965106 224.90937,63.965106 C 222.88733,63.965106 221.30642,63.144846 220.32402,61.397024 L 220.26918,61.397024 L 220.26918,63.638433 L 218.08261,63.638433 L 218.08261,49.524713"
|
||||
id="path111"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 245.92615,49.524713 L 248.24625,49.524713 L 248.24625,51.406065 L 248.30109,51.406065 C 249.33834,49.7417 251.41283,49.193271 252.86021,49.193271 C 257.17611,49.193271 259.30307,52.555383 259.30307,56.539845 C 259.30307,60.524306 257.14511,63.965106 252.80298,63.965106 C 250.86917,63.965106 249.06412,63.283146 248.30109,61.726082 L 248.24625,61.726082 L 248.24625,69.015429 L 245.92615,69.015429 L 245.92615,49.524713 z M 256.84467,56.704374 C 256.84467,53.945534 255.78119,51.241537 252.61222,51.241537 C 249.41941,51.241537 248.16279,53.809618 248.16279,56.597072 C 248.16279,59.241457 249.36218,61.919225 252.47869,61.919225 C 255.47837,61.919225 256.84467,59.351143 256.84467,56.704374"
|
||||
id="path115"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 276.21139,63.638433 L 273.75776,63.638433 L 269.79715,52.088026 L 269.7423,52.088026 L 265.61954,63.638433 L 262.999,63.638433 L 268.59537,49.603401 L 267.63919,47.199848 C 267.20284,46.384357 266.6854,45.916999 265.72923,45.916999 C 265.26664,45.916999 264.79928,46.107757 264.36292,46.246057 L 264.36292,44.116719 C 264.88512,43.923576 265.42878,43.871118 265.97721,43.871118 C 268.04932,43.871118 269.0055,44.882136 270.06898,47.638592 L 276.21139,63.638433"
|
||||
id="path119"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 293.21987,69.015428 L 290.89827,69.015428 L 290.89827,66.176946 L 293.21987,66.176946 L 293.21987,69.015428 z M 290.89739,49.524712 L 293.21899,49.524712 L 293.21899,63.638194 L 290.89739,63.638194 L 290.89739,49.524712 z"
|
||||
id="path123"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 299.31698,49.524713 L 301.63708,49.524713 L 301.63708,57.49602 C 301.63708,60.030719 303.001,61.919225 305.81468,61.919225 C 307.58635,61.919225 308.68082,60.798521 308.68082,59.079313 L 308.68082,49.524713 L 311.00092,49.524713 L 311.00092,58.805098 C 311.00092,61.835768 309.85398,63.965106 306.14135,63.965106 C 304.1217,63.965106 302.53602,63.144846 301.55362,61.397024 L 301.50116,61.397024 L 301.50116,63.638433 L 299.31698,63.638433 L 299.31698,49.524713"
|
||||
id="path127"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 316.99072,49.524713 L 319.31082,49.524713 L 319.31082,57.49602 C 319.31082,60.030719 320.67712,61.919225 323.4908,61.919225 C 325.26486,61.919225 326.35695,60.798521 326.35695,59.079313 L 326.35695,49.524713 L 328.67704,49.524713 L 328.67704,58.805098 C 328.67704,61.835768 327.53249,63.965106 323.81748,63.965106 C 321.79783,63.965106 320.21215,63.144846 319.22974,61.397024 L 319.17729,61.397024 L 319.17729,63.638433 L 316.99072,63.638433 L 316.99072,49.524713"
|
||||
id="path131"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 336.35983,56.566074 C 336.35983,60.00449 338.32464,61.919225 340.75681,61.919225 C 343.1842,61.919225 345.1514,60.00449 345.1514,56.566074 C 345.1514,53.153887 343.1842,51.241537 340.75681,51.241537 C 338.32464,51.241537 336.35983,53.153887 336.35983,56.566074 z M 333.90143,56.566074 C 333.90143,52.443313 336.27637,49.193271 340.75681,49.193271 C 345.23247,49.193271 347.60741,52.443313 347.60741,56.566074 C 347.60741,60.71268 345.23247,63.965106 340.75681,63.965106 C 336.27637,63.965106 333.90143,60.71268 333.90143,56.566074"
|
||||
id="path135"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 364.35598,63.638433 L 361.92381,63.638433 L 358.07765,51.871039 L 358.02281,51.871039 L 354.06219,63.638433 L 351.47027,63.638433 L 356.70896,49.524713 L 359.19359,49.524713 L 364.35598,63.638433"
|
||||
id="path139"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 381.48844,51.377452 C 381.19038,51.267766 380.97101,51.239152 380.78263,51.239152 C 380.04345,51.239152 380.04345,51.730355 380.04345,52.82483 L 380.04345,60.085562 C 380.04345,63.390448 377.28699,63.962722 374.74752,63.962722 C 371.60717,63.962722 368.90555,62.737101 368.76964,59.241457 L 371.08974,59.241457 C 371.19704,61.315953 372.6468,61.919225 374.61161,61.919225 C 376.08521,61.919225 377.75196,61.587783 377.75196,59.570515 C 377.75196,57.820309 375.5654,57.984838 372.9997,57.493636 C 370.59615,57.028663 368.22121,56.346702 368.22121,53.153887 C 368.22121,50.33782 370.32432,49.193271 372.89002,49.193271 C 374.85721,49.193271 376.57642,49.872847 377.86165,51.377452 C 377.86165,49.849002 378.6223,49.193271 379.82646,49.193271 C 380.56326,49.193271 381.08308,49.331571 381.48844,49.574787 L 381.48844,51.377452 z M 377.72335,54.436736 C 377.72335,53.153887 376.46673,51.239152 373.40983,51.239152 C 371.98868,51.239152 370.67961,51.787582 370.67961,53.316032 C 370.67961,55.03524 371.98868,55.58367 373.51952,55.8555 C 375.0742,56.129715 376.82202,56.155944 377.72335,56.811675 L 377.72335,54.436736"
|
||||
id="path143"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 389.36437,67.868494 L 387.04427,67.868494 L 387.04427,63.638432 L 384.64072,63.638432 L 384.64072,61.587781 L 387.04427,61.587781 L 387.04427,52.605456 C 387.04427,50.013529 387.99806,49.524712 390.404,49.524712 L 392.17567,49.524712 L 392.17567,51.568209 L 391.10981,51.568209 C 389.66243,51.568209 389.36437,51.761351 389.36437,52.824828 L 389.36437,61.587781 L 392.17567,61.587781 L 392.17567,63.638432 L 389.36437,63.638432 L 389.36437,67.868494"
|
||||
id="path147"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 399.6963,69.015428 L 397.37353,69.015428 L 397.37353,66.176946 L 399.6963,66.176946 L 399.6963,69.015428 z M 397.37382,49.524712 L 399.69659,49.524712 L 399.69659,63.638194 L 397.37382,63.638194 L 397.37382,49.524712 z"
|
||||
id="path151"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 407.48639,56.566074 C 407.48639,60.00449 409.45358,61.919225 411.88098,61.919225 C 414.31076,61.919225 416.27796,60.00449 416.27796,56.566074 C 416.27796,53.153887 414.31076,51.241537 411.88098,51.241537 C 409.45358,51.241537 407.48639,53.153887 407.48639,56.566074 z M 405.02799,56.566074 C 405.02799,52.443313 407.40293,49.193271 411.88098,49.193271 C 416.36141,49.193271 418.73635,52.443313 418.73635,56.566074 C 418.73635,60.71268 416.36141,63.965106 411.88098,63.965106 C 407.40293,63.965106 405.02799,60.71268 405.02799,56.566074"
|
||||
id="path155"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:evenodd;stroke:none" /><path
|
||||
d="M 423.96313,49.524713 L 426.28323,49.524713 L 426.28323,57.49602 C 426.28323,60.030719 427.64715,61.919225 430.46083,61.919225 C 432.23488,61.919225 433.32697,60.798521 433.32697,59.079313 L 433.32697,49.524713 L 435.64707,49.524713 L 435.64707,58.805098 C 435.64707,61.835768 434.50013,63.965106 430.7875,63.965106 C 428.76785,63.965106 427.18217,63.144846 426.20215,61.397024 L 426.14731,61.397024 L 426.14731,63.638433 L 423.96313,63.638433 L 423.96313,49.524713"
|
||||
id="path159"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="M 442.15669,49.524713 L 445.18856,49.524713 L 445.18856,52.554191 L 442.15669,52.554191 L 442.15669,49.524713 z"
|
||||
id="path161"
|
||||
style="fill:#4a6571;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g></svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
141
keba/kebadiscovery.cpp
Normal file
141
keba/kebadiscovery.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "kebadiscovery.h"
|
||||
#include "kecontactdatalayer.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
KebaDiscovery::KebaDiscovery(KeContactDataLayer *kebaDataLayer, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_kebaDataLayer(kebaDataLayer),
|
||||
m_networkDeviceDiscovery(networkDeviceDiscovery)
|
||||
{
|
||||
// Timer for waiting if network devices responded to the "report 1 request"
|
||||
m_responseTimer.setInterval(2000);
|
||||
m_responseTimer.setSingleShot(true);
|
||||
connect(&m_responseTimer, &QTimer::timeout, this, [=](){
|
||||
|
||||
// Fill in all network device infos we have
|
||||
for (int i = 0; i < m_results.count(); i++) {
|
||||
m_results[i].networkDeviceInfo = m_networkDeviceInfos.get(m_results.at(i).address);
|
||||
}
|
||||
|
||||
qCInfo(dcKeba()) << "Discovery: Finished successfully. Found" << m_results.count() << "Keba Wallbox";
|
||||
emit discoveryFinished();
|
||||
});
|
||||
|
||||
// Read data from the keba data layer and verify if it is a keba report
|
||||
connect (m_kebaDataLayer, &KeContactDataLayer::datagramReceived, this, [=](const QHostAddress &address, const QByteArray &datagram){
|
||||
|
||||
// Try to convert the received data to a json document
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(datagram, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcKeba()) << "Discovery: Received data from the keba data link but failed to parse the data as JSON:" << datagram << ":" << error.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify JSON data
|
||||
QVariantMap dataMap = jsonDoc.toVariant().toMap();
|
||||
if (!dataMap.contains("ID") || !dataMap.contains("Serial") || !dataMap.contains("Product") || !dataMap.contains("Firmware")) {
|
||||
qCDebug(dcKeba()) << "Discovery: Received valid JSON data on data layer but they don't seem to be what we are listening for:" << qUtf8Printable(jsonDoc.toJson());
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataMap.value("ID").toInt() != 1) {
|
||||
qCDebug(dcKeba()) << "Discovery: Received valid Keba JSON data on data layer but this is not a report 1 we requested for:" << qUtf8Printable(jsonDoc.toJson());
|
||||
return;
|
||||
}
|
||||
|
||||
// We have received a report 1 datagram, let's add it to the result
|
||||
KebaDiscoveryResult result;
|
||||
result.address = address;
|
||||
result.product = dataMap.value("Product").toString();
|
||||
result.serialNumber = dataMap.value("Serial").toString();
|
||||
result.firmwareVersion = dataMap.value("Firmware").toString();
|
||||
|
||||
bool alreadyDiscovered = false;
|
||||
foreach (const KebaDiscoveryResult &r, m_results) {
|
||||
if (r.serialNumber == result.serialNumber) {
|
||||
alreadyDiscovered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyDiscovered) {
|
||||
m_results.append(result);
|
||||
qCDebug(dcKeba()) << "Discovery: -->" << address.toString() << result.product << result.serialNumber << result.firmwareVersion;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KebaDiscovery::~KebaDiscovery()
|
||||
{
|
||||
qCDebug(dcKeba()) << "Discovery: Destructing";
|
||||
}
|
||||
|
||||
void KebaDiscovery::startDiscovery()
|
||||
{
|
||||
// Clean up
|
||||
cleanup();
|
||||
|
||||
qCInfo(dcKeba()) << "Discovery: Start searching for Keba wallboxes in the network...";
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
|
||||
// Imedialty check any new device gets discovered
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &KebaDiscovery::sendReportRequest);
|
||||
|
||||
// Check what might be left on finished
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||
qCDebug(dcKeba()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
|
||||
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
|
||||
|
||||
qCDebug(dcKeba()) << "Discovery: Network discovery finished. Start finishing discovery...";
|
||||
m_responseTimer.start();
|
||||
});
|
||||
}
|
||||
|
||||
QList<KebaDiscovery::KebaDiscoveryResult> KebaDiscovery::discoveryResults() const
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
|
||||
void KebaDiscovery::sendReportRequest(const QHostAddress &address)
|
||||
{
|
||||
m_verifiedAddresses.append(address);
|
||||
m_kebaDataLayer->write(address, QByteArray("report 1\n"));
|
||||
}
|
||||
|
||||
void KebaDiscovery::cleanup()
|
||||
{
|
||||
m_networkDeviceInfos.clear();
|
||||
m_verifiedAddresses.clear();
|
||||
m_results.clear();
|
||||
}
|
||||
|
||||
|
||||
73
keba/kebadiscovery.h
Normal file
73
keba/kebadiscovery.h
Normal file
@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef KEBADISCOVERY_H
|
||||
#define KEBADISCOVERY_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
|
||||
#include <network/networkdeviceinfos.h>
|
||||
|
||||
class KeContactDataLayer;
|
||||
class NetworkDeviceDiscovery;
|
||||
|
||||
class KebaDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef struct KebaDiscoveryResult {
|
||||
QString product;
|
||||
QString serialNumber;
|
||||
QString firmwareVersion;
|
||||
QHostAddress address;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
} KebaDiscoveryResult;
|
||||
|
||||
explicit KebaDiscovery(KeContactDataLayer *kebaDataLayer, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
|
||||
~KebaDiscovery();
|
||||
|
||||
void startDiscovery();
|
||||
|
||||
QList<KebaDiscoveryResult> discoveryResults() const;
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private slots:
|
||||
void sendReportRequest(const QHostAddress &address);
|
||||
|
||||
private:
|
||||
KeContactDataLayer *m_kebaDataLayer = nullptr;
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
QTimer m_responseTimer;
|
||||
|
||||
NetworkDeviceInfos m_networkDeviceInfos;
|
||||
QList<QHostAddress> m_verifiedAddresses;
|
||||
QList<KebaDiscoveryResult> m_results;
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
#endif // KEBADISCOVERY_H
|
||||
294
keba/kebaproductinfo.cpp
Normal file
294
keba/kebaproductinfo.cpp
Normal file
@ -0,0 +1,294 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "kebaproductinfo.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
KebaProductInfo::KebaProductInfo(const QString &productString) :
|
||||
m_productString(productString)
|
||||
{
|
||||
// Examples
|
||||
|
||||
// BMW-10-EC240522-E1R
|
||||
// KC-P30-EC240122-E0R
|
||||
// KC-P30-EC220112-000-DE
|
||||
// KC-P30-EC2404B2-M0A-GE
|
||||
// KC-P30-EC2204U2-E00-PV
|
||||
|
||||
qCDebug(dcKeba()) << "Parsing product information from" << productString.count() << productString;
|
||||
if (m_productString.count() < 19) {
|
||||
qCWarning(dcKeba()) << "Invalid product information string size for" << productString << ". Cannot parse.";
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList subStrings = productString.split('-');
|
||||
if (subStrings.count() < 4) {
|
||||
qCWarning(dcKeba()) << "Invalid product information format" << subStrings << ". Cannot parse" << productString;
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Manufacturer
|
||||
// 2. Model
|
||||
// 3. Desciptor
|
||||
// 4. Meter infos
|
||||
|
||||
// Parse the product string according to Keba Product code definitions
|
||||
m_manufacturer = subStrings.at(0);
|
||||
if (m_manufacturer == "KC") {
|
||||
m_manufacturer = "KeConnect";
|
||||
}
|
||||
qCDebug(dcKeba()) << "Manufacturer:" << m_manufacturer;
|
||||
m_model = subStrings.at(1);
|
||||
qCDebug(dcKeba()) << "Model:" << m_model;
|
||||
|
||||
QString descriptor = subStrings.at(2); // EC240522
|
||||
m_countryCode = descriptor.at(0); // E
|
||||
qCDebug(dcKeba()) << "Country:" << m_countryCode;
|
||||
|
||||
QChar connectorValue = descriptor.at(1);
|
||||
if (connectorValue.toLower() == QChar('s')) {
|
||||
m_connector = ConnectorSocket;
|
||||
qCDebug(dcKeba()) << "Connector: Socket";
|
||||
} else if (connectorValue.toLower() == QChar('c')) {
|
||||
m_connector = ConnectorCable;
|
||||
qCDebug(dcKeba()) << "Connector: Cable";
|
||||
} else {
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QChar connectorTypeValue = descriptor.at(2);
|
||||
if (connectorTypeValue.isDigit() && connectorTypeValue == QChar('1')) {
|
||||
m_connectorType = Type1;
|
||||
} else if (connectorTypeValue.isDigit() && connectorTypeValue == QChar('2')) {
|
||||
m_connectorType = Type2;
|
||||
} else if (connectorTypeValue.toLower() == QChar('s')) {
|
||||
m_connectorType = Shutter;
|
||||
} else {
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcKeba()) << "Connector type:" << m_connectorType;
|
||||
|
||||
QChar connectorCurrentValue = descriptor.at(3);
|
||||
if (connectorCurrentValue == QChar('1')) {
|
||||
m_current = Current13A;
|
||||
} else if (connectorCurrentValue == QChar('2')) {
|
||||
m_current = Current16A;
|
||||
} else if (connectorCurrentValue == QChar('3')) {
|
||||
m_current = Current20A;
|
||||
} else if (connectorCurrentValue == QChar('4')) {
|
||||
m_current = Current32A;
|
||||
} else {
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcKeba()) << "Current:" << m_current;
|
||||
|
||||
// KC-P30-EC24 01 22-E0R
|
||||
QString cableValue = descriptor.mid(4, 2);
|
||||
if (cableValue == "00") {
|
||||
m_cable = NoCable;
|
||||
qCDebug(dcKeba()) << "Cable: No cable";
|
||||
} else if (cableValue == "01") {
|
||||
m_cable = Cable4m;
|
||||
qCDebug(dcKeba()) << "Cable: 4 meter";
|
||||
} else if (cableValue == "05") {
|
||||
m_cable = Cable5m;
|
||||
qCDebug(dcKeba()) << "Cable: 5 meter";
|
||||
} else if (cableValue == "04") {
|
||||
m_cable = Cable6m;
|
||||
qCDebug(dcKeba()) << "Cable: 6 meter";
|
||||
} else if (cableValue == "07") {
|
||||
m_cable = Cable5p5m;
|
||||
qCDebug(dcKeba()) << "Cable: 5.5 meter";
|
||||
} else {
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// KC-P30-EC2401 2 2-E0R
|
||||
QChar seriesValue = descriptor.at(6);
|
||||
if (seriesValue == QChar('0')) {
|
||||
m_series = SeriesE;
|
||||
qCDebug(dcKeba()) << "Series: E";
|
||||
} else if (seriesValue == QChar('1')) {
|
||||
m_series = SeriesB;
|
||||
qCDebug(dcKeba()) << "Series: B";
|
||||
} else if (seriesValue == QChar('2')) {
|
||||
m_series = SeriesC;
|
||||
qCDebug(dcKeba()) << "Series: C";
|
||||
} else if (seriesValue == QChar('3')) {
|
||||
m_series = SeriesA;
|
||||
qCDebug(dcKeba()) << "Series: A";
|
||||
} else if (seriesValue.toLower() == QChar('b')) {
|
||||
m_series = SeriesXWlan;
|
||||
qCDebug(dcKeba()) << "Series: X (Wlan)";
|
||||
} else if (seriesValue.toLower() == QChar('c')) {
|
||||
m_series = SeriesXWlan3G;
|
||||
qCDebug(dcKeba()) << "Series: X (Wlan + 3G)";
|
||||
} else if (seriesValue.toLower() == QChar('e')) {
|
||||
m_series = SeriesXWlan4G;
|
||||
qCDebug(dcKeba()) << "Series: X (Wlan + 4G)";
|
||||
} else if (seriesValue.toLower() == QChar('g')) {
|
||||
m_series = SeriesX3G;
|
||||
qCDebug(dcKeba()) << "Series: X (3G)";
|
||||
} else if (seriesValue.toLower() == QChar('h')) {
|
||||
m_series = SeriesX4G;
|
||||
qCDebug(dcKeba()) << "Series: X (4G)";
|
||||
} else if (seriesValue.toLower() == QChar('u')) {
|
||||
m_series = SeriesSpecial;
|
||||
qCDebug(dcKeba()) << "Series: Special" + m_productString.right(2);
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Series: Unknown" << productString << "value:" << seriesValue;
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// KC-P30-EC24012 2 -E0R
|
||||
QChar phaseCountValue = descriptor.at(7);
|
||||
if (phaseCountValue == QChar('1')) {
|
||||
m_phaseCount = 1;
|
||||
} else if (phaseCountValue == QChar('2')) {
|
||||
m_phaseCount = 3;
|
||||
} else {
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcKeba()) << "Phases:" << m_phaseCount;
|
||||
|
||||
// Meter infos
|
||||
QString meterInfos = subStrings.at(3);
|
||||
|
||||
QChar meterValue = meterInfos.at(0);
|
||||
if (meterValue == QChar('0')) {
|
||||
m_meter = NoMeter;
|
||||
qCDebug(dcKeba()) << "Meter: No meter";
|
||||
} else if (meterValue.toLower() == QChar('e')) {
|
||||
m_meter = MeterNotCalibrated;
|
||||
qCDebug(dcKeba()) << "Meter: Not calibrated meter";
|
||||
} else if (meterValue.toLower() == QChar('m')) {
|
||||
m_meter = MeterCalibrated;
|
||||
qCDebug(dcKeba()) << "Meter: Calibrated meter";
|
||||
} else if (meterValue.toLower() == QChar('l')) {
|
||||
m_meter = MeterCalibratedNationalCertified;
|
||||
qCDebug(dcKeba()) << "Meter: Calibrated meter (national certified)";
|
||||
} else {
|
||||
m_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QChar authValue = meterInfos.at(2);
|
||||
if (authValue == QChar('0')) {
|
||||
m_authorization = NoAuthorization;
|
||||
qCDebug(dcKeba()) << "Authorization: No authorization";
|
||||
} else if (authValue.toLower() == QChar('r')) {
|
||||
m_authorization = Rfid;
|
||||
qCDebug(dcKeba()) << "Authorization: RFID";
|
||||
} else if (authValue.toLower() == QChar('k')) {
|
||||
m_authorization = Key;
|
||||
qCDebug(dcKeba()) << "Authorization: Key";
|
||||
} else {
|
||||
// Note: we don't require this info, so we don't mark it as invalid.
|
||||
qCDebug(dcKeba()) << "Authorization: Unknown" << authValue;
|
||||
}
|
||||
|
||||
m_germanEdition = m_productString.toUpper().endsWith("DE");
|
||||
qCDebug(dcKeba()) << "German Edition:" << m_germanEdition;
|
||||
}
|
||||
|
||||
bool KebaProductInfo::isValid() const
|
||||
{
|
||||
return m_isValid;
|
||||
}
|
||||
|
||||
QString KebaProductInfo::productString() const
|
||||
{
|
||||
return m_productString;
|
||||
}
|
||||
|
||||
QString KebaProductInfo::manufacturer() const
|
||||
{
|
||||
return m_manufacturer;
|
||||
}
|
||||
|
||||
QString KebaProductInfo::model() const
|
||||
{
|
||||
return m_model;
|
||||
}
|
||||
|
||||
QString KebaProductInfo::countryCode() const
|
||||
{
|
||||
return m_countryCode;
|
||||
}
|
||||
|
||||
KebaProductInfo::Connector KebaProductInfo::connector() const
|
||||
{
|
||||
return m_connector;
|
||||
}
|
||||
|
||||
KebaProductInfo::ConnectorType KebaProductInfo::connectorType() const
|
||||
{
|
||||
return m_connectorType;
|
||||
}
|
||||
|
||||
KebaProductInfo::ConnectorCurrent KebaProductInfo::current() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
KebaProductInfo::Cable KebaProductInfo::cable() const
|
||||
{
|
||||
return m_cable;
|
||||
}
|
||||
|
||||
KebaProductInfo::Series KebaProductInfo::series() const
|
||||
{
|
||||
return m_series;
|
||||
}
|
||||
|
||||
int KebaProductInfo::phaseCount() const
|
||||
{
|
||||
return m_phaseCount;
|
||||
}
|
||||
|
||||
KebaProductInfo::Meter KebaProductInfo::meter() const
|
||||
{
|
||||
return m_meter;
|
||||
}
|
||||
|
||||
KebaProductInfo::Authorization KebaProductInfo::authorization() const
|
||||
{
|
||||
return m_authorization;
|
||||
}
|
||||
|
||||
bool KebaProductInfo::germanEdition() const
|
||||
{
|
||||
return m_germanEdition;
|
||||
}
|
||||
132
keba/kebaproductinfo.h
Normal file
132
keba/kebaproductinfo.h
Normal file
@ -0,0 +1,132 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef KEBAPRODUCTINFO_H
|
||||
#define KEBAPRODUCTINFO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class KebaProductInfo
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
enum Connector {
|
||||
ConnectorSocket,
|
||||
ConnectorCable
|
||||
};
|
||||
Q_ENUM(Connector)
|
||||
|
||||
enum ConnectorType {
|
||||
Type1,
|
||||
Type2,
|
||||
Shutter
|
||||
};
|
||||
Q_ENUM(ConnectorType)
|
||||
|
||||
enum ConnectorCurrent {
|
||||
Current13A = 1,
|
||||
Current16A = 2,
|
||||
Current20A = 3,
|
||||
Current32A = 4
|
||||
};
|
||||
Q_ENUM(ConnectorCurrent)
|
||||
|
||||
enum Cable {
|
||||
NoCable = 0,
|
||||
Cable4m = 1,
|
||||
Cable6m = 4,
|
||||
Cable5m = 5,
|
||||
Cable5p5m = 7
|
||||
};
|
||||
Q_ENUM(Cable)
|
||||
|
||||
enum Series {
|
||||
SeriesE,
|
||||
SeriesB,
|
||||
SeriesC,
|
||||
SeriesA,
|
||||
SeriesXWlan,
|
||||
SeriesXWlan3G,
|
||||
SeriesXWlan4G,
|
||||
SeriesX3G,
|
||||
SeriesX4G,
|
||||
SeriesSpecial
|
||||
};
|
||||
Q_ENUM(Series)
|
||||
|
||||
enum Meter {
|
||||
NoMeter,
|
||||
MeterNotCalibrated,
|
||||
MeterCalibrated,
|
||||
MeterCalibratedNationalCertified
|
||||
};
|
||||
Q_ENUM(Meter)
|
||||
|
||||
enum Authorization {
|
||||
NoAuthorization,
|
||||
Rfid,
|
||||
Key
|
||||
};
|
||||
Q_ENUM(Authorization)
|
||||
|
||||
KebaProductInfo(const QString &productString);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
QString productString() const;
|
||||
|
||||
// Porperties in the string
|
||||
QString manufacturer() const; // KC (KeConnect), BMW...
|
||||
QString model() const; // P30
|
||||
QString countryCode() const; // E
|
||||
Connector connector() const; // Socket / Cable
|
||||
ConnectorType connectorType() const; // Type 1 / Type 2
|
||||
ConnectorCurrent current() const; // 13A, 16A ...
|
||||
Cable cable() const; // 4m, 6m...
|
||||
Series series() const; // x, c, a...
|
||||
int phaseCount() const; // 1 or 3
|
||||
Meter meter() const; // No meter, Calibrated, ...
|
||||
Authorization authorization() const;
|
||||
bool germanEdition() const;
|
||||
|
||||
private:
|
||||
bool m_isValid = true;
|
||||
|
||||
QString m_productString;
|
||||
QString m_manufacturer;
|
||||
QString m_model;
|
||||
QString m_countryCode;
|
||||
Connector m_connector;
|
||||
ConnectorType m_connectorType;
|
||||
ConnectorCurrent m_current;
|
||||
Cable m_cable;
|
||||
Series m_series;
|
||||
int m_phaseCount;
|
||||
Meter m_meter;
|
||||
Authorization m_authorization;
|
||||
bool m_germanEdition = false;
|
||||
};
|
||||
|
||||
#endif // KEBAPRODUCTINFO_H
|
||||
584
keba/kecontact.cpp
Normal file
584
keba/kecontact.cpp
Normal file
@ -0,0 +1,584 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "kecontact.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QJsonDocument>
|
||||
|
||||
KeContact::KeContact(const QHostAddress &address, KeContactDataLayer *dataLayer, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_dataLayer(dataLayer),
|
||||
m_address(address)
|
||||
{
|
||||
qCDebug(dcKeba()) << "Creating KeContact connection for address" << m_address;
|
||||
m_requestTimeoutTimer = new QTimer(this);
|
||||
m_requestTimeoutTimer->setSingleShot(true);
|
||||
connect(m_requestTimeoutTimer, &QTimer::timeout, this, [this]() {
|
||||
// This timer will be started when a request is sent and stopped or resetted when a response has been received
|
||||
setReachable(false);
|
||||
|
||||
if (m_currentRequest.isValid()) {
|
||||
// Schedule pause timer to send next request
|
||||
qCWarning(dcKeba()) << "Command timeouted" << m_currentRequest.command();
|
||||
emit commandExecuted(m_currentRequest.requestId(), false);
|
||||
}
|
||||
|
||||
// Timeout...send the next request right the way since at least 5 seconds passed since tha last command
|
||||
m_currentRequest = KeContactRequest();
|
||||
sendNextCommand();
|
||||
});
|
||||
|
||||
m_pauseTimer = new QTimer(this);
|
||||
m_pauseTimer->setSingleShot(true);
|
||||
connect(m_pauseTimer, &QTimer::timeout, this, [this](){
|
||||
sendNextCommand();
|
||||
});
|
||||
|
||||
connect(m_dataLayer, &KeContactDataLayer::datagramReceived, this, &KeContact::onReceivedDatagram);
|
||||
}
|
||||
|
||||
KeContact::~KeContact()
|
||||
{
|
||||
qCDebug(dcKeba()) << "Deleting KeContact connection for address" << m_address.toString();
|
||||
}
|
||||
|
||||
QHostAddress KeContact::address() const
|
||||
{
|
||||
return m_address;
|
||||
}
|
||||
|
||||
QUuid KeContact::start(const QByteArray &rfidToken, const QByteArray &rfidClassifier)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
QByteArray datagram = "start " + rfidToken + " " + rfidClassifier;
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Start: Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
QUuid KeContact::stop(const QByteArray &rfidToken)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
QByteArray datagram = "stop " + rfidToken;
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Stop: Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
void KeContact::setAddress(const QHostAddress &address)
|
||||
{
|
||||
if (m_address == address)
|
||||
return;
|
||||
|
||||
qCDebug(dcKeba()) << "Updating Keba connection address from" << m_address.toString() << "to" << address.toString();
|
||||
m_address = address;
|
||||
}
|
||||
|
||||
bool KeContact::reachable() const
|
||||
{
|
||||
return m_reachable;
|
||||
}
|
||||
|
||||
void KeContact::sendCommand(const QByteArray &command)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_dataLayer->write(m_address, command);
|
||||
m_requestTimeoutTimer->start(5000);
|
||||
}
|
||||
|
||||
void KeContact::sendNextCommand()
|
||||
{
|
||||
// No message left, we are done
|
||||
if (m_requestQueue.isEmpty())
|
||||
return;
|
||||
|
||||
// Still a request pending
|
||||
if (m_currentRequest.isValid())
|
||||
return;
|
||||
|
||||
m_currentRequest = m_requestQueue.dequeue();
|
||||
sendCommand(m_currentRequest.command());
|
||||
}
|
||||
|
||||
void KeContact::setReachable(bool reachable)
|
||||
{
|
||||
if (m_reachable == reachable)
|
||||
return;
|
||||
|
||||
if (reachable) {
|
||||
qCDebug(dcKeba()) << "The keba wallbox on" << m_address.toString() << "is now reachable again.";
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "The keba wallbox on" << m_address.toString() << "is not reachable any more.";
|
||||
m_requestQueue.clear();
|
||||
m_currentRequest = KeContactRequest();
|
||||
}
|
||||
|
||||
m_reachable = reachable;
|
||||
emit reachableChanged(m_reachable);
|
||||
}
|
||||
|
||||
QUuid KeContact::enableOutput(bool state)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
// Print information that we are executing now the update action;
|
||||
QByteArray datagram;
|
||||
if (state){
|
||||
datagram.append("ena 1");
|
||||
} else{
|
||||
datagram.append("ena 0");
|
||||
}
|
||||
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
request.setDelayUntilNextCommand(2000);
|
||||
qCDebug(dcKeba()) << "Enable output: Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
QUuid KeContact::setMaxAmpere(int milliAmpere)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
if (milliAmpere < 6000 || milliAmpere > 63000) {
|
||||
qCWarning(dcKeba()) << "KeContact: Set max ampere, currtime mA out of range [6000, 63000]" << milliAmpere;
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
// Print information that we are executing now the update action
|
||||
qCDebug(dcKeba()) << "Update max current to : " << milliAmpere;
|
||||
QString commandLine = QString("currtime %1 1").arg(milliAmpere);
|
||||
QByteArray datagram = commandLine.toUtf8();
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
request.setDelayUntilNextCommand(1200);
|
||||
qCDebug(dcKeba()) << "Set max charging amps: Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
QUuid KeContact::setMaxAmpereGeneral(int milliAmpere)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
if (milliAmpere < 6000 || milliAmpere > 63000) {
|
||||
qCWarning(dcKeba()) << "KeContact: Set max ampere curr, mA out of range [6000, 63000]" << milliAmpere;
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
// Print information that we are executing now the update action
|
||||
qCDebug(dcKeba()) << "Update general max current to: " << milliAmpere;
|
||||
QString commandLine = QString("curr %1").arg(milliAmpere);
|
||||
QByteArray datagram = commandLine.toUtf8();
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
request.setDelayUntilNextCommand(1200);
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
QUuid KeContact::displayMessage(const QByteArray &message)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
/* Text shown on the display. Maximum 23 ASCII characters can be used. 0 .. 23 characters
|
||||
~ == Σ
|
||||
$ == blank
|
||||
, == comma
|
||||
*/
|
||||
|
||||
qCDebug(dcKeba()) << "Set display message: " << message;
|
||||
QByteArray datagram;
|
||||
QByteArray modifiedMessage = message;
|
||||
modifiedMessage.replace(" ", "$");
|
||||
if (modifiedMessage.size() > 23) {
|
||||
modifiedMessage.resize(23);
|
||||
}
|
||||
datagram.append("display 0 0 0 0 " + modifiedMessage);
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Display message: Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
QUuid KeContact::chargeWithEnergyLimit(double energy)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
QByteArray datagram;
|
||||
datagram.append("setenergy " + QVariant(static_cast<int>(energy*10000)).toByteArray());
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Charge with energy limit: Datagram: " << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
QUuid KeContact::setFailsafe(int timeout, int current, bool save)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
data.append("failsave");
|
||||
data.append(" "+QVariant(timeout).toByteArray());
|
||||
data.append(" "+QVariant(current).toByteArray());
|
||||
data.append((save ? " 1":" 0"));
|
||||
KeContactRequest request(QUuid::createUuid(), data);
|
||||
qCDebug(dcKeba()) << "Set failsafe mode: Datagram: " << data;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
void KeContact::getDeviceInformation()
|
||||
{
|
||||
QByteArray data;
|
||||
data.append("i");
|
||||
KeContactRequest request(QUuid::createUuid(), data);
|
||||
qCDebug(dcKeba()) << "Get device information: Datagram: " << data;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
}
|
||||
|
||||
void KeContact::getReport1()
|
||||
{
|
||||
getReport(1);
|
||||
}
|
||||
|
||||
void KeContact::getReport2()
|
||||
{
|
||||
getReport(2);
|
||||
}
|
||||
|
||||
void KeContact::getReport3()
|
||||
{
|
||||
getReport(3);
|
||||
}
|
||||
|
||||
void KeContact::getReport1XX(int reportNumber)
|
||||
{
|
||||
getReport(reportNumber);
|
||||
}
|
||||
|
||||
QUuid KeContact::setOutputX2(bool state)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
|
||||
QByteArray datagram;
|
||||
datagram.append("output " + QVariant((state ? 1 : 0)).toByteArray());
|
||||
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Set Output X2, state:" << state << "Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
void KeContact::getReport(int reportNumber)
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray datagram;
|
||||
datagram.append("report " + QVariant(reportNumber).toByteArray());
|
||||
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Get report" << reportNumber << "Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
}
|
||||
|
||||
QUuid KeContact::unlockCharger()
|
||||
{
|
||||
if (!m_dataLayer) {
|
||||
qCWarning(dcKeba()) << "UDP socket not initialized";
|
||||
setReachable(false);
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
QByteArray datagram;
|
||||
datagram.append("unlock");
|
||||
|
||||
KeContactRequest request(QUuid::createUuid(), datagram);
|
||||
qCDebug(dcKeba()) << "Unlock charger: Datagram:" << datagram;
|
||||
m_requestQueue.enqueue(request);
|
||||
sendNextCommand();
|
||||
return request.requestId();
|
||||
}
|
||||
|
||||
void KeContact::onReceivedDatagram(const QHostAddress &address, const QByteArray &datagram)
|
||||
{
|
||||
// Make sure the datagram is for this keba
|
||||
if (address != m_address)
|
||||
return;
|
||||
|
||||
if (datagram.contains("TCH-OK")){
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
|
||||
//Command response has been received, now send the next command
|
||||
m_requestTimeoutTimer->stop();
|
||||
|
||||
if (m_currentRequest.isValid()) {
|
||||
if (datagram.contains("done")) {
|
||||
qCDebug(dcKeba()) << "Command" << m_currentRequest.command() << "finished successfully";
|
||||
emit commandExecuted(m_currentRequest.requestId(), true);
|
||||
} else {
|
||||
qCWarning(dcKeba()) << "Command" << m_currentRequest.command() << "finished with error" << datagram;
|
||||
emit commandExecuted(m_currentRequest.requestId(), false);
|
||||
}
|
||||
|
||||
// Schedule pause timer to send next request
|
||||
m_pauseTimer->start(m_currentRequest.delayUntilNextCommand());
|
||||
m_currentRequest = KeContactRequest();
|
||||
} else {
|
||||
//Probably the response has taken too long and the requestId has been already removed
|
||||
qCWarning(dcKeba()) << "Received command OK response without pending request." << datagram;
|
||||
}
|
||||
} else if (datagram.left(8).contains("Firmware")){
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
|
||||
// Command response has been received, now send the next command
|
||||
m_requestTimeoutTimer->stop();
|
||||
if (m_currentRequest.isValid()) {
|
||||
// Schedule pause timer to send next request
|
||||
m_pauseTimer->start(m_currentRequest.delayUntilNextCommand());
|
||||
m_currentRequest = KeContactRequest();
|
||||
}
|
||||
|
||||
qCDebug(dcKeba()) << "Firmware information received";
|
||||
QByteArrayList firmware = datagram.split(':');
|
||||
if (firmware.length() >= 2) {
|
||||
emit deviceInformationReceived(firmware[1]);
|
||||
}
|
||||
} else {
|
||||
//Command response has been received, now send the next command
|
||||
m_requestTimeoutTimer->stop();
|
||||
if (m_currentRequest.isValid()) {
|
||||
// Schedule pause timer to send next request
|
||||
m_pauseTimer->start(m_currentRequest.delayUntilNextCommand());
|
||||
m_currentRequest = KeContactRequest();
|
||||
}
|
||||
|
||||
// Convert the rawdata to a json document
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(datagram, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcKeba()) << "Failed to parse JSON data" << datagram << ":" << error.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap data = jsonDoc.toVariant().toMap();
|
||||
|
||||
if (data.contains("ID")) {
|
||||
int id = data.value("ID").toInt();
|
||||
if (id == 1) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
|
||||
ReportOne reportOne;
|
||||
//qCDebug(dcKeba()) << "Report 1 received";
|
||||
reportOne.product = data.value("Product").toString();
|
||||
reportOne.firmware = data.value("Firmware").toString();
|
||||
reportOne.serialNumber = data.value("Serial").toString();
|
||||
//"Backend:"
|
||||
//"timeQ": 3
|
||||
//"DIP-Sw1": "0x22"
|
||||
//"DIP-Sw2":
|
||||
reportOne.dipSw1 = data.value("DIP-Sw1").toString().remove("0x").toUInt(nullptr, 16);
|
||||
reportOne.dipSw2 = data.value("DIP-Sw2").toString().remove("0x").toUInt(nullptr, 16);
|
||||
|
||||
if (data.contains("COM-module")) {
|
||||
reportOne.comModule = (data.value("COM-module").toInt() == 1);
|
||||
} else {
|
||||
reportOne.comModule = false;
|
||||
}
|
||||
if (data.contains("Sec")) {
|
||||
reportOne.comModule = data.value("Sec").toInt();
|
||||
} else {
|
||||
reportOne.comModule = 0;
|
||||
}
|
||||
emit reportOneReceived(reportOne);
|
||||
|
||||
} else if (id == 2) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
|
||||
ReportTwo reportTwo;
|
||||
//qCDebug(dcKeba()) << "Report 2 received";
|
||||
int state = data.value("State").toInt();
|
||||
reportTwo.state = State(state);
|
||||
reportTwo.error1 = data.value("Error1").toInt();
|
||||
reportTwo.error2 = data.value("Error2").toInt();
|
||||
reportTwo.plugState = PlugState(data.value("Plug").toInt());
|
||||
reportTwo.enableUser = data.value("Enable user").toBool();
|
||||
reportTwo.enableSys = data.value("Enable sys").toBool();
|
||||
reportTwo.maxCurrent = data.value("Max curr").toInt() / 1000.00;
|
||||
reportTwo.maxCurrentPercentage = data.value("Max curr %").toInt() / 10.00;
|
||||
reportTwo.currentHardwareLimitation = data.value("Curr HW").toInt() / 1000.00;
|
||||
reportTwo.currentUser = data.value("Curr user").toInt() / 1000.00;
|
||||
reportTwo.currTimer = data.value("Curr timer").toInt() / 1000.00;
|
||||
reportTwo.timeoutCt = data.value("Tmo CT").toInt();
|
||||
reportTwo.currentFailsafe = data.value("Curr FS").toInt() / 1000.00;
|
||||
reportTwo.timeoutFailsafe = data.value("Tmo FS").toInt();
|
||||
reportTwo.setEnergy = data.value("Setenergy").toInt() / 10000.00;
|
||||
reportTwo.output = data.value("Output").toInt();
|
||||
reportTwo.input= data.value("Input").toInt();
|
||||
reportTwo.serialNumber = data.value("Serial").toString();
|
||||
reportTwo.seconds = data.value("Sec").toInt();
|
||||
// Not documented:
|
||||
//"AuthON": 0
|
||||
//"Authreq": 0
|
||||
emit reportTwoReceived(reportTwo);
|
||||
|
||||
} else if (id == 3) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
|
||||
ReportThree reportThree;
|
||||
//qCDebug(dcKeba()) << "Report 3 received";
|
||||
reportThree.currentPhase1 = data.value("I1").toInt() / 1000.00;
|
||||
reportThree.currentPhase2 = data.value("I2").toInt() / 1000.00;
|
||||
reportThree.currentPhase3 = data.value("I3").toInt() / 1000.00;
|
||||
reportThree.voltagePhase1 = data.value("U1").toInt();
|
||||
reportThree.voltagePhase2 = data.value("U2").toInt();
|
||||
reportThree.voltagePhase3 = data.value("U3").toInt();
|
||||
reportThree.power = data.value("P").toInt() / 1000.00;
|
||||
reportThree.powerFactor = data.value("PF").toInt() / 10.00;
|
||||
reportThree.energySession = data.value("E pres").toInt() / 10000.00;
|
||||
reportThree.energyTotal = data.value("E total").toInt() / 10000.00;
|
||||
reportThree.serialNumber = data.value("Serial").toString();
|
||||
reportThree.seconds = data.value("Sec").toInt();
|
||||
emit reportThreeReceived(reportThree);
|
||||
} else if (id >= 100) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
|
||||
Report1XX report;
|
||||
//qCDebug(dcKeba()) << "Report" << id << "received";
|
||||
report.sessionId = data.value("Session ID").toInt();
|
||||
report.currHW = data.value("Curr HW").toInt();
|
||||
report.startEnergy = data.value("E start").toInt() / 10000.00;
|
||||
report.presentEnergy = data.value("E pres").toInt() / 10000.00;
|
||||
report.startTime = data.value("started[s]").toInt();
|
||||
report.endTime = data.value("ended[s]").toInt();
|
||||
report.stopReason = data.value("reason").toInt();
|
||||
report.rfidTag = data.value("RFID tag").toByteArray();
|
||||
report.rfidClass = data.value("RFID class").toByteArray();
|
||||
report.serialNumber = data.value("Serial").toString();
|
||||
report.seconds = data.value("Sec").toInt();
|
||||
emit report1XXReceived(id, report);
|
||||
}
|
||||
} else {
|
||||
// Broadcast message, lets see what we recognize
|
||||
|
||||
if (data.contains("State")) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
emit broadcastReceived(BroadcastType::BroadcastTypeState, data.value("State"));
|
||||
}
|
||||
if (data.contains("Plug")) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
emit broadcastReceived(BroadcastType::BroadcastTypePlug, data.value("Plug"));
|
||||
}
|
||||
if (data.contains("Input")) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
emit broadcastReceived(BroadcastType::BroadcastTypeInput, data.value("Input"));
|
||||
}
|
||||
if (data.contains("Enable sys")) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
emit broadcastReceived(BroadcastType::BroadcastTypeEnableSys, data.value("Enable sys"));
|
||||
}
|
||||
if (data.contains("Max curr")) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
emit broadcastReceived(BroadcastType::BroadcastTypeMaxCurr, data.value("Max curr"));
|
||||
}
|
||||
if (data.contains("E pres")) {
|
||||
// We received valid data from the address over the data link, so the wallbox must be reachable
|
||||
setReachable(true);
|
||||
emit broadcastReceived(BroadcastType::BroadcastTypeEPres, data.value("E pres"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
239
keba/kecontact.h
Normal file
239
keba/kecontact.h
Normal file
@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef KECONTACT_H
|
||||
#define KECONTACT_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QByteArray>
|
||||
#include <QUdpSocket>
|
||||
#include <QUuid>
|
||||
#include <QQueue>
|
||||
|
||||
#include "kecontactdatalayer.h"
|
||||
|
||||
class KeContactRequest
|
||||
{
|
||||
public:
|
||||
KeContactRequest() = default;
|
||||
KeContactRequest(const QUuid &requestId, const QByteArray &command) : m_requestId(requestId), m_command(command) { }
|
||||
|
||||
QUuid requestId() const { return m_requestId; }
|
||||
QByteArray command() const { return m_command; }
|
||||
|
||||
uint delayUntilNextCommand() const { return m_delayUntilNextCommand; }
|
||||
void setDelayUntilNextCommand(uint delayUntilNextCommand) { m_delayUntilNextCommand = delayUntilNextCommand; }
|
||||
|
||||
bool isValid() { return !m_requestId.isNull() && !m_command.isEmpty(); }
|
||||
|
||||
private:
|
||||
QUuid m_requestId;
|
||||
QByteArray m_command;
|
||||
uint m_delayUntilNextCommand = 200;
|
||||
};
|
||||
|
||||
|
||||
class KeContact : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum DipSwitchOne {
|
||||
// Power settings
|
||||
// DIP 6 7 8 (Bit 2, 1, 0)
|
||||
// 0 0 0 : 10A
|
||||
// 1 0 0 : 13A
|
||||
// 0 1 0 : 16A
|
||||
// 1 1 0 : 20A
|
||||
// 0 0 1 : 25A
|
||||
// 1 0 1 : 32A
|
||||
DipSwitchOnePin8 = 0x01,
|
||||
DipSwitchOnePin7 = 0x02,
|
||||
DipSwitchOnePin6 = 0x04,
|
||||
DipSwitchOnePin5 = 0x08,
|
||||
DipSwitchOnePin4 = 0x10,
|
||||
DipSwitchOneSmartHomeInterface = 0x20, // 3
|
||||
DipSwitchOneExternalInputX2 = 0x40, // 2
|
||||
DipSwitchOneExternalInputX1 = 0x80 // 1
|
||||
};
|
||||
Q_ENUM(DipSwitchOne)
|
||||
Q_DECLARE_FLAGS(DipSwitchOneFlag, DipSwitchOne)
|
||||
|
||||
enum State {
|
||||
StateStarting = 0,
|
||||
StateNotReady,
|
||||
StateReady,
|
||||
StateCharging,
|
||||
StateError,
|
||||
StateAuthorizationRejected
|
||||
};
|
||||
Q_ENUM(State)
|
||||
|
||||
enum PlugState {
|
||||
PlugStateUnplugged = 0,
|
||||
PlugStatePluggedOnChargingStation = 1,
|
||||
PlugStatePluggedOnChargingStationAndPlugLocked = 3,
|
||||
PlugStatePluggedOnChargingStationAndPluggedOnEV = 5,
|
||||
PlugStatePluggedOnChargingStationAndPlugLockedAndPluggedOnEV = 7
|
||||
};
|
||||
Q_ENUM(PlugState)
|
||||
|
||||
enum BroadcastType {
|
||||
BroadcastTypeState = 0,
|
||||
BroadcastTypePlug,
|
||||
BroadcastTypeInput,
|
||||
BroadcastTypeEnableSys,
|
||||
BroadcastTypeMaxCurr,
|
||||
BroadcastTypeEPres
|
||||
};
|
||||
Q_ENUM(BroadcastType)
|
||||
|
||||
struct ReportOne {
|
||||
QString product; // Model name (variant
|
||||
QString serialNumber; // Serial number
|
||||
QString firmware; // Firmware version
|
||||
bool comModule; // Communication module is installed (only P30)
|
||||
int seconds; // Current system clock since restart of the charging station.(only P30)
|
||||
quint8 dipSw1; // Dip Switch 1 flag
|
||||
quint8 dipSw2; // Dip Switch 2 flag
|
||||
};
|
||||
|
||||
struct ReportTwo {
|
||||
State state; //Current state of the charging station
|
||||
int error1; //Detail code for state 4; exceptions see FAQ on www.kecontact.com
|
||||
int error2; //Detail code for state 4 exception #6 see FAQ on www.kecontact.com
|
||||
PlugState plugState; //Current condition of the loading connection
|
||||
bool enableSys; //Enable state for charging (contains Enable input, RFID, UDP,..).
|
||||
bool enableUser; //Enable condition via UDP.
|
||||
double maxCurrent; //Current preset value via Control pilot in ampere.
|
||||
double maxCurrentPercentage; //Current preset value via Control pilot in 0,1% of the PWM value
|
||||
double currentHardwareLimitation; //Highest possible charging current of the charging connection. Contains device maximum, DIP-switch setting, cable coding and temperature reduction.
|
||||
double currentUser; //Current preset value of the user via UDP; A.
|
||||
double currentFailsafe; //Current preset value for the Failsafe function.
|
||||
int timeoutFailsafe; //Communication timeout before triggering the Failsafe function.
|
||||
int currTimer; //Shows the current preset value of currtime.
|
||||
int timeoutCt; //Shows the remaining time until the current value is accepted.
|
||||
double setEnergy; //Shows the set energy limit
|
||||
bool output; //State of the output X2.
|
||||
bool input; //State of the potential free Enable input X1. When using the input, please pay attention to the information in the installation manual.
|
||||
QString serialNumber; //Serial number
|
||||
int seconds; //Current system clock since restart of the charging station.
|
||||
};
|
||||
|
||||
struct ReportThree {
|
||||
int voltagePhase1; //voltage in V
|
||||
int voltagePhase2; //voltage in V
|
||||
int voltagePhase3; //voltage in V
|
||||
double currentPhase1; //current in A
|
||||
double currentPhase2; //current in A
|
||||
double currentPhase3; //current in A
|
||||
double power; //Current power in W (Real Power).
|
||||
double powerFactor; //Power factor (cosphi)
|
||||
double energySession; //Power consumption of the current loading session in kWh; Reset with new loading session (state = 2).
|
||||
double energyTotal; //Total power consumption (persistent) without current loading session kWh; Is summed up after each completed charging session (state = 0).
|
||||
QString serialNumber;
|
||||
int seconds; //Current system clock since restart of the charging station.
|
||||
};
|
||||
|
||||
struct Report1XX {
|
||||
int sessionId; // running session counter; not resettable"
|
||||
double currHW; // maximum charging current of the cable and the charging station setting
|
||||
double startEnergy; // total energy value at the beginning of the session"
|
||||
double presentEnergy; // delivered energy until now (equal to E pres in report 3)"
|
||||
int startTime; // system time when the session was started (seconds from reboot;
|
||||
int endTime; // system time when the session has ended"
|
||||
int stopReason; // reason for stopping the session (1 = vehicle unplug; 10 = Rfid token)"
|
||||
QByteArray rfidTag; // RFID Token ID if session started with rfid
|
||||
QByteArray rfidClass; // RFID classifier shows the defined color code
|
||||
QString serialNumber; // serial number of the charging station"
|
||||
int seconds; // current time when the report was generated
|
||||
};
|
||||
|
||||
explicit KeContact(const QHostAddress &address, KeContactDataLayer *dataLayer, QObject *parent = nullptr);
|
||||
~KeContact();
|
||||
|
||||
QHostAddress address() const;
|
||||
void setAddress(const QHostAddress &address);
|
||||
|
||||
bool reachable() const;
|
||||
|
||||
QUuid start(const QByteArray &rfidToken, const QByteArray &rfidClassifier); // Command “start”
|
||||
QUuid stop(const QByteArray &rfidToken); // Command “stop”
|
||||
|
||||
QUuid enableOutput(bool state); // Command “ena”
|
||||
QUuid setMaxAmpere(int milliAmpere); // Command "currtime"
|
||||
QUuid setMaxAmpereGeneral(int milliAmpere); // Command "curr"
|
||||
QUuid unlockCharger(); // Command “unlock"
|
||||
QUuid displayMessage(const QByteArray &message); // Command “display”
|
||||
QUuid chargeWithEnergyLimit(double energy); // Command “setenergy”
|
||||
QUuid setFailsafe(int timeout, int current, bool save); // Command “failsafe”
|
||||
|
||||
void getDeviceInformation(); // Command “i”
|
||||
void getReport1(); // Command “report”
|
||||
void getReport2();
|
||||
void getReport3();
|
||||
void getReport1XX(int reportNumber = 100); // Command “report 1xx”
|
||||
|
||||
// Command “currtime”
|
||||
QUuid setOutputX2(bool state); // Command “output”
|
||||
|
||||
private:
|
||||
KeContactDataLayer *m_dataLayer = nullptr;
|
||||
bool m_reachable = false;
|
||||
|
||||
QHostAddress m_address;
|
||||
|
||||
QTimer *m_requestTimeoutTimer = nullptr;
|
||||
QTimer *m_pauseTimer = nullptr;
|
||||
int m_serialNumber = 0;
|
||||
|
||||
KeContactRequest m_currentRequest;
|
||||
QQueue<KeContactRequest> m_requestQueue;
|
||||
|
||||
void getReport(int reportNumber);
|
||||
|
||||
void sendCommand(const QByteArray &command);
|
||||
void sendNextCommand();
|
||||
void setReachable(bool reachable);
|
||||
|
||||
signals:
|
||||
void reachableChanged(bool status);
|
||||
void commandExecuted(QUuid requestId, bool success);
|
||||
void deviceInformationReceived(const QString &firmware);
|
||||
void reportOneReceived(const KeContact::ReportOne &reportOne);
|
||||
void reportTwoReceived(const KeContact::ReportTwo &reportTwo);
|
||||
void reportThreeReceived(const KeContact::ReportThree &reportThree);
|
||||
void report1XXReceived(int reportNumber, const KeContact::Report1XX &report);
|
||||
void broadcastReceived(KeContact::BroadcastType type, const QVariant &content);
|
||||
|
||||
private slots:
|
||||
void onReceivedDatagram(const QHostAddress &address, const QByteArray &datagram);
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KeContact::DipSwitchOneFlag);
|
||||
|
||||
#endif // KECONTACT_H
|
||||
|
||||
95
keba/kecontactdatalayer.cpp
Normal file
95
keba/kecontactdatalayer.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "kecontactdatalayer.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
KeContactDataLayer::KeContactDataLayer(QObject *parent) : QObject(parent)
|
||||
{
|
||||
qCDebug(dcKeba()) << "KeContactDataLayer: Creating UDP socket";
|
||||
m_udpSocket = new QUdpSocket(this);
|
||||
connect(m_udpSocket, &QUdpSocket::readyRead, this, &KeContactDataLayer::readPendingDatagrams);
|
||||
connect(m_udpSocket, &QUdpSocket::stateChanged, this, &KeContactDataLayer::onSocketStateChanged);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5 , 15, 0)
|
||||
connect(m_udpSocket, &QUdpSocket::errorOccurred, this, &KeContactDataLayer::onSocketError);
|
||||
#else
|
||||
connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||
#endif
|
||||
}
|
||||
|
||||
KeContactDataLayer::~KeContactDataLayer()
|
||||
{
|
||||
qCDebug(dcKeba()) << "KeContactDataLayer: Deleting UDP socket";
|
||||
}
|
||||
|
||||
bool KeContactDataLayer::init()
|
||||
{
|
||||
m_udpSocket->close();
|
||||
m_initialized = false;
|
||||
|
||||
if (!m_udpSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress)) {
|
||||
qCWarning(dcKeba()) << "KeContactDataLayer: Cannot bind to port" << m_port;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeContactDataLayer::initialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void KeContactDataLayer::write(const QHostAddress &address, const QByteArray &data)
|
||||
{
|
||||
qCDebug(dcKeba()) << "KeContactDataLayer: -->" << address.toString() << data;
|
||||
m_udpSocket->writeDatagram(data, address, m_port);
|
||||
}
|
||||
|
||||
void KeContactDataLayer::readPendingDatagrams()
|
||||
{
|
||||
QUdpSocket *socket= qobject_cast<QUdpSocket*>(sender());
|
||||
|
||||
QByteArray datagram;
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
|
||||
while (socket->hasPendingDatagrams()) {
|
||||
datagram.resize(socket->pendingDatagramSize());
|
||||
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
||||
qCDebug(dcKeba()) << "KeContactDataLayer: <--" << senderAddress.toString() << datagram;
|
||||
emit datagramReceived(senderAddress, datagram);
|
||||
}
|
||||
}
|
||||
|
||||
void KeContactDataLayer::onSocketError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qCWarning(dcKeba()) << "KeContactDataLayer: Socket error" << error;
|
||||
}
|
||||
|
||||
void KeContactDataLayer::onSocketStateChanged(QAbstractSocket::SocketState socketState)
|
||||
{
|
||||
qCDebug(dcKeba()) << "KeContactDataLayer: Socket state changed" << socketState;
|
||||
}
|
||||
58
keba/kecontactdatalayer.h
Normal file
58
keba/kecontactdatalayer.h
Normal file
@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of nymea-plugins.
|
||||
*
|
||||
* nymea-plugins is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nymea-plugins 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef KECONTACTDATALAYER_H
|
||||
#define KECONTACTDATALAYER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
|
||||
class KeContactDataLayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KeContactDataLayer(QObject *parent = nullptr);
|
||||
~KeContactDataLayer();
|
||||
|
||||
bool init();
|
||||
bool initialized() const;
|
||||
|
||||
void write(const QHostAddress &address, const QByteArray &data);
|
||||
|
||||
private:
|
||||
bool m_initialized = false;
|
||||
int m_port = 7090;
|
||||
QUdpSocket *m_udpSocket = nullptr;
|
||||
|
||||
signals:
|
||||
void datagramReceived(const QHostAddress &address, const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
void onSocketError(QAbstractSocket::SocketError error);
|
||||
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
|
||||
|
||||
};
|
||||
|
||||
#endif // KECONTACTDATALAYER_H
|
||||
13
keba/meta.json
Normal file
13
keba/meta.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Keba",
|
||||
"tagline": "Control wallboxes made by Keba.",
|
||||
"icon": "keba.svg",
|
||||
"stability": "consumer",
|
||||
"offline": true,
|
||||
"technologies": [
|
||||
"network"
|
||||
],
|
||||
"categories": [
|
||||
"energy"
|
||||
]
|
||||
}
|
||||
430
keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts
Normal file
430
keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-de.ts
Normal file
@ -0,0 +1,430 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="de">
|
||||
<context>
|
||||
<name>IntegrationPluginKeba</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="69"/>
|
||||
<source>The communication could not be established.</source>
|
||||
<translation>Die Kommunikation konnte nicht aufgebaut werden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="76"/>
|
||||
<source>The network discovery is not available. Please enter the IP address manually.</source>
|
||||
<translation>Das Durchsuchen des Netzwerks ist leider nicht möglich. Bitte geben Sie die IP Adresse manuell ein.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="148"/>
|
||||
<source>Error opening network port.</source>
|
||||
<translation>Fehler beim Öffnen des Netzwerkports.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="158"/>
|
||||
<source>Already configured for this IP address.</source>
|
||||
<translation>Es wurde bereits ein Gerät für diese IP Addresse eingerichtet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="203"/>
|
||||
<source>The required communication interface is not enabled on this keba. Please make sure the DIP switch 1.3 is switched on and try again.</source>
|
||||
<translation>Die notwendige Schnittstelle ist nicht eingeschaltet. Bitte stellen Sie sicher, dass DIP switch 1.3 eingeschaltet ist und versuchen Sie es erneut.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="248"/>
|
||||
<source>This model does not support communication with smart devices.</source>
|
||||
<translation>Dieses Model unterstützt keine Kommunikation mit Smarten Geräten.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Keba</name>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="108"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="111"/>
|
||||
<source>Activity</source>
|
||||
<extracomment>The name of the StateType ({955ffd64-42f6-4000-94c5-c7f862daa438}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass keba</extracomment>
|
||||
<translation>Aktivität</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="114"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="117"/>
|
||||
<source>Car plugged in</source>
|
||||
<extracomment>The name of the StateType ({faf68cc9-f014-4db5-94fa-0f10a0b85fb1}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({6c227717-f420-4dcd-bd52-49973715603b}) of ThingClass keba</extracomment>
|
||||
<translation>Auto angesteckt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="120"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="123"/>
|
||||
<source>Charging</source>
|
||||
<extracomment>The name of the StateType ({38affdf2-f62e-458c-b738-8db81aa13790}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({c9785626-2501-478d-8c18-c42ad5d9a269}) of ThingClass keba</extracomment>
|
||||
<translation>Lade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="126"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="129"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="132"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="135"/>
|
||||
<source>Charging enabled</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: power, ID: {63f84293-62aa-420d-bc0d-cc48618c6526})
|
||||
----------
|
||||
The name of the StateType ({63f84293-62aa-420d-bc0d-cc48618c6526}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981})
|
||||
----------
|
||||
The name of the StateType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass keba</extracomment>
|
||||
<translation>Laden ermöglicht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="138"/>
|
||||
<source>Charging session finished</source>
|
||||
<extracomment>The name of the EventType ({dac02c37-f051-481a-ae99-1de0885ef37a}) of ThingClass keba</extracomment>
|
||||
<translation>Ladevorgang beendet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="141"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="144"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({995f2ccf-2082-434e-a46d-c506862e6d6a}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({ce813458-d7d8-4f40-9648-dba4c41e92f0}) of ThingClass keba</extracomment>
|
||||
<translation>Verbunden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="147"/>
|
||||
<source>Current phase A</source>
|
||||
<extracomment>The name of the StateType ({31ec17b0-11e3-4332-92b0-fea821cf024f}) of ThingClass keba</extracomment>
|
||||
<translation>Ladestrom Phase A</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="150"/>
|
||||
<source>Current phase B</source>
|
||||
<extracomment>The name of the StateType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass keba</extracomment>
|
||||
<translation>Ladestrom Phase B</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="153"/>
|
||||
<source>Current phase C</source>
|
||||
<extracomment>The name of the StateType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass keba</extracomment>
|
||||
<translation>Ladestrom Phase C</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="156"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="159"/>
|
||||
<source>Display</source>
|
||||
<extracomment>The name of the ActionType ({e756c842-bec5-42ee-a28b-280d48e834b1}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({158b1a8f-fde9-4191-bf42-4ece5fe582e6}) of ThingClass keba</extracomment>
|
||||
<translation>Anzeige</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="162"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="165"/>
|
||||
<source>Display message</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: display, ID: {ec14a880-0546-431c-ab4e-578d56ecbfb9})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: display, ID: {4e69a761-f4f1-42d0-83db-380894a86ebc})</extracomment>
|
||||
<translation>Nachricht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="168"/>
|
||||
<source>Duration</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: keba, EventType: chargingSessionFinished, ID: {60494d6f-853b-42b8-894e-108a52ed6feb})</extracomment>
|
||||
<translation>Dauer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="171"/>
|
||||
<source>Energy</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: keba, EventType: chargingSessionFinished, ID: {c8de58b6-b671-4fee-b552-d2c14a37a769})</extracomment>
|
||||
<translation>Energie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="174"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="177"/>
|
||||
<source>Error 1</source>
|
||||
<extracomment>The name of the StateType ({8380c340-84ee-4d62-84b0-7c5738ab66bc}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({b44bc948-1234-4f87-9a22-bfb6de09df4d}) of ThingClass keba</extracomment>
|
||||
<translation>Error 1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="219"/>
|
||||
<source>KeConnect German Edition</source>
|
||||
<extracomment>The name of the ThingClass ({c5bca9d2-2a17-40c4-8bb2-ba89783a6dd1})</extracomment>
|
||||
<translation>KeConnect Deutschland Edition</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="180"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="183"/>
|
||||
<source>Error 2</source>
|
||||
<extracomment>The name of the StateType ({afe287a2-35e2-4762-a6bf-79d7c31d32ab}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({afca201a-5213-43fe-bfec-cae6ce7509d2}) of ThingClass keba</extracomment>
|
||||
<translation>Error 2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="186"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="189"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="192"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="195"/>
|
||||
<source>Failsafe mode</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: failsafeMode, ID: {bfad6a1a-40e0-4b32-9f42-09efd5a7e94c})
|
||||
----------
|
||||
The name of the StateType ({bfad6a1a-40e0-4b32-9f42-09efd5a7e94c}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: failsafeMode, ID: {f1758c5c-2c02-41cb-93ec-b778a3c78d28})
|
||||
----------
|
||||
The name of the StateType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass keba</extracomment>
|
||||
<translation>Failsafe Module</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="198"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="201"/>
|
||||
<source>Firmware</source>
|
||||
<extracomment>The name of the StateType ({d473770e-c5b4-4845-8215-0dea304ea202}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({e941ace5-fb7f-4dc2-b3f2-188233f4e934}) of ThingClass keba</extracomment>
|
||||
<translation>Firmware</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="204"/>
|
||||
<source>ID</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: keba, EventType: chargingSessionFinished, ID: {33446eae-f2cc-4cf2-af29-b3a45e4b91c0})</extracomment>
|
||||
<translation>ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="207"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="210"/>
|
||||
<source>IP address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {8324cad1-0d9d-4e48-b472-8c22eb7a1057})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {730cd3d3-5f0e-4028-a8c2-ced7574f13f3})</extracomment>
|
||||
<translation>IP Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="213"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="216"/>
|
||||
<source>Input</source>
|
||||
<extracomment>The name of the StateType ({0ca0921d-5516-44fb-9483-242d9bb7a2d0}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({ba600276-8b36-4404-b8ec-415245e5bc15}) of ThingClass keba</extracomment>
|
||||
<translation>Eingang</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="222"/>
|
||||
<source>Keba</source>
|
||||
<extracomment>The name of the vendor ({f7cda40b-829a-4675-abaa-485697430f5f})</extracomment>
|
||||
<translation>Keba</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="225"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="228"/>
|
||||
<source>Keba KeContact</source>
|
||||
<extracomment>The name of the ThingClass ({900dacec-cae7-4a37-95ba-501846368ea2})
|
||||
----------
|
||||
The name of the plugin Keba ({9142b09f-30a9-43d0-9ede-2f8debe075ac})</extracomment>
|
||||
<translation>Keba KeContact</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="231"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="234"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {e438179a-5202-4106-a622-d9e10a74fed9})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {c2df921d-ff8b-411c-9b1d-04a437d7dfa6})</extracomment>
|
||||
<translation>MAC Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="237"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="240"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="243"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="246"/>
|
||||
<source>Maximal charging current</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: maxChargingCurrent, ID: {2a72ad9e-96bd-4281-afb7-ce4f5c6f5052})
|
||||
----------
|
||||
The name of the StateType ({2a72ad9e-96bd-4281-afb7-ce4f5c6f5052}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: maxChargingCurrent, ID: {593656f0-babf-4308-8767-68f34e10fb15})
|
||||
----------
|
||||
The name of the StateType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass keba</extracomment>
|
||||
<translation>Maximaler Ladestrom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="249"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="252"/>
|
||||
<source>Maximal charging current in percent</source>
|
||||
<extracomment>The name of the StateType ({33631b7f-a675-4625-8095-31e09e03a010}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) of ThingClass keba</extracomment>
|
||||
<translation>Maximaler Ladestrom in Prozent</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="255"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="258"/>
|
||||
<source>Maximal hardware charging current</source>
|
||||
<extracomment>The name of the StateType ({f94a2381-28a8-478e-ac44-0902a5be8885}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({33e2ed95-f01e-44db-8156-34d124a8ecc8}) of ThingClass keba</extracomment>
|
||||
<translation>Maximaler Ladestrom der Hardware</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="261"/>
|
||||
<source>Number of connected phases</source>
|
||||
<extracomment>The name of the StateType ({6713b2e7-41b3-4596-a304-3065726bdbe4}) of ThingClass keba</extracomment>
|
||||
<translation>Anzahl angeschlossener Phasen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="348"/>
|
||||
<source>Voltage phase A</source>
|
||||
<extracomment>The name of the StateType ({4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) of ThingClass keba</extracomment>
|
||||
<translation>Spannung Phase A</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="351"/>
|
||||
<source>Voltage phase B</source>
|
||||
<extracomment>The name of the StateType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass keba</extracomment>
|
||||
<translation>Spannung Phase B</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="354"/>
|
||||
<source>Voltage phase C</source>
|
||||
<extracomment>The name of the StateType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass keba</extracomment>
|
||||
<translation>Spannung Phase C</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="264"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="267"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="270"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="273"/>
|
||||
<source>Output X2</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: outputX2, ID: {043ea799-4348-44f9-985d-bee2ba280957})
|
||||
----------
|
||||
The name of the StateType ({043ea799-4348-44f9-985d-bee2ba280957}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: outputX2, ID: {96b2d176-6460-4109-8824-3af4679c6573})
|
||||
----------
|
||||
The name of the StateType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass keba</extracomment>
|
||||
<translation>Ausgang X2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="276"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="279"/>
|
||||
<source>Plug state</source>
|
||||
<extracomment>The name of the StateType ({82aa0d67-eea6-4a5e-b7ab-2848a4012490}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass keba</extracomment>
|
||||
<translation>Stecker-Status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="282"/>
|
||||
<source>Power consumption</source>
|
||||
<extracomment>The name of the StateType ({7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) of ThingClass keba</extracomment>
|
||||
<translation>Leistungsaufnahme</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="285"/>
|
||||
<source>Power factor</source>
|
||||
<extracomment>The name of the StateType ({889c3c9a-96b4-4408-bd9a-d79e36ed9296}) of ThingClass keba</extracomment>
|
||||
<translation>Leistungsfaktor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="288"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="291"/>
|
||||
<source>Product name</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {5e49d289-9e32-47a8-8b30-43cb949695c8})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {a996c698-4831-4977-8979-f76f78ac7da8})</extracomment>
|
||||
<translation>Produktname</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="294"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="297"/>
|
||||
<source>Serial number</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {6f732eb9-1711-4da0-a9a4-abcfa19f5e34})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {45255155-318b-4204-8ce6-2c106a56286d})</extracomment>
|
||||
<translation>Seriennummer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="300"/>
|
||||
<source>Session ID</source>
|
||||
<extracomment>The name of the StateType ({1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) of ThingClass keba</extracomment>
|
||||
<translation>Session ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="303"/>
|
||||
<source>Session energy</source>
|
||||
<extracomment>The name of the StateType ({8e277efe-21ef-4536-bfc0-901b32d44d7c}) of ThingClass keba</extracomment>
|
||||
<translation>Session Energie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="306"/>
|
||||
<source>Session time</source>
|
||||
<extracomment>The name of the StateType ({a6f35ea0-aaea-438b-b818-6d161762611e}) of ThingClass keba</extracomment>
|
||||
<translation>Sessiondauer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="309"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="312"/>
|
||||
<source>Set charging enabled</source>
|
||||
<extracomment>The name of the ActionType ({63f84293-62aa-420d-bc0d-cc48618c6526}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass keba</extracomment>
|
||||
<translation>Ermölgliche Laden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="315"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="318"/>
|
||||
<source>Set failsafe mode</source>
|
||||
<extracomment>The name of the ActionType ({bfad6a1a-40e0-4b32-9f42-09efd5a7e94c}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass keba</extracomment>
|
||||
<translation>Setze Failsafe Modus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="321"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="324"/>
|
||||
<source>Set maximal charging current</source>
|
||||
<extracomment>The name of the ActionType ({2a72ad9e-96bd-4281-afb7-ce4f5c6f5052}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass keba</extracomment>
|
||||
<translation>Setze maximaler Ladestrom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="327"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="330"/>
|
||||
<source>Set output X2</source>
|
||||
<extracomment>The name of the ActionType ({043ea799-4348-44f9-985d-bee2ba280957}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass keba</extracomment>
|
||||
<translation>Setze Ausgang X2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="333"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="336"/>
|
||||
<source>System enabled</source>
|
||||
<extracomment>The name of the StateType ({8ade4b68-e44e-425c-87ea-a35d176f337d}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({e5631593-f486-47cb-9951-b7597d0b769b}) of ThingClass keba</extracomment>
|
||||
<translation>System eingeschaltet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="339"/>
|
||||
<source>Total energy consumed</source>
|
||||
<extracomment>The name of the StateType ({41e179b3-29a2-43ec-b537-023a527081e8}) of ThingClass keba</extracomment>
|
||||
<translation>Gesamter Energieverbrauch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="342"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="345"/>
|
||||
<source>Uptime</source>
|
||||
<extracomment>The name of the StateType ({2cffff03-63b2-468d-b2ef-a4741401d7c8}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) of ThingClass keba</extracomment>
|
||||
<translation>Betriebszeit</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
430
keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts
Normal file
430
keba/translations/9142b09f-30a9-43d0-9ede-2f8debe075ac-en_US.ts
Normal file
@ -0,0 +1,430 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>IntegrationPluginKeba</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="69"/>
|
||||
<source>The communication could not be established.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="76"/>
|
||||
<source>The network discovery is not available. Please enter the IP address manually.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="148"/>
|
||||
<source>Error opening network port.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="158"/>
|
||||
<source>Already configured for this IP address.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="203"/>
|
||||
<source>The required communication interface is not enabled on this keba. Please make sure the DIP switch 1.3 is switched on and try again.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../integrationpluginkeba.cpp" line="248"/>
|
||||
<source>This model does not support communication with smart devices.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Keba</name>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="108"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="111"/>
|
||||
<source>Activity</source>
|
||||
<extracomment>The name of the StateType ({955ffd64-42f6-4000-94c5-c7f862daa438}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({539e5602-6dd9-465d-9705-3bb59bcf8982}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="114"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="117"/>
|
||||
<source>Car plugged in</source>
|
||||
<extracomment>The name of the StateType ({faf68cc9-f014-4db5-94fa-0f10a0b85fb1}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({6c227717-f420-4dcd-bd52-49973715603b}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="120"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="123"/>
|
||||
<source>Charging</source>
|
||||
<extracomment>The name of the StateType ({38affdf2-f62e-458c-b738-8db81aa13790}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({c9785626-2501-478d-8c18-c42ad5d9a269}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="126"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="129"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="132"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="135"/>
|
||||
<source>Charging enabled</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: power, ID: {63f84293-62aa-420d-bc0d-cc48618c6526})
|
||||
----------
|
||||
The name of the StateType ({63f84293-62aa-420d-bc0d-cc48618c6526}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: power, ID: {83ed0774-2a91-434d-b03c-d920d02f2981})
|
||||
----------
|
||||
The name of the StateType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="138"/>
|
||||
<source>Charging session finished</source>
|
||||
<extracomment>The name of the EventType ({dac02c37-f051-481a-ae99-1de0885ef37a}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="141"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="144"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({995f2ccf-2082-434e-a46d-c506862e6d6a}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({ce813458-d7d8-4f40-9648-dba4c41e92f0}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="147"/>
|
||||
<source>Current phase A</source>
|
||||
<extracomment>The name of the StateType ({31ec17b0-11e3-4332-92b0-fea821cf024f}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="150"/>
|
||||
<source>Current phase B</source>
|
||||
<extracomment>The name of the StateType ({cdc7e10a-0d0a-4e93-ad2c-d34ffca45c97}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="153"/>
|
||||
<source>Current phase C</source>
|
||||
<extracomment>The name of the StateType ({da838dc8-85f0-4e55-b4b5-cb93a43b373d}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="156"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="159"/>
|
||||
<source>Display</source>
|
||||
<extracomment>The name of the ActionType ({e756c842-bec5-42ee-a28b-280d48e834b1}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({158b1a8f-fde9-4191-bf42-4ece5fe582e6}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="162"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="165"/>
|
||||
<source>Display message</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: display, ID: {ec14a880-0546-431c-ab4e-578d56ecbfb9})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: display, ID: {4e69a761-f4f1-42d0-83db-380894a86ebc})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="168"/>
|
||||
<source>Duration</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: keba, EventType: chargingSessionFinished, ID: {60494d6f-853b-42b8-894e-108a52ed6feb})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="171"/>
|
||||
<source>Energy</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: keba, EventType: chargingSessionFinished, ID: {c8de58b6-b671-4fee-b552-d2c14a37a769})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="174"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="177"/>
|
||||
<source>Error 1</source>
|
||||
<extracomment>The name of the StateType ({8380c340-84ee-4d62-84b0-7c5738ab66bc}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({b44bc948-1234-4f87-9a22-bfb6de09df4d}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="219"/>
|
||||
<source>KeConnect German Edition</source>
|
||||
<extracomment>The name of the ThingClass ({c5bca9d2-2a17-40c4-8bb2-ba89783a6dd1})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="180"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="183"/>
|
||||
<source>Error 2</source>
|
||||
<extracomment>The name of the StateType ({afe287a2-35e2-4762-a6bf-79d7c31d32ab}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({afca201a-5213-43fe-bfec-cae6ce7509d2}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="186"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="189"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="192"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="195"/>
|
||||
<source>Failsafe mode</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: failsafeMode, ID: {bfad6a1a-40e0-4b32-9f42-09efd5a7e94c})
|
||||
----------
|
||||
The name of the StateType ({bfad6a1a-40e0-4b32-9f42-09efd5a7e94c}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: failsafeMode, ID: {f1758c5c-2c02-41cb-93ec-b778a3c78d28})
|
||||
----------
|
||||
The name of the StateType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="198"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="201"/>
|
||||
<source>Firmware</source>
|
||||
<extracomment>The name of the StateType ({d473770e-c5b4-4845-8215-0dea304ea202}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({e941ace5-fb7f-4dc2-b3f2-188233f4e934}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="204"/>
|
||||
<source>ID</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: keba, EventType: chargingSessionFinished, ID: {33446eae-f2cc-4cf2-af29-b3a45e4b91c0})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="207"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="210"/>
|
||||
<source>IP address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {8324cad1-0d9d-4e48-b472-8c22eb7a1057})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {730cd3d3-5f0e-4028-a8c2-ced7574f13f3})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="213"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="216"/>
|
||||
<source>Input</source>
|
||||
<extracomment>The name of the StateType ({0ca0921d-5516-44fb-9483-242d9bb7a2d0}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({ba600276-8b36-4404-b8ec-415245e5bc15}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="222"/>
|
||||
<source>Keba</source>
|
||||
<extracomment>The name of the vendor ({f7cda40b-829a-4675-abaa-485697430f5f})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="225"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="228"/>
|
||||
<source>Keba KeContact</source>
|
||||
<extracomment>The name of the ThingClass ({900dacec-cae7-4a37-95ba-501846368ea2})
|
||||
----------
|
||||
The name of the plugin Keba ({9142b09f-30a9-43d0-9ede-2f8debe075ac})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="231"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="234"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {e438179a-5202-4106-a622-d9e10a74fed9})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {c2df921d-ff8b-411c-9b1d-04a437d7dfa6})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="237"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="240"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="243"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="246"/>
|
||||
<source>Maximal charging current</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: maxChargingCurrent, ID: {2a72ad9e-96bd-4281-afb7-ce4f5c6f5052})
|
||||
----------
|
||||
The name of the StateType ({2a72ad9e-96bd-4281-afb7-ce4f5c6f5052}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: maxChargingCurrent, ID: {593656f0-babf-4308-8767-68f34e10fb15})
|
||||
----------
|
||||
The name of the StateType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="249"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="252"/>
|
||||
<source>Maximal charging current in percent</source>
|
||||
<extracomment>The name of the StateType ({33631b7f-a675-4625-8095-31e09e03a010}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({3c7b83a0-0e42-47bf-9788-dde6aab5ceea}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="255"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="258"/>
|
||||
<source>Maximal hardware charging current</source>
|
||||
<extracomment>The name of the StateType ({f94a2381-28a8-478e-ac44-0902a5be8885}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({33e2ed95-f01e-44db-8156-34d124a8ecc8}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="261"/>
|
||||
<source>Number of connected phases</source>
|
||||
<extracomment>The name of the StateType ({6713b2e7-41b3-4596-a304-3065726bdbe4}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="348"/>
|
||||
<source>Voltage phase A</source>
|
||||
<extracomment>The name of the StateType ({4a2d75d8-a3a0-4b40-9ca7-e8b6f11d0ef9}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="351"/>
|
||||
<source>Voltage phase B</source>
|
||||
<extracomment>The name of the StateType ({c8344ca5-21ac-4cd1-8f4b-e5ed202c5862}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="354"/>
|
||||
<source>Voltage phase C</source>
|
||||
<extracomment>The name of the StateType ({5f01e86c-0943-4849-a01a-db441916ebd5}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="264"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="267"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="270"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="273"/>
|
||||
<source>Output X2</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, ActionType: outputX2, ID: {043ea799-4348-44f9-985d-bee2ba280957})
|
||||
----------
|
||||
The name of the StateType ({043ea799-4348-44f9-985d-bee2ba280957}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, ActionType: outputX2, ID: {96b2d176-6460-4109-8824-3af4679c6573})
|
||||
----------
|
||||
The name of the StateType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="276"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="279"/>
|
||||
<source>Plug state</source>
|
||||
<extracomment>The name of the StateType ({82aa0d67-eea6-4a5e-b7ab-2848a4012490}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({3b4d29f3-3101-47ad-90fd-269b6348783b}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="282"/>
|
||||
<source>Power consumption</source>
|
||||
<extracomment>The name of the StateType ({7af9e93b-099d-4d9d-a480-9c0f66aecd8b}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="285"/>
|
||||
<source>Power factor</source>
|
||||
<extracomment>The name of the StateType ({889c3c9a-96b4-4408-bd9a-d79e36ed9296}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="288"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="291"/>
|
||||
<source>Product name</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {5e49d289-9e32-47a8-8b30-43cb949695c8})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {a996c698-4831-4977-8979-f76f78ac7da8})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="294"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="297"/>
|
||||
<source>Serial number</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: kebaSimple, Type: thing, ID: {6f732eb9-1711-4da0-a9a4-abcfa19f5e34})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: keba, Type: thing, ID: {45255155-318b-4204-8ce6-2c106a56286d})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="300"/>
|
||||
<source>Session ID</source>
|
||||
<extracomment>The name of the StateType ({1d30ce60-2ea0-450f-817e-5c88f59ebfbf}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="303"/>
|
||||
<source>Session energy</source>
|
||||
<extracomment>The name of the StateType ({8e277efe-21ef-4536-bfc0-901b32d44d7c}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="306"/>
|
||||
<source>Session time</source>
|
||||
<extracomment>The name of the StateType ({a6f35ea0-aaea-438b-b818-6d161762611e}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="309"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="312"/>
|
||||
<source>Set charging enabled</source>
|
||||
<extracomment>The name of the ActionType ({63f84293-62aa-420d-bc0d-cc48618c6526}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({83ed0774-2a91-434d-b03c-d920d02f2981}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="315"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="318"/>
|
||||
<source>Set failsafe mode</source>
|
||||
<extracomment>The name of the ActionType ({bfad6a1a-40e0-4b32-9f42-09efd5a7e94c}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({f1758c5c-2c02-41cb-93ec-b778a3c78d28}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="321"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="324"/>
|
||||
<source>Set maximal charging current</source>
|
||||
<extracomment>The name of the ActionType ({2a72ad9e-96bd-4281-afb7-ce4f5c6f5052}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({593656f0-babf-4308-8767-68f34e10fb15}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="327"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="330"/>
|
||||
<source>Set output X2</source>
|
||||
<extracomment>The name of the ActionType ({043ea799-4348-44f9-985d-bee2ba280957}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the ActionType ({96b2d176-6460-4109-8824-3af4679c6573}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="333"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="336"/>
|
||||
<source>System enabled</source>
|
||||
<extracomment>The name of the StateType ({8ade4b68-e44e-425c-87ea-a35d176f337d}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({e5631593-f486-47cb-9951-b7597d0b769b}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="339"/>
|
||||
<source>Total energy consumed</source>
|
||||
<extracomment>The name of the StateType ({41e179b3-29a2-43ec-b537-023a527081e8}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="342"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/keba/plugininfo.h" line="345"/>
|
||||
<source>Uptime</source>
|
||||
<extracomment>The name of the StateType ({2cffff03-63b2-468d-b2ef-a4741401d7c8}) of ThingClass kebaSimple
|
||||
----------
|
||||
The name of the StateType ({3421ecf9-c95f-4dc1-ad0c-144e9b6ae056}) of ThingClass keba</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
Loading…
x
Reference in New Issue
Block a user