refactor: reworked emergency mode; reworked hysteresis algorithm; improved detection of connection state for MANUAL & BOILER type sensors

This commit is contained in:
Yurii
2024-10-31 01:36:21 +03:00
parent 11b1277d79
commit a6e8953807
10 changed files with 245 additions and 451 deletions

View File

@@ -565,12 +565,12 @@ public:
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MIN)] = -99;
doc[FPSTR(HA_MAX)] = 99;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MIN)] = -146;
doc[FPSTR(HA_MAX)] = 211;
}

View File

@@ -105,6 +105,14 @@ protected:
tMqtt->disable();
}
if (settings.sensors.indoor.type == SensorType::MANUAL) {
vars.sensors.indoor.connected = !settings.mqtt.enable || vars.states.mqtt;
}
if (settings.sensors.outdoor.type == SensorType::MANUAL) {
vars.sensors.outdoor.connected = !settings.mqtt.enable || vars.states.mqtt;
}
} else {
if (this->telnetStarted) {
telnetStream->stop();
@@ -114,6 +122,14 @@ protected:
if (tMqtt->isEnabled()) {
tMqtt->disable();
}
if (settings.sensors.indoor.type == SensorType::MANUAL) {
vars.sensors.indoor.connected = false;
}
if (settings.sensors.outdoor.type == SensorType::MANUAL) {
vars.sensors.outdoor.connected = false;
}
}
this->yield();
@@ -189,42 +205,22 @@ protected:
}
void emergency() {
if (!settings.emergency.enable && vars.states.emergency) {
this->emergencyDetected = false;
vars.states.emergency = false;
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
}
if (!settings.emergency.enable) {
return;
}
// flags
uint8_t emergencyFlags = 0b00000000;
// set network flag
if (settings.emergency.onNetworkFault && !network->isConnected()) {
// set outdoor sensor flag
if (settings.equitherm.enable && !vars.sensors.outdoor.connected) {
emergencyFlags |= 0b00000001;
}
// set mqtt flag
if (settings.emergency.onMqttFault && (!tMqtt->isEnabled() || !tMqtt->isConnected())) {
// set indoor sensor flag
if (!settings.equitherm.enable && settings.pid.enable && !vars.sensors.indoor.connected) {
emergencyFlags |= 0b00000010;
}
// set outdoor sensor flag
if (settings.sensors.outdoor.type == SensorType::DS18B20 || settings.sensors.outdoor.type == SensorType::BLUETOOTH) {
if (settings.emergency.onOutdoorSensorDisconnect && !vars.sensors.outdoor.connected) {
emergencyFlags |= 0b00000100;
}
}
// set indoor sensor flag
if (settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) {
if (settings.emergency.onIndoorSensorDisconnect && !vars.sensors.indoor.connected) {
emergencyFlags |= 0b00001000;
}
// set indoor sensor flag for OT native heating control
if (settings.opentherm.nativeHeatingControl && !vars.sensors.indoor.connected) {
emergencyFlags |= 0b00000100;
}
// if any flags is true

View File

@@ -22,11 +22,11 @@ protected:
bool isInitialized = false;
unsigned long initializedTime = 0;
unsigned int initializedMemberIdCode = 0;
bool pump = true;
unsigned long lastSuccessResponse = 0;
unsigned long prevUpdateNonEssentialVars = 0;
unsigned long dhwSetTempTime = 0;
unsigned long heatingSetTempTime = 0;
bool heatingBlocking = false;
byte configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED;
#if defined(ARDUINO_ARCH_ESP32)
@@ -136,7 +136,10 @@ protected:
}
}
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && vars.cascadeControl.input && this->isReady();
bool heatingEnabled = (vars.states.emergency || settings.heating.enable)
&& vars.cascadeControl.input
&& this->isReady()
&& !this->heatingBlocking;
bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled;
if (settings.opentherm.heatingCh1ToCh2) {
heatingCh2Enabled = heatingEnabled;
@@ -182,6 +185,10 @@ protected:
} else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) {
Log.swarningln(FPSTR(L_OT), F("Disconnected"));
if (settings.sensors.outdoor.type == SensorType::BOILER) {
vars.sensors.outdoor.connected = false;
}
vars.states.otStatus = false;
this->isInitialized = false;
}
@@ -386,9 +393,17 @@ protected:
// Get outdoor temp (if necessary)
if (settings.sensors.outdoor.type == SensorType::BOILER) {
if (this->updateOutdoorTemp()) {
if (!vars.sensors.outdoor.connected) {
vars.sensors.outdoor.connected = true;
}
Log.snoticeln(FPSTR(L_OT), F("Received outdoor temp: %.2f"), vars.temperatures.outdoor);
} else {
if (vars.sensors.outdoor.connected) {
vars.sensors.outdoor.connected = false;
}
Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp"));
}
}
@@ -489,9 +504,17 @@ protected:
// Get outdoor temp (if necessary)
if (settings.sensors.outdoor.type == SensorType::BOILER) {
if (this->updateOutdoorTemp()) {
if (!vars.sensors.outdoor.connected) {
vars.sensors.outdoor.connected = true;
}
Log.snoticeln(FPSTR(L_OT), F("Received outdoor temp: %.2f"), vars.temperatures.outdoor);
} else {
if (vars.sensors.outdoor.connected) {
vars.sensors.outdoor.connected = false;
}
Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp"));
}
}
@@ -564,7 +587,7 @@ protected:
float indoorTemp = 0.0f;
float convertedTemp = 0.0f;
if (!vars.states.emergency || settings.sensors.indoor.type != SensorType::MANUAL) {
if (!vars.sensors.indoor.connected) {
indoorTemp = vars.temperatures.indoor;
convertedTemp = convertTemp(indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
}
@@ -595,11 +618,6 @@ protected:
}
}
// force enable pump
if (!this->pump) {
this->pump = true;
}
} else {
// Update heating temp
if (heatingEnabled && (this->needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001f)) {
@@ -634,18 +652,22 @@ protected:
// Hysteresis
// Only if enabled PID or/and Equitherm
if (settings.heating.hysteresis > 0 && (!vars.states.emergency || settings.emergency.usePid) && (settings.equitherm.enable || settings.pid.enable)) {
float halfHyst = settings.heating.hysteresis / 2;
if (this->pump && vars.temperatures.indoor - settings.heating.target + 0.0001f >= halfHyst) {
this->pump = false;
// Only if enabled PID or/and Equitherm or Native heating control via OT
bool useHyst = false;
if (settings.heating.hysteresis > 0.01f && vars.sensors.indoor.connected) {
useHyst = settings.equitherm.enable || settings.pid.enable || settings.opentherm.nativeHeatingControl;
}
} else if (!this->pump && vars.temperatures.indoor - settings.heating.target - 0.0001f <= -(halfHyst)) {
this->pump = true;
if (useHyst) {
if (!this->heatingBlocking && vars.temperatures.indoor - settings.heating.target + 0.0001f >= settings.heating.hysteresis) {
this->heatingBlocking = true;
} else if (this->heatingBlocking && vars.temperatures.indoor - settings.heating.target - 0.0001f <= -(settings.heating.hysteresis)) {
this->heatingBlocking = false;
}
} else if (!this->pump) {
this->pump = true;
} else if (this->heatingBlocking) {
this->heatingBlocking = false;
}
}
}

View File

@@ -29,27 +29,55 @@ protected:
#endif
void loop() {
float newTemp = vars.parameters.heatingSetpoint;
if (vars.states.emergency) {
if (settings.heating.turbo) {
settings.heating.turbo = false;
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
}
newTemp = this->getEmergencyModeTemp();
} else {
if (settings.heating.turbo && (fabs(settings.heating.target - vars.temperatures.indoor) < 1 || !settings.heating.enable || (settings.equitherm.enable && settings.pid.enable))) {
settings.heating.turbo = false;
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
}
newTemp = this->getNormalModeTemp();
bool pidIntergralResetted = false;
if (fabs(pidRegulator.Kp - settings.pid.p_factor) >= 0.0001f) {
pidRegulator.Kp = settings.pid.p_factor;
pidRegulator.integral = 0;
pidIntergralResetted = true;
}
if (fabs(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
pidRegulator.Ki = settings.pid.i_factor;
pidRegulator.integral = 0;
pidIntergralResetted = true;
}
if (fabs(pidRegulator.Kd - settings.pid.d_factor) >= 0.0001f) {
pidRegulator.Kd = settings.pid.d_factor;
pidRegulator.integral = 0;
pidIntergralResetted = true;
}
if (!settings.pid.enable && fabs(pidRegulator.integral) > 0.01f) {
pidRegulator.integral = 0.0f;
pidIntergralResetted = true;
}
if (pidIntergralResetted) {
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
if (settings.heating.turbo) {
if (!settings.heating.enable || vars.states.emergency) {
settings.heating.turbo = false;
} else if (settings.pid.enable) {
settings.heating.turbo = false; // not implemented
} else if (fabs(settings.heating.target - vars.temperatures.indoor) < 1.0f) {
settings.heating.turbo = false;
}
if (!settings.heating.turbo) {
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
}
}
float newTemp = vars.states.emergency
? settings.emergency.target
: this->getNormalModeTemp();
// Limits
newTemp = constrain(
newTemp,
@@ -57,58 +85,12 @@ protected:
!settings.opentherm.nativeHeatingControl ? settings.heating.maxTemp : THERMOSTAT_INDOOR_MAX_TEMP
);
if (fabs(vars.parameters.heatingSetpoint - newTemp) > 0.4999f) {
if (fabs(vars.parameters.heatingSetpoint - newTemp) > 0.09f) {
vars.parameters.heatingSetpoint = newTemp;
}
}
float getEmergencyModeTemp() {
float newTemp = 0;
// if use equitherm
if (settings.emergency.useEquitherm) {
float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp);
if (fabs(prevEtResult - etResult) > 0.4999f) {
prevEtResult = etResult;
newTemp += etResult;
Log.sinfoln(FPSTR(L_REGULATOR_EQUITHERM), F("New emergency result: %.2f"), etResult);
} else {
newTemp += prevEtResult;
}
} else if(settings.emergency.usePid) {
if (vars.parameters.heatingEnabled) {
float pidResult = getPidTemp(
settings.heating.minTemp,
settings.heating.maxTemp
);
if (fabs(prevPidResult - pidResult) > 0.4999f) {
prevPidResult = pidResult;
newTemp += pidResult;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("New emergency result: %.2f"), pidResult);
} else {
newTemp += prevPidResult;
}
} else if (!vars.parameters.heatingEnabled && prevPidResult != 0) {
newTemp += prevPidResult;
}
} else {
// default temp, manual mode
newTemp = settings.emergency.target;
}
return newTemp;
}
float getNormalModeTemp() {
float newTemp = 0;
@@ -116,17 +98,49 @@ protected:
prevHeatingTarget = settings.heating.target;
Log.sinfoln(FPSTR(L_REGULATOR), F("New target: %.2f"), settings.heating.target);
if (/*settings.equitherm.enable && */settings.pid.enable) {
/*if (settings.pid.enable) {
pidRegulator.integral = 0;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
}*/
}
// if use equitherm
if (settings.equitherm.enable) {
float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp);
unsigned short minTemp = settings.heating.minTemp;
unsigned short maxTemp = settings.heating.maxTemp;
float targetTemp = settings.heating.target;
float indoorTemp = vars.temperatures.indoor;
float outdoorTemp = vars.temperatures.outdoor;
if (fabs(prevEtResult - etResult) > 0.4999f) {
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
minTemp = f2c(minTemp);
maxTemp = f2c(maxTemp);
targetTemp = f2c(targetTemp);
indoorTemp = f2c(indoorTemp);
outdoorTemp = f2c(outdoorTemp);
}
if (!vars.sensors.indoor.connected || settings.pid.enable) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = 0;
} else {
etRegulator.Kt = settings.heating.turbo ? 10.0f : settings.equitherm.t_factor;
etRegulator.indoorTemp = indoorTemp;
}
etRegulator.setLimits(minTemp, maxTemp);
etRegulator.Kn = settings.equitherm.n_factor;
etRegulator.Kk = settings.equitherm.k_factor;
etRegulator.targetTemp = targetTemp;
etRegulator.outdoorTemp = outdoorTemp;
float etResult = etRegulator.getResult();
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
etResult = c2f(etResult);
}
if (fabs(prevEtResult - etResult) > 0.09f) {
prevEtResult = etResult;
newTemp += etResult;
@@ -140,13 +154,14 @@ protected:
// if use pid
if (settings.pid.enable) {
//if (vars.parameters.heatingEnabled) {
if (settings.heating.enable) {
float pidResult = getPidTemp(
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
settings.pid.maxTemp
);
if (settings.heating.enable && vars.sensors.indoor.connected) {
pidRegulator.setLimits(settings.pid.minTemp, settings.pid.maxTemp);
pidRegulator.setDt(settings.pid.dt * 1000u);
pidRegulator.input = vars.temperatures.indoor;
pidRegulator.setpoint = settings.heating.target;
if (fabs(prevPidResult - pidResult) > 0.4999f) {
float pidResult = pidRegulator.getResultTimer();
if (fabs(prevPidResult - pidResult) > 0.09f) {
prevPidResult = pidResult;
newTemp += pidResult;
@@ -156,13 +171,10 @@ protected:
} else {
newTemp += prevPidResult;
}
} else {
newTemp += prevPidResult;
}
} else if (fabs(pidRegulator.integral) > 0.0001f) {
pidRegulator.integral = 0;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
// default temp, manual mode
@@ -172,96 +184,4 @@ protected:
return newTemp;
}
/**
* @brief Get the Equitherm Temp
* Calculations in degrees C, conversion occurs when using F
*
* @param minTemp
* @param maxTemp
* @return float
*/
float getEquithermTemp(int minTemp, int maxTemp) {
float targetTemp = vars.states.emergency ? settings.emergency.target : settings.heating.target;
float indoorTemp = vars.temperatures.indoor;
float outdoorTemp = vars.temperatures.outdoor;
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
minTemp = f2c(minTemp);
maxTemp = f2c(maxTemp);
targetTemp = f2c(targetTemp);
indoorTemp = f2c(indoorTemp);
outdoorTemp = f2c(outdoorTemp);
}
if (vars.states.emergency) {
if (settings.sensors.indoor.type == SensorType::MANUAL) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = 0;
} else if ((settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) && !vars.sensors.indoor.connected) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = 0;
} else {
etRegulator.Kt = settings.equitherm.t_factor;
etRegulator.indoorTemp = indoorTemp;
}
etRegulator.outdoorTemp = outdoorTemp;
} else if (settings.pid.enable) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = round(indoorTemp);
etRegulator.outdoorTemp = round(outdoorTemp);
} else {
if (settings.heating.turbo) {
etRegulator.Kt = 10;
} else {
etRegulator.Kt = settings.equitherm.t_factor;
}
etRegulator.indoorTemp = indoorTemp;
etRegulator.outdoorTemp = outdoorTemp;
}
etRegulator.setLimits(minTemp, maxTemp);
etRegulator.Kn = settings.equitherm.n_factor;
etRegulator.Kk = settings.equitherm.k_factor;
etRegulator.targetTemp = targetTemp;
float result = etRegulator.getResult();
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
result = c2f(result);
}
return result;
}
float getPidTemp(int minTemp, int maxTemp) {
if (fabs(pidRegulator.Kp - settings.pid.p_factor) >= 0.0001f) {
pidRegulator.Kp = settings.pid.p_factor;
pidRegulator.integral = 0;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
if (fabs(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
pidRegulator.Ki = settings.pid.i_factor;
pidRegulator.integral = 0;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
if (fabs(pidRegulator.Kd - settings.pid.d_factor) >= 0.0001f) {
pidRegulator.Kd = settings.pid.d_factor;
pidRegulator.integral = 0;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
pidRegulator.setLimits(minTemp, maxTemp);
pidRegulator.setDt(settings.pid.dt * 1000u);
pidRegulator.input = vars.temperatures.indoor;
pidRegulator.setpoint = vars.states.emergency ? settings.emergency.target : settings.heating.target;
return pidRegulator.getResultTimer();
}
};

View File

@@ -86,15 +86,8 @@ struct Settings {
} mqtt;
struct {
bool enable = false;
float target = DEFAULT_HEATING_TARGET_TEMP;
unsigned short tresholdTime = 120;
bool useEquitherm = false;
bool usePid = false;
bool onNetworkFault = true;
bool onMqttFault = true;
bool onIndoorSensorDisconnect = false;
bool onOutdoorSensorDisconnect = false;
} emergency;
struct {
@@ -119,8 +112,8 @@ struct Settings {
float i_factor = 0.0055f;
float d_factor = 0;
unsigned short dt = 180;
byte minTemp = 0;
byte maxTemp = DEFAULT_HEATING_MAX_TEMP;
short minTemp = 0;
short maxTemp = DEFAULT_HEATING_MAX_TEMP;
} pid;
struct {

View File

@@ -386,15 +386,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["mqtt"]["interval"] = src.mqtt.interval;
dst["mqtt"]["homeAssistantDiscovery"] = src.mqtt.homeAssistantDiscovery;
dst["emergency"]["enable"] = src.emergency.enable;
dst["emergency"]["target"] = roundd(src.emergency.target, 2);
dst["emergency"]["tresholdTime"] = src.emergency.tresholdTime;
dst["emergency"]["useEquitherm"] = src.emergency.useEquitherm;
dst["emergency"]["usePid"] = src.emergency.usePid;
dst["emergency"]["onNetworkFault"] = src.emergency.onNetworkFault;
dst["emergency"]["onMqttFault"] = src.emergency.onMqttFault;
dst["emergency"]["onIndoorSensorDisconnect"] = src.emergency.onIndoorSensorDisconnect;
dst["emergency"]["onOutdoorSensorDisconnect"] = src.emergency.onOutdoorSensorDisconnect;
}
dst["heating"]["enable"] = src.heating.enable;
@@ -409,6 +402,11 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["dhw"]["minTemp"] = src.dhw.minTemp;
dst["dhw"]["maxTemp"] = src.dhw.maxTemp;
dst["equitherm"]["enable"] = src.equitherm.enable;
dst["equitherm"]["n_factor"] = roundd(src.equitherm.n_factor, 3);
dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3);
dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3);
dst["pid"]["enable"] = src.pid.enable;
dst["pid"]["p_factor"] = roundd(src.pid.p_factor, 3);
dst["pid"]["i_factor"] = roundd(src.pid.i_factor, 4);
@@ -417,11 +415,6 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["pid"]["minTemp"] = src.pid.minTemp;
dst["pid"]["maxTemp"] = src.pid.maxTemp;
dst["equitherm"]["enable"] = src.equitherm.enable;
dst["equitherm"]["n_factor"] = roundd(src.equitherm.n_factor, 3);
dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3);
dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3);
dst["sensors"]["outdoor"]["type"] = static_cast<byte>(src.sensors.outdoor.type);
dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio;
@@ -861,8 +854,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
dst.opentherm.nativeHeatingControl = value;
if (value) {
dst.emergency.useEquitherm = false;
dst.emergency.usePid = false;
dst.equitherm.enable = false;
dst.pid.enable = false;
}
@@ -956,15 +947,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// emergency
if (src["emergency"]["enable"].is<bool>()) {
bool value = src["emergency"]["enable"].as<bool>();
if (value != dst.emergency.enable) {
dst.emergency.enable = value;
changed = true;
}
}
if (!src["emergency"]["tresholdTime"].isNull()) {
unsigned short value = src["emergency"]["tresholdTime"].as<unsigned short>();
@@ -973,83 +955,49 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true;
}
}
}
if (src["emergency"]["useEquitherm"].is<bool>()) {
bool value = src["emergency"]["useEquitherm"].as<bool>();
if (!dst.opentherm.nativeHeatingControl && dst.sensors.outdoor.type != SensorType::MANUAL && dst.sensors.outdoor.type != SensorType::BLUETOOTH) {
if (value != dst.emergency.useEquitherm) {
dst.emergency.useEquitherm = value;
changed = true;
}
// equitherm
if (src["equitherm"]["enable"].is<bool>()) {
bool value = src["equitherm"]["enable"].as<bool>();
} else if (dst.emergency.useEquitherm) {
dst.emergency.useEquitherm = false;
changed = true;
}
if (dst.emergency.useEquitherm && dst.emergency.usePid) {
dst.emergency.usePid = false;
if (!dst.opentherm.nativeHeatingControl) {
if (value != dst.equitherm.enable) {
dst.equitherm.enable = value;
changed = true;
}
} else if (dst.equitherm.enable) {
dst.equitherm.enable = false;
changed = true;
}
}
if (src["emergency"]["usePid"].is<bool>()) {
bool value = src["emergency"]["usePid"].as<bool>();
if (!src["equitherm"]["n_factor"].isNull()) {
float value = src["equitherm"]["n_factor"].as<float>();
if (!dst.opentherm.nativeHeatingControl && dst.sensors.indoor.type != SensorType::MANUAL && dst.sensors.indoor.type != SensorType::BLUETOOTH) {
if (value != dst.emergency.usePid) {
dst.emergency.usePid = value;
changed = true;
}
} else if (dst.emergency.usePid) {
dst.emergency.usePid = false;
changed = true;
}
if (dst.emergency.usePid && dst.emergency.useEquitherm) {
dst.emergency.useEquitherm = false;
changed = true;
}
if (value > 0 && value <= 10 && fabs(value - dst.equitherm.n_factor) > 0.0001f) {
dst.equitherm.n_factor = roundd(value, 3);
changed = true;
}
}
if (src["emergency"]["onNetworkFault"].is<bool>()) {
bool value = src["emergency"]["onNetworkFault"].as<bool>();
if (!src["equitherm"]["k_factor"].isNull()) {
float value = src["equitherm"]["k_factor"].as<float>();
if (value != dst.emergency.onNetworkFault) {
dst.emergency.onNetworkFault = value;
changed = true;
}
if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.k_factor) > 0.0001f) {
dst.equitherm.k_factor = roundd(value, 3);
changed = true;
}
}
if (src["emergency"]["onMqttFault"].is<bool>()) {
bool value = src["emergency"]["onMqttFault"].as<bool>();
if (!src["equitherm"]["t_factor"].isNull()) {
float value = src["equitherm"]["t_factor"].as<float>();
if (value != dst.emergency.onMqttFault) {
dst.emergency.onMqttFault = value;
changed = true;
}
}
if (src["emergency"]["onIndoorSensorDisconnect"].is<bool>()) {
bool value = src["emergency"]["onIndoorSensorDisconnect"].as<bool>();
if (value != dst.emergency.onIndoorSensorDisconnect) {
dst.emergency.onIndoorSensorDisconnect = value;
dst.emergency.usePid = false;
changed = true;
}
}
if (src["emergency"]["onOutdoorSensorDisconnect"].is<bool>()) {
bool value = src["emergency"]["onOutdoorSensorDisconnect"].as<bool>();
if (value != dst.emergency.onOutdoorSensorDisconnect) {
dst.emergency.onOutdoorSensorDisconnect = value;
dst.emergency.useEquitherm = false;
changed = true;
}
if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.t_factor) > 0.0001f) {
dst.equitherm.t_factor = roundd(value, 3);
changed = true;
}
}
@@ -1106,66 +1054,27 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (!src["pid"]["maxTemp"].isNull()) {
unsigned char value = src["pid"]["maxTemp"].as<unsigned char>();
if (isValidTemp(value, dst.system.unitSystem) && value > dst.pid.minTemp && value != dst.pid.maxTemp) {
dst.pid.maxTemp = value;
changed = true;
}
}
if (!src["pid"]["minTemp"].isNull()) {
unsigned char value = src["pid"]["minTemp"].as<unsigned char>();
short value = src["pid"]["minTemp"].as<short>();
if (isValidTemp(value, dst.system.unitSystem) && value < dst.pid.maxTemp && value != dst.pid.minTemp) {
if (isValidTemp(value, dst.system.unitSystem, dst.equitherm.enable ? -99.9f : 0.0f) && value != dst.pid.minTemp) {
dst.pid.minTemp = value;
changed = true;
}
}
if (!src["pid"]["maxTemp"].isNull()) {
short value = src["pid"]["maxTemp"].as<short>();
// equitherm
if (src["equitherm"]["enable"].is<bool>()) {
bool value = src["equitherm"]["enable"].as<bool>();
if (!dst.opentherm.nativeHeatingControl) {
if (value != dst.equitherm.enable) {
dst.equitherm.enable = value;
changed = true;
}
} else if (dst.equitherm.enable) {
dst.equitherm.enable = false;
if (isValidTemp(value, dst.system.unitSystem) && value != dst.pid.maxTemp) {
dst.pid.maxTemp = value;
changed = true;
}
}
if (!src["equitherm"]["n_factor"].isNull()) {
float value = src["equitherm"]["n_factor"].as<float>();
if (value > 0 && value <= 10 && fabs(value - dst.equitherm.n_factor) > 0.0001f) {
dst.equitherm.n_factor = roundd(value, 3);
changed = true;
}
}
if (!src["equitherm"]["k_factor"].isNull()) {
float value = src["equitherm"]["k_factor"].as<float>();
if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.k_factor) > 0.0001f) {
dst.equitherm.k_factor = roundd(value, 3);
changed = true;
}
}
if (!src["equitherm"]["t_factor"].isNull()) {
float value = src["equitherm"]["t_factor"].as<float>();
if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.t_factor) > 0.0001f) {
dst.equitherm.t_factor = roundd(value, 3);
changed = true;
}
if (dst.pid.maxTemp < dst.pid.minTemp) {
dst.pid.maxTemp = dst.pid.minTemp;
changed = true;
}
@@ -1215,6 +1124,11 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (dst.heating.maxTemp < dst.heating.minTemp) {
dst.heating.maxTemp = dst.heating.minTemp;
changed = true;
}
// dhw
if (src["dhw"]["enable"].is<bool>()) {
@@ -1229,7 +1143,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["dhw"]["minTemp"].isNull()) {
unsigned char value = src["dhw"]["minTemp"].as<unsigned char>();
if (value >= vars.parameters.dhwMinTemp && value < vars.parameters.dhwMaxTemp && value != dst.dhw.minTemp) {
if (value >= vars.parameters.dhwMinTemp && value != dst.dhw.minTemp) {
dst.dhw.minTemp = value;
changed = true;
}
@@ -1238,12 +1152,16 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["dhw"]["maxTemp"].isNull()) {
unsigned char value = src["dhw"]["maxTemp"].as<unsigned char>();
if (value > vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp && value != dst.dhw.maxTemp) {
if (value > vars.parameters.dhwMinTemp && value != dst.dhw.maxTemp) {
dst.dhw.maxTemp = value;
changed = true;
}
}
if (dst.dhw.maxTemp < dst.dhw.minTemp) {
dst.dhw.maxTemp = dst.dhw.minTemp;
changed = true;
}
// sensors
if (!src["sensors"]["outdoor"]["type"].isNull()) {
@@ -1260,7 +1178,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
case static_cast<byte>(SensorType::MANUAL):
if (dst.sensors.outdoor.type != SensorType::MANUAL) {
dst.sensors.outdoor.type = SensorType::MANUAL;
dst.emergency.useEquitherm = false;
changed = true;
}
break;
@@ -1276,7 +1193,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
case static_cast<byte>(SensorType::BLUETOOTH):
if (dst.sensors.outdoor.type != SensorType::BLUETOOTH) {
dst.sensors.outdoor.type = SensorType::BLUETOOTH;
dst.emergency.useEquitherm = false;
changed = true;
}
break;
@@ -1335,8 +1251,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
case static_cast<byte>(SensorType::MANUAL):
if (dst.sensors.indoor.type != SensorType::MANUAL) {
dst.sensors.indoor.type = SensorType::MANUAL;
dst.emergency.usePid = false;
dst.opentherm.nativeHeatingControl = false;
changed = true;
}
break;
@@ -1352,7 +1266,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
case static_cast<byte>(SensorType::BLUETOOTH):
if (dst.sensors.indoor.type != SensorType::BLUETOOTH) {
dst.sensors.indoor.type = SensorType::BLUETOOTH;
dst.emergency.usePid = false;
changed = true;
}
break;
@@ -1597,7 +1510,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// force check emergency target
{
float value = !src["emergency"]["target"].isNull() ? src["emergency"]["target"].as<float>() : dst.emergency.target;
bool noRegulators = !dst.opentherm.nativeHeatingControl && !dst.emergency.useEquitherm && !dst.emergency.usePid;
bool noRegulators = !dst.opentherm.nativeHeatingControl;
bool valid = isValidTemp(
value,
dst.system.unitSystem,