// 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 "sgreadyadapter.h" #include "types/action.h" #include "integrations/thingmanager.h" #include "integrations/thingactioninfo.h" #include #include Q_DECLARE_LOGGING_CATEGORY(dcNymeaEnergy) SgReadyAdapter::SgReadyAdapter(ThingManager *thingManager, const ThingId &relay1ThingId, const ThingId &relay2ThingId, double normalPowerW, QObject *parent) : ILoadAdapter(parent), m_thingManager(thingManager), m_normalPowerW(normalPowerW) { m_relay1 = thingManager->findConfiguredThing(relay1ThingId); m_relay2 = thingManager->findConfiguredThing(relay2ThingId); if (!m_relay1) qCWarning(dcNymeaEnergy()) << "SgReadyAdapter: relay1 not found:" << relay1ThingId; if (!m_relay2) qCWarning(dcNymeaEnergy()) << "SgReadyAdapter: relay2 not found:" << relay2ThingId; } QString SgReadyAdapter::adapterId() const { return QStringLiteral("sgready"); } LoadRole SgReadyAdapter::role() const { return LoadRole::HeatPump; } ThingId SgReadyAdapter::thingId() const { return m_relay1 ? m_relay1->id() : ThingId(); } void SgReadyAdapter::applyPower(double targetPowerW) { SgMode mode; if (targetPowerW <= 0) mode = SgMode::SG1_Blocked; else if (targetPowerW < m_normalPowerW) mode = SgMode::SG2_Normal; else if (qFuzzyCompare(targetPowerW, m_normalPowerW)) mode = SgMode::SG3_Surplus; else mode = SgMode::SG4_Boost; applyMode(mode); } void SgReadyAdapter::testConnection() { if (!m_relay1 || !m_relay2) { emit testResult(false, QStringLiteral("One or both relay Things not found")); return; } // SG2 → wait 1s → SG3 → wait 1s → SG2 → emit success applyMode(SgMode::SG2_Normal); QTimer::singleShot(1000, this, [this]() { applyMode(SgMode::SG3_Surplus); QTimer::singleShot(1000, this, [this]() { applyMode(SgMode::SG2_Normal); emit testResult(true, QStringLiteral("SG-Ready connection test OK")); }); }); } double SgReadyAdapter::currentPowerW() const { switch (m_currentMode) { case SgMode::SG1_Blocked: return 0; case SgMode::SG2_Normal: return m_normalPowerW; case SgMode::SG3_Surplus: return m_normalPowerW * 1.2; case SgMode::SG4_Boost: return m_normalPowerW * 1.5; } return 0; } bool SgReadyAdapter::isReachable() const { auto checkConnected = [](Thing *thing) -> bool { if (!thing) return false; foreach (const StateType &st, thing->thingClass().stateTypes()) { if (st.name() == QLatin1String("connected")) return thing->stateValue(st.id()).toBool(); } return true; }; return checkConnected(m_relay1) && checkConnected(m_relay2); } void SgReadyAdapter::applyMode(SgMode mode) { // SG-Ready contact matrix: // SG1: r1=OFF r2=OFF (blocked) // SG2: r1=OFF r2=ON (normal) // SG3: r1=ON r2=OFF (surplus) // SG4: r1=ON r2=ON (boost) bool r1on = (mode == SgMode::SG3_Surplus || mode == SgMode::SG4_Boost); bool r2on = (mode == SgMode::SG2_Normal || mode == SgMode::SG4_Boost); setRelayPower(m_relay1, r1on); setRelayPower(m_relay2, r2on); m_currentMode = mode; qCDebug(dcNymeaEnergy()) << "SgReadyAdapter: mode" << static_cast(mode) << "r1=" << r1on << "r2=" << r2on; emit powerApplied(currentPowerW(), true); } void SgReadyAdapter::setRelayPower(Thing *thing, bool on) { if (!thing) return; ActionType powerActionType; foreach (const ActionType &at, thing->thingClass().actionTypes()) { if (at.name() == QLatin1String("power")) { powerActionType = at; break; } } if (powerActionType.id().isNull()) { qCWarning(dcNymeaEnergy()) << "SgReadyAdapter: no 'power' action on" << thing->name(); return; } ParamTypeId powerParamTypeId; foreach (const ParamType &pt, powerActionType.paramTypes()) { if (pt.name() == QLatin1String("power")) { powerParamTypeId = pt.id(); break; } } Action action(powerActionType.id(), thing->id(), Action::TriggeredByRule); action.setParams(ParamList() << Param(powerParamTypeId, on)); m_thingManager->executeAction(action); }