nymea-plugins-genericthings/genericheatingcooling/integrationplugingenerichea...

256 lines
14 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationplugingenericheatingcooling.h"
#include "plugininfo.h"
#include <QDebug>
#include <QtMath>
IntegrationPluginGenericHeatingCooling::IntegrationPluginGenericHeatingCooling()
{
}
void IntegrationPluginGenericHeatingCooling::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
if (thing->thingClassId() == thermostatThingClassId) {
thermostatCheckPowerOutputState(thing); // check the initial values
connect(thing, &Thing::settingChanged, thing, [this, thing] (const ParamTypeId &paramTypeId, const QVariant &value) {
Q_UNUSED(value)
if (paramTypeId == thermostatSettingsTemperatureDifferenceParamTypeId) {
thermostatCheckPowerOutputState(thing);
}
});
} else if (thing->thingClassId() == sgReadyThingClassId) {
bool relay1 = thing->stateValue(sgReadyRelay1StateTypeId).toBool();
bool relay2 = thing->stateValue(sgReadyRelay2StateTypeId).toBool();
QString operatingModeString = sgReadyOperatingMode(relay1, relay2);
thing->setStateValue(sgReadySgReadyModeStateTypeId, operatingModeString);
thing->setStateValue(sgReadyOperatingModeDescriptionStateTypeId, sgReadyOperatingModeDescription(operatingModeString));
}
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginGenericHeatingCooling::executeAction(ThingActionInfo *info)
{
Thing *thing = info->thing();
Action action = info->action();
if (thing->thingClassId() == heatingThingClassId) {
if (action.actionTypeId() == heatingPowerActionTypeId) {
thing->setStateValue(heatingPowerStateTypeId, action.param(heatingPowerActionPowerParamTypeId).value());
return info->finish(Thing::ThingErrorNoError);
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else if (thing->thingClassId() == ventilationThingClassId) {
if (action.actionTypeId() == ventilationPowerActionTypeId) {
thing->setStateValue(ventilationPowerStateTypeId, action.param(ventilationPowerActionPowerParamTypeId).value());
info->finish(Thing::ThingErrorNoError);
return;
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else if (thing->thingClassId() == ventilationFlowThingClassId) {
if (action.actionTypeId() == ventilationFlowPowerActionTypeId) {
thing->setStateValue(ventilationFlowPowerStateTypeId, action.param(ventilationFlowPowerActionPowerParamTypeId).value());
info->finish(Thing::ThingErrorNoError);
return;
} else if (action.actionTypeId() == ventilationFlowFlowRateActionTypeId) {
thing->setStateValue(ventilationFlowFlowRateStateTypeId, action.param(ventilationFlowFlowRateActionFlowRateParamTypeId).value());
info->finish(Thing::ThingErrorNoError);
return;
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else if (thing->thingClassId() == thermostatThingClassId) {
if (action.actionTypeId() == thermostatTemperatureActionTypeId) {
thing->setStateValue(thermostatTemperatureStateTypeId, action.param(thermostatTemperatureActionTemperatureParamTypeId).value());
} else if (action.actionTypeId() == thermostatTargetTemperatureActionTypeId) {
double minSetting = thing->setting(thermostatSettingsMinTargetTemperatureParamTypeId).toDouble();
double maxSetting = thing->setting(thermostatSettingsMaxTargetTemperatureParamTypeId).toDouble();
double newTemp = action.param(thermostatTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble();
newTemp = qMax(newTemp, minSetting);
newTemp = qMin(newTemp, maxSetting);
thing->setStateValue(thermostatTargetTemperatureStateTypeId, newTemp);
} else if (action.actionTypeId() == thermostatHeatingOnActionTypeId) {
thing->setStateValue(thermostatHeatingOnStateTypeId, action.param(thermostatHeatingOnActionHeatingOnParamTypeId).value());
} else if (action.actionTypeId() == thermostatCoolingOnActionTypeId) {
thing->setStateValue(thermostatCoolingOnStateTypeId, action.param(thermostatCoolingOnActionCoolingOnParamTypeId).value());
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
thermostatCheckPowerOutputState(thing);
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == heatingThingClassId) {
if (action.actionTypeId() == heatingPowerActionTypeId) {
thing->setStateValue(heatingPowerStateTypeId, action.paramValue(heatingPowerActionPowerParamTypeId).toBool());
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == coolingThingClassId) {
if (action.actionTypeId() == coolingPowerActionTypeId) {
thing->setStateValue(coolingPowerStateTypeId, action.paramValue(coolingPowerActionPowerParamTypeId).toBool());
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == sgReadyThingClassId) {
if (action.actionTypeId() == sgReadyRelay1ActionTypeId) {
thing->setStateValue(sgReadyRelay1StateTypeId, action.param(sgReadyRelay1ActionRelay1ParamTypeId).value());
QString operatingMode = sgReadyOperatingMode(thing->stateValue(sgReadyRelay1StateTypeId).toBool(), thing->stateValue(sgReadyRelay2StateTypeId).toBool());
thing->setStateValue(sgReadySgReadyModeStateTypeId, operatingMode);
thing->setStateValue(sgReadyOperatingModeDescriptionStateTypeId, sgReadyOperatingModeDescription(operatingMode));
info->finish(Thing::ThingErrorNoError);
return;
}
if (action.actionTypeId() == sgReadyRelay2ActionTypeId) {
thing->setStateValue(sgReadyRelay2StateTypeId, action.param(sgReadyRelay2ActionRelay2ParamTypeId).value());
QString operatingMode = sgReadyOperatingMode(thing->stateValue(sgReadyRelay1StateTypeId).toBool(), thing->stateValue(sgReadyRelay2StateTypeId).toBool());
thing->setStateValue(sgReadySgReadyModeStateTypeId, operatingMode);
thing->setStateValue(sgReadyOperatingModeDescriptionStateTypeId, sgReadyOperatingModeDescription(operatingMode));
info->finish(Thing::ThingErrorNoError);
return;
}
if (action.actionTypeId() == sgReadySgReadyModeActionTypeId) {
QString operatingMode = action.paramValue(sgReadySgReadyModeActionSgReadyModeParamTypeId).toString();
thing->setStateValue(sgReadySgReadyModeStateTypeId, operatingMode);
thing->setStateValue(sgReadyOperatingModeDescriptionStateTypeId, sgReadyOperatingModeDescription(operatingMode));
if (operatingMode == "Off") {
thing->setStateValue(sgReadyRelay1StateTypeId, true);
thing->setStateValue(sgReadyRelay2StateTypeId, false);
} else if (operatingMode == "Low") {
thing->setStateValue(sgReadyRelay1StateTypeId, false);
thing->setStateValue(sgReadyRelay2StateTypeId, false);
} else if (operatingMode == "Standard") {
thing->setStateValue(sgReadyRelay1StateTypeId, false);
thing->setStateValue(sgReadyRelay2StateTypeId, true);
} else if (operatingMode == "High") {
thing->setStateValue(sgReadyRelay1StateTypeId, true);
thing->setStateValue(sgReadyRelay2StateTypeId, true);
}
info->finish(Thing::ThingErrorNoError);
return;
}
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
} else if (thing->thingClassId() == simpleHeatPumpThingClassId) {
if (action.actionTypeId() == simpleHeatPumpPowerActionTypeId) {
thing->setStateValue(simpleHeatPumpPowerStateTypeId, action.paramValue(simpleHeatPumpPowerActionPowerParamTypeId).toBool());
info->finish(Thing::ThingErrorNoError);
}
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginGenericHeatingCooling::thermostatCheckPowerOutputState(Thing *thing)
{
bool autoControl = thing->setting(thermostatSettingsAutoControlParamTypeId).toBool();
if (!autoControl) {
return;
}
double targetTemperature = thing->stateValue(thermostatTargetTemperatureStateTypeId).toDouble();
double actualTemperature = thing->stateValue(thermostatTemperatureStateTypeId).toDouble();
double temperatureDifference = thing->setting(thermostatSettingsTemperatureDifferenceParamTypeId).toDouble();
if (actualTemperature <= (targetTemperature-temperatureDifference)) {
thing->setStateValue(thermostatHeatingOnStateTypeId, true);
} else if (actualTemperature >= targetTemperature) {
thing->setStateValue(thermostatHeatingOnStateTypeId, false);
}
if (actualTemperature >= (targetTemperature+temperatureDifference)) {
thing->setStateValue(thermostatCoolingOnStateTypeId, true);
} else if (actualTemperature <= targetTemperature) {
thing->setStateValue(thermostatCoolingOnStateTypeId, false);
}
}
QString IntegrationPluginGenericHeatingCooling::sgReadyOperatingModeDescription(const QString &operatingModeString)
{
if (operatingModeString == "Off") {
return "Stop heating.";
} else if (operatingModeString == "Low") {
return "Normal mode, with partial heat storage filling.";
} else if (operatingModeString == "Standard") {
return "Increased room and heat storage temperature.";
} else if (operatingModeString == "High") {
return "Start heating.";
}
return QString("Unknown operating mode %1").arg(operatingModeString);
}
QString IntegrationPluginGenericHeatingCooling::sgReadyOperatingMode(bool relay1, bool relay2)
{
if (relay1 && !relay2) {
/*
* Operating state 1 (Relay state: 1: 0):
* This operating state is downward compatible with the often fixed times
* activated EVU lock and includes a maximum of 2 hours of "hard" lock time.
*/
return "Off";
} else if (!relay1 && !relay2) {
/*
* Operating state 2 (Relay state: 0: 0):
* In this circuit, the heat pump runs in energy-efficient normal mode
* with partial heat storage filling for the maximum two-hour EVU lock.
*/
return "Low";
} else if (!relay1 && relay2) {
/*
* Operating state 3 (Relay state: 0: 1):
* In this operating state, the heat pump within the controller runs in amplified mode
* Operation for space heating and hot water preparation. It's not one
* definitive start-up command, but a switch-on recommendation according to the current increase.
*/
return "Standard";
} else {
/*
* Operating state 4 (Relay state 1: 1):
* This is a definitive start-up command, insofar as this is possible within the framework of the rule settings.
* For this operating state, different control models must be set on the controller for different tariff and usage models:
* Variant 1: The heat pump (compressor) is actively switched on.
* Variant 2: The heat pump (compressor and electrical auxiliary heating) is actively switched on, optional: higher temperature in the heat storage
*/
return "High";
}
}