mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +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);
|
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) {
|
bool publishInputHeatingHysteresis(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
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_NAME)] = F("Heating hysteresis");
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
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_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_MIN)] = 0;
|
||||||
doc[FPSTR(HA_MAX)] = 15;
|
doc[FPSTR(HA_MAX)] = 15;
|
||||||
doc[FPSTR(HA_STEP)] = 0.01f;
|
doc[FPSTR(HA_STEP)] = 0.01f;
|
||||||
|
|||||||
@@ -486,6 +486,7 @@ protected:
|
|||||||
void publishHaEntities() {
|
void publishHaEntities() {
|
||||||
// heating
|
// heating
|
||||||
this->haHelper->publishSwitchHeatingTurbo(false);
|
this->haHelper->publishSwitchHeatingTurbo(false);
|
||||||
|
this->haHelper->publishSwitchHeatingHysteresis();
|
||||||
this->haHelper->publishInputHeatingHysteresis(settings.system.unitSystem);
|
this->haHelper->publishInputHeatingHysteresis(settings.system.unitSystem);
|
||||||
this->haHelper->publishInputHeatingTurboFactor(false);
|
this->haHelper->publishInputHeatingTurboFactor(false);
|
||||||
this->haHelper->publishInputHeatingMinTemp(settings.system.unitSystem);
|
this->haHelper->publishInputHeatingMinTemp(settings.system.unitSystem);
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ protected:
|
|||||||
vars.master.heating.enabled = this->isReady()
|
vars.master.heating.enabled = this->isReady()
|
||||||
&& settings.heating.enabled
|
&& settings.heating.enabled
|
||||||
&& vars.cascadeControl.input
|
&& vars.cascadeControl.input
|
||||||
&& !vars.master.heating.blocking
|
&& (!vars.master.heating.blocking || settings.heating.hysteresis.action != HysteresisAction::DISABLE_HEATING)
|
||||||
&& !vars.master.heating.overheat;
|
&& !vars.master.heating.overheat;
|
||||||
|
|
||||||
// DHW settings
|
// DHW settings
|
||||||
|
|||||||
@@ -59,12 +59,23 @@ protected:
|
|||||||
this->turbo();
|
this->turbo();
|
||||||
this->hysteresis();
|
this->hysteresis();
|
||||||
|
|
||||||
vars.master.heating.targetTemp = settings.heating.target;
|
if (vars.master.heating.blocking && settings.heating.hysteresis.action == HysteresisAction::SET_ZERO_TARGET) {
|
||||||
vars.master.heating.setpointTemp = roundf(constrain(
|
vars.master.heating.targetTemp = 0.0f;
|
||||||
this->getHeatingSetpointTemp(),
|
vars.master.heating.setpointTemp = 0.0f;
|
||||||
this->getHeatingMinSetpointTemp(),
|
|
||||||
this->getHeatingMaxSetpointTemp()
|
// tick if PID enabled
|
||||||
), 0);
|
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::setValueByType(
|
||||||
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.setpointTemp,
|
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.setpointTemp,
|
||||||
@@ -92,15 +103,15 @@ protected:
|
|||||||
|
|
||||||
void hysteresis() {
|
void hysteresis() {
|
||||||
bool useHyst = false;
|
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;
|
useHyst = settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.options.nativeHeatingControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useHyst) {
|
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;
|
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;
|
vars.master.heating.blocking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,12 +103,17 @@ struct Settings {
|
|||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
bool turbo = false;
|
bool turbo = false;
|
||||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||||
float hysteresis = 0.5f;
|
|
||||||
float turboFactor = 7.5f;
|
float turboFactor = 7.5f;
|
||||||
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
|
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
|
||||||
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||||
uint8_t maxModulation = 100;
|
uint8_t maxModulation = 100;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled = true;
|
||||||
|
float value = 0.5f;
|
||||||
|
HysteresisAction action = HysteresisAction::DISABLE_HEATING;
|
||||||
|
} hysteresis;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t highTemp = 95;
|
uint8_t highTemp = 95;
|
||||||
uint8_t lowTemp = 90;
|
uint8_t lowTemp = 90;
|
||||||
|
|||||||
@@ -163,4 +163,9 @@ enum class UnitSystem : uint8_t {
|
|||||||
IMPERIAL = 1
|
IMPERIAL = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class HysteresisAction : uint8_t {
|
||||||
|
DISABLE_HEATING = 0,
|
||||||
|
SET_ZERO_TARGET = 1
|
||||||
|
};
|
||||||
|
|
||||||
char buffer[255];
|
char buffer[255];
|
||||||
@@ -34,6 +34,7 @@ const char L_CASCADE_OUTPUT[] PROGMEM = "CASCADE.OUTPUT";
|
|||||||
const char L_EXTPUMP[] PROGMEM = "EXTPUMP";
|
const char L_EXTPUMP[] PROGMEM = "EXTPUMP";
|
||||||
|
|
||||||
|
|
||||||
|
const char S_ACTION[] PROGMEM = "action";
|
||||||
const char S_ACTIONS[] PROGMEM = "actions";
|
const char S_ACTIONS[] PROGMEM = "actions";
|
||||||
const char S_ACTIVE[] PROGMEM = "active";
|
const char S_ACTIVE[] PROGMEM = "active";
|
||||||
const char S_ADDRESS[] PROGMEM = "address";
|
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_ENABLED)] = src.heating.enabled;
|
||||||
heating[FPSTR(S_TURBO)] = src.heating.turbo;
|
heating[FPSTR(S_TURBO)] = src.heating.turbo;
|
||||||
heating[FPSTR(S_TARGET)] = roundf(src.heating.target, 2);
|
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_TURBO_FACTOR)] = roundf(src.heating.turboFactor, 3);
|
||||||
heating[FPSTR(S_MIN_TEMP)] = src.heating.minTemp;
|
heating[FPSTR(S_MIN_TEMP)] = src.heating.minTemp;
|
||||||
heating[FPSTR(S_MAX_TEMP)] = src.heating.maxTemp;
|
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()) {
|
if (src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)][FPSTR(S_ENABLED)].is<bool>()) {
|
||||||
float value = src[FPSTR(S_HEATING)][FPSTR(S_HYSTERESIS)].as<float>();
|
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) {
|
if (value != dst.heating.hysteresis.enabled) {
|
||||||
dst.heating.hysteresis = roundf(value, 2);
|
dst.heating.hysteresis.enabled = value;
|
||||||
changed = true;
|
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()) {
|
if (!src[FPSTR(S_HEATING)][FPSTR(S_TURBO_FACTOR)].isNull()) {
|
||||||
float value = src[FPSTR(S_HEATING)][FPSTR(S_TURBO_FACTOR)].as<float>();
|
float value = src[FPSTR(S_HEATING)][FPSTR(S_TURBO_FACTOR)].as<float>();
|
||||||
|
|
||||||
|
|||||||
@@ -192,21 +192,48 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid">
|
<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>
|
<label>
|
||||||
<span data-i18n>settings.heating.turboFactor</span>
|
<span data-i18n>settings.heating.turboFactor</span>
|
||||||
<input type="number" inputmode="decimal" name="heating[turboFactor]" min="1.5" max="10" step="0.1" required>
|
<input type="number" inputmode="decimal" name="heating[turboFactor]" min="1.5" max="10" step="0.1" required>
|
||||||
</label>
|
</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>
|
</div>
|
||||||
|
|
||||||
<label>
|
<hr />
|
||||||
<span data-i18n>settings.maxModulation</span>
|
|
||||||
<input type="number" inputmode="numeric" name="heating[maxModulation]" min="1" max="100" step="1" required>
|
<details>
|
||||||
</label>
|
<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 />
|
<hr />
|
||||||
|
|
||||||
@@ -956,7 +983,9 @@
|
|||||||
"min": data.system.unitSystem == 0 ? 1 : 33,
|
"min": data.system.unitSystem == 0 ? 1 : 33,
|
||||||
"max": data.system.unitSystem == 0 ? 100 : 212
|
"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[turboFactor]']", data.heating.turboFactor);
|
||||||
setInputValue("[name='heating[maxModulation]']", data.heating.maxModulation);
|
setInputValue("[name='heating[maxModulation]']", data.heating.maxModulation);
|
||||||
setInputValue("[name='heating[overheatProtection][highTemp]']", data.heating.overheatProtection.highTemp, {
|
setInputValue("[name='heating[overheatProtection][highTemp]']", data.heating.overheatProtection.highTemp, {
|
||||||
|
|||||||
Reference in New Issue
Block a user