v2c: hw-validated improvements (Trydan fw2.4.6)
- Decode SlaveError (reg 0x0BC5) with full table (codes 0-10) into new slaveErrorMessage state; code 04 (WiFi reconnect) also surfaces in statusMessage for immediate SAV visibility. - Add L1/L2/L3 phase powers (regs 0x0BD9-0x0BDB, confirmed hw) as diagnostic states polled every 30 s. - Add RegChargeTime (0x0BC6) to enum for completeness (not yet exposed). - Collapse debian/changelog to single 1.15.0+etm1 entry (new plugin). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
076a0dcae9
commit
189d58d9ce
48
debian/changelog
vendored
48
debian/changelog
vendored
@ -1,36 +1,18 @@
|
||||
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
|
||||
depuis nymea-plugins-modbus experimental-silo (commit a652793), en
|
||||
attente de la release master/stable upstream. Nom paquet nymea-plugin-abbterra
|
||||
(nom upstream conserve pour transition sans reinstall).
|
||||
|
||||
-- ETM-Schurig SARL <contact@etm-schurig.eu> Sun, 01 Jun 2026 10:00:00 +0200
|
||||
|
||||
etm-powersync-plugins-modbus (1.15.0+etm2) trixie; urgency=medium
|
||||
|
||||
* Ajout plugin ABB B2x (B21/B23/B24, Modbus RTU) : tensions, courants,
|
||||
puissances par phase (int32 signe pour injection PV), frequence,
|
||||
energie import/export (uint64 x4 registres, 0.01 kWh).
|
||||
|
||||
-- ETM-Schurig SARL <contact@etm-schurig.eu> Sun, 01 Jun 2026 09:00:00 +0200
|
||||
|
||||
etm-powersync-plugins-modbus (1.15.0+etm1) trixie; urgency=medium
|
||||
|
||||
* Initial ETM packaging of the eastron plugin (Eastron SDM72/120/630, Modbus RTU).
|
||||
* Built against nymea 1.15.0 ; libnymea-modbus consommee via paquet systeme.
|
||||
* Ajout plugin V2C Trydan (borne de charge EV, Modbus TCP) : interface
|
||||
evcharger complete — charge enable/disable (PauseState+Lock miroir),
|
||||
consigne courant (Intensity), telemetrie (ChargePower, HousePower,
|
||||
PowerFV, ChargeState IEC 61851). Puissances par phase L1/L2/L3 (regs
|
||||
3033-3035, confirmes hw fw2.4.6). Decodage SlaveError (code 0-10) avec
|
||||
message d'erreur lisible ; code 04 (reconnexion WiFi) remonte en
|
||||
statusMessage. Gestion conflit Dynamic/PauseDynamic sans ecriture du
|
||||
registre Dynamic (evite la coupure de telemetrie, cf. evcc #28047).
|
||||
Decouverte LAN via HTTP /RealTimeData. Transport abstrait TCP (Etape 1) ;
|
||||
RTU (Etape 2) sans modification du plugin. Pas de NetworkDeviceMonitor :
|
||||
les pings ICMP saturent le WiFi ESP32 de la borne. Connectivite derivee
|
||||
des resultats Modbus (timeout 5 s, 0 retry, k_errorLimit=3).
|
||||
Teste sur borne reelle Trydan fw2.4.6 (192.168.1.127).
|
||||
|
||||
-- ETM-Schurig SARL <contact@etm-schurig.eu> Fri, 13 Jun 2026 10:00:00 +0200
|
||||
|
||||
-- ETM-Schurig SARL <contact@etm-schurig.eu> Sun, 31 May 2026 18:30:00 +0200
|
||||
|
||||
@ -239,6 +239,29 @@ void IntegrationPluginV2c::executeAction(ThingActionInfo *info)
|
||||
|
||||
// --- Private ---
|
||||
|
||||
/*!
|
||||
* SlaveError register (0x0BC5) firmware table, confirmed on Trydan fw2.4.6.
|
||||
* Returns empty string for code 0 (no error).
|
||||
* Code 4 also triggers a statusMessage update in updateThingStates().
|
||||
*/
|
||||
static QString decodeSlaveError(int code)
|
||||
{
|
||||
switch (code) {
|
||||
case 0: return QString();
|
||||
case 1: return QStringLiteral("Erreur communication Modbus");
|
||||
case 2: return QStringLiteral("Erreur lecture registre");
|
||||
case 3: return QStringLiteral("Erreur esclave");
|
||||
case 4: return QStringLiteral("Borne en reconnexion WiFi");
|
||||
case 5: return QStringLiteral("Attente communication");
|
||||
case 6: return QStringLiteral("Mauvaise IP");
|
||||
case 7: return QStringLiteral("Esclave introuvable");
|
||||
case 8: return QStringLiteral("Mauvais esclave");
|
||||
case 9: return QStringLiteral("Pas de réponse");
|
||||
case 10: return QStringLiteral("Pince non connectée");
|
||||
default: return QStringLiteral("Code erreur inconnu (%1)").arg(code);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginV2c::updateThingStates(Thing *thing, TrydanModbusMaster *master)
|
||||
{
|
||||
thing->setStateValue(trydanConnectedStateTypeId, master->reachable());
|
||||
@ -259,9 +282,21 @@ void IntegrationPluginV2c::updateThingStates(Thing *thing, TrydanModbusMaster *m
|
||||
thing->setStateValue(trydanCurrentPowerStateTypeId, static_cast<double>(master->chargePower()));
|
||||
thing->setStateValue(trydanHousePowerStateTypeId, static_cast<double>(master->housePower()));
|
||||
thing->setStateValue(trydanSolarPowerStateTypeId, static_cast<double>(master->powerFV()));
|
||||
thing->setStateValue(trydanSlaveErrorStateTypeId, static_cast<uint>(master->slaveError()));
|
||||
thing->setStateValue(trydanChargeEnergyStateTypeId, static_cast<double>(master->chargeEnergy()));
|
||||
thing->setStateValue(trydanIntensityStateTypeId, static_cast<double>(master->intensity()));
|
||||
thing->setStateValue(trydanPowerL1StateTypeId, static_cast<double>(master->powerL1()));
|
||||
thing->setStateValue(trydanPowerL2StateTypeId, static_cast<double>(master->powerL2()));
|
||||
thing->setStateValue(trydanPowerL3StateTypeId, static_cast<double>(master->powerL3()));
|
||||
|
||||
const int slaveErr = master->slaveError();
|
||||
thing->setStateValue(trydanSlaveErrorStateTypeId, static_cast<uint>(slaveErr));
|
||||
const QString errMsg = decodeSlaveError(slaveErr);
|
||||
thing->setStateValue(trydanSlaveErrorMessageStateTypeId, errMsg);
|
||||
// Code 4: borne drops WiFi mid-session and reconnects — surface it prominently.
|
||||
if (slaveErr == 4) {
|
||||
thing->setStateValue(trydanStatusMessageStateTypeId,
|
||||
QStringLiteral("Borne en reconnexion WiFi"));
|
||||
}
|
||||
|
||||
// Dynamic==1 means the V2C internal PV optimizer is configured and running.
|
||||
const bool conflict = (master->dynamicMode() == 1);
|
||||
|
||||
@ -184,6 +184,41 @@
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-7890-ab12-cd34ef567890",
|
||||
"name": "powerL1",
|
||||
"displayName": "Phase L1 power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "b2c3d4e5-f6a7-8901-bc23-de45fa678901",
|
||||
"name": "powerL2",
|
||||
"displayName": "Phase L2 power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "c3d4e5f6-a7b8-9012-cd34-ef56ab789012",
|
||||
"name": "powerL3",
|
||||
"displayName": "Phase L3 power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "d4e5f6a7-b8c9-0123-de45-fa67bc890123",
|
||||
"name": "slaveErrorMessage",
|
||||
"displayName": "Charger error message",
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"cached": false
|
||||
}
|
||||
],
|
||||
"actionTypes": []
|
||||
|
||||
@ -38,7 +38,8 @@ public:
|
||||
RegChargeState = 0x0BC2, ///< 0=A(disconnected) 1=B(connected) 2=C(charging)
|
||||
RegChargePower = 0x0BC3, ///< W
|
||||
RegChargeEnergy = 0x0BC4, ///< kWh session — diagnostic only, NOT sessionEnergy
|
||||
RegSlaveError = 0x0BC5, ///< firmware error code
|
||||
RegSlaveError = 0x0BC5, ///< firmware error code (0-10, cf. decodeSlaveError table)
|
||||
RegChargeTime = 0x0BC6, ///< s, session duration (confirmed hw fw2.4.6, not yet exposed)
|
||||
RegHousePower = 0x0BC8, ///< W, CT clamp (if installed)
|
||||
RegPowerFV = 0x0BC9, ///< W, PV production seen by charger (if configured)
|
||||
RegPauseState = 0x0BCA, ///< 0=active 1=paused
|
||||
@ -48,6 +49,9 @@ public:
|
||||
RegMinIntensity = 0x0BD1, ///< A, lower bound
|
||||
RegMaxIntensity = 0x0BD2, ///< A, upper bound (firmware-version–dependent, verify on hardware)
|
||||
RegPauseDynamic = 0x0BD3, ///< 0=optimizer runs 1=optimizer suspended by HEMS
|
||||
RegPower_L1 = 0x0BD9, ///< W, phase L1 power (confirmed hw fw2.4.6)
|
||||
RegPower_L2 = 0x0BDA, ///< W, phase L2 power
|
||||
RegPower_L3 = 0x0BDB, ///< W, phase L3 power
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -93,6 +97,12 @@ public:
|
||||
int maxIntensity() const { return m_maxIntensity; }
|
||||
/*! \brief PauseDynamic: 0=optimizer runs, 1=HEMS has suspended it */
|
||||
int pauseDynamic() const { return m_pauseDynamic; }
|
||||
/*! \brief Phase L1 power in watts (diagnostic, 0 if not available) */
|
||||
float powerL1() const { return m_powerL1; }
|
||||
/*! \brief Phase L2 power in watts (diagnostic, 0 if not available) */
|
||||
float powerL2() const { return m_powerL2; }
|
||||
/*! \brief Phase L3 power in watts (diagnostic, 0 if not available) */
|
||||
float powerL3() const { return m_powerL3; }
|
||||
|
||||
bool reachable() const { return m_reachable; }
|
||||
|
||||
@ -204,6 +214,9 @@ protected:
|
||||
int m_minIntensity = 6;
|
||||
int m_maxIntensity = 32;
|
||||
int m_pauseDynamic = 0;
|
||||
float m_powerL1 = 0.0f;
|
||||
float m_powerL2 = 0.0f;
|
||||
float m_powerL3 = 0.0f;
|
||||
|
||||
bool m_reachable = false;
|
||||
};
|
||||
|
||||
@ -175,6 +175,15 @@ QList<TrydanModbusTcpMaster::ReadStep> TrydanModbusTcpMaster::buildPollSteps()
|
||||
{ RegPauseDynamic, [this](bool ok, quint16 h, quint16 l) {
|
||||
if (ok) m_pauseDynamic = decodeIntFromFloat32(h, l);
|
||||
}},
|
||||
{ RegPower_L1, [this](bool ok, quint16 h, quint16 l) {
|
||||
if (ok) m_powerL1 = decodeFloat32BB(h, l);
|
||||
}},
|
||||
{ RegPower_L2, [this](bool ok, quint16 h, quint16 l) {
|
||||
if (ok) m_powerL2 = decodeFloat32BB(h, l);
|
||||
}},
|
||||
{ RegPower_L3, [this](bool ok, quint16 h, quint16 l) {
|
||||
if (ok) m_powerL3 = decodeFloat32BB(h, l);
|
||||
}},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user