mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +05:00
feat: added more hysteresis settings
This commit is contained in:
@@ -439,6 +439,28 @@ public:
|
||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SWITCH), F("heating_turbo")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSwitchHeatingHysteresis(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_hysteresis"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||
doc[FPSTR(HA_NAME)] = F("Use heating hysteresis");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
||||
doc[FPSTR(HA_STATE_ON)] = true;
|
||||
doc[FPSTR(HA_STATE_OFF)] = false;
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.hysteresis.enabled }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||
doc[FPSTR(HA_PAYLOAD_ON)] = F("{\"heating\": {\"hysteresis\" : {\"enabled\" : true}}}");
|
||||
doc[FPSTR(HA_PAYLOAD_OFF)] = F("{\"heating\": {\"hysteresis\" : {\"enabled\" : false}}}");
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
||||
doc.shrinkToFit();
|
||||
|
||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SWITCH), F("heating_hysteresis")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishInputHeatingHysteresis(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||
@@ -458,9 +480,9 @@ public:
|
||||
doc[FPSTR(HA_NAME)] = F("Heating hysteresis");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.hysteresis|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.hysteresis.value|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"hysteresis\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"hysteresis\" : {\"value\" : {{ value }}}}}");
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 15;
|
||||
doc[FPSTR(HA_STEP)] = 0.01f;
|
||||
|
||||
@@ -486,6 +486,7 @@ protected:
|
||||
void publishHaEntities() {
|
||||
// heating
|
||||
this->haHelper->publishSwitchHeatingTurbo(false);
|
||||
this->haHelper->publishSwitchHeatingHysteresis();
|
||||
this->haHelper->publishInputHeatingHysteresis(settings.system.unitSystem);
|
||||
this->haHelper->publishInputHeatingTurboFactor(false);
|
||||
this->haHelper->publishInputHeatingMinTemp(settings.system.unitSystem);
|
||||
|
||||
@@ -171,7 +171,7 @@ protected:
|
||||
vars.master.heating.enabled = this->isReady()
|
||||
&& settings.heating.enabled
|
||||
&& vars.cascadeControl.input
|
||||
&& !vars.master.heating.blocking
|
||||
&& (!vars.master.heating.blocking || settings.heating.hysteresis.action != HysteresisAction::DISABLE_HEATING)
|
||||
&& !vars.master.heating.overheat;
|
||||
|
||||
// DHW settings
|
||||
|
||||
@@ -59,12 +59,23 @@ protected:
|
||||
this->turbo();
|
||||
this->hysteresis();
|
||||
|
||||
vars.master.heating.targetTemp = settings.heating.target;
|
||||
vars.master.heating.setpointTemp = roundf(constrain(
|
||||
this->getHeatingSetpointTemp(),
|
||||
this->getHeatingMinSetpointTemp(),
|
||||
this->getHeatingMaxSetpointTemp()
|
||||
), 0);
|
||||
if (vars.master.heating.blocking && settings.heating.hysteresis.action == HysteresisAction::SET_ZERO_TARGET) {
|
||||
vars.master.heating.targetTemp = 0.0f;
|
||||
vars.master.heating.setpointTemp = 0.0f;
|
||||
|
||||
// tick if PID enabled
|
||||
if (settings.pid.enabled) {
|
||||
this->getHeatingSetpointTemp();
|
||||
}
|
||||
|
||||
} else {
|
||||
vars.master.heating.targetTemp = settings.heating.target;
|
||||
vars.master.heating.setpointTemp = roundf(constrain(
|
||||
this->getHeatingSetpointTemp(),
|
||||
this->getHeatingMinSetpointTemp(),
|
||||
this->getHeatingMaxSetpointTemp()
|
||||
), 0);
|
||||
}
|
||||
|
||||
Sensors::setValueByType(
|
||||
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.setpointTemp,
|
||||
@@ -92,15 +103,15 @@ protected:
|
||||
|
||||
void hysteresis() {
|
||||
bool useHyst = false;
|
||||
if (settings.heating.hysteresis > 0.01f && this->indoorSensorsConnected) {
|
||||
if (settings.heating.hysteresis.enabled && this->indoorSensorsConnected) {
|
||||
useHyst = settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.options.nativeHeatingControl;
|
||||
}
|
||||
|
||||
if (useHyst) {
|
||||
if (!vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target + 0.0001f >= settings.heating.hysteresis) {
|
||||
if (!vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target + 0.0001f >= settings.heating.hysteresis.value) {
|
||||
vars.master.heating.blocking = true;
|
||||
|
||||
} else if (vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target - 0.0001f <= -(settings.heating.hysteresis)) {
|
||||
} else if (vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target - 0.0001f <= -(settings.heating.hysteresis.value)) {
|
||||
vars.master.heating.blocking = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,12 +103,17 @@ struct Settings {
|
||||
bool enabled = true;
|
||||
bool turbo = false;
|
||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||
float hysteresis = 0.5f;
|
||||
float turboFactor = 7.5f;
|
||||
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
|
||||
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||
uint8_t maxModulation = 100;
|
||||
|
||||
struct {
|
||||
bool enabled = true;
|
||||
float value = 0.5f;
|
||||
HysteresisAction action = HysteresisAction::DISABLE_HEATING;
|
||||
} hysteresis;
|
||||
|
||||
struct {
|
||||
uint8_t highTemp = 95;
|
||||
uint8_t lowTemp = 90;
|
||||
|
||||
@@ -163,4 +163,9 @@ enum class UnitSystem : uint8_t {
|
||||
IMPERIAL = 1
|
||||
};
|
||||
|
||||
enum class HysteresisAction : uint8_t {
|
||||
DISABLE_HEATING = 0,
|
||||
SET_ZERO_TARGET = 1
|
||||
};
|
||||
|
||||
char buffer[255];
|
||||
@@ -34,6 +34,7 @@ const char L_CASCADE_OUTPUT[] PROGMEM = "CASCADE.OUTPUT";
|
||||
const char L_EXTPUMP[] PROGMEM = "EXTPUMP";
|
||||
|
||||
|
||||
const char S_ACTION[] PROGMEM = "action";
|
||||
const char S_ACTIONS[] PROGMEM = "actions";
|
||||
const char S_ACTIVE[] PROGMEM = "active";
|
||||
const char S_ADDRESS[] PROGMEM = "address";
|
||||
|
||||
38
src/utils.h
38
src/utils.h
@@ -490,7 +490,9 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
heating[FPSTR(S_ENABLED)] = src.heating.enabled;
|
||||
heating[FPSTR(S_TURBO)] = src.heating.turbo;
|
||||
heating[FPSTR(S_TARGET)] = roundf(src.heating.target, 2);
|
||||
heating[FPSTR(S_HYSTERESIS)] = roundf(src.heating.hysteresis, 3);
|
||||
heating[FPSTR(S_HYSTERESIS)][FPSTR(S_ENABLED)] = src.heating.hysteresis.enabled;
|
||||
heating[FPSTR(S_HYSTERESIS)][FPSTR(S_VALUE)] = roundf(src.heating.hysteresis.value, 3);
|
||||
heating[FPSTR(S_HYSTERESIS)][FPSTR(S_ACTION)] = static_cast<uint8_t>(src.heating.hysteresis.action);
|
||||
heating[FPSTR(S_TURBO_FACTOR)] = roundf(src.heating.turboFactor, 3);
|
||||
heating[FPSTR(S_MIN_TEMP)] = src.heating.minTemp;
|
||||
heating[FPSTR(S_MAX_TEMP)] = src.heating.maxTemp;
|
||||
@@ -1303,15 +1305,41 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)].isNull()) {
|
||||
float value = src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)].as<float>();
|
||||
if (src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_ENABLED)].is<bool>()) {
|
||||
bool value = src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_ENABLED)].as<bool>();
|
||||
|
||||
if (value >= 0.0f && value <= 15.0f && fabsf(value - dst.heating.hysteresis) > 0.0001f) {
|
||||
dst.heating.hysteresis = roundf(value, 2);
|
||||
if (value != dst.heating.hysteresis.enabled) {
|
||||
dst.heating.hysteresis.enabled = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_VALUE)].isNull()) {
|
||||
float value = src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_VALUE)].as<float>();
|
||||
|
||||
if (value >= 0.0f && value <= 15.0f && fabsf(value - dst.heating.hysteresis.value) > 0.0001f) {
|
||||
dst.heating.hysteresis.value = roundf(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_ACTION)].isNull()) {
|
||||
uint8_t value = src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_ACTION)].as<uint8_t>();
|
||||
|
||||
switch (value) {
|
||||
case static_cast<uint8_t>(HysteresisAction::DISABLE_HEATING):
|
||||
case static_cast<uint8_t>(HysteresisAction::SET_ZERO_TARGET):
|
||||
if (static_cast<uint8_t>(dst.heating.hysteresis.action) != value) {
|
||||
dst.heating.hysteresis.action = static_cast<HysteresisAction>(value);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_HEATING)][FPSTR(S_TURBO_FACTOR)].isNull()) {
|
||||
float value = src[FPSTR(S_HEATING)][FPSTR(S_TURBO_FACTOR)].as<float>();
|
||||
|
||||
|
||||
@@ -192,21 +192,48 @@
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.heating.hyst</span>
|
||||
<input type="number" inputmode="decimal" name="heating[hysteresis]" min="0" max="5" step="0.05" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.heating.turboFactor</span>
|
||||
<input type="number" inputmode="decimal" name="heating[turboFactor]" min="1.5" max="10" step="0.1" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.maxModulation</span>
|
||||
<input type="number" inputmode="numeric" name="heating[maxModulation]" min="1" max="100" step="1" required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.maxModulation</span>
|
||||
<input type="number" inputmode="numeric" name="heating[maxModulation]" min="1" max="100" step="1" required>
|
||||
</label>
|
||||
<hr />
|
||||
|
||||
<details>
|
||||
<summary><b data-i18n>settings.heating.hyst.title</b></summary>
|
||||
|
||||
<div>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox" name="heating[hysteresis][enabled]" value="true">
|
||||
<span data-i18n>settings.enable</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.heating.hyst.value</span>
|
||||
<input type="number" inputmode="decimal" name="heating[hysteresis][value]" min="0" max="5" step="0.05" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.heating.hyst.action.title</span>
|
||||
<select name="heating[hysteresis][action]">
|
||||
<option value="0" data-i18n>settings.heating.hyst.action.disableHeating</option>
|
||||
<option value="1" data-i18n>settings.heating.hyst.action.set0target</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<small data-i18n>settings.heating.hyst.desc</small>
|
||||
</details>
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -956,7 +983,9 @@
|
||||
"min": data.system.unitSystem == 0 ? 1 : 33,
|
||||
"max": data.system.unitSystem == 0 ? 100 : 212
|
||||
});
|
||||
setInputValue("[name='heating[hysteresis]']", data.heating.hysteresis);
|
||||
setCheckboxValue("[name='heating[hysteresis][enabled]']", data.heating.hysteresis.enabled);
|
||||
setInputValue("[name='heating[hysteresis][value]']", data.heating.hysteresis.value);
|
||||
setSelectValue("[name='heating[hysteresis][action]']", data.heating.hysteresis.action);
|
||||
setInputValue("[name='heating[turboFactor]']", data.heating.turboFactor);
|
||||
setInputValue("[name='heating[maxModulation]']", data.heating.maxModulation);
|
||||
setInputValue("[name='heating[overheatProtection][highTemp]']", data.heating.overheatProtection.highTemp, {
|
||||
|
||||
Reference in New Issue
Block a user