Add PCE EV11.3 support
This commit is contained in:
parent
cc92df2d74
commit
22ef68ffa0
9
debian/control
vendored
9
debian/control
vendored
@ -246,6 +246,15 @@ Description: nymea integration plugin for UniPi devices
|
||||
This package contains the nymea integration plugin for UniPi devices.
|
||||
|
||||
|
||||
Package: nymea-plugin-pcelectric
|
||||
Architecture: any
|
||||
Section: libs
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
Description: nymea integration plugin for PCE wallboxes
|
||||
This package contains the nymea integration plugin for wallboxes made by PC Electric.
|
||||
|
||||
|
||||
Package: nymea-plugin-phoenixconnect
|
||||
Architecture: any
|
||||
Section: libs
|
||||
|
||||
2
debian/nymea-plugin-pcelectric.install.in
vendored
Normal file
2
debian/nymea-plugin-pcelectric.install.in
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginpcelectric.so
|
||||
pcelectric/translations/*qm usr/share/nymea/translations/
|
||||
@ -17,6 +17,7 @@ PLUGIN_DIRS = \
|
||||
modbuscommander \
|
||||
mtec \
|
||||
mypv \
|
||||
pcelectric \
|
||||
phoenixconnect \
|
||||
schrack \
|
||||
senseair \
|
||||
|
||||
334
pcelectric/EV11.3-registers.json
Normal file
334
pcelectric/EV11.3-registers.json
Normal file
@ -0,0 +1,334 @@
|
||||
{
|
||||
"className": "EV11",
|
||||
"protocol": "TCP",
|
||||
"endianness": "BigEndian",
|
||||
"errorLimitUntilNotReachable": 10,
|
||||
"checkReachableRegister": "chargingState",
|
||||
"enums": [
|
||||
{
|
||||
"name": "ChargingState",
|
||||
"values": [
|
||||
{
|
||||
"key": "Initializing",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"key": "A1",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"key": "A2",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"key": "B1",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"key": "B2",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"key": "C1",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"key": "C2",
|
||||
"value": 6
|
||||
},
|
||||
{
|
||||
"key": "Error",
|
||||
"value": 7
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ChargingRelayState",
|
||||
"values": [
|
||||
{
|
||||
"key": "NoCharging",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"key": "SinglePhase",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"key": "TheePhase",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Error",
|
||||
"values": [
|
||||
{
|
||||
"key": "NoError",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"key": "Overheating",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"key": "DCFaultCurrent",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"key": "ChargingWithVentilation",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"key": "CPErrorEF",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"key": "CPErrorBypass",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"key": "CPErrorDiodFault",
|
||||
"value": 6
|
||||
},
|
||||
{
|
||||
"key": "DCFaultCurrentCalibrating",
|
||||
"value": 7
|
||||
},
|
||||
{
|
||||
"key": "DCFaultCurrentCommunication",
|
||||
"value": 8
|
||||
},
|
||||
{
|
||||
"key": "DCFaultCurrentError",
|
||||
"value": 9
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"id": "status",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "chargingState",
|
||||
"address": 100,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"enum": "ChargingState",
|
||||
"description": "Current charging state",
|
||||
"defaultValue": "ChargingStateInitializing",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "chargingRelayState",
|
||||
"address": 101,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"enum": "ChargingRelayState",
|
||||
"description": "Charging relay state",
|
||||
"defaultValue": "ChargingRelayStateNoCharging",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "maxChargingCurrentDip",
|
||||
"address": 102,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Maximum charging current (DIP)",
|
||||
"unit": "mA",
|
||||
"defaultValue": "6000",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "phaseAutoSwitch",
|
||||
"address": 103,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Automatic phase switching",
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "activeChargingCurrent",
|
||||
"address": 104,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Active charging current",
|
||||
"unit": "mA",
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "sessionDuration",
|
||||
"address": 105,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Session durration",
|
||||
"unit": "10 seconds",
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "powerMeter0",
|
||||
"address": 106,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Current session energy",
|
||||
"unit": "kWh",
|
||||
"staticScaleFactor": -2,
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "powerMeter1",
|
||||
"address": 107,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Last session energy",
|
||||
"unit": "kWh",
|
||||
"staticScaleFactor": -2,
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "powerMeter3",
|
||||
"address": 108,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Penultimate session energy",
|
||||
"unit": "kWh",
|
||||
"staticScaleFactor": -2,
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "temperature",
|
||||
"address": 109,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Onboard temperature",
|
||||
"unit": "°C",
|
||||
"staticScaleFactor": -1,
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "error",
|
||||
"address": 110,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"enum": "Error",
|
||||
"description": "Error",
|
||||
"defaultValue": "ErrorNoError",
|
||||
"access": "R"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "initInfos",
|
||||
"readSchedule": "init",
|
||||
"registers": [
|
||||
{
|
||||
"id": "firmwareRevision",
|
||||
"address": 135,
|
||||
"size": 2,
|
||||
"type": "string",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Firmware revision (ASCII)",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "hardwareRevision",
|
||||
"address": 137,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Hardware revision",
|
||||
"defaultValue": "0",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "serialNumber",
|
||||
"address": 138,
|
||||
"size": 3,
|
||||
"type": "raw",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Serial number",
|
||||
"access": "R"
|
||||
},
|
||||
{
|
||||
"id": "macAddress",
|
||||
"address": 141,
|
||||
"size": 3,
|
||||
"type": "raw",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "MAC address",
|
||||
"access": "R"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"registers": [
|
||||
{
|
||||
"id": "chargingCurrent",
|
||||
"address": 200,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Write charging current",
|
||||
"unit": "mA",
|
||||
"access": "WO"
|
||||
},
|
||||
{
|
||||
"id": "chargingCurrentOffline",
|
||||
"address": 201,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Write charging current",
|
||||
"unit": "mA",
|
||||
"access": "WO"
|
||||
},
|
||||
{
|
||||
"id": "maxChargingTime",
|
||||
"address": 202,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Max charging time",
|
||||
"unit": "Minutes",
|
||||
"access": "WO"
|
||||
},
|
||||
{
|
||||
"id": "heartbeat",
|
||||
"address": 203,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Heartbeat (write < 60s to keep alive)",
|
||||
"access": "WO"
|
||||
},
|
||||
{
|
||||
"id": "ledBrightness",
|
||||
"address": 204,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "LED brightness",
|
||||
"unit": "%",
|
||||
"access": "WO"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
pcelectric/README.md
Normal file
3
pcelectric/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# PC Electric
|
||||
|
||||
|
||||
398
pcelectric/integrationpluginpcelectric.cpp
Normal file
398
pcelectric/integrationpluginpcelectric.cpp
Normal file
@ -0,0 +1,398 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "integrationpluginpcelectric.h"
|
||||
#include "pcelectricdiscovery.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <hardwaremanager.h>
|
||||
#include <hardware/electricity.h>
|
||||
|
||||
IntegrationPluginPcElectric::IntegrationPluginPcElectric()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcPcElectric()) << "The network discovery is not available on this platform.";
|
||||
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
|
||||
PcElectricDiscovery *discovery = new PcElectricDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 1, info);
|
||||
connect(discovery, &PcElectricDiscovery::discoveryFinished, info, [=](){
|
||||
foreach (const PcElectricDiscovery::Result &result, discovery->results()) {
|
||||
|
||||
ThingDescriptor descriptor(ev11ThingClassId, "PCE EV11.3 (" + result.serialNumber + ")", "Version: " + result.firmwareRevision + " - " + result.networkDeviceInfo.address().toString());
|
||||
qCDebug(dcPcElectric()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||
|
||||
// Check if we already have set up this device
|
||||
Things existingThings = myThings().filterByParam(ev11ThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||
if (existingThings.count() == 1) {
|
||||
qCDebug(dcPcElectric()) << "This PCE wallbox already exists in the system:" << result.networkDeviceInfo;
|
||||
descriptor.setThingId(existingThings.first()->id());
|
||||
}
|
||||
|
||||
ParamList params;
|
||||
params << Param(ev11ThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||
// Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults...
|
||||
descriptor.setParams(params);
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
// Start the discovery process
|
||||
discovery->startDiscovery();
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
qCDebug(dcPcElectric()) << "Setup thing" << thing << thing->params();
|
||||
|
||||
if (m_connections.contains(thing)) {
|
||||
qCDebug(dcPcElectric()) << "Reconfiguring existing thing" << thing->name();
|
||||
m_connections.take(thing)->deleteLater();
|
||||
|
||||
if (m_monitors.contains(thing)) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
}
|
||||
|
||||
MacAddress macAddress = MacAddress(thing->paramValue(ev11ThingMacAddressParamTypeId).toString());
|
||||
if (!macAddress.isValid()) {
|
||||
qCWarning(dcPcElectric()) << "The configured mac address is not valid" << thing->params();
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not known. Please reconfigure the thing."));
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
|
||||
m_monitors.insert(thing, monitor);
|
||||
|
||||
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
|
||||
if (m_monitors.contains(thing)) {
|
||||
qCDebug(dcPcElectric()) << "Unregistering monitor because setup has been aborted.";
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
});
|
||||
|
||||
// Only make sure the connection is working in the initial setup, otherwise we let the monitor do the work
|
||||
if (info->isInitialSetup()) {
|
||||
// Continue with setup only if we know that the network device is reachable
|
||||
if (monitor->reachable()) {
|
||||
setupConnection(info);
|
||||
} else {
|
||||
// otherwise wait until we reach the networkdevice before setting up the device
|
||||
qCDebug(dcPcElectric()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable.";
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
|
||||
if (reachable) {
|
||||
qCDebug(dcPcElectric()) << "Network device" << thing->name() << "is now reachable. Continue with the setup...";
|
||||
setupConnection(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setupConnection(info);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::postSetupThing(Thing *thing)
|
||||
{
|
||||
qCDebug(dcPcElectric()) << "Post setup thing" << thing->name();
|
||||
if (!m_refreshTimer) {
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(1);
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, [this] {
|
||||
foreach (PceWallbox *connection, m_connections) {
|
||||
if (connection->reachable()) {
|
||||
connection->update();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
qCDebug(dcPcElectric()) << "Starting refresh timer...";
|
||||
m_refreshTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::thingRemoved(Thing *thing)
|
||||
{
|
||||
qCDebug(dcPcElectric()) << "Thing removed" << thing->name();
|
||||
|
||||
if (m_connections.contains(thing)) {
|
||||
PceWallbox *connection = m_connections.take(thing);
|
||||
connection->disconnectDevice();
|
||||
connection->deleteLater();
|
||||
}
|
||||
|
||||
// Unregister related hardware resources
|
||||
if (m_monitors.contains(thing))
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
|
||||
if (myThings().isEmpty() && m_refreshTimer) {
|
||||
qCDebug(dcPcElectric()) << "Stopping reconnect timer";
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||
m_refreshTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
PceWallbox *connection = m_connections.value(thing);
|
||||
if (!connection->reachable()) {
|
||||
qCWarning(dcPcElectric()) << "Could not execute action because the connection is not available.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->action().actionTypeId() == ev11PowerActionTypeId) {
|
||||
bool power = info->action().paramValue(ev11PowerActionPowerParamTypeId).toBool();
|
||||
quint16 chargingCurrent = 0;
|
||||
if (power) {
|
||||
chargingCurrent = thing->stateValue(ev11MaxChargingCurrentStateTypeId).toUInt() * 1000;
|
||||
if (thing->stateValue(ev11DesiredPhaseCountStateTypeId).toUInt() == 3) {
|
||||
// If 3 phase charging is enabled, we set the first bit
|
||||
chargingCurrent |= static_cast<quint16>(1) << 15;
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << chargingCurrent << "mA";
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(chargingCurrent);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, power, chargingCurrent](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set power state to" << power << "(" << chargingCurrent << "mA)" << reply->errorString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set power state to" << power << "(" << chargingCurrent << "mA)";
|
||||
thing->setStateValue(ev11PowerStateTypeId, power);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
return;
|
||||
} else if (info->action().actionTypeId() == ev11MaxChargingCurrentActionTypeId) {
|
||||
uint desiredChargingCurrent = info->action().paramValue(ev11MaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt();
|
||||
qCDebug(dcPcElectric()) << "Set max charging current to" << desiredChargingCurrent << "A";
|
||||
if (thing->stateValue(ev11PowerStateTypeId).toBool()) {
|
||||
// The charging is enabled, let's write the value to the wallbox
|
||||
quint16 finalChargingCurrent = static_cast<quint16>(desiredChargingCurrent * 1000);
|
||||
if (thing->stateValue(ev11DesiredPhaseCountStateTypeId).toUInt() == 3) {
|
||||
// If 3 phase charging is enabled, we set the first bit
|
||||
finalChargingCurrent |= static_cast<quint16>(1) << 15;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << finalChargingCurrent << "mA";
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(finalChargingCurrent);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, desiredChargingCurrent](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set charging current to" << desiredChargingCurrent << "mA" << reply->errorString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set charging current to" << desiredChargingCurrent << "mA";
|
||||
thing->setStateValue(ev11MaxChargingCurrentStateTypeId, desiredChargingCurrent);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else {
|
||||
// Save the value in the state, but do not send the value to the wallbox since the power state is reflected using the charging current...
|
||||
qCDebug(dcPcElectric()) << "Setting charging current to" << desiredChargingCurrent << "without synching to wallbox since the power state is false";
|
||||
thing->setStateValue(ev11MaxChargingCurrentStateTypeId, desiredChargingCurrent);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return;
|
||||
} else if (info->action().actionTypeId() == ev11DesiredPhaseCountActionTypeId) {
|
||||
uint desiredPhaseCount = info->action().paramValue(ev11DesiredPhaseCountActionDesiredPhaseCountParamTypeId).toUInt();
|
||||
qCDebug(dcPcElectric()) << "Desried phase count changed" << desiredPhaseCount;
|
||||
thing->setStateValue(ev11DesiredPhaseCountStateTypeId, desiredPhaseCount);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
// Update the max charging current according to the new desired phase count
|
||||
if (thing->stateValue(ev11PowerStateTypeId).toBool()) {
|
||||
uint chargingCurrent = thing->stateValue(ev11MaxChargingCurrentStateTypeId).toUInt();
|
||||
quint16 finalChargingCurrent = static_cast<quint16>(chargingCurrent * 1000);
|
||||
if (thing->stateValue(ev11DesiredPhaseCountStateTypeId).toUInt() == 3) {
|
||||
// If 3 phase charging is enabled, we set the first bit
|
||||
finalChargingCurrent |= static_cast<quint16>(1) << 15;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << finalChargingCurrent << "mA";
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(finalChargingCurrent);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, finalChargingCurrent](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set charging current to" << finalChargingCurrent << "mA" << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set charging current to" << finalChargingCurrent << "mA";
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Q_ASSERT_X(false, "IntegrationPluginPcElectric::executeAction", QString("Unhandled action: %1").arg(info->action().actionTypeId().toString()).toLocal8Bit());
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
|
||||
|
||||
qCDebug(dcPcElectric()) << "Setting up PCE wallbox finished successfully" << monitor->networkDeviceInfo().address().toString();
|
||||
|
||||
PceWallbox *connection = new PceWallbox(monitor->networkDeviceInfo().address(), 502, 1, this);
|
||||
connect(info, &ThingSetupInfo::aborted, connection, &PceWallbox::deleteLater);
|
||||
|
||||
// Monitor reachability
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
|
||||
if (!thing->setupComplete())
|
||||
return;
|
||||
|
||||
qCDebug(dcPcElectric()) << "Network device monitor for" << thing->name() << (reachable ? "is now reachable" : "is not reachable any more" );
|
||||
if (reachable && !thing->stateValue("connected").toBool()) {
|
||||
connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address());
|
||||
connection->connectDevice();
|
||||
} else if (!reachable) {
|
||||
// Note: We disable autoreconnect explicitly and we will
|
||||
// connect the device once the monitor says it is reachable again
|
||||
connection->disconnectDevice();
|
||||
}
|
||||
});
|
||||
|
||||
// Connection reachability
|
||||
connect(connection, &PceWallbox::reachableChanged, thing, [thing](bool reachable){
|
||||
qCInfo(dcPcElectric()) << "Reachable changed to" << reachable << "for" << thing;
|
||||
thing->setStateValue("connected", reachable);
|
||||
});
|
||||
|
||||
connect(connection, &PceWallbox::updateFinished, thing, [thing, connection](){
|
||||
qCDebug(dcPcElectric()) << "Update finished for" << thing;
|
||||
qCDebug(dcPcElectric()) << connection;
|
||||
if (!connection->phaseAutoSwitch()) {
|
||||
// Note: if auto phase switching is disabled, the wallbox forces 3 phase charging
|
||||
thing->setStatePossibleValues(ev11DesiredPhaseCountStateTypeId, { 3 }); // Disable phase switching (default 3)
|
||||
thing->setStateValue(ev11DesiredPhaseCountStateTypeId, 3);
|
||||
thing->setStateValue(ev11PhaseCountStateTypeId, 3);
|
||||
} else {
|
||||
thing->setStatePossibleValues(ev11DesiredPhaseCountStateTypeId, { 1, 3 }); // Enable phase switching
|
||||
}
|
||||
|
||||
if (connection->chargingRelayState() != EV11ModbusTcpConnection::ChargingRelayStateNoCharging) {
|
||||
if (connection->chargingRelayState() == EV11ModbusTcpConnection::ChargingRelayStateSinglePhase) {
|
||||
thing->setStateValue(ev11PhaseCountStateTypeId, 1);
|
||||
} else if (connection->chargingRelayState() == EV11ModbusTcpConnection::ChargingRelayStateTheePhase) {
|
||||
thing->setStateValue(ev11PhaseCountStateTypeId, 3);
|
||||
}
|
||||
}
|
||||
|
||||
thing->setStateMaxValue(ev11MaxChargingCurrentStateTypeId, connection->maxChargingCurrentDip() / 1000);
|
||||
thing->setStateValue(ev11PluggedInStateTypeId, connection->chargingState() >= PceWallbox::ChargingStateB1 &&
|
||||
connection->chargingState() < PceWallbox::ChargingStateError);
|
||||
|
||||
thing->setStateValue(ev11ChargingStateTypeId, connection->chargingState() == PceWallbox::ChargingStateC2);
|
||||
if (connection->chargingRelayState() != EV11ModbusTcpConnection::ChargingRelayStateNoCharging) {
|
||||
thing->setStateValue(ev11PhaseCountStateTypeId, connection->chargingRelayState() == EV11ModbusTcpConnection::ChargingRelayStateSinglePhase ? 1 : 3);
|
||||
}
|
||||
|
||||
thing->setStateValue(ev11CurrentVersionStateTypeId, connection->firmwareRevision());
|
||||
thing->setStateValue(ev11SessionEnergyStateTypeId, connection->powerMeter0());
|
||||
thing->setStateValue(ev11TemperatureStateTypeId, connection->temperature());
|
||||
|
||||
switch (connection->error()) {
|
||||
case EV11ModbusTcpConnection::ErrorNoError:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "Kein Fehler aktiv");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorOverheating:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "Übertemperatur. Ladevorgang wird automatisch fortgesetzt.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorDCFaultCurrent:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor ausgelöst.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorChargingWithVentilation:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "Ladeanforderung mit Belüftung.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorCPErrorEF:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, Fehlercode E oder F.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorCPErrorBypass:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, bypass.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorCPErrorDiodFault:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, Diode defekt.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorDCFaultCurrentCalibrating:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Kalibrirung.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorDCFaultCurrentCommunication:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Kommunikationsfehler.");
|
||||
break;
|
||||
case EV11ModbusTcpConnection::ErrorDCFaultCurrentError:
|
||||
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Fehler.");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
connect(thing, &Thing::settingChanged, connection, [thing, connection](const ParamTypeId ¶mTypeId, const QVariant &value){
|
||||
if (paramTypeId == ev11SettingsLedBrightnessParamTypeId) {
|
||||
quint16 percentage = value.toUInt();
|
||||
qCDebug(dcPcElectric()) << "Set LED brightness" << percentage << "%";
|
||||
QueuedModbusReply *reply = connection->setLedBrightness(percentage);
|
||||
connect(reply, &QueuedModbusReply::finished, thing, [reply, percentage](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set led brightness to" << percentage << "%" << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set led brightness to" << percentage << "%";
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
m_connections.insert(thing, connection);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
// Connect reight the way if the monitor indicates reachable, otherwise the connect will handle the connect later
|
||||
if (monitor->reachable())
|
||||
connection->connectDevice();
|
||||
}
|
||||
69
pcelectric/integrationpluginpcelectric.h
Normal file
69
pcelectric/integrationpluginpcelectric.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef INTEGRATIONPLUGINPCELECTRIC_H
|
||||
#define INTEGRATIONPLUGINPCELECTRIC_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <integrations/integrationplugin.h>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
#include <plugintimer.h>
|
||||
|
||||
#include "pcewallbox.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
class IntegrationPluginPcElectric : public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginpcelectric.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginPcElectric();
|
||||
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_refreshTimer = nullptr;
|
||||
QHash<Thing *, PceWallbox *> m_connections;
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
|
||||
void setupConnection(ThingSetupInfo *info);
|
||||
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINPCELECTRIC_H
|
||||
148
pcelectric/integrationpluginpcelectric.json
Normal file
148
pcelectric/integrationpluginpcelectric.json
Normal file
@ -0,0 +1,148 @@
|
||||
{
|
||||
"name": "PcElectric",
|
||||
"displayName": "PC Electric",
|
||||
"id": "aa7ff833-a8e0-45cc-a1ef-65f05871f272",
|
||||
"paramTypes":[ ],
|
||||
"vendors": [
|
||||
{
|
||||
"name": "PcElectric",
|
||||
"displayName": "PC Electric GmbH",
|
||||
"id": "b365937b-f1d6-46bf-9ff1-e787373b8aa6",
|
||||
"thingClasses": [
|
||||
{
|
||||
"name": "ev11",
|
||||
"displayName": "PCE EV11.3",
|
||||
"id": "88d96940-a940-4a07-8176-5e6aba7ca832",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": ["evcharger", "connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "0a3f8d12-9d33-4ae2-b763-9568f32e8da1",
|
||||
"name":"macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress",
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"settingsTypes": [
|
||||
{
|
||||
"id": "3a1329a2-84cc-47b9-a6c2-e96fdfd0c454",
|
||||
"name": "ledBrightness",
|
||||
"displayName": "LED brightness",
|
||||
"type": "uint",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 50
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "ca8d680c-c2f8-456a-a246-9c6cd64e25a7",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"cached": false,
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "c12a7a27-fa56-450c-a1ec-717c868554f2",
|
||||
"name": "power",
|
||||
"displayName": "Charging enabled",
|
||||
"displayNameEvent": "Charging enabled or disabled",
|
||||
"displayNameAction": "Enable or disable charging",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "b5bbf23c-06db-463b-bb5c-3aea38e18818",
|
||||
"name": "maxChargingCurrent",
|
||||
"displayName": "Maximum charging current",
|
||||
"displayNameEvent": "Maximum charging current changed",
|
||||
"displayNameAction": "Set maximum charging current",
|
||||
"type": "uint",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 6,
|
||||
"minValue": 6,
|
||||
"maxValue": 16,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "50164bbd-9802-4cf6-82de-626b74293a1b",
|
||||
"name": "pluggedIn",
|
||||
"displayName": "Plugged in",
|
||||
"displayNameEvent": "Plugged or unplugged",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "b7972cd7-471a-46bd-ab99-f49997f12309",
|
||||
"name": "charging",
|
||||
"displayName": "Charging",
|
||||
"displayNameEvent": "Charging started or stopped",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "bca88c23-e940-40c1-afca-eb511fd17aab",
|
||||
"name": "phaseCount",
|
||||
"displayName": "Active phases",
|
||||
"type": "uint",
|
||||
"minValue": 1,
|
||||
"maxValue": 3,
|
||||
"defaultValue": 3
|
||||
},
|
||||
{
|
||||
"id": "d91f7d96-2599-400a-91da-d164477098b7",
|
||||
"name": "desiredPhaseCount",
|
||||
"displayName": "Desired phase count",
|
||||
"displayNameAction": "Set desired phase count",
|
||||
"type": "uint",
|
||||
"minValue": 1,
|
||||
"maxValue": 3,
|
||||
"possibleValues": [1, 3],
|
||||
"defaultValue": 3,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "3da3ee80-e9e7-4237-85a6-b4adcb2f483b",
|
||||
"name": "sessionEnergy",
|
||||
"displayName": "Session energy",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "bb092562-377e-458e-bb8a-735af9036652",
|
||||
"name": "temperature",
|
||||
"displayName": "Onboard temperature",
|
||||
"displayNameEvent": "Onboard temperature changed",
|
||||
"unit": "DegreeCelsius",
|
||||
"type": "double",
|
||||
"defaultValue": 0,
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "2ea1a53f-b2b0-452d-8060-cdb114db05a7",
|
||||
"name": "error",
|
||||
"displayName": "Error",
|
||||
"type": "QString",
|
||||
"defaultValue": "Kein Fehler",
|
||||
"suggestLogging": true
|
||||
},
|
||||
{
|
||||
"id": "142b4276-e2e9-4149-adc4-89d9d3e31117",
|
||||
"name": "currentVersion",
|
||||
"displayName": "Firmware version",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
13
pcelectric/meta.json
Normal file
13
pcelectric/meta.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "PC Electric",
|
||||
"tagline": "Integrate the PCE EV11.3 wallbox with nymea.",
|
||||
"icon": "pce.png",
|
||||
"stability": "consumer",
|
||||
"offline": true,
|
||||
"technologies": [
|
||||
"modbus"
|
||||
],
|
||||
"categories": [
|
||||
"energy"
|
||||
]
|
||||
}
|
||||
BIN
pcelectric/pce.png
Normal file
BIN
pcelectric/pce.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
17
pcelectric/pcelectric.pro
Normal file
17
pcelectric/pcelectric.pro
Normal file
@ -0,0 +1,17 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
# Generate modbus connection
|
||||
MODBUS_CONNECTIONS += EV11.3-registers.json
|
||||
#MODBUS_TOOLS_CONFIG += VERBOSE
|
||||
include(../modbus.pri)
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginpcelectric.h \
|
||||
pcelectricdiscovery.h \
|
||||
pcewallbox.h
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginpcelectric.cpp \
|
||||
pcelectricdiscovery.cpp \
|
||||
pcewallbox.cpp
|
||||
|
||||
169
pcelectric/pcelectricdiscovery.cpp
Normal file
169
pcelectric/pcelectricdiscovery.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "pcelectricdiscovery.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
PcElectricDiscovery::PcElectricDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress, QObject *parent)
|
||||
: QObject{parent},
|
||||
m_networkDeviceDiscovery{networkDeviceDiscovery},
|
||||
m_port{port},
|
||||
m_modbusAddress{modbusAddress}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<PcElectricDiscovery::Result> PcElectricDiscovery::results() const
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
|
||||
|
||||
void PcElectricDiscovery::startDiscovery()
|
||||
{
|
||||
qCInfo(dcPcElectric()) << "Discovery: Start searching for PCE wallboxes in the network...";
|
||||
m_startDateTime = QDateTime::currentDateTime();
|
||||
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &PcElectricDiscovery::checkNetworkDevice);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||
// Finish with some delay so the last added network device information objects still can be checked.
|
||||
QTimer::singleShot(3000, this, [this](){
|
||||
qCDebug(dcPcElectric()) << "Discovery: Grace period timer triggered.";
|
||||
finishDiscovery();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void PcElectricDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
|
||||
{
|
||||
EV11ModbusTcpConnection *connection = new EV11ModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
|
||||
m_connections.append(connection);
|
||||
|
||||
connect(connection, &EV11ModbusTcpConnection::reachableChanged, this, [=](bool reachable){
|
||||
if (!reachable) {
|
||||
// Disconnected ... done with this connection
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
// Modbus TCP connected...ok, let's try to initialize it!
|
||||
connect(connection, &EV11ModbusTcpConnection::initializationFinished, this, [=](bool success){
|
||||
if (!success) {
|
||||
qCDebug(dcPcElectric()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the mac address from the registers and compair with the network device info mac address.
|
||||
// If they match, we most likly found a PCE wallbox
|
||||
|
||||
QByteArray macRawData;
|
||||
QDataStream stream(&macRawData, QIODevice::WriteOnly);
|
||||
for (int i = 0; i < connection->macAddress().count(); i++)
|
||||
stream << connection->macAddress().at(i);
|
||||
|
||||
MacAddress registerMacAddress(macRawData);
|
||||
qCDebug(dcPcElectric()) << "Fetched mac address" << macRawData.toHex() << registerMacAddress;
|
||||
|
||||
// According to PCE the HW revision must be 0
|
||||
if (registerMacAddress == MacAddress(networkDeviceInfo.macAddress()) && connection->hardwareRevision() == 0) {
|
||||
|
||||
// Parse the serial number
|
||||
QByteArray serialRawData;
|
||||
QDataStream stream(&serialRawData, QIODevice::ReadWrite);
|
||||
stream << static_cast<quint16>(0);
|
||||
for (int i = 0; i < connection->serialNumber().count(); i++)
|
||||
stream << connection->serialNumber().at(i);
|
||||
|
||||
quint64 serialNumber = serialRawData.toHex().toULongLong(nullptr, 16);
|
||||
qCDebug(dcPcElectric()) << "Serial number" << serialRawData.toHex() << serialNumber;
|
||||
|
||||
Result result;
|
||||
result.serialNumber = QString::number(serialNumber);
|
||||
result.firmwareRevision = connection->firmwareRevision();
|
||||
result.networkDeviceInfo = networkDeviceInfo;
|
||||
m_results.append(result);
|
||||
|
||||
qCInfo(dcPcElectric()) << "Discovery: --> Found"
|
||||
<< "Serial number:" << result.serialNumber
|
||||
<< "Firmware revision:" << result.firmwareRevision
|
||||
<< result.networkDeviceInfo;
|
||||
}
|
||||
|
||||
// Done with this connection
|
||||
cleanupConnection(connection);
|
||||
});
|
||||
|
||||
// Initializing...
|
||||
if (!connection->initialize()) {
|
||||
qCDebug(dcPcElectric()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// If we get any error...skip this host...
|
||||
connect(connection->modbusTcpMaster(), &ModbusTcpMaster::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
|
||||
if (error != QModbusDevice::NoError) {
|
||||
qCDebug(dcPcElectric()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// If check reachability failed...skip this host...
|
||||
connect(connection, &EV11ModbusTcpConnection::checkReachabilityFailed, this, [=](){
|
||||
qCDebug(dcPcElectric()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
|
||||
cleanupConnection(connection);
|
||||
});
|
||||
|
||||
// Try to connect, maybe it works, maybe not...
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
void PcElectricDiscovery::cleanupConnection(EV11ModbusTcpConnection *connection)
|
||||
{
|
||||
m_connections.removeAll(connection);
|
||||
connection->disconnectDevice();
|
||||
connection->deleteLater();
|
||||
}
|
||||
|
||||
void PcElectricDiscovery::finishDiscovery()
|
||||
{
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||
|
||||
// Cleanup any leftovers...we don't care any more
|
||||
foreach (EV11ModbusTcpConnection *connection, m_connections)
|
||||
cleanupConnection(connection);
|
||||
|
||||
qCInfo(dcPcElectric()) << "Discovery: Finished the discovery process. Found" << m_results.count()
|
||||
<< "PCE wallboxes in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||
emit discoveryFinished();
|
||||
}
|
||||
76
pcelectric/pcelectricdiscovery.h
Normal file
76
pcelectric/pcelectricdiscovery.h
Normal file
@ -0,0 +1,76 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef PCELECTRICDISCOVERY_H
|
||||
#define PCELECTRICDISCOVERY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "ev11modbustcpconnection.h"
|
||||
|
||||
class PcElectricDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PcElectricDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress, QObject *parent = nullptr);
|
||||
|
||||
typedef struct Result {
|
||||
QString serialNumber;
|
||||
QString firmwareRevision;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
} Result;
|
||||
|
||||
QList<PcElectricDiscovery::Result> results() const;
|
||||
|
||||
public slots:
|
||||
void startDiscovery();
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private:
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
quint16 m_port;
|
||||
quint16 m_modbusAddress;
|
||||
QDateTime m_startDateTime;
|
||||
|
||||
QList<EV11ModbusTcpConnection *> m_connections;
|
||||
|
||||
QList<Result> m_results;
|
||||
|
||||
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
|
||||
void cleanupConnection(EV11ModbusTcpConnection *connection);
|
||||
|
||||
void finishDiscovery();
|
||||
};
|
||||
|
||||
#endif // PCELECTRICDISCOVERY_H
|
||||
267
pcelectric/pcewallbox.cpp
Normal file
267
pcelectric/pcewallbox.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "pcewallbox.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <modbusdatautils.h>
|
||||
|
||||
PceWallbox::PceWallbox(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent)
|
||||
: EV11ModbusTcpConnection{hostAddress, port, slaveId, parent}
|
||||
{
|
||||
// Timer for resetting the heartbeat register (watchdog)
|
||||
m_timer.setInterval(30000);
|
||||
m_timer.setSingleShot(false);
|
||||
connect(&m_timer, &QTimer::timeout, this, &PceWallbox::sendHeartbeat);
|
||||
|
||||
connect(this, &EV11ModbusTcpConnection::reachableChanged, this, [this](bool reachable){
|
||||
if (!reachable) {
|
||||
m_timer.stop();
|
||||
|
||||
qDeleteAll(m_queue);
|
||||
m_queue.clear();
|
||||
|
||||
if (m_currentReply) {
|
||||
m_currentReply = nullptr;
|
||||
}
|
||||
|
||||
} else {
|
||||
initialize();
|
||||
}
|
||||
});
|
||||
|
||||
connect(this, &EV11ModbusTcpConnection::initializationFinished, this, [this](bool success){
|
||||
if (success) {
|
||||
qCDebug(dcPcElectric()) << "Connection initialized successfully" << m_modbusTcpMaster->hostAddress().toString();
|
||||
m_timer.start();
|
||||
|
||||
sendHeartbeat();
|
||||
update();
|
||||
|
||||
} else {
|
||||
qCWarning(dcPcElectric()) << "Connection initialization failed for" << m_modbusTcpMaster->hostAddress().toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool PceWallbox::update()
|
||||
{
|
||||
if (m_aboutToDelete)
|
||||
return false;
|
||||
|
||||
if (!reachable())
|
||||
return false;
|
||||
|
||||
// Make sure we only have one update call in the queue
|
||||
foreach (QueuedModbusReply *r, m_queue) {
|
||||
if (r->dataUnit().startAddress() == readBlockInitInfosDataUnit().startAddress()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, readBlockStatusDataUnit(), this);
|
||||
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
||||
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
|
||||
|
||||
if (m_currentReply == reply)
|
||||
m_currentReply = nullptr;
|
||||
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
emit updateFinished();
|
||||
sendNextRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
const QModbusDataUnit unit = reply->reply()->result();
|
||||
const QVector<quint16> blockValues = unit.values();
|
||||
processBlockStatusRegisterValues(blockValues);
|
||||
|
||||
emit updateFinished();
|
||||
sendNextRequest();
|
||||
});
|
||||
|
||||
enqueueRequest(reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
QueuedModbusReply *PceWallbox::setChargingCurrent(quint16 chargingCurrent)
|
||||
{
|
||||
if (m_aboutToDelete)
|
||||
return nullptr;
|
||||
|
||||
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setChargingCurrentDataUnit(chargingCurrent), this);
|
||||
|
||||
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
||||
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
|
||||
if (m_currentReply == reply)
|
||||
m_currentReply = nullptr;
|
||||
|
||||
sendNextRequest();
|
||||
return;
|
||||
});
|
||||
|
||||
enqueueRequest(reply, true);
|
||||
return reply;
|
||||
}
|
||||
|
||||
QueuedModbusReply *PceWallbox::setLedBrightness(quint16 percentage)
|
||||
{
|
||||
if (m_aboutToDelete)
|
||||
return nullptr;
|
||||
|
||||
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setLedBrightnessDataUnit(percentage), this);
|
||||
|
||||
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
||||
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
|
||||
if (m_currentReply == reply)
|
||||
m_currentReply = nullptr;
|
||||
|
||||
sendNextRequest();
|
||||
return;
|
||||
});
|
||||
|
||||
enqueueRequest(reply, true);
|
||||
return reply;
|
||||
}
|
||||
|
||||
void PceWallbox::gracefullDeleteLater()
|
||||
{
|
||||
// Clean up the queue
|
||||
m_aboutToDelete = true;
|
||||
cleanupQueue();
|
||||
|
||||
m_timer.stop();
|
||||
|
||||
if (!m_currentReply) {
|
||||
qCDebug(dcPcElectric()) << "Deleting object without pending request...";
|
||||
// No pending request, we can close the connection and delete the object
|
||||
disconnect(this, nullptr, nullptr, nullptr);
|
||||
disconnectDevice();
|
||||
deleteLater();
|
||||
} else {
|
||||
qCDebug(dcPcElectric()) << "Pending request, deleting object once the request is finished...";
|
||||
}
|
||||
}
|
||||
|
||||
void PceWallbox::sendHeartbeat()
|
||||
{
|
||||
if (m_aboutToDelete)
|
||||
return;
|
||||
|
||||
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setHeartbeatDataUnit(m_heartbeat++), this);
|
||||
|
||||
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
||||
|
||||
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
|
||||
if (m_currentReply == reply)
|
||||
m_currentReply = nullptr;
|
||||
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Failed to send heartbeat to" << m_modbusTcpMaster->hostAddress().toString() << reply->errorString();
|
||||
} else {
|
||||
qCDebug(dcPcElectric()) << "Successfully sent heartbeat to" << m_modbusTcpMaster->hostAddress().toString();
|
||||
}
|
||||
|
||||
sendNextRequest();
|
||||
return;
|
||||
});
|
||||
|
||||
enqueueRequest(reply, true);
|
||||
}
|
||||
|
||||
void PceWallbox::sendNextRequest()
|
||||
{
|
||||
if (m_queue.isEmpty())
|
||||
return;
|
||||
|
||||
if (m_currentReply)
|
||||
return;
|
||||
|
||||
if (m_aboutToDelete) {
|
||||
disconnect(this, nullptr, nullptr, nullptr);
|
||||
disconnectDevice();
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentReply = m_queue.dequeue();
|
||||
switch(m_currentReply->requestType()) {
|
||||
case QueuedModbusReply::RequestTypeRead:
|
||||
qCDebug(dcPcElectric()) << "--> Reading" << ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType())
|
||||
<< "register:" << m_currentReply->dataUnit().startAddress()
|
||||
<< "length" << m_currentReply->dataUnit().valueCount();
|
||||
m_currentReply->setReply(m_modbusTcpMaster->sendReadRequest(m_currentReply->dataUnit(), m_slaveId));
|
||||
break;
|
||||
case QueuedModbusReply::RequestTypeWrite:
|
||||
qCDebug(dcPcElectric()) << "--> Writing" << ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType())
|
||||
<< "register:" << m_currentReply->dataUnit().startAddress()
|
||||
<< "length:" << m_currentReply->dataUnit().valueCount()
|
||||
<< "values:" << m_currentReply->dataUnit().values();
|
||||
m_currentReply->setReply(m_modbusTcpMaster->sendWriteRequest(m_currentReply->dataUnit(), m_slaveId));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_currentReply->reply()) {
|
||||
qCWarning(dcPcElectric()) << "Error occurred while sending" << m_currentReply->requestType()
|
||||
<< ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType())
|
||||
<< "register:" << m_currentReply->dataUnit().startAddress()
|
||||
<< "length:" << m_currentReply->dataUnit().valueCount()
|
||||
<< "to" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString();
|
||||
m_currentReply->deleteLater();
|
||||
m_currentReply = nullptr;
|
||||
sendNextRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currentReply->reply()->isFinished()) {
|
||||
qCWarning(dcPcElectric()) << "Reply immediatly finished";
|
||||
m_currentReply->deleteLater();
|
||||
m_currentReply = nullptr;
|
||||
sendNextRequest();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PceWallbox::enqueueRequest(QueuedModbusReply *reply, bool prepend)
|
||||
{
|
||||
if (prepend) {
|
||||
m_queue.prepend(reply);
|
||||
} else {
|
||||
m_queue.enqueue(reply);
|
||||
}
|
||||
|
||||
sendNextRequest();
|
||||
}
|
||||
|
||||
void PceWallbox::cleanupQueue()
|
||||
{
|
||||
qDeleteAll(m_queue);
|
||||
m_queue.clear();
|
||||
}
|
||||
77
pcelectric/pcewallbox.h
Normal file
77
pcelectric/pcewallbox.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef PCEWALLBOX_H
|
||||
#define PCEWALLBOX_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QQueue>
|
||||
#include <QObject>
|
||||
|
||||
#include <queuedmodbusreply.h>
|
||||
|
||||
#include "ev11modbustcpconnection.h"
|
||||
|
||||
class PceWallbox : public EV11ModbusTcpConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PceWallbox(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);
|
||||
|
||||
bool update() override;
|
||||
|
||||
QueuedModbusReply *setChargingCurrent(quint16 chargingCurrent); // mA
|
||||
|
||||
QueuedModbusReply *setLedBrightness(quint16 percentage);
|
||||
|
||||
// Note: the modbus implementation of the wallbox gets stuck if a Modbus request has been sent
|
||||
// and we disconnect the socket before the response has arrived. Only a reboot of the wallbox
|
||||
// fixes the broken communication afterwards. This method waits for the current request before closing the
|
||||
// socket and deletes it self.
|
||||
// IMPORTNAT: do not use the object after this call, this is a temporary workaround
|
||||
void gracefullDeleteLater();
|
||||
|
||||
private slots:
|
||||
void sendHeartbeat();
|
||||
|
||||
private:
|
||||
QTimer m_timer;
|
||||
quint16 m_heartbeat = 1;
|
||||
QueuedModbusReply *m_currentReply = nullptr;
|
||||
QQueue<QueuedModbusReply *> m_queue;
|
||||
bool m_aboutToDelete = false;
|
||||
|
||||
void sendNextRequest();
|
||||
void enqueueRequest(QueuedModbusReply *reply, bool prepend = false);
|
||||
|
||||
void cleanupQueue();
|
||||
};
|
||||
|
||||
#endif // PCEWALLBOX_H
|
||||
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="de" sourcelanguage="en">
|
||||
<context>
|
||||
<name>IntegrationPluginPcElectric</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginpcelectric.cpp" line="53"/>
|
||||
<source>The network device discovery is not available.</source>
|
||||
<translation>Die Netzwerk Suche ist nicht verfügbar.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PcElectric</name>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="43"/>
|
||||
<source>Active phases</source>
|
||||
<extracomment>The name of the StateType ({bca88c23-e940-40c1-afca-eb511fd17aab}) of ThingClass ev11</extracomment>
|
||||
<translation>Aktive Phasen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="46"/>
|
||||
<source>Charging</source>
|
||||
<extracomment>The name of the StateType ({b7972cd7-471a-46bd-ab99-f49997f12309}) of ThingClass ev11</extracomment>
|
||||
<translation>Lädt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="49"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="52"/>
|
||||
<source>Charging enabled</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, ActionType: power, ID: {c12a7a27-fa56-450c-a1ec-717c868554f2})
|
||||
----------
|
||||
The name of the StateType ({c12a7a27-fa56-450c-a1ec-717c868554f2}) of ThingClass ev11</extracomment>
|
||||
<translation>Laden eingeschalten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="55"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({ca8d680c-c2f8-456a-a246-9c6cd64e25a7}) of ThingClass ev11</extracomment>
|
||||
<translation>Verbunden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="58"/>
|
||||
<source>Enable or disable charging</source>
|
||||
<extracomment>The name of the ActionType ({c12a7a27-fa56-450c-a1ec-717c868554f2}) of ThingClass ev11</extracomment>
|
||||
<translation>Laden starten/stoppen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="61"/>
|
||||
<source>Firmware version</source>
|
||||
<extracomment>The name of the StateType ({142b4276-e2e9-4149-adc4-89d9d3e31117}) of ThingClass ev11</extracomment>
|
||||
<translation>Firmware Version</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="64"/>
|
||||
<source>Hardware version</source>
|
||||
<extracomment>The name of the StateType ({b6e65baf-6dcd-4db1-a3dc-962a4c33d157}) of ThingClass ev11</extracomment>
|
||||
<translation>Hardware Version</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="67"/>
|
||||
<source>LED brightness</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, Type: settings, ID: {3a1329a2-84cc-47b9-a6c2-e96fdfd0c454})</extracomment>
|
||||
<translation>LED Helligkeit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="70"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, Type: thing, ID: {0a3f8d12-9d33-4ae2-b763-9568f32e8da1})</extracomment>
|
||||
<translation>MAC Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="73"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="76"/>
|
||||
<source>Maximum charging current</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, ActionType: maxChargingCurrent, ID: {b5bbf23c-06db-463b-bb5c-3aea38e18818})
|
||||
----------
|
||||
The name of the StateType ({b5bbf23c-06db-463b-bb5c-3aea38e18818}) of ThingClass ev11</extracomment>
|
||||
<translation>Maximaler Ladestrom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="79"/>
|
||||
<source>Maximum offline charging current</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, Type: settings, ID: {93654273-c4d3-4389-a81e-c0f065d9cd92})</extracomment>
|
||||
<translation>Maximaler Ladestrom offline</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="82"/>
|
||||
<source>PC Electric</source>
|
||||
<extracomment>The name of the plugin PcElectric ({aa7ff833-a8e0-45cc-a1ef-65f05871f272})</extracomment>
|
||||
<translation>PC Electric</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="85"/>
|
||||
<source>PC Electric GmbH</source>
|
||||
<extracomment>The name of the vendor ({b365937b-f1d6-46bf-9ff1-e787373b8aa6})</extracomment>
|
||||
<translation>PC Electric GmbH</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="88"/>
|
||||
<source>PCE EV11.X</source>
|
||||
<extracomment>The name of the ThingClass ({88d96940-a940-4a07-8176-5e6aba7ca832})</extracomment>
|
||||
<translation>PCE EV11.X</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="91"/>
|
||||
<source>Plugged in</source>
|
||||
<extracomment>The name of the StateType ({50164bbd-9802-4cf6-82de-626b74293a1b}) of ThingClass ev11</extracomment>
|
||||
<translation>Angesteckt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="94"/>
|
||||
<source>Set maximum charging current</source>
|
||||
<extracomment>The name of the ActionType ({b5bbf23c-06db-463b-bb5c-3aea38e18818}) of ThingClass ev11</extracomment>
|
||||
<translation>Setze maximalen Ladestrom</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>IntegrationPluginPcElectric</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginpcelectric.cpp" line="53"/>
|
||||
<source>The network device discovery is not available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PcElectric</name>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="43"/>
|
||||
<source>Active phases</source>
|
||||
<extracomment>The name of the StateType ({bca88c23-e940-40c1-afca-eb511fd17aab}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="46"/>
|
||||
<source>Charging</source>
|
||||
<extracomment>The name of the StateType ({b7972cd7-471a-46bd-ab99-f49997f12309}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="49"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="52"/>
|
||||
<source>Charging enabled</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, ActionType: power, ID: {c12a7a27-fa56-450c-a1ec-717c868554f2})
|
||||
----------
|
||||
The name of the StateType ({c12a7a27-fa56-450c-a1ec-717c868554f2}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="55"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({ca8d680c-c2f8-456a-a246-9c6cd64e25a7}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="58"/>
|
||||
<source>Enable or disable charging</source>
|
||||
<extracomment>The name of the ActionType ({c12a7a27-fa56-450c-a1ec-717c868554f2}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="61"/>
|
||||
<source>Firmware version</source>
|
||||
<extracomment>The name of the StateType ({142b4276-e2e9-4149-adc4-89d9d3e31117}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="64"/>
|
||||
<source>Hardware version</source>
|
||||
<extracomment>The name of the StateType ({b6e65baf-6dcd-4db1-a3dc-962a4c33d157}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="67"/>
|
||||
<source>LED brightness</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, Type: settings, ID: {3a1329a2-84cc-47b9-a6c2-e96fdfd0c454})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="70"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, Type: thing, ID: {0a3f8d12-9d33-4ae2-b763-9568f32e8da1})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="73"/>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="76"/>
|
||||
<source>Maximum charging current</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, ActionType: maxChargingCurrent, ID: {b5bbf23c-06db-463b-bb5c-3aea38e18818})
|
||||
----------
|
||||
The name of the StateType ({b5bbf23c-06db-463b-bb5c-3aea38e18818}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="79"/>
|
||||
<source>Maximum offline charging current</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: ev11, Type: settings, ID: {93654273-c4d3-4389-a81e-c0f065d9cd92})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="82"/>
|
||||
<source>PC Electric</source>
|
||||
<extracomment>The name of the plugin PcElectric ({aa7ff833-a8e0-45cc-a1ef-65f05871f272})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="85"/>
|
||||
<source>PC Electric GmbH</source>
|
||||
<extracomment>The name of the vendor ({b365937b-f1d6-46bf-9ff1-e787373b8aa6})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="88"/>
|
||||
<source>PCE EV11.X</source>
|
||||
<extracomment>The name of the ThingClass ({88d96940-a940-4a07-8176-5e6aba7ca832})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="91"/>
|
||||
<source>Plugged in</source>
|
||||
<extracomment>The name of the StateType ({50164bbd-9802-4cf6-82de-626b74293a1b}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/pcelectric/plugininfo.h" line="94"/>
|
||||
<source>Set maximum charging current</source>
|
||||
<extracomment>The name of the ActionType ({b5bbf23c-06db-463b-bb5c-3aea38e18818}) of ThingClass ev11</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
Reference in New Issue
Block a user