fix(v2c): correct logging category dcV2C + debian packaging + modbus.pri path

Build fixes found during first local compilation (qmake6 + g++ -Werror):

1. Logging category: nymea-plugininfocompiler generates 'dcV2C' (uppercase C)
   from the JSON vendor name "V2C". All three source files used 'dcV2c'
   (lowercase c) — corrected to 'dcV2C' in integrationpluginv2c.cpp,
   trydanmodbustcpmaster.cpp, v2ctcpdiscovery.cpp.

2. modbus.pri: hardcoded include(/usr/include/nymea-modbus/modbus-tool.pri)
   fails when libnymea-modbus-dev is not system-installed. Changed to a
   conditional: checks $$[QT_INSTALL_PREFIX] first, then NYMEA_MODBUS_PATH
   override, with a graceful fallback (warning + manual C++17 CONFIG flag).
   The v2c plugin has MODBUS_CONNECTIONS empty so the tool loop is a no-op
   either way; the fallback is safe.

3. debian/: add powersync-plugin-v2c package entry (control, .install,
   changelog entry 1.15.0+etm4).

Build verified: libnymea_integrationpluginv2c.so links against libnymea.so.1,
libnymea-modbus.so.1, Qt6Network/SerialBus/Core, 0 compiler warnings
(with -Werror -Wall -Wextra -std=c++17).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Schurig 2026-06-12 15:20:11 +02:00
parent f0be2d00a2
commit 2ac00e1330
7 changed files with 51 additions and 14 deletions

13
debian/changelog vendored
View File

@ -1,3 +1,16 @@
etm-powersync-plugins-modbus (1.15.0+etm4) trixie; urgency=medium
* Ajout plugin V2C Trydan (borne de charge EV, Modbus TCP) : interface
evcharger complète — charge enable/disable (PauseState+Lock miroir),
consigne courant (Intensity), telemetrie (ChargePower, HousePower,
PowerFV, ChargeState IEC 61851). Gestion conflit Dynamic/PauseDynamic
sans ecriture du registre Dynamic (evite la coupure de telemetrie,
cf. evcc issue #28047). Decouverte LAN via HTTP /RealTimeData.
Transport abstrait (TCP Etape 1, RTU Etape 2 prevu).
Statut : Partiel — non teste sur borne reelle.
-- ETM-Schurig SARL <contact@etm-schurig.eu> Thu, 12 Jun 2026 12:00:00 +0200
etm-powersync-plugins-modbus (1.15.0+etm3) trixie; urgency=medium
* Ajout plugin ABB Terra AC (borne de charge, Modbus TCP/RTU) : vendoring

12
debian/control vendored
View File

@ -41,3 +41,15 @@ Depends: ${shlibs:Depends},
Description: ABB Terra AC charging station (Modbus TCP/RTU) - vendored from upstream
nymea integration plugin for ABB Terra AC wallbox over Modbus TCP or RTU.
Vendored from nymea-plugins-modbus experimental-silo, pending upstream release.
Package: powersync-plugin-v2c
Architecture: any
Section: libs
Depends: ${shlibs:Depends},
${misc:Depends},
Description: PowerSync integration plugin for V2C Trydan EV charger (Modbus TCP)
nymea integration plugin for the V2C Trydan / Trydan Pro EV charger over
Modbus TCP (port 502), for use by the ETM PowerSync home energy management
system. Implements the evcharger interface: charge enable/disable, current
setpoint, plugged-in / charging state, house and solar power telemetry.
Status: Partial — not yet validated on real hardware.

1
debian/powersync-plugin-v2c.install vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/*/nymea/plugins/libnymea_integrationpluginv2c.so

View File

@ -1,11 +1,22 @@
QT += network serialport serialbus
# libnymea-modbus depuis le paquet systeme (libnymea-modbus-dev), plus les sources locales
# libnymea-modbus depuis le paquet systeme (libnymea-modbus-dev) ou via NYMEA_MODBUS_PATH.
CONFIG += link_pkgconfig
PKGCONFIG += nymea-modbus
OTHER_FILES += $${MODBUS_CONNECTIONS}
# Outil de generation des connexions modbus (sdm*-registers.json -> .cpp/.h),
# fourni par libnymea-modbus-dev
include(/usr/include/nymea-modbus/modbus-tool.pri)
# Locate modbus-tool.pri: system install first, then NYMEA_MODBUS_PATH override
# (for builds against a local libnymea-modbus source tree).
# When MODBUS_CONNECTIONS is empty (e.g. v2c plugin), the tool's for-loop is a
# no-op; we still need the file for the C++ standard CONFIG lines below.
MODBUS_TOOL_PRI = $$[QT_INSTALL_PREFIX]/include/nymea-modbus/modbus-tool.pri
!isEmpty(NYMEA_MODBUS_PATH): MODBUS_TOOL_PRI = $$NYMEA_MODBUS_PATH/modbus-tool.pri
exists($$MODBUS_TOOL_PRI) {
include($$MODBUS_TOOL_PRI)
} else {
warning("modbus-tool.pri not found at $$MODBUS_TOOL_PRI — MODBUS_CONNECTIONS code-gen disabled")
greaterThan(QT_MAJOR_VERSION, 5): CONFIG *= c++17
lessThan(QT_MAJOR_VERSION, 6): CONFIG *= c++11
}

View File

@ -279,7 +279,7 @@ void IntegrationPluginV2c::updateThingStates(Thing *thing, TrydanModbusMaster *m
thing->setStateValue(trydanOptimizerSuspendedStateTypeId, master->pauseDynamic() == 1);
if (conflict) {
qCDebug(dcV2c()) << thing->name()
qCDebug(dcV2C()) << thing->name()
<< ": V2C internal optimizer is active (Dynamic==1). "
"HEMS will suppress it via PauseDynamic if suspendInternalOptimizer==true.";
}
@ -302,7 +302,7 @@ void IntegrationPluginV2c::handleDynamicConflict(Thing *thing, TrydanModbusMaste
const bool suspendAllowed = thing->setting(trydanSettingsSuspendInternalOptimizerParamTypeId).toBool();
if (!suspendAllowed) {
qCWarning(dcV2c()) << thing->name()
qCWarning(dcV2C()) << thing->name()
<< ": V2C internal optimizer active but suspendInternalOptimizer==false. "
"HEMS and V2C PID may fight — expect oscillations.";
return;
@ -314,7 +314,7 @@ void IntegrationPluginV2c::handleDynamicConflict(Thing *thing, TrydanModbusMaste
// Only write on transition to avoid unnecessary flash wear.
if (master->pauseDynamic() != desired) {
qCDebug(dcV2c()) << thing->name()
qCDebug(dcV2C()) << thing->name()
<< ": writing PauseDynamic =" << desired
<< (hemsInControl ? "(HEMS taking control)" : "(releasing to V2C PID)");
master->writePauseDynamic(static_cast<quint16>(desired));

View File

@ -200,7 +200,7 @@ void TrydanModbusTcpMaster::doNextRead(QList<ReadStep> steps,
static_cast<int>(m_slaveId));
if (!reply) {
qCWarning(dcV2c()) << "TrydanModbusTcpMaster: sendReadRequest returned null for reg" << Qt::hex << address;
qCWarning(dcV2C()) << "TrydanModbusTcpMaster: sendReadRequest returned null for reg" << Qt::hex << address;
onDone(false);
return;
}
@ -210,7 +210,7 @@ void TrydanModbusTcpMaster::doNextRead(QList<ReadStep> steps,
reply->deleteLater();
if (reply->error() != QModbusDevice::NoError) {
qCWarning(dcV2c()) << "TrydanModbusTcpMaster: read error for reg"
qCWarning(dcV2C()) << "TrydanModbusTcpMaster: read error for reg"
<< Qt::hex << steps.at(index).first
<< ":" << reply->errorString();
callback(false, 0, 0);
@ -233,7 +233,7 @@ void TrydanModbusTcpMaster::writeFC6(quint16 address, quint16 value,
QModbusReply *reply = m_modbusTcpMaster->sendWriteRequest(unit, static_cast<int>(m_slaveId));
if (!reply) {
qCWarning(dcV2c()) << "TrydanModbusTcpMaster: sendWriteRequest returned null for reg" << Qt::hex << address;
qCWarning(dcV2C()) << "TrydanModbusTcpMaster: sendWriteRequest returned null for reg" << Qt::hex << address;
callback(address, false);
return;
}
@ -261,7 +261,7 @@ void TrydanModbusTcpMaster::handleError()
{
m_errorCount++;
if (m_errorCount >= k_errorLimit) {
qCWarning(dcV2c()) << "TrydanModbusTcpMaster: error limit reached, marking unreachable";
qCWarning(dcV2C()) << "TrydanModbusTcpMaster: error limit reached, marking unreachable";
setReachable(false);
}
}

View File

@ -19,7 +19,7 @@ V2cTcpDiscovery::V2cTcpDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery,
void V2cTcpDiscovery::startDiscovery()
{
qCInfo(dcV2c()) << "Discovery: searching for V2C Trydan chargers on the network...";
qCInfo(dcV2C()) << "Discovery: searching for V2C Trydan chargers on the network...";
m_startTime = QDateTime::currentDateTime();
m_pendingProbes = 0;
m_scanFinished = false;
@ -84,7 +84,7 @@ void V2cTcpDiscovery::probeAddress(const QHostAddress &address)
info.setAddress(address);
result.networkDeviceInfo = info;
}
qCDebug(dcV2c()) << "Discovery: found V2C Trydan at" << address.toString();
qCDebug(dcV2C()) << "Discovery: found V2C Trydan at" << address.toString();
m_results.append(result);
}
}
@ -103,7 +103,7 @@ void V2cTcpDiscovery::finishDiscovery()
m_finished = true;
const qint64 ms = QDateTime::currentMSecsSinceEpoch() - m_startTime.toMSecsSinceEpoch();
qCInfo(dcV2c()) << "Discovery: finished in"
qCInfo(dcV2C()) << "Discovery: finished in"
<< QTime::fromMSecsSinceStartOfDay(static_cast<int>(ms)).toString("mm:ss.zzz")
<< "with" << m_results.count() << "V2C Trydan(s) found.";
emit discoveryFinished();