// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync #include "evadapter.h" #include "../energyarbitrator.h" #include "../../evcharger.h" #include "../../types/chargingaction.h" #include "plugininfo.h" EvAdapter::EvAdapter(EvCharger *evCharger, EnergyArbitrator *parent) : QObject(parent) , m_charger(evCharger) , m_parent(parent) { } LoadDescriptor EvAdapter::descriptor() const { LoadDescriptor d; d.id = m_charger->thing()->id().toString(); d.label = m_charger->name(); d.adapter = QStringLiteral("evcharger"); d.priority = 100; d.declared.minA = m_charger->maxChargingCurrentMinValue(); d.declared.maxA = m_charger->maxChargingCurrentMaxValue(); d.declared.phases = static_cast(m_charger->phaseCount()); d.limits.chargingEnabledLockS = static_cast(m_charger->chargingEnabledLockDuration()); d.limits.currentChangeLockS = static_cast(m_charger->chargingCurrentLockDuration()); d.supportedKinds = { LoadAction::Setpoint }; return d; } LoadTelemetry EvAdapter::telemetry() const { LoadTelemetry t; t.currentPowerW = m_charger->currentPower(); t.available = m_charger->available(); t.lastActionAt = m_lastActionAt; return t; } LoadContext EvAdapter::toLoadContext(const QDateTime &now) const { Q_UNUSED(now) // L'EV n'a pas de verrou de palier (pas de waterfall ECS) — now inutilisé ici. LoadContext ctx; ctx.id = m_charger->thing()->id().toString(); ctx.adapter = QStringLiteral("evcharger"); ctx.label = m_charger->name(); ctx.declared = descriptor().declared; ctx.limits = descriptor().limits; ctx.telemetry.currentPowerW = m_charger->currentPower(); ctx.telemetry.pluggedIn = m_charger->pluggedIn(); ctx.telemetry.charging = m_charger->charging(); return ctx; } LoadAction EvAdapter::applyAction(const LoadAction &action, const QDateTime &now) { if (action.kind != LoadAction::Setpoint) return action; if (action.reason.isEmpty()) { qCWarning(dcNymeaEnergy()) << "[EvAdapter]" << m_charger->name() << "— LoadAction sans reason rejetée."; return action; } const uint minA = m_charger->maxChargingCurrentMinValue(); const uint maxA = m_charger->maxChargingCurrentMaxValue(); const uint clampedA = static_cast( qBound(static_cast(minA), action.currentA, static_cast(maxA))); const uint phases = (m_charger->canSetPhaseCount() && action.phaseCount > 0) ? qBound(1u, action.phaseCount, m_charger->phaseCount()) : m_charger->phaseCount(); const auto issuer = (action.funding == LoadAction::Surplus) ? ChargingAction::ChargingActionIssuerSurplusCharging : ChargingAction::ChargingActionIssuerTimeRequirement; ChargingAction ca(action.chargingEnabled, clampedA, phases, issuer, false); m_parent->doExecuteChargingAction(m_charger, ca, now); // now = temps de cycle (injectable) m_lastActionAt = now; LoadAction applied = action; applied.currentA = clampedA; applied.phaseCount = phases; return applied; }