// SPDX-License-Identifier: GPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of nymea-energy-plugin-nymea. * * nymea-energy-plugin-nymea.s free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nymea-energy-plugin-nymea.s distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nymea-energy-plugin-nymea. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "evchargeradapter.h" #include "types/action.h" #include "integrations/thingmanager.h" #include "integrations/thingactioninfo.h" #include #include Q_DECLARE_LOGGING_CATEGORY(dcNymeaEnergy) EvChargerAdapter::EvChargerAdapter(ThingManager *thingManager, const ThingId &thingId, int phases, int minA, int maxA, QObject *parent) : ILoadAdapter(parent), m_thingManager(thingManager), m_phases(phases), m_minA(minA), m_maxA(maxA) { m_thing = thingManager->findConfiguredThing(thingId); if (!m_thing) qCWarning(dcNymeaEnergy()) << "EvChargerAdapter: thing not found:" << thingId; } QString EvChargerAdapter::adapterId() const { return QStringLiteral("evcharger"); } LoadRole EvChargerAdapter::role() const { return LoadRole::EVCharger; } ThingId EvChargerAdapter::thingId() const { return m_thing ? m_thing->id() : ThingId(); } void EvChargerAdapter::applyPower(double targetPowerW) { if (!m_thing) { qCWarning(dcNymeaEnergy()) << "EvChargerAdapter::applyPower: no thing"; emit powerApplied(0, false); return; } if (targetPowerW <= 0) { // Disable charging ActionType powerActionType; foreach (const ActionType &at, m_thing->thingClass().actionTypes()) { if (at.name() == QLatin1String("power")) { powerActionType = at; break; } } if (powerActionType.id().isNull()) { qCWarning(dcNymeaEnergy()) << "EvChargerAdapter: no 'power' action on" << m_thing->name(); emit powerApplied(0, false); return; } ParamTypeId powerParamId; foreach (const ParamType &pt, powerActionType.paramTypes()) { if (pt.name() == QLatin1String("power")) { powerParamId = pt.id(); break; } } Action action(powerActionType.id(), m_thing->id(), Action::TriggeredByRule); action.setParams(ParamList() << Param(powerParamId, false)); ThingActionInfo *info = m_thingManager->executeAction(action); connect(info, &ThingActionInfo::finished, this, [this, info]() { emit powerApplied(0, info->status() == Thing::ThingErrorNoError); }); return; } // Convert W → A, clamped to [minA, maxA] double amps = targetPowerW / (m_phases * 230.0); int clampedA = static_cast(qBound(static_cast(m_minA), amps, static_cast(m_maxA))); // Find "maxChargingCurrent" action type (evcharger interface) ActionType currentActionType; foreach (const ActionType &at, m_thing->thingClass().actionTypes()) { if (at.name() == QLatin1String("maxChargingCurrent")) { currentActionType = at; break; } } if (currentActionType.id().isNull()) { qCWarning(dcNymeaEnergy()) << "EvChargerAdapter: no 'maxChargingCurrent' action on" << m_thing->name(); emit powerApplied(0, false); return; } ParamTypeId currentParamId; foreach (const ParamType &pt, currentActionType.paramTypes()) { if (pt.name() == QLatin1String("maxChargingCurrent")) { currentParamId = pt.id(); break; } } Action action(currentActionType.id(), m_thing->id(), Action::TriggeredByRule); action.setParams(ParamList() << Param(currentParamId, clampedA)); ThingActionInfo *info = m_thingManager->executeAction(action); double appliedW = clampedA * m_phases * 230.0; connect(info, &ThingActionInfo::finished, this, [this, appliedW, info]() { emit powerApplied(appliedW, info->status() == Thing::ThingErrorNoError); }); } void EvChargerAdapter::testConnection() { if (!m_thing) { emit testResult(false, QStringLiteral("Thing not found")); return; } // Check "connected" state foreach (const StateType &st, m_thing->thingClass().stateTypes()) { if (st.name() == QLatin1String("connected")) { bool connected = m_thing->stateValue(st.id()).toBool(); emit testResult(connected, connected ? QStringLiteral("EV charger connected") : QStringLiteral("EV charger not connected")); return; } } // No "connected" state — assume reachable emit testResult(true, QStringLiteral("EV charger reachable")); } double EvChargerAdapter::currentPowerW() const { if (!m_thing) return 0; foreach (const StateType &st, m_thing->thingClass().stateTypes()) { if (st.name() == QLatin1String("currentPower")) return m_thing->stateValue(st.id()).toDouble(); } return 0; } bool EvChargerAdapter::isReachable() const { if (!m_thing) return false; foreach (const StateType &st, m_thing->thingClass().stateTypes()) { if (st.name() == QLatin1String("connected")) return m_thing->stateValue(st.id()).toBool(); } return true; }